source: mainline/uspace/lib/fdisk/src/fdisk.c@ e11c527

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e11c527 was f63a0073, checked in by Jiri Svoboda <jiri@…>, 10 years ago

Fix dummy partition handling when label creation fails.

  • Property mode set to 100644
File size: 24.7 KB
Line 
1/*
2 * Copyright (c) 2015 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libfdisk
30 * @{
31 */
32/**
33 * @file Disk management library.
34 */
35
36#include <adt/list.h>
37#include <block.h>
38#include <errno.h>
39#include <fdisk.h>
40#include <loc.h>
41#include <mem.h>
42#include <stdlib.h>
43#include <str.h>
44#include <vbd.h>
45#include <vol.h>
46
47static int fdisk_dev_add_parts(fdisk_dev_t *);
48static void fdisk_dev_remove_parts(fdisk_dev_t *);
49static int fdisk_part_spec_prepare(fdisk_dev_t *, fdisk_part_spec_t *,
50 vbd_part_spec_t *);
51static void fdisk_pri_part_insert_lists(fdisk_dev_t *, fdisk_part_t *);
52static void fdisk_log_part_insert_lists(fdisk_dev_t *, fdisk_part_t *);
53static int fdisk_update_dev_info(fdisk_dev_t *);
54static uint64_t fdisk_ba_align_up(fdisk_dev_t *, uint64_t);
55static uint64_t fdisk_ba_align_down(fdisk_dev_t *, uint64_t);
56static int fdisk_part_get_max_free_range(fdisk_dev_t *, fdisk_spc_t, aoff64_t *,
57 aoff64_t *);
58static void fdisk_free_range_first(fdisk_dev_t *, fdisk_spc_t, fdisk_free_range_t *);
59static bool fdisk_free_range_next(fdisk_free_range_t *);
60static bool fdisk_free_range_get(fdisk_free_range_t *, aoff64_t *, aoff64_t *);
61
62static void fdisk_dev_info_delete(fdisk_dev_info_t *info)
63{
64 if (info == NULL)
65 return;
66
67 if (info->blk_inited)
68 block_fini(info->svcid);
69
70 free(info->svcname);
71 free(info);
72}
73
74int fdisk_create(fdisk_t **rfdisk)
75{
76 fdisk_t *fdisk = NULL;
77 int rc;
78
79 fdisk = calloc(1, sizeof(fdisk_t));
80 if (fdisk == NULL) {
81 rc = ENOMEM;
82 goto error;
83 }
84
85 rc = vol_create(&fdisk->vol);
86 if (rc != EOK) {
87 rc = EIO;
88 goto error;
89 }
90
91 rc = vbd_create(&fdisk->vbd);
92 if (rc != EOK) {
93 rc = EIO;
94 goto error;
95 }
96
97 *rfdisk = fdisk;
98 return EOK;
99error:
100 fdisk_destroy(fdisk);
101
102 return rc;
103}
104
105void fdisk_destroy(fdisk_t *fdisk)
106{
107 if (fdisk == NULL)
108 return;
109
110 vol_destroy(fdisk->vol);
111 vbd_destroy(fdisk->vbd);
112 free(fdisk);
113}
114
115int fdisk_dev_list_get(fdisk_t *fdisk, fdisk_dev_list_t **rdevlist)
116{
117 fdisk_dev_list_t *devlist = NULL;
118 fdisk_dev_info_t *info;
119 service_id_t *svcs = NULL;
120 size_t count, i;
121 int rc;
122
123 devlist = calloc(1, sizeof(fdisk_dev_list_t));
124 if (devlist == NULL)
125 return ENOMEM;
126
127 list_initialize(&devlist->devinfos);
128
129 rc = vbd_get_disks(fdisk->vbd, &svcs, &count);
130 if (rc != EOK) {
131 rc = EIO;
132 goto error;
133 }
134
135 for (i = 0; i < count; i++) {
136 info = calloc(1, sizeof(fdisk_dev_info_t));
137 if (info == NULL) {
138 rc = ENOMEM;
139 goto error;
140 }
141
142 info->svcid = svcs[i];
143 info->devlist = devlist;
144 list_append(&info->ldevlist, &devlist->devinfos);
145 }
146
147 *rdevlist = devlist;
148 free(svcs);
149 return EOK;
150error:
151 free(svcs);
152 fdisk_dev_list_free(devlist);
153 return rc;
154}
155
156void fdisk_dev_list_free(fdisk_dev_list_t *devlist)
157{
158 fdisk_dev_info_t *info;
159
160 if (devlist == NULL)
161 return;
162
163 while (!list_empty(&devlist->devinfos)) {
164 info = list_get_instance(list_first(
165 &devlist->devinfos), fdisk_dev_info_t,
166 ldevlist);
167
168 list_remove(&info->ldevlist);
169 fdisk_dev_info_delete(info);
170 }
171
172 free(devlist);
173}
174
175fdisk_dev_info_t *fdisk_dev_first(fdisk_dev_list_t *devlist)
176{
177 if (list_empty(&devlist->devinfos))
178 return NULL;
179
180 return list_get_instance(list_first(&devlist->devinfos),
181 fdisk_dev_info_t, ldevlist);
182}
183
184fdisk_dev_info_t *fdisk_dev_next(fdisk_dev_info_t *devinfo)
185{
186 link_t *lnext;
187
188 lnext = list_next(&devinfo->ldevlist,
189 &devinfo->devlist->devinfos);
190 if (lnext == NULL)
191 return NULL;
192
193 return list_get_instance(lnext, fdisk_dev_info_t,
194 ldevlist);
195}
196
197void fdisk_dev_info_get_svcid(fdisk_dev_info_t *info, service_id_t *rsid)
198{
199 *rsid = info->svcid;
200}
201
202int fdisk_dev_info_get_svcname(fdisk_dev_info_t *info, char **rname)
203{
204 char *name;
205 int rc;
206
207 if (info->svcname == NULL) {
208 rc = loc_service_get_name(info->svcid,
209 &info->svcname);
210 if (rc != EOK)
211 return rc;
212 }
213
214 name = str_dup(info->svcname);
215 if (name == NULL)
216 return ENOMEM;
217
218 *rname = name;
219 return EOK;
220}
221
222int fdisk_dev_info_capacity(fdisk_dev_info_t *info, fdisk_cap_t *cap)
223{
224 size_t bsize;
225 aoff64_t nblocks;
226 int rc;
227
228 if (!info->blk_inited) {
229 rc = block_init(info->svcid, 2048);
230 if (rc != EOK)
231 return rc;
232
233 info->blk_inited = true;
234 }
235
236 rc = block_get_bsize(info->svcid, &bsize);
237 if (rc != EOK)
238 return EIO;
239
240 rc = block_get_nblocks(info->svcid, &nblocks);
241 if (rc != EOK)
242 return EIO;
243
244 fdisk_cap_from_blocks(nblocks, bsize, cap);
245 return EOK;
246}
247
248/** Add partition to our inventory. */
249static int fdisk_part_add(fdisk_dev_t *dev, vbd_part_id_t partid,
250 fdisk_part_t **rpart)
251{
252 fdisk_part_t *part;
253 vbd_part_info_t pinfo;
254 vol_part_info_t vpinfo;
255 int rc;
256
257 part = calloc(1, sizeof(fdisk_part_t));
258 if (part == NULL)
259 return ENOMEM;
260
261 rc = vbd_part_get_info(dev->fdisk->vbd, partid, &pinfo);
262 if (rc != EOK) {
263 rc = EIO;
264 goto error;
265 }
266
267 if (pinfo.svc_id != 0) {
268 /*
269 * Normally vol service discovers the partition asynchronously.
270 * Here we need to make sure the partition is already known to it.
271 */
272 rc = vol_part_add(dev->fdisk->vol, pinfo.svc_id);
273 if (rc != EOK && rc != EEXIST) {
274 rc = EIO;
275 goto error;
276 }
277
278 rc = vol_part_info(dev->fdisk->vol, pinfo.svc_id, &vpinfo);
279 if (rc != EOK) {
280 rc = EIO;
281 goto error;
282 }
283
284 part->pcnt = vpinfo.pcnt;
285 part->fstype = vpinfo.fstype;
286 }
287
288 part->dev = dev;
289 part->index = pinfo.index;
290 part->block0 = pinfo.block0;
291 part->nblocks = pinfo.nblocks;
292 part->pkind = pinfo.pkind;
293 part->svc_id = pinfo.svc_id;
294
295 switch (part->pkind) {
296 case lpk_primary:
297 case lpk_extended:
298 fdisk_pri_part_insert_lists(dev, part);
299 break;
300 case lpk_logical:
301 fdisk_log_part_insert_lists(dev, part);
302 break;
303 }
304
305 list_append(&part->lparts, &dev->parts);
306
307 if (part->pkind == lpk_extended)
308 dev->ext_part = part;
309
310 fdisk_cap_from_blocks(part->nblocks, dev->dinfo.block_size,
311 &part->capacity);
312 part->part_id = partid;
313
314 if (rpart != NULL)
315 *rpart = part;
316 return EOK;
317error:
318 free(part);
319 return rc;
320}
321
322/** Remove partition from our inventory. */
323static void fdisk_part_remove(fdisk_part_t *part)
324{
325 list_remove(&part->lparts);
326 if (link_used(&part->lpri_ba))
327 list_remove(&part->lpri_ba);
328 if (link_used(&part->lpri_idx))
329 list_remove(&part->lpri_idx);
330 if (link_used(&part->llog_ba))
331 list_remove(&part->llog_ba);
332 free(part);
333}
334
335static void fdisk_pri_part_insert_lists(fdisk_dev_t *dev, fdisk_part_t *part)
336{
337 link_t *link;
338 fdisk_part_t *p;
339
340 /* Insert to list by block address */
341 link = list_first(&dev->pri_ba);
342 while (link != NULL) {
343 p = list_get_instance(link, fdisk_part_t, lpri_ba);
344 if (p->block0 > part->block0) {
345 list_insert_before(&part->lpri_ba, &p->lpri_ba);
346 break;
347 }
348
349 link = list_next(link, &dev->pri_ba);
350 }
351
352 if (link == NULL)
353 list_append(&part->lpri_ba, &dev->pri_ba);
354
355 /* Insert to list by index */
356 link = list_first(&dev->pri_idx);
357 while (link != NULL) {
358 p = list_get_instance(link, fdisk_part_t, lpri_idx);
359 if (p->index > part->index) {
360 list_insert_before(&part->lpri_idx, &p->lpri_idx);
361 break;
362 }
363
364 link = list_next(link, &dev->pri_idx);
365 }
366
367 if (link == NULL)
368 list_append(&part->lpri_idx, &dev->pri_idx);
369}
370
371static void fdisk_log_part_insert_lists(fdisk_dev_t *dev, fdisk_part_t *part)
372{
373 link_t *link;
374 fdisk_part_t *p;
375
376 /* Insert to list by block address */
377 link = list_first(&dev->log_ba);
378 while (link != NULL) {
379 p = list_get_instance(link, fdisk_part_t, llog_ba);
380 if (p->block0 > part->block0) {
381 list_insert_before(&part->llog_ba, &p->llog_ba);
382 break;
383 }
384
385 link = list_next(link, &dev->log_ba);
386 }
387
388 if (link == NULL)
389 list_append(&part->llog_ba, &dev->log_ba);
390}
391
392static int fdisk_dev_add_parts(fdisk_dev_t *dev)
393{
394 service_id_t *psids = NULL;
395 size_t nparts, i;
396 int rc;
397
398 rc = fdisk_update_dev_info(dev);
399 if (rc != EOK) {
400 rc = EIO;
401 goto error;
402 }
403
404 rc = vbd_label_get_parts(dev->fdisk->vbd, dev->sid, &psids, &nparts);
405 if (rc != EOK) {
406 rc = EIO;
407 goto error;
408 }
409
410 for (i = 0; i < nparts; i++) {
411 rc = fdisk_part_add(dev, psids[i], NULL);
412 if (rc != EOK) {
413 goto error;
414 }
415 }
416
417 free(psids);
418 return EOK;
419error:
420 fdisk_dev_remove_parts(dev);
421 return rc;
422}
423
424static void fdisk_dev_remove_parts(fdisk_dev_t *dev)
425{
426 fdisk_part_t *part;
427
428 part = fdisk_part_first(dev);
429 while (part != NULL) {
430 fdisk_part_remove(part);
431 part = fdisk_part_first(dev);
432 }
433}
434
435int fdisk_dev_open(fdisk_t *fdisk, service_id_t sid, fdisk_dev_t **rdev)
436{
437 vbd_disk_info_t vinfo;
438 fdisk_dev_t *dev = NULL;
439 service_id_t *psids = NULL;
440 size_t nparts, i;
441 int rc;
442
443 dev = calloc(1, sizeof(fdisk_dev_t));
444 if (dev == NULL)
445 return ENOMEM;
446
447 dev->fdisk = fdisk;
448 dev->sid = sid;
449 list_initialize(&dev->parts);
450 list_initialize(&dev->pri_idx);
451 list_initialize(&dev->pri_ba);
452 list_initialize(&dev->log_ba);
453
454 rc = vbd_disk_info(fdisk->vbd, sid, &vinfo);
455 if (rc != EOK) {
456 rc = EIO;
457 goto error;
458 }
459
460 rc = fdisk_update_dev_info(dev);
461 if (rc != EOK) {
462 rc = EIO;
463 goto error;
464 }
465
466 rc = vbd_label_get_parts(fdisk->vbd, sid, &psids, &nparts);
467 if (rc != EOK) {
468 rc = EIO;
469 goto error;
470 }
471
472 for (i = 0; i < nparts; i++) {
473 rc = fdisk_part_add(dev, psids[i], NULL);
474 if (rc != EOK) {
475 goto error;
476 }
477 }
478
479 free(psids);
480 *rdev = dev;
481 return EOK;
482error:
483 fdisk_dev_close(dev);
484 return rc;
485}
486
487void fdisk_dev_close(fdisk_dev_t *dev)
488{
489 if (dev == NULL)
490 return;
491
492 fdisk_dev_remove_parts(dev);
493 free(dev);
494}
495
496/** Erase contents of unlabeled disk. */
497int fdisk_dev_erase(fdisk_dev_t *dev)
498{
499 fdisk_part_t *part;
500 int rc;
501
502 if (dev->dinfo.ltype != lt_none)
503 return EINVAL;
504
505 part = fdisk_part_first(dev);
506 assert(part != NULL);
507 if (part->pcnt == vpc_empty)
508 return EINVAL;
509
510 rc = vol_part_empty(dev->fdisk->vol, part->svc_id);
511 if (rc != EOK) {
512 return rc;
513 }
514
515 part->pcnt = vpc_empty;
516 return EOK;
517}
518
519void fdisk_dev_get_flags(fdisk_dev_t *dev, fdisk_dev_flags_t *rflags)
520{
521 fdisk_dev_flags_t flags;
522 fdisk_part_t *part;
523
524 flags = 0;
525
526 /* fdf_can_create_label */
527 if (dev->dinfo.ltype == lt_none) {
528 part = fdisk_part_first(dev);
529 assert(part != NULL);
530 if (part->pcnt == vpc_empty)
531 flags |= fdf_can_create_label;
532 else
533 flags |= fdf_can_erase_dev;
534 } else {
535 flags |= fdf_can_delete_label;
536 }
537
538 *rflags = flags;
539}
540
541int fdisk_dev_get_svcname(fdisk_dev_t *dev, char **rname)
542{
543 char *name;
544 int rc;
545
546 rc = loc_service_get_name(dev->sid, &name);
547 if (rc != EOK)
548 return rc;
549
550 *rname = name;
551 return EOK;
552}
553
554int fdisk_dev_capacity(fdisk_dev_t *dev, fdisk_cap_t *cap)
555{
556 size_t bsize;
557 aoff64_t nblocks;
558 int rc;
559
560 rc = block_init(dev->sid, 2048);
561 if (rc != EOK)
562 return rc;
563
564 rc = block_get_bsize(dev->sid, &bsize);
565 if (rc != EOK)
566 return EIO;
567
568 rc = block_get_nblocks(dev->sid, &nblocks);
569 if (rc != EOK)
570 return EIO;
571
572 block_fini(dev->sid);
573
574 fdisk_cap_from_blocks(nblocks, bsize, cap);
575 return EOK;
576}
577
578int fdisk_label_get_info(fdisk_dev_t *dev, fdisk_label_info_t *info)
579{
580 vbd_disk_info_t vinfo;
581 uint64_t b0, nb;
582 uint64_t hdrb;
583 int rc;
584
585 rc = vbd_disk_info(dev->fdisk->vbd, dev->sid, &vinfo);
586 if (rc != EOK) {
587 rc = EIO;
588 goto error;
589 }
590
591 info->ltype = vinfo.ltype;
592 info->flags = vinfo.flags;
593
594 if ((info->flags & lf_can_create_pri) != 0 ||
595 (info->flags & lf_can_create_ext) != 0) {
596 /* Verify there is enough space to create partition */
597
598 rc = fdisk_part_get_max_free_range(dev, spc_pri, &b0, &nb);
599 if (rc != EOK)
600 info->flags &= ~(lf_can_create_pri | lf_can_create_ext);
601 }
602
603 if ((info->flags & lf_can_create_log) != 0) {
604 /* Verify there is enough space to create logical partition */
605 hdrb = max(1, dev->align);
606 rc = fdisk_part_get_max_free_range(dev, spc_log, &b0, &nb);
607 if (rc != EOK || nb <= hdrb)
608 info->flags &= ~lf_can_create_log;
609 }
610
611 return EOK;
612error:
613 return rc;
614}
615
616int fdisk_label_create(fdisk_dev_t *dev, label_type_t ltype)
617{
618 fdisk_part_t *part;
619 int rc;
620
621 /* Disk must not contain a label. */
622 if (dev->dinfo.ltype != lt_none)
623 return EEXIST;
624
625 /* Dummy partition spanning entire disk must be considered empty */
626 part = fdisk_part_first(dev);
627 assert(part != NULL);
628 if (part->pcnt != vpc_empty)
629 return EEXIST;
630
631 /* Remove dummy partition */
632 fdisk_dev_remove_parts(dev);
633
634 rc = vbd_label_create(dev->fdisk->vbd, dev->sid, ltype);
635 if (rc != EOK) {
636 /* Re-add dummy partition */
637 (void) fdisk_dev_add_parts(dev);
638 return rc;
639 }
640
641 rc = fdisk_update_dev_info(dev);
642 if (rc != EOK)
643 return rc;
644
645 return EOK;
646}
647
648int fdisk_label_destroy(fdisk_dev_t *dev)
649{
650 fdisk_part_t *part;
651 fdisk_dev_flags_t dflags;
652 int rc;
653
654 part = fdisk_part_first(dev);
655 while (part != NULL) {
656 rc = fdisk_part_destroy(part);
657 if (rc != EOK)
658 return EIO;
659 part = fdisk_part_first(dev);
660 }
661
662 rc = vbd_label_delete(dev->fdisk->vbd, dev->sid);
663 if (rc != EOK)
664 return EIO;
665
666 rc = fdisk_dev_add_parts(dev);
667 if (rc != EOK)
668 return rc;
669
670 /* Make sure device is considered empty */
671 fdisk_dev_get_flags(dev, &dflags);
672 if ((dflags & fdf_can_erase_dev) != 0) {
673 rc = fdisk_dev_erase(dev);
674 if (rc != EOK)
675 return rc;
676 }
677
678 return EOK;
679}
680
681fdisk_part_t *fdisk_part_first(fdisk_dev_t *dev)
682{
683 link_t *link;
684
685 link = list_first(&dev->parts);
686 if (link == NULL)
687 return NULL;
688
689 return list_get_instance(link, fdisk_part_t, lparts);
690}
691
692fdisk_part_t *fdisk_part_next(fdisk_part_t *part)
693{
694 link_t *link;
695
696 link = list_next(&part->lparts, &part->dev->parts);
697 if (link == NULL)
698 return NULL;
699
700 return list_get_instance(link, fdisk_part_t, lparts);
701}
702
703int fdisk_part_get_info(fdisk_part_t *part, fdisk_part_info_t *info)
704{
705 info->capacity = part->capacity;
706 info->pcnt = part->pcnt;
707 info->fstype = part->fstype;
708 info->pkind = part->pkind;
709 return EOK;
710}
711
712/** Get size of largest free block. */
713int fdisk_part_get_max_avail(fdisk_dev_t *dev, fdisk_spc_t spc, fdisk_cap_t *cap)
714{
715 int rc;
716 uint64_t b0;
717 uint64_t nb;
718 aoff64_t hdrb;
719
720 rc = fdisk_part_get_max_free_range(dev, spc, &b0, &nb);
721 if (rc != EOK)
722 return rc;
723
724 /* For logical partitions we need to subtract header size */
725 if (spc == spc_log) {
726 hdrb = max(1, dev->align);
727 if (nb <= hdrb)
728 return ENOSPC;
729 nb -= hdrb;
730 }
731
732 fdisk_cap_from_blocks(nb, dev->dinfo.block_size, cap);
733 return EOK;
734}
735
736/** Get total free space capacity. */
737int fdisk_part_get_tot_avail(fdisk_dev_t *dev, fdisk_spc_t spc,
738 fdisk_cap_t *cap)
739{
740 fdisk_free_range_t fr;
741 uint64_t hdrb;
742 uint64_t b0;
743 uint64_t nb;
744 uint64_t totb;
745
746 if (spc == spc_log)
747 hdrb = max(1, dev->align);
748 else
749 hdrb = 0;
750
751 totb = 0;
752 fdisk_free_range_first(dev, spc, &fr);
753 do {
754 if (fdisk_free_range_get(&fr, &b0, &nb)) {
755 if (nb > hdrb)
756 totb += nb - hdrb;
757 }
758 } while (fdisk_free_range_next(&fr));
759
760 fdisk_cap_from_blocks(totb, dev->dinfo.block_size, cap);
761 return EOK;
762}
763
764int fdisk_part_create(fdisk_dev_t *dev, fdisk_part_spec_t *pspec,
765 fdisk_part_t **rpart)
766{
767 fdisk_part_t *part;
768 vbd_part_spec_t vpspec;
769 vbd_part_id_t partid;
770 int rc;
771
772 rc = fdisk_part_spec_prepare(dev, pspec, &vpspec);
773 if (rc != EOK)
774 return EIO;
775
776 rc = vbd_part_create(dev->fdisk->vbd, dev->sid, &vpspec, &partid);
777 if (rc != EOK)
778 return EIO;
779
780 rc = fdisk_part_add(dev, partid, &part);
781 if (rc != EOK) {
782 /* Try rolling back */
783 (void) vbd_part_delete(dev->fdisk->vbd, partid);
784 return EIO;
785 }
786
787 if (part->svc_id != 0) {
788 rc = vol_part_mkfs(dev->fdisk->vol, part->svc_id, pspec->fstype);
789 if (rc != EOK && rc != ENOTSUP) {
790 fdisk_part_remove(part);
791 (void) vbd_part_delete(dev->fdisk->vbd, partid);
792 return EIO;
793 }
794
795 part->pcnt = vpc_fs;
796 part->fstype = pspec->fstype;
797 }
798
799 if (rpart != NULL)
800 *rpart = part;
801 return EOK;
802}
803
804int fdisk_part_destroy(fdisk_part_t *part)
805{
806 int rc;
807
808 rc = vbd_part_delete(part->dev->fdisk->vbd, part->part_id);
809 if (rc != EOK)
810 return EIO;
811
812 fdisk_part_remove(part);
813 return EOK;
814}
815
816void fdisk_pspec_init(fdisk_part_spec_t *pspec)
817{
818 memset(pspec, 0, sizeof(fdisk_part_spec_t));
819}
820
821int fdisk_ltype_format(label_type_t ltype, char **rstr)
822{
823 const char *sltype;
824 char *s;
825
826 sltype = NULL;
827 switch (ltype) {
828 case lt_none:
829 sltype = "None";
830 break;
831 case lt_mbr:
832 sltype = "MBR";
833 break;
834 case lt_gpt:
835 sltype = "GPT";
836 break;
837 }
838
839 s = str_dup(sltype);
840 if (s == NULL)
841 return ENOMEM;
842
843 *rstr = s;
844 return EOK;
845}
846
847int fdisk_fstype_format(vol_fstype_t fstype, char **rstr)
848{
849 const char *sfstype;
850 char *s;
851
852 sfstype = NULL;
853 switch (fstype) {
854 case fs_exfat:
855 sfstype = "ExFAT";
856 break;
857 case fs_fat:
858 sfstype = "FAT";
859 break;
860 case fs_minix:
861 sfstype = "MINIX";
862 break;
863 case fs_ext4:
864 sfstype = "Ext4";
865 break;
866 }
867
868 s = str_dup(sfstype);
869 if (s == NULL)
870 return ENOMEM;
871
872 *rstr = s;
873 return EOK;
874}
875
876int fdisk_pkind_format(label_pkind_t pkind, char **rstr)
877{
878 const char *spkind;
879 char *s;
880
881 spkind = NULL;
882 switch (pkind) {
883 case lpk_primary:
884 spkind = "Primary";
885 break;
886 case lpk_extended:
887 spkind = "Extended";
888 break;
889 case lpk_logical:
890 spkind = "Logical";
891 break;
892 }
893
894 s = str_dup(spkind);
895 if (s == NULL)
896 return ENOMEM;
897
898 *rstr = s;
899 return EOK;
900}
901
902/** Get free partition index. */
903static int fdisk_part_get_free_idx(fdisk_dev_t *dev, int *rindex)
904{
905 link_t *link;
906 fdisk_part_t *part;
907 int nidx;
908
909 link = list_first(&dev->pri_idx);
910 nidx = 1;
911 while (link != NULL) {
912 part = list_get_instance(link, fdisk_part_t, lpri_idx);
913 if (part->index > nidx)
914 break;
915 nidx = part->index + 1;
916 link = list_next(link, &dev->pri_idx);
917 }
918
919 if (nidx > 4 /* XXXX actual number of slots*/) {
920 return ELIMIT;
921 }
922
923 *rindex = nidx;
924 return EOK;
925}
926
927/** Get free range of blocks.
928 *
929 * Get free range of blocks of at least the specified size (first fit).
930 */
931static int fdisk_part_get_free_range(fdisk_dev_t *dev, aoff64_t nblocks,
932 fdisk_spc_t spc, aoff64_t *rblock0, aoff64_t *rnblocks)
933{
934 fdisk_free_range_t fr;
935 uint64_t b0;
936 uint64_t nb;
937
938 fdisk_free_range_first(dev, spc, &fr);
939 do {
940 if (fdisk_free_range_get(&fr, &b0, &nb)) {
941 if (nb >= nblocks) {
942 *rblock0 = b0;
943 *rnblocks = nb;
944 return EOK;
945 }
946 }
947 } while (fdisk_free_range_next(&fr));
948
949 /* No conforming free range found */
950 return ENOSPC;
951}
952
953/** Get largest free range of blocks.
954 *
955 * Get free range of blocks of the maximum size.
956 */
957static int fdisk_part_get_max_free_range(fdisk_dev_t *dev, fdisk_spc_t spc,
958 aoff64_t *rblock0, aoff64_t *rnblocks)
959{
960 fdisk_free_range_t fr;
961 uint64_t b0;
962 uint64_t nb;
963 uint64_t best_b0;
964 uint64_t best_nb;
965
966 best_b0 = best_nb = 0;
967 fdisk_free_range_first(dev, spc, &fr);
968 do {
969 if (fdisk_free_range_get(&fr, &b0, &nb)) {
970 if (nb > best_nb) {
971 best_b0 = b0;
972 best_nb = nb;
973 }
974 }
975 } while (fdisk_free_range_next(&fr));
976
977 if (best_nb == 0)
978 return ENOSPC;
979
980 *rblock0 = best_b0;
981 *rnblocks = best_nb;
982 return EOK;
983}
984
985/** Prepare new partition specification for VBD. */
986static int fdisk_part_spec_prepare(fdisk_dev_t *dev, fdisk_part_spec_t *pspec,
987 vbd_part_spec_t *vpspec)
988{
989 aoff64_t nom_blocks;
990 aoff64_t min_blocks;
991 aoff64_t max_blocks;
992 aoff64_t act_blocks;
993 aoff64_t fblock0;
994 aoff64_t fnblocks;
995 aoff64_t hdrb;
996 label_pcnt_t pcnt;
997 fdisk_spc_t spc;
998 int index;
999 int rc;
1000
1001 rc = fdisk_cap_to_blocks(&pspec->capacity, fcv_nom, dev->dinfo.block_size,
1002 &nom_blocks);
1003 if (rc != EOK)
1004 return rc;
1005
1006 rc = fdisk_cap_to_blocks(&pspec->capacity, fcv_min, dev->dinfo.block_size,
1007 &min_blocks);
1008 if (rc != EOK)
1009 return rc;
1010
1011 rc = fdisk_cap_to_blocks(&pspec->capacity, fcv_max, dev->dinfo.block_size,
1012 &max_blocks);
1013 if (rc != EOK)
1014 return rc;
1015
1016 nom_blocks = fdisk_ba_align_up(dev, nom_blocks);
1017 min_blocks = fdisk_ba_align_up(dev, min_blocks);
1018 max_blocks = fdisk_ba_align_up(dev, max_blocks);
1019
1020 pcnt = -1;
1021
1022 switch (pspec->fstype) {
1023 case fs_exfat:
1024 pcnt = lpc_exfat;
1025 break;
1026 case fs_fat:
1027 pcnt = lpc_fat32; /* XXX Detect FAT12/16 vs FAT32 */
1028 break;
1029 case fs_minix:
1030 pcnt = lpc_minix;
1031 break;
1032 case fs_ext4:
1033 pcnt = lpc_ext4;
1034 break;
1035 }
1036
1037 if (pcnt < 0)
1038 return EINVAL;
1039
1040 if (pspec->pkind == lpk_logical) {
1041 hdrb = max(1, dev->align);
1042 spc = spc_log;
1043 } else {
1044 hdrb = 0;
1045 spc = spc_pri;
1046 }
1047
1048 rc = fdisk_part_get_free_range(dev, hdrb + nom_blocks, spc,
1049 &fblock0, &fnblocks);
1050
1051 if (rc == EOK) {
1052 /*
1053 * If the size of the free range would still give the same capacity
1054 * when rounded, allocate entire range. Otherwise allocate exactly
1055 * what we were asked for.
1056 */
1057 if (fnblocks <= max_blocks) {
1058 act_blocks = fnblocks;
1059 } else {
1060 act_blocks = hdrb + nom_blocks;
1061 }
1062 } else {
1063 assert(rc == ENOSPC);
1064
1065 /*
1066 * There is no free range that can contain exactly the requested
1067 * capacity. Try to allocate at least such number of blocks
1068 * that would still fullfill the request within the limits
1069 * of the precision with witch the capacity was specified
1070 * (i.e. when rounded up).
1071 */
1072 rc = fdisk_part_get_free_range(dev, hdrb + min_blocks, spc,
1073 &fblock0, &fnblocks);
1074 if (rc != EOK)
1075 return rc;
1076
1077 assert(fnblocks < hdrb + nom_blocks);
1078 act_blocks = fnblocks;
1079 }
1080
1081 if (pspec->pkind != lpk_logical) {
1082 rc = fdisk_part_get_free_idx(dev, &index);
1083 if (rc != EOK)
1084 return EIO;
1085 } else {
1086 index = 0;
1087 }
1088
1089 memset(vpspec, 0, sizeof(vbd_part_spec_t));
1090 vpspec->index = index;
1091 vpspec->hdr_blocks = hdrb;
1092 vpspec->block0 = fblock0 + hdrb;
1093 vpspec->nblocks = act_blocks - hdrb;
1094 vpspec->pkind = pspec->pkind;
1095
1096 if (pspec->pkind != lpk_extended) {
1097 rc = vbd_suggest_ptype(dev->fdisk->vbd, dev->sid, pcnt,
1098 &vpspec->ptype);
1099 if (rc != EOK)
1100 return EIO;
1101 }
1102
1103 return EOK;
1104}
1105
1106static int fdisk_update_dev_info(fdisk_dev_t *dev)
1107{
1108 int rc;
1109 size_t align_bytes;
1110 uint64_t avail_cap;
1111
1112 rc = vbd_disk_info(dev->fdisk->vbd, dev->sid, &dev->dinfo);
1113 if (rc != EOK)
1114 return EIO;
1115
1116 /* Capacity available for partition in bytes */
1117 avail_cap = dev->dinfo.anblocks * dev->dinfo.block_size;
1118
1119 /* Determine optimum alignment */
1120 align_bytes = 1024 * 1024; /* 1 MiB */
1121 while (align_bytes > 1 && avail_cap / align_bytes < 256) {
1122 align_bytes = align_bytes / 16;
1123 }
1124
1125 dev->align = align_bytes / dev->dinfo.block_size;
1126 if (dev->align < 1)
1127 dev->align = 1;
1128 return EOK;
1129}
1130
1131static uint64_t fdisk_ba_align_up(fdisk_dev_t *dev, uint64_t ba)
1132{
1133 return ((ba + dev->align - 1) / dev->align) * dev->align;
1134}
1135
1136static uint64_t fdisk_ba_align_down(fdisk_dev_t *dev, uint64_t ba)
1137{
1138 return ba - (ba % dev->align);
1139}
1140
1141static void fdisk_free_range_first(fdisk_dev_t *dev, fdisk_spc_t spc,
1142 fdisk_free_range_t *fr)
1143{
1144 link_t *link;
1145
1146 fr->dev = dev;
1147 fr->spc = spc;
1148
1149 if (fr->spc == spc_pri) {
1150 /* Primary partitions */
1151 fr->b0 = fdisk_ba_align_up(fr->dev, fr->dev->dinfo.ablock0);
1152
1153 link = list_first(&dev->pri_ba);
1154 if (link != NULL)
1155 fr->npart = list_get_instance(link, fdisk_part_t, lpri_ba);
1156 else
1157 fr->npart = NULL;
1158 } else { /* fr->spc == spc_log */
1159 /* Logical partitions */
1160 fr->b0 = fdisk_ba_align_up(fr->dev, fr->dev->ext_part->block0);
1161
1162 link = list_first(&dev->log_ba);
1163 if (link != NULL)
1164 fr->npart = list_get_instance(link, fdisk_part_t, llog_ba);
1165 else
1166 fr->npart = NULL;
1167 }
1168}
1169
1170static bool fdisk_free_range_next(fdisk_free_range_t *fr)
1171{
1172 link_t *link;
1173
1174 if (fr->npart == NULL)
1175 return false;
1176
1177 fr->b0 = fdisk_ba_align_up(fr->dev, fr->npart->block0 +
1178 fr->npart->nblocks);
1179
1180 if (fr->spc == spc_pri) {
1181 /* Primary partitions */
1182 link = list_next(&fr->npart->lpri_ba, &fr->dev->pri_ba);
1183 if (link != NULL)
1184 fr->npart = list_get_instance(link, fdisk_part_t, lpri_ba);
1185 else
1186 fr->npart = NULL;
1187 } else { /* fr->spc == spc_log */
1188 /* Logical partitions */
1189 link = list_next(&fr->npart->llog_ba, &fr->dev->log_ba);
1190 if (link != NULL)
1191 fr->npart = list_get_instance(link, fdisk_part_t, llog_ba);
1192 else
1193 fr->npart = NULL;
1194 }
1195
1196 return true;
1197}
1198
1199static bool fdisk_free_range_get(fdisk_free_range_t *fr,
1200 aoff64_t *b0, aoff64_t *nb)
1201{
1202 aoff64_t b1;
1203
1204 if (fr->npart != NULL) {
1205 b1 = fdisk_ba_align_down(fr->dev, fr->npart->block0);
1206 } else {
1207 if (fr->spc == spc_pri) {
1208 b1 = fdisk_ba_align_down(fr->dev,
1209 fr->dev->dinfo.ablock0 + fr->dev->dinfo.anblocks);
1210 } else { /* fr->spc == spc_log */
1211 b1 = fdisk_ba_align_down(fr->dev,
1212 fr->dev->ext_part->block0 + fr->dev->ext_part->nblocks);
1213 }
1214 }
1215
1216 if (b1 < fr->b0)
1217 return false;
1218
1219 *b0 = fr->b0;
1220 *nb = b1 - fr->b0;
1221
1222 return true;
1223}
1224
1225/** @}
1226 */
Note: See TracBrowser for help on using the repository browser.