Fdisk needs to unmount partition before trying to delete it
[helenos.git] / uspace / lib / fdisk / src / fdisk.c
blob8c484976198443760600c0d68757808989013145
1 /*
2 * Copyright (c) 2024 Jiri Svoboda
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
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.
29 /** @addtogroup libfdisk
30 * @{
32 /**
33 * @file Disk management library.
36 #include <adt/list.h>
37 #include <capa.h>
38 #include <errno.h>
39 #include <fdisk.h>
40 #include <loc.h>
41 #include <macros.h>
42 #include <mem.h>
43 #include <stdlib.h>
44 #include <str.h>
45 #include <vbd.h>
46 #include <vol.h>
48 static errno_t fdisk_dev_add_parts(fdisk_dev_t *);
49 static void fdisk_dev_remove_parts(fdisk_dev_t *);
50 static errno_t fdisk_part_spec_prepare(fdisk_dev_t *, fdisk_part_spec_t *,
51 vbd_part_spec_t *);
52 static void fdisk_pri_part_insert_lists(fdisk_dev_t *, fdisk_part_t *);
53 static void fdisk_log_part_insert_lists(fdisk_dev_t *, fdisk_part_t *);
54 static errno_t fdisk_update_dev_info(fdisk_dev_t *);
55 static uint64_t fdisk_ba_align_up(fdisk_dev_t *, uint64_t);
56 static uint64_t fdisk_ba_align_down(fdisk_dev_t *, uint64_t);
57 static errno_t fdisk_part_get_max_free_range(fdisk_dev_t *, fdisk_spc_t, aoff64_t *,
58 aoff64_t *);
59 static void fdisk_free_range_first(fdisk_dev_t *, fdisk_spc_t, fdisk_free_range_t *);
60 static bool fdisk_free_range_next(fdisk_free_range_t *);
61 static bool fdisk_free_range_get(fdisk_free_range_t *, aoff64_t *, aoff64_t *);
63 static void fdisk_dev_info_delete(fdisk_dev_info_t *info)
65 if (info == NULL)
66 return;
68 free(info->svcname);
69 free(info);
72 errno_t fdisk_create(fdisk_t **rfdisk)
74 fdisk_t *fdisk = NULL;
75 errno_t rc;
77 fdisk = calloc(1, sizeof(fdisk_t));
78 if (fdisk == NULL) {
79 rc = ENOMEM;
80 goto error;
83 rc = vol_create(&fdisk->vol);
84 if (rc != EOK) {
85 rc = EIO;
86 goto error;
89 rc = vbd_create(&fdisk->vbd);
90 if (rc != EOK) {
91 rc = EIO;
92 goto error;
95 *rfdisk = fdisk;
96 return EOK;
97 error:
98 fdisk_destroy(fdisk);
100 return rc;
103 void fdisk_destroy(fdisk_t *fdisk)
105 if (fdisk == NULL)
106 return;
108 vol_destroy(fdisk->vol);
109 vbd_destroy(fdisk->vbd);
110 free(fdisk);
113 errno_t fdisk_dev_list_get(fdisk_t *fdisk, fdisk_dev_list_t **rdevlist)
115 fdisk_dev_list_t *devlist = NULL;
116 fdisk_dev_info_t *info;
117 service_id_t *svcs = NULL;
118 size_t count, i;
119 errno_t rc;
121 devlist = calloc(1, sizeof(fdisk_dev_list_t));
122 if (devlist == NULL)
123 return ENOMEM;
125 devlist->fdisk = fdisk;
126 list_initialize(&devlist->devinfos);
128 rc = vbd_get_disks(fdisk->vbd, &svcs, &count);
129 if (rc != EOK) {
130 rc = EIO;
131 goto error;
134 for (i = 0; i < count; i++) {
135 info = calloc(1, sizeof(fdisk_dev_info_t));
136 if (info == NULL) {
137 rc = ENOMEM;
138 goto error;
141 info->svcid = svcs[i];
142 info->devlist = devlist;
143 list_append(&info->ldevlist, &devlist->devinfos);
146 *rdevlist = devlist;
147 free(svcs);
148 return EOK;
149 error:
150 free(svcs);
151 fdisk_dev_list_free(devlist);
152 return rc;
155 void fdisk_dev_list_free(fdisk_dev_list_t *devlist)
157 fdisk_dev_info_t *info;
159 if (devlist == NULL)
160 return;
162 while (!list_empty(&devlist->devinfos)) {
163 info = list_get_instance(list_first(
164 &devlist->devinfos), fdisk_dev_info_t,
165 ldevlist);
167 list_remove(&info->ldevlist);
168 fdisk_dev_info_delete(info);
171 free(devlist);
174 fdisk_dev_info_t *fdisk_dev_first(fdisk_dev_list_t *devlist)
176 if (list_empty(&devlist->devinfos))
177 return NULL;
179 return list_get_instance(list_first(&devlist->devinfos),
180 fdisk_dev_info_t, ldevlist);
183 fdisk_dev_info_t *fdisk_dev_next(fdisk_dev_info_t *devinfo)
185 link_t *lnext;
187 lnext = list_next(&devinfo->ldevlist,
188 &devinfo->devlist->devinfos);
189 if (lnext == NULL)
190 return NULL;
192 return list_get_instance(lnext, fdisk_dev_info_t,
193 ldevlist);
196 void fdisk_dev_info_get_svcid(fdisk_dev_info_t *info, service_id_t *rsid)
198 *rsid = info->svcid;
201 errno_t fdisk_dev_info_get_svcname(fdisk_dev_info_t *info, char **rname)
203 char *name;
204 errno_t rc;
206 if (info->svcname == NULL) {
207 rc = loc_service_get_name(info->svcid,
208 &info->svcname);
209 if (rc != EOK)
210 return rc;
213 name = str_dup(info->svcname);
214 if (name == NULL)
215 return ENOMEM;
217 *rname = name;
218 return EOK;
221 errno_t fdisk_dev_info_capacity(fdisk_dev_info_t *info, capa_spec_t *capa)
223 vbd_disk_info_t vinfo;
224 errno_t rc;
226 rc = vbd_disk_info(info->devlist->fdisk->vbd, info->svcid, &vinfo);
227 if (rc != EOK)
228 return EIO;
230 capa_from_blocks(vinfo.nblocks, vinfo.block_size, capa);
231 return EOK;
234 /** Add partition to our inventory. */
235 static errno_t fdisk_part_add(fdisk_dev_t *dev, vbd_part_id_t partid,
236 fdisk_part_t **rpart)
238 fdisk_part_t *part;
239 vbd_part_info_t pinfo;
240 vol_part_info_t vpinfo;
241 errno_t rc;
243 part = calloc(1, sizeof(fdisk_part_t));
244 if (part == NULL)
245 return ENOMEM;
247 rc = vbd_part_get_info(dev->fdisk->vbd, partid, &pinfo);
248 if (rc != EOK) {
249 rc = EIO;
250 goto error;
253 if (pinfo.svc_id != 0) {
255 * Normally vol service discovers the partition asynchronously.
256 * Here we need to make sure the partition is already known to it.
258 rc = vol_part_add(dev->fdisk->vol, pinfo.svc_id);
259 if (rc != EOK && rc != EEXIST) {
260 rc = EIO;
261 goto error;
264 rc = vol_part_info(dev->fdisk->vol, pinfo.svc_id, &vpinfo);
265 if (rc != EOK) {
266 rc = EIO;
267 goto error;
270 part->pcnt = vpinfo.pcnt;
271 part->fstype = vpinfo.fstype;
272 part->label = str_dup(vpinfo.label);
275 part->dev = dev;
276 part->index = pinfo.index;
277 part->block0 = pinfo.block0;
278 part->nblocks = pinfo.nblocks;
279 part->pkind = pinfo.pkind;
280 part->svc_id = pinfo.svc_id;
282 switch (part->pkind) {
283 case lpk_primary:
284 case lpk_extended:
285 fdisk_pri_part_insert_lists(dev, part);
286 break;
287 case lpk_logical:
288 fdisk_log_part_insert_lists(dev, part);
289 break;
292 list_append(&part->lparts, &dev->parts);
294 if (part->pkind == lpk_extended)
295 dev->ext_part = part;
297 capa_from_blocks(part->nblocks, dev->dinfo.block_size,
298 &part->capacity);
299 part->part_id = partid;
301 if (rpart != NULL)
302 *rpart = part;
303 return EOK;
304 error:
305 if (part != NULL)
306 free(part->label);
307 free(part);
308 return rc;
311 /** Remove partition from our inventory. */
312 static void fdisk_part_remove(fdisk_part_t *part)
314 list_remove(&part->lparts);
315 if (link_used(&part->lpri_ba))
316 list_remove(&part->lpri_ba);
317 if (link_used(&part->lpri_idx))
318 list_remove(&part->lpri_idx);
319 if (link_used(&part->llog_ba))
320 list_remove(&part->llog_ba);
322 free(part->label);
323 free(part);
326 static void fdisk_pri_part_insert_lists(fdisk_dev_t *dev, fdisk_part_t *part)
328 link_t *link;
329 fdisk_part_t *p;
331 /* Insert to list by block address */
332 link = list_first(&dev->pri_ba);
333 while (link != NULL) {
334 p = list_get_instance(link, fdisk_part_t, lpri_ba);
335 if (p->block0 > part->block0) {
336 list_insert_before(&part->lpri_ba, &p->lpri_ba);
337 break;
340 link = list_next(link, &dev->pri_ba);
343 if (link == NULL)
344 list_append(&part->lpri_ba, &dev->pri_ba);
346 /* Insert to list by index */
347 link = list_first(&dev->pri_idx);
348 while (link != NULL) {
349 p = list_get_instance(link, fdisk_part_t, lpri_idx);
350 if (p->index > part->index) {
351 list_insert_before(&part->lpri_idx, &p->lpri_idx);
352 break;
355 link = list_next(link, &dev->pri_idx);
358 if (link == NULL)
359 list_append(&part->lpri_idx, &dev->pri_idx);
362 static void fdisk_log_part_insert_lists(fdisk_dev_t *dev, fdisk_part_t *part)
364 link_t *link;
365 fdisk_part_t *p;
367 /* Insert to list by block address */
368 link = list_first(&dev->log_ba);
369 while (link != NULL) {
370 p = list_get_instance(link, fdisk_part_t, llog_ba);
371 if (p->block0 > part->block0) {
372 list_insert_before(&part->llog_ba, &p->llog_ba);
373 break;
376 link = list_next(link, &dev->log_ba);
379 if (link == NULL)
380 list_append(&part->llog_ba, &dev->log_ba);
383 static errno_t fdisk_dev_add_parts(fdisk_dev_t *dev)
385 service_id_t *psids = NULL;
386 size_t nparts, i;
387 errno_t rc;
389 rc = fdisk_update_dev_info(dev);
390 if (rc != EOK) {
391 rc = EIO;
392 goto error;
395 rc = vbd_label_get_parts(dev->fdisk->vbd, dev->sid, &psids, &nparts);
396 if (rc != EOK) {
397 rc = EIO;
398 goto error;
401 for (i = 0; i < nparts; i++) {
402 rc = fdisk_part_add(dev, psids[i], NULL);
403 if (rc != EOK) {
404 goto error;
408 free(psids);
409 return EOK;
410 error:
411 fdisk_dev_remove_parts(dev);
412 return rc;
415 static void fdisk_dev_remove_parts(fdisk_dev_t *dev)
417 fdisk_part_t *part;
419 part = fdisk_part_first(dev);
420 while (part != NULL) {
421 fdisk_part_remove(part);
422 part = fdisk_part_first(dev);
426 errno_t fdisk_dev_open(fdisk_t *fdisk, service_id_t sid, fdisk_dev_t **rdev)
428 fdisk_dev_t *dev = NULL;
429 service_id_t *psids = NULL;
430 size_t nparts, i;
431 errno_t rc;
433 dev = calloc(1, sizeof(fdisk_dev_t));
434 if (dev == NULL)
435 return ENOMEM;
437 dev->fdisk = fdisk;
438 dev->sid = sid;
439 list_initialize(&dev->parts);
440 list_initialize(&dev->pri_idx);
441 list_initialize(&dev->pri_ba);
442 list_initialize(&dev->log_ba);
444 rc = fdisk_update_dev_info(dev);
445 if (rc != EOK) {
446 rc = EIO;
447 goto error;
450 rc = vbd_label_get_parts(fdisk->vbd, sid, &psids, &nparts);
451 if (rc != EOK) {
452 rc = EIO;
453 goto error;
456 for (i = 0; i < nparts; i++) {
457 rc = fdisk_part_add(dev, psids[i], NULL);
458 if (rc != EOK) {
459 goto error;
463 free(psids);
464 *rdev = dev;
465 return EOK;
466 error:
467 fdisk_dev_close(dev);
468 return rc;
471 void fdisk_dev_close(fdisk_dev_t *dev)
473 if (dev == NULL)
474 return;
476 fdisk_dev_remove_parts(dev);
477 free(dev);
480 /** Erase contents of unlabeled disk. */
481 errno_t fdisk_dev_erase(fdisk_dev_t *dev)
483 fdisk_part_t *part;
484 errno_t rc;
486 if (dev->dinfo.ltype != lt_none)
487 return EINVAL;
489 part = fdisk_part_first(dev);
490 assert(part != NULL);
491 if (part->pcnt == vpc_empty)
492 return EINVAL;
494 rc = vol_part_empty(dev->fdisk->vol, part->svc_id);
495 if (rc != EOK) {
496 return rc;
499 part->pcnt = vpc_empty;
500 return EOK;
503 void fdisk_dev_get_flags(fdisk_dev_t *dev, fdisk_dev_flags_t *rflags)
505 fdisk_dev_flags_t flags;
506 fdisk_part_t *part;
508 flags = 0;
510 /* fdf_can_create_label */
511 if (dev->dinfo.ltype == lt_none) {
512 part = fdisk_part_first(dev);
513 assert(part != NULL);
514 if (part->pcnt == vpc_empty)
515 flags |= fdf_can_create_label;
516 else
517 flags |= fdf_can_erase_dev;
518 } else {
519 flags |= fdf_can_delete_label;
522 *rflags = flags;
525 errno_t fdisk_dev_get_svcname(fdisk_dev_t *dev, char **rname)
527 char *name;
528 errno_t rc;
530 rc = loc_service_get_name(dev->sid, &name);
531 if (rc != EOK)
532 return rc;
534 *rname = name;
535 return EOK;
538 errno_t fdisk_dev_capacity(fdisk_dev_t *dev, capa_spec_t *capa)
540 capa_from_blocks(dev->dinfo.nblocks, dev->dinfo.block_size, capa);
541 return EOK;
544 errno_t fdisk_label_get_info(fdisk_dev_t *dev, fdisk_label_info_t *info)
546 vbd_disk_info_t vinfo;
547 uint64_t b0, nb;
548 uint64_t hdrb;
549 errno_t rc;
551 rc = vbd_disk_info(dev->fdisk->vbd, dev->sid, &vinfo);
552 if (rc != EOK) {
553 rc = EIO;
554 goto error;
557 info->ltype = vinfo.ltype;
558 info->flags = vinfo.flags;
560 if ((info->flags & lf_can_create_pri) != 0 ||
561 (info->flags & lf_can_create_ext) != 0) {
562 /* Verify there is enough space to create partition */
564 rc = fdisk_part_get_max_free_range(dev, spc_pri, &b0, &nb);
565 if (rc != EOK)
566 info->flags &= ~(lf_can_create_pri | lf_can_create_ext);
569 if ((info->flags & lf_can_create_log) != 0) {
570 /* Verify there is enough space to create logical partition */
571 hdrb = max(1, dev->align);
572 rc = fdisk_part_get_max_free_range(dev, spc_log, &b0, &nb);
573 if (rc != EOK || nb <= hdrb)
574 info->flags &= ~lf_can_create_log;
577 return EOK;
578 error:
579 return rc;
582 errno_t fdisk_label_create(fdisk_dev_t *dev, label_type_t ltype)
584 fdisk_part_t *part;
585 errno_t rc;
587 /* Disk must not contain a label. */
588 if (dev->dinfo.ltype != lt_none)
589 return EEXIST;
591 /* Dummy partition spanning entire disk must be considered empty */
592 part = fdisk_part_first(dev);
593 assert(part != NULL);
594 if (part->pcnt != vpc_empty)
595 return EEXIST;
597 /* Remove dummy partition */
598 fdisk_dev_remove_parts(dev);
600 rc = vbd_label_create(dev->fdisk->vbd, dev->sid, ltype);
601 if (rc != EOK) {
602 /* Re-add dummy partition */
603 (void) fdisk_dev_add_parts(dev);
604 return rc;
607 rc = fdisk_update_dev_info(dev);
608 if (rc != EOK)
609 return rc;
611 return EOK;
614 errno_t fdisk_label_destroy(fdisk_dev_t *dev)
616 fdisk_part_t *part;
617 fdisk_dev_flags_t dflags;
618 errno_t rc;
620 part = fdisk_part_first(dev);
621 while (part != NULL) {
622 rc = fdisk_part_destroy(part);
623 if (rc != EOK)
624 return EIO;
625 part = fdisk_part_first(dev);
628 rc = vbd_label_delete(dev->fdisk->vbd, dev->sid);
629 if (rc != EOK)
630 return EIO;
632 rc = fdisk_dev_add_parts(dev);
633 if (rc != EOK)
634 return rc;
636 /* Make sure device is considered empty */
637 fdisk_dev_get_flags(dev, &dflags);
638 if ((dflags & fdf_can_erase_dev) != 0) {
639 rc = fdisk_dev_erase(dev);
640 if (rc != EOK)
641 return rc;
644 return EOK;
647 fdisk_part_t *fdisk_part_first(fdisk_dev_t *dev)
649 link_t *link;
651 link = list_first(&dev->parts);
652 if (link == NULL)
653 return NULL;
655 return list_get_instance(link, fdisk_part_t, lparts);
658 fdisk_part_t *fdisk_part_next(fdisk_part_t *part)
660 link_t *link;
662 link = list_next(&part->lparts, &part->dev->parts);
663 if (link == NULL)
664 return NULL;
666 return list_get_instance(link, fdisk_part_t, lparts);
669 errno_t fdisk_part_get_info(fdisk_part_t *part, fdisk_part_info_t *info)
671 info->capacity = part->capacity;
672 info->pcnt = part->pcnt;
673 info->fstype = part->fstype;
674 info->pkind = part->pkind;
675 info->label = part->label;
676 info->svc_id = part->svc_id;
677 return EOK;
680 /** Get size of largest free block. */
681 errno_t fdisk_part_get_max_avail(fdisk_dev_t *dev, fdisk_spc_t spc,
682 capa_spec_t *capa)
684 errno_t rc;
685 uint64_t b0;
686 uint64_t nb;
687 aoff64_t hdrb;
689 rc = fdisk_part_get_max_free_range(dev, spc, &b0, &nb);
690 if (rc != EOK)
691 return rc;
693 /* For logical partitions we need to subtract header size */
694 if (spc == spc_log) {
695 hdrb = max(1, dev->align);
696 if (nb <= hdrb)
697 return ENOSPC;
698 nb -= hdrb;
701 capa_from_blocks(nb, dev->dinfo.block_size, capa);
702 return EOK;
705 /** Get total free space capacity. */
706 errno_t fdisk_part_get_tot_avail(fdisk_dev_t *dev, fdisk_spc_t spc,
707 capa_spec_t *capa)
709 fdisk_free_range_t fr;
710 uint64_t hdrb;
711 uint64_t b0;
712 uint64_t nb;
713 uint64_t totb;
715 if (spc == spc_log)
716 hdrb = max(1, dev->align);
717 else
718 hdrb = 0;
720 totb = 0;
721 fdisk_free_range_first(dev, spc, &fr);
722 do {
723 if (fdisk_free_range_get(&fr, &b0, &nb)) {
724 if (nb > hdrb)
725 totb += nb - hdrb;
727 } while (fdisk_free_range_next(&fr));
729 capa_from_blocks(totb, dev->dinfo.block_size, capa);
730 return EOK;
733 /** Create partition.
735 * Create new partition based on a specification.
737 * @param dev Fdisk device
738 * @param pspec Partition specification
739 * @param rpart Place to store pointer to new partition
741 * @return EOK on success or error code
743 errno_t fdisk_part_create(fdisk_dev_t *dev, fdisk_part_spec_t *pspec,
744 fdisk_part_t **rpart)
746 fdisk_part_t *part = NULL;
747 vbd_part_spec_t vpspec;
748 vbd_part_id_t partid = 0;
749 vol_part_info_t vpinfo;
750 const char *label;
751 const char *mountp;
752 errno_t rc;
754 label = pspec->label != NULL ? pspec->label : "";
755 mountp = pspec->mountp != NULL ? pspec->mountp : "";
757 rc = fdisk_part_spec_prepare(dev, pspec, &vpspec);
758 if (rc != EOK) {
759 rc = EIO;
760 goto error;
763 rc = vbd_part_create(dev->fdisk->vbd, dev->sid, &vpspec, &partid);
764 if (rc != EOK) {
765 rc = EIO;
766 goto error;
769 rc = fdisk_part_add(dev, partid, &part);
770 if (rc != EOK) {
771 rc = EIO;
772 goto error;
775 if (part->svc_id != 0) {
776 rc = vol_part_mkfs(dev->fdisk->vol, part->svc_id, pspec->fstype,
777 label, mountp);
778 if (rc != EOK && rc != ENOTSUP) {
779 rc = EIO;
780 goto error;
783 /* Get the real label value */
784 rc = vol_part_info(dev->fdisk->vol, part->svc_id, &vpinfo);
785 if (rc != EOK) {
786 rc = EIO;
787 goto error;
790 part->pcnt = vpinfo.pcnt;
791 part->fstype = vpinfo.fstype;
792 part->label = str_dup(vpinfo.label);
794 if (part->label == NULL) {
795 rc = EIO;
796 goto error;
800 if (rpart != NULL)
801 *rpart = part;
802 return EOK;
803 error:
804 /* Try rolling back */
805 if (part != NULL)
806 fdisk_part_remove(part);
807 if (partid != 0)
808 (void) vbd_part_delete(dev->fdisk->vbd, partid);
809 return rc;
812 /** Destroy partition.
814 * @param part Partition
815 * @return EOK on success or error code
817 errno_t fdisk_part_destroy(fdisk_part_t *part)
819 errno_t rc;
821 rc = vol_part_eject(part->dev->fdisk->vol, part->svc_id);
822 if (rc != EOK)
823 return EIO;
825 rc = vbd_part_delete(part->dev->fdisk->vbd, part->part_id);
826 if (rc != EOK)
827 return EIO;
829 fdisk_part_remove(part);
830 return EOK;
833 /** Set partition mount point.
835 * @param part Fdisk partition
836 * @param mountp Mount point
838 * @return EOK on success or error code
840 errno_t fdisk_part_set_mountp(fdisk_part_t *part, const char *mountp)
842 return vol_part_set_mountp(part->dev->fdisk->vol,
843 part->svc_id, mountp);
846 void fdisk_pspec_init(fdisk_part_spec_t *pspec)
848 memset(pspec, 0, sizeof(fdisk_part_spec_t));
851 /** Get free partition index. */
852 static errno_t fdisk_part_get_free_idx(fdisk_dev_t *dev, int *rindex)
854 link_t *link;
855 fdisk_part_t *part;
856 int nidx;
858 link = list_first(&dev->pri_idx);
859 nidx = 1;
860 while (link != NULL) {
861 part = list_get_instance(link, fdisk_part_t, lpri_idx);
862 if (part->index > nidx)
863 break;
864 nidx = part->index + 1;
865 link = list_next(link, &dev->pri_idx);
868 if (nidx > 4 /* XXXX actual number of slots */) {
869 return ELIMIT;
872 *rindex = nidx;
873 return EOK;
876 /** Get free range of blocks.
878 * Get free range of blocks of at least the specified size (first fit).
880 static errno_t fdisk_part_get_free_range(fdisk_dev_t *dev, aoff64_t nblocks,
881 fdisk_spc_t spc, aoff64_t *rblock0, aoff64_t *rnblocks)
883 fdisk_free_range_t fr;
884 uint64_t b0;
885 uint64_t nb;
887 fdisk_free_range_first(dev, spc, &fr);
888 do {
889 if (fdisk_free_range_get(&fr, &b0, &nb)) {
890 if (nb >= nblocks) {
891 *rblock0 = b0;
892 *rnblocks = nb;
893 return EOK;
896 } while (fdisk_free_range_next(&fr));
898 /* No conforming free range found */
899 return ENOSPC;
902 /** Get largest free range of blocks.
904 * Get free range of blocks of the maximum size.
906 static errno_t fdisk_part_get_max_free_range(fdisk_dev_t *dev, fdisk_spc_t spc,
907 aoff64_t *rblock0, aoff64_t *rnblocks)
909 fdisk_free_range_t fr;
910 uint64_t b0;
911 uint64_t nb;
912 uint64_t best_b0;
913 uint64_t best_nb;
915 best_b0 = best_nb = 0;
916 fdisk_free_range_first(dev, spc, &fr);
917 do {
918 if (fdisk_free_range_get(&fr, &b0, &nb)) {
919 if (nb > best_nb) {
920 best_b0 = b0;
921 best_nb = nb;
924 } while (fdisk_free_range_next(&fr));
926 if (best_nb == 0)
927 return ENOSPC;
929 *rblock0 = best_b0;
930 *rnblocks = best_nb;
931 return EOK;
934 /** Prepare new partition specification for VBD. */
935 static errno_t fdisk_part_spec_prepare(fdisk_dev_t *dev, fdisk_part_spec_t *pspec,
936 vbd_part_spec_t *vpspec)
938 aoff64_t nom_blocks;
939 aoff64_t min_blocks;
940 aoff64_t max_blocks;
941 aoff64_t act_blocks;
942 aoff64_t fblock0;
943 aoff64_t fnblocks;
944 aoff64_t hdrb;
945 label_pcnt_t pcnt;
946 fdisk_spc_t spc;
947 int index;
948 errno_t rc;
950 rc = capa_to_blocks(&pspec->capacity, cv_nom, dev->dinfo.block_size,
951 &nom_blocks);
952 if (rc != EOK)
953 return rc;
955 rc = capa_to_blocks(&pspec->capacity, cv_min, dev->dinfo.block_size,
956 &min_blocks);
957 if (rc != EOK)
958 return rc;
960 rc = capa_to_blocks(&pspec->capacity, cv_max, dev->dinfo.block_size,
961 &max_blocks);
962 if (rc != EOK)
963 return rc;
965 nom_blocks = fdisk_ba_align_up(dev, nom_blocks);
966 min_blocks = fdisk_ba_align_up(dev, min_blocks);
967 max_blocks = fdisk_ba_align_up(dev, max_blocks);
969 pcnt = LPC_LIMIT;
971 switch (pspec->fstype) {
972 case fs_exfat:
973 pcnt = lpc_exfat;
974 break;
975 case fs_fat:
976 pcnt = lpc_fat32; /* XXX Detect FAT12/16 vs FAT32 */
977 break;
978 case fs_minix:
979 pcnt = lpc_minix;
980 break;
981 case fs_ext4:
982 pcnt = lpc_ext4;
983 break;
984 case fs_cdfs:
985 return EINVAL; /* You cannot create an ISO partition */
988 if (pcnt == LPC_LIMIT)
989 return EINVAL;
991 if (pspec->pkind == lpk_logical) {
992 hdrb = max(1, dev->align);
993 spc = spc_log;
994 } else {
995 hdrb = 0;
996 spc = spc_pri;
999 rc = fdisk_part_get_free_range(dev, hdrb + nom_blocks, spc,
1000 &fblock0, &fnblocks);
1002 if (rc == EOK) {
1004 * If the size of the free range would still give the same capacity
1005 * when rounded, allocate entire range. Otherwise allocate exactly
1006 * what we were asked for.
1008 if (fnblocks <= max_blocks) {
1009 act_blocks = fnblocks;
1010 } else {
1011 act_blocks = hdrb + nom_blocks;
1013 } else {
1014 assert(rc == ENOSPC);
1017 * There is no free range that can contain exactly the requested
1018 * capacity. Try to allocate at least such number of blocks
1019 * that would still fullfill the request within the limits
1020 * of the precision with witch the capacity was specified
1021 * (i.e. when rounded up).
1023 rc = fdisk_part_get_free_range(dev, hdrb + min_blocks, spc,
1024 &fblock0, &fnblocks);
1025 if (rc != EOK)
1026 return rc;
1028 assert(fnblocks < hdrb + nom_blocks);
1029 act_blocks = fnblocks;
1032 if (pspec->pkind != lpk_logical) {
1033 rc = fdisk_part_get_free_idx(dev, &index);
1034 if (rc != EOK)
1035 return EIO;
1036 } else {
1037 index = 0;
1040 memset(vpspec, 0, sizeof(vbd_part_spec_t));
1041 vpspec->index = index;
1042 vpspec->hdr_blocks = hdrb;
1043 vpspec->block0 = fblock0 + hdrb;
1044 vpspec->nblocks = act_blocks - hdrb;
1045 vpspec->pkind = pspec->pkind;
1047 if (pspec->pkind != lpk_extended) {
1048 rc = vbd_suggest_ptype(dev->fdisk->vbd, dev->sid, pcnt,
1049 &vpspec->ptype);
1050 if (rc != EOK)
1051 return EIO;
1054 return EOK;
1057 static errno_t fdisk_update_dev_info(fdisk_dev_t *dev)
1059 errno_t rc;
1060 size_t align_bytes;
1061 uint64_t avail_cap;
1063 rc = vbd_disk_info(dev->fdisk->vbd, dev->sid, &dev->dinfo);
1064 if (rc != EOK)
1065 return EIO;
1067 /* Capacity available for partition in bytes */
1068 avail_cap = dev->dinfo.anblocks * dev->dinfo.block_size;
1070 /* Determine optimum alignment */
1071 align_bytes = 1024 * 1024; /* 1 MiB */
1072 while (align_bytes > 1 && avail_cap / align_bytes < 256) {
1073 align_bytes = align_bytes / 16;
1076 dev->align = align_bytes / dev->dinfo.block_size;
1077 if (dev->align < 1)
1078 dev->align = 1;
1079 return EOK;
1082 static uint64_t fdisk_ba_align_up(fdisk_dev_t *dev, uint64_t ba)
1084 return ((ba + dev->align - 1) / dev->align) * dev->align;
1087 static uint64_t fdisk_ba_align_down(fdisk_dev_t *dev, uint64_t ba)
1089 return ba - (ba % dev->align);
1092 static void fdisk_free_range_first(fdisk_dev_t *dev, fdisk_spc_t spc,
1093 fdisk_free_range_t *fr)
1095 link_t *link;
1097 fr->dev = dev;
1098 fr->spc = spc;
1100 if (fr->spc == spc_pri) {
1101 /* Primary partitions */
1102 fr->b0 = fdisk_ba_align_up(fr->dev, fr->dev->dinfo.ablock0);
1104 link = list_first(&dev->pri_ba);
1105 if (link != NULL)
1106 fr->npart = list_get_instance(link, fdisk_part_t, lpri_ba);
1107 else
1108 fr->npart = NULL;
1109 } else { /* fr->spc == spc_log */
1110 /* Logical partitions */
1111 fr->b0 = fdisk_ba_align_up(fr->dev, fr->dev->ext_part->block0);
1113 link = list_first(&dev->log_ba);
1114 if (link != NULL)
1115 fr->npart = list_get_instance(link, fdisk_part_t, llog_ba);
1116 else
1117 fr->npart = NULL;
1121 static bool fdisk_free_range_next(fdisk_free_range_t *fr)
1123 link_t *link;
1125 if (fr->npart == NULL)
1126 return false;
1128 fr->b0 = fdisk_ba_align_up(fr->dev, fr->npart->block0 +
1129 fr->npart->nblocks);
1131 if (fr->spc == spc_pri) {
1132 /* Primary partitions */
1133 link = list_next(&fr->npart->lpri_ba, &fr->dev->pri_ba);
1134 if (link != NULL)
1135 fr->npart = list_get_instance(link, fdisk_part_t, lpri_ba);
1136 else
1137 fr->npart = NULL;
1138 } else { /* fr->spc == spc_log */
1139 /* Logical partitions */
1140 link = list_next(&fr->npart->llog_ba, &fr->dev->log_ba);
1141 if (link != NULL)
1142 fr->npart = list_get_instance(link, fdisk_part_t, llog_ba);
1143 else
1144 fr->npart = NULL;
1147 return true;
1150 static bool fdisk_free_range_get(fdisk_free_range_t *fr,
1151 aoff64_t *b0, aoff64_t *nb)
1153 aoff64_t b1;
1155 if (fr->npart != NULL) {
1156 b1 = fdisk_ba_align_down(fr->dev, fr->npart->block0);
1157 } else {
1158 if (fr->spc == spc_pri) {
1159 b1 = fdisk_ba_align_down(fr->dev,
1160 fr->dev->dinfo.ablock0 + fr->dev->dinfo.anblocks);
1161 } else { /* fr->spc == spc_log */
1162 b1 = fdisk_ba_align_down(fr->dev,
1163 fr->dev->ext_part->block0 + fr->dev->ext_part->nblocks);
1167 if (b1 < fr->b0)
1168 return false;
1170 *b0 = fr->b0;
1171 *nb = b1 - fr->b0;
1173 return true;
1176 /** Get volume label support. */
1177 errno_t fdisk_get_vollabel_support(fdisk_dev_t *dev, vol_fstype_t fstype,
1178 vol_label_supp_t *vlsupp)
1180 return vol_part_get_lsupp(dev->fdisk->vol, fstype, vlsupp);
1183 /** @}