source: mainline/uspace/lib/fdisk/src/fdisk.c@ 719a208

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

Probing for ExFAT, Ext4 and CDFS.

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