source: mainline/uspace/lib/fdisk/src/fdisk.c@ 9e53406

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

libfdisk should not depend on libblock nor should it access the block devices directly.

  • Property mode set to 100644
File size: 24.1 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 }
827
828 s = str_dup(sfstype);
829 if (s == NULL)
830 return ENOMEM;
831
832 *rstr = s;
833 return EOK;
834}
835
836int fdisk_pkind_format(label_pkind_t pkind, char **rstr)
837{
838 const char *spkind;
839 char *s;
840
841 spkind = NULL;
842 switch (pkind) {
843 case lpk_primary:
844 spkind = "Primary";
845 break;
846 case lpk_extended:
847 spkind = "Extended";
848 break;
849 case lpk_logical:
850 spkind = "Logical";
851 break;
852 }
853
854 s = str_dup(spkind);
855 if (s == NULL)
856 return ENOMEM;
857
858 *rstr = s;
859 return EOK;
860}
861
862/** Get free partition index. */
863static int fdisk_part_get_free_idx(fdisk_dev_t *dev, int *rindex)
864{
865 link_t *link;
866 fdisk_part_t *part;
867 int nidx;
868
869 link = list_first(&dev->pri_idx);
870 nidx = 1;
871 while (link != NULL) {
872 part = list_get_instance(link, fdisk_part_t, lpri_idx);
873 if (part->index > nidx)
874 break;
875 nidx = part->index + 1;
876 link = list_next(link, &dev->pri_idx);
877 }
878
879 if (nidx > 4 /* XXXX actual number of slots*/) {
880 return ELIMIT;
881 }
882
883 *rindex = nidx;
884 return EOK;
885}
886
887/** Get free range of blocks.
888 *
889 * Get free range of blocks of at least the specified size (first fit).
890 */
891static int fdisk_part_get_free_range(fdisk_dev_t *dev, aoff64_t nblocks,
892 fdisk_spc_t spc, aoff64_t *rblock0, aoff64_t *rnblocks)
893{
894 fdisk_free_range_t fr;
895 uint64_t b0;
896 uint64_t nb;
897
898 fdisk_free_range_first(dev, spc, &fr);
899 do {
900 if (fdisk_free_range_get(&fr, &b0, &nb)) {
901 if (nb >= nblocks) {
902 *rblock0 = b0;
903 *rnblocks = nb;
904 return EOK;
905 }
906 }
907 } while (fdisk_free_range_next(&fr));
908
909 /* No conforming free range found */
910 return ENOSPC;
911}
912
913/** Get largest free range of blocks.
914 *
915 * Get free range of blocks of the maximum size.
916 */
917static int fdisk_part_get_max_free_range(fdisk_dev_t *dev, fdisk_spc_t spc,
918 aoff64_t *rblock0, aoff64_t *rnblocks)
919{
920 fdisk_free_range_t fr;
921 uint64_t b0;
922 uint64_t nb;
923 uint64_t best_b0;
924 uint64_t best_nb;
925
926 best_b0 = best_nb = 0;
927 fdisk_free_range_first(dev, spc, &fr);
928 do {
929 if (fdisk_free_range_get(&fr, &b0, &nb)) {
930 if (nb > best_nb) {
931 best_b0 = b0;
932 best_nb = nb;
933 }
934 }
935 } while (fdisk_free_range_next(&fr));
936
937 if (best_nb == 0)
938 return ENOSPC;
939
940 *rblock0 = best_b0;
941 *rnblocks = best_nb;
942 return EOK;
943}
944
945/** Prepare new partition specification for VBD. */
946static int fdisk_part_spec_prepare(fdisk_dev_t *dev, fdisk_part_spec_t *pspec,
947 vbd_part_spec_t *vpspec)
948{
949 aoff64_t nom_blocks;
950 aoff64_t min_blocks;
951 aoff64_t max_blocks;
952 aoff64_t act_blocks;
953 aoff64_t fblock0;
954 aoff64_t fnblocks;
955 aoff64_t hdrb;
956 label_pcnt_t pcnt;
957 fdisk_spc_t spc;
958 int index;
959 int rc;
960
961 rc = fdisk_cap_to_blocks(&pspec->capacity, fcv_nom, dev->dinfo.block_size,
962 &nom_blocks);
963 if (rc != EOK)
964 return rc;
965
966 rc = fdisk_cap_to_blocks(&pspec->capacity, fcv_min, dev->dinfo.block_size,
967 &min_blocks);
968 if (rc != EOK)
969 return rc;
970
971 rc = fdisk_cap_to_blocks(&pspec->capacity, fcv_max, dev->dinfo.block_size,
972 &max_blocks);
973 if (rc != EOK)
974 return rc;
975
976 nom_blocks = fdisk_ba_align_up(dev, nom_blocks);
977 min_blocks = fdisk_ba_align_up(dev, min_blocks);
978 max_blocks = fdisk_ba_align_up(dev, max_blocks);
979
980 pcnt = -1;
981
982 switch (pspec->fstype) {
983 case fs_exfat:
984 pcnt = lpc_exfat;
985 break;
986 case fs_fat:
987 pcnt = lpc_fat32; /* XXX Detect FAT12/16 vs FAT32 */
988 break;
989 case fs_minix:
990 pcnt = lpc_minix;
991 break;
992 case fs_ext4:
993 pcnt = lpc_ext4;
994 break;
995 }
996
997 if (pcnt < 0)
998 return EINVAL;
999
1000 if (pspec->pkind == lpk_logical) {
1001 hdrb = max(1, dev->align);
1002 spc = spc_log;
1003 } else {
1004 hdrb = 0;
1005 spc = spc_pri;
1006 }
1007
1008 rc = fdisk_part_get_free_range(dev, hdrb + nom_blocks, spc,
1009 &fblock0, &fnblocks);
1010
1011 if (rc == EOK) {
1012 /*
1013 * If the size of the free range would still give the same capacity
1014 * when rounded, allocate entire range. Otherwise allocate exactly
1015 * what we were asked for.
1016 */
1017 if (fnblocks <= max_blocks) {
1018 act_blocks = fnblocks;
1019 } else {
1020 act_blocks = hdrb + nom_blocks;
1021 }
1022 } else {
1023 assert(rc == ENOSPC);
1024
1025 /*
1026 * There is no free range that can contain exactly the requested
1027 * capacity. Try to allocate at least such number of blocks
1028 * that would still fullfill the request within the limits
1029 * of the precision with witch the capacity was specified
1030 * (i.e. when rounded up).
1031 */
1032 rc = fdisk_part_get_free_range(dev, hdrb + min_blocks, spc,
1033 &fblock0, &fnblocks);
1034 if (rc != EOK)
1035 return rc;
1036
1037 assert(fnblocks < hdrb + nom_blocks);
1038 act_blocks = fnblocks;
1039 }
1040
1041 if (pspec->pkind != lpk_logical) {
1042 rc = fdisk_part_get_free_idx(dev, &index);
1043 if (rc != EOK)
1044 return EIO;
1045 } else {
1046 index = 0;
1047 }
1048
1049 memset(vpspec, 0, sizeof(vbd_part_spec_t));
1050 vpspec->index = index;
1051 vpspec->hdr_blocks = hdrb;
1052 vpspec->block0 = fblock0 + hdrb;
1053 vpspec->nblocks = act_blocks - hdrb;
1054 vpspec->pkind = pspec->pkind;
1055
1056 if (pspec->pkind != lpk_extended) {
1057 rc = vbd_suggest_ptype(dev->fdisk->vbd, dev->sid, pcnt,
1058 &vpspec->ptype);
1059 if (rc != EOK)
1060 return EIO;
1061 }
1062
1063 return EOK;
1064}
1065
1066static int fdisk_update_dev_info(fdisk_dev_t *dev)
1067{
1068 int rc;
1069 size_t align_bytes;
1070 uint64_t avail_cap;
1071
1072 rc = vbd_disk_info(dev->fdisk->vbd, dev->sid, &dev->dinfo);
1073 if (rc != EOK)
1074 return EIO;
1075
1076 /* Capacity available for partition in bytes */
1077 avail_cap = dev->dinfo.anblocks * dev->dinfo.block_size;
1078
1079 /* Determine optimum alignment */
1080 align_bytes = 1024 * 1024; /* 1 MiB */
1081 while (align_bytes > 1 && avail_cap / align_bytes < 256) {
1082 align_bytes = align_bytes / 16;
1083 }
1084
1085 dev->align = align_bytes / dev->dinfo.block_size;
1086 if (dev->align < 1)
1087 dev->align = 1;
1088 return EOK;
1089}
1090
1091static uint64_t fdisk_ba_align_up(fdisk_dev_t *dev, uint64_t ba)
1092{
1093 return ((ba + dev->align - 1) / dev->align) * dev->align;
1094}
1095
1096static uint64_t fdisk_ba_align_down(fdisk_dev_t *dev, uint64_t ba)
1097{
1098 return ba - (ba % dev->align);
1099}
1100
1101static void fdisk_free_range_first(fdisk_dev_t *dev, fdisk_spc_t spc,
1102 fdisk_free_range_t *fr)
1103{
1104 link_t *link;
1105
1106 fr->dev = dev;
1107 fr->spc = spc;
1108
1109 if (fr->spc == spc_pri) {
1110 /* Primary partitions */
1111 fr->b0 = fdisk_ba_align_up(fr->dev, fr->dev->dinfo.ablock0);
1112
1113 link = list_first(&dev->pri_ba);
1114 if (link != NULL)
1115 fr->npart = list_get_instance(link, fdisk_part_t, lpri_ba);
1116 else
1117 fr->npart = NULL;
1118 } else { /* fr->spc == spc_log */
1119 /* Logical partitions */
1120 fr->b0 = fdisk_ba_align_up(fr->dev, fr->dev->ext_part->block0);
1121
1122 link = list_first(&dev->log_ba);
1123 if (link != NULL)
1124 fr->npart = list_get_instance(link, fdisk_part_t, llog_ba);
1125 else
1126 fr->npart = NULL;
1127 }
1128}
1129
1130static bool fdisk_free_range_next(fdisk_free_range_t *fr)
1131{
1132 link_t *link;
1133
1134 if (fr->npart == NULL)
1135 return false;
1136
1137 fr->b0 = fdisk_ba_align_up(fr->dev, fr->npart->block0 +
1138 fr->npart->nblocks);
1139
1140 if (fr->spc == spc_pri) {
1141 /* Primary partitions */
1142 link = list_next(&fr->npart->lpri_ba, &fr->dev->pri_ba);
1143 if (link != NULL)
1144 fr->npart = list_get_instance(link, fdisk_part_t, lpri_ba);
1145 else
1146 fr->npart = NULL;
1147 } else { /* fr->spc == spc_log */
1148 /* Logical partitions */
1149 link = list_next(&fr->npart->llog_ba, &fr->dev->log_ba);
1150 if (link != NULL)
1151 fr->npart = list_get_instance(link, fdisk_part_t, llog_ba);
1152 else
1153 fr->npart = NULL;
1154 }
1155
1156 return true;
1157}
1158
1159static bool fdisk_free_range_get(fdisk_free_range_t *fr,
1160 aoff64_t *b0, aoff64_t *nb)
1161{
1162 aoff64_t b1;
1163
1164 if (fr->npart != NULL) {
1165 b1 = fdisk_ba_align_down(fr->dev, fr->npart->block0);
1166 } else {
1167 if (fr->spc == spc_pri) {
1168 b1 = fdisk_ba_align_down(fr->dev,
1169 fr->dev->dinfo.ablock0 + fr->dev->dinfo.anblocks);
1170 } else { /* fr->spc == spc_log */
1171 b1 = fdisk_ba_align_down(fr->dev,
1172 fr->dev->ext_part->block0 + fr->dev->ext_part->nblocks);
1173 }
1174 }
1175
1176 if (b1 < fr->b0)
1177 return false;
1178
1179 *b0 = fr->b0;
1180 *nb = b1 - fr->b0;
1181
1182 return true;
1183}
1184
1185/** @}
1186 */
Note: See TracBrowser for help on using the repository browser.