source: mainline/uspace/lib/fdisk/src/fdisk.c@ 12dcd5f

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

Fix sysinst.

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