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

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

Suggest maximum available free space as the default for partition size.

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