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

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

Display total free space size.

  • Property mode set to 100644
File size: 27.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 <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
765/** Get size of largest free block. */
766int fdisk_part_get_max_avail(fdisk_dev_t *dev, fdisk_spc_t spc, fdisk_cap_t *cap)
767{
768 int rc;
769 uint64_t b0;
770 uint64_t nb;
771 aoff64_t hdrb;
772
773 rc = fdisk_part_get_max_free_range(dev, spc, &b0, &nb);
774 if (rc != EOK)
775 return rc;
776
777 /* For logical partitions we need to subtract header size */
778 if (spc == spc_log) {
779 hdrb = max(1, dev->align);
780 if (nb <= hdrb)
781 return ENOSPC;
782 nb -= hdrb;
783 }
784
785 cap->value = nb * dev->dinfo.block_size;
786 cap->cunit = cu_byte;
787 return EOK;
788}
789
790/** Get total free space capacity. */
791int fdisk_part_get_tot_avail(fdisk_dev_t *dev, fdisk_spc_t spc,
792 fdisk_cap_t *cap)
793{
794 fdisk_free_range_t fr;
795 uint64_t hdrb;
796 uint64_t b0;
797 uint64_t nb;
798 uint64_t totb;
799
800 if (spc == spc_log)
801 hdrb = max(1, dev->align);
802 else
803 hdrb = 0;
804
805 totb = 0;
806 fdisk_free_range_first(dev, spc, &fr);
807 do {
808 if (fdisk_free_range_get(&fr, &b0, &nb)) {
809 if (nb > hdrb)
810 totb += nb - hdrb;
811 }
812 } while (fdisk_free_range_next(&fr));
813
814 cap->value = totb * dev->dinfo.block_size;
815 cap->cunit = cu_byte;
816 return EOK;
817}
818
819int fdisk_part_create(fdisk_dev_t *dev, fdisk_part_spec_t *pspec,
820 fdisk_part_t **rpart)
821{
822 fdisk_part_t *part;
823 vbd_part_spec_t vpspec;
824 vbd_part_id_t partid;
825 int rc;
826
827 printf("fdisk_part_create()\n");
828
829 rc = fdisk_part_spec_prepare(dev, pspec, &vpspec);
830 if (rc != EOK)
831 return EIO;
832
833 printf("fdisk_part_create() - call vbd_part_create\n");
834 rc = vbd_part_create(dev->fdisk->vbd, dev->sid, &vpspec, &partid);
835 if (rc != EOK)
836 return EIO;
837
838 printf("fdisk_part_create() - call fdisk_part_add\n");
839 rc = fdisk_part_add(dev, partid, &part);
840 if (rc != EOK) {
841 /* Try rolling back */
842 (void) vbd_part_delete(dev->fdisk->vbd, partid);
843 return EIO;
844 }
845
846 if (part->svc_id != 0) {
847 rc = vol_part_mkfs(dev->fdisk->vol, part->svc_id, pspec->fstype);
848 if (rc != EOK && rc != ENOTSUP) {
849 printf("mkfs failed\n");
850 fdisk_part_remove(part);
851 (void) vbd_part_delete(dev->fdisk->vbd, partid);
852 return EIO;
853 }
854 }
855
856 printf("fdisk_part_create() - done\n");
857 if (part->svc_id != 0) {
858 part->pcnt = vpc_fs;
859 part->fstype = pspec->fstype;
860 }
861
862 part->capacity = pspec->capacity;
863
864 if (rpart != NULL)
865 *rpart = part;
866 return EOK;
867}
868
869int fdisk_part_destroy(fdisk_part_t *part)
870{
871 int rc;
872
873 rc = vbd_part_delete(part->dev->fdisk->vbd, part->part_id);
874 if (rc != EOK)
875 return EIO;
876
877 fdisk_part_remove(part);
878 return EOK;
879}
880
881void fdisk_pspec_init(fdisk_part_spec_t *pspec)
882{
883 memset(pspec, 0, sizeof(fdisk_part_spec_t));
884}
885
886int fdisk_cap_format(fdisk_cap_t *cap, char **rstr)
887{
888 int rc;
889 const char *sunit;
890
891 sunit = NULL;
892
893 if (cap->cunit < 0 || cap->cunit >= CU_LIMIT)
894 assert(false);
895
896 sunit = cu_str[cap->cunit];
897 rc = asprintf(rstr, "%" PRIu64 " %s", cap->value, sunit);
898 if (rc < 0)
899 return ENOMEM;
900
901 return EOK;
902}
903
904int fdisk_cap_parse(const char *str, fdisk_cap_t *cap)
905{
906 char *eptr;
907 char *p;
908 unsigned long val;
909 int i;
910
911 val = strtoul(str, &eptr, 10);
912
913 while (*eptr == ' ')
914 ++eptr;
915
916 if (*eptr == '\0') {
917 cap->cunit = cu_byte;
918 } else {
919 for (i = 0; i < CU_LIMIT; i++) {
920 if (str_lcasecmp(eptr, cu_str[i],
921 str_length(cu_str[i])) == 0) {
922 p = eptr + str_size(cu_str[i]);
923 while (*p == ' ')
924 ++p;
925 if (*p == '\0')
926 goto found;
927 }
928 }
929
930 return EINVAL;
931found:
932 cap->cunit = i;
933 }
934
935 cap->value = val;
936 return EOK;
937}
938
939int fdisk_ltype_format(label_type_t ltype, char **rstr)
940{
941 const char *sltype;
942 char *s;
943
944 sltype = NULL;
945 switch (ltype) {
946 case lt_none:
947 sltype = "None";
948 break;
949 case lt_mbr:
950 sltype = "MBR";
951 break;
952 case lt_gpt:
953 sltype = "GPT";
954 break;
955 }
956
957 s = str_dup(sltype);
958 if (s == NULL)
959 return ENOMEM;
960
961 *rstr = s;
962 return EOK;
963}
964
965int fdisk_fstype_format(vol_fstype_t fstype, char **rstr)
966{
967 const char *sfstype;
968 char *s;
969
970 sfstype = NULL;
971 switch (fstype) {
972 case fs_exfat:
973 sfstype = "ExFAT";
974 break;
975 case fs_fat:
976 sfstype = "FAT";
977 break;
978 case fs_minix:
979 sfstype = "MINIX";
980 break;
981 case fs_ext4:
982 sfstype = "Ext4";
983 break;
984 }
985
986 s = str_dup(sfstype);
987 if (s == NULL)
988 return ENOMEM;
989
990 *rstr = s;
991 return EOK;
992}
993
994int fdisk_pkind_format(label_pkind_t pkind, char **rstr)
995{
996 const char *spkind;
997 char *s;
998
999 spkind = NULL;
1000 switch (pkind) {
1001 case lpk_primary:
1002 spkind = "Primary";
1003 break;
1004 case lpk_extended:
1005 spkind = "Extended";
1006 break;
1007 case lpk_logical:
1008 spkind = "Logical";
1009 break;
1010 }
1011
1012 s = str_dup(spkind);
1013 if (s == NULL)
1014 return ENOMEM;
1015
1016 *rstr = s;
1017 return EOK;
1018}
1019
1020/** Get free partition index. */
1021static int fdisk_part_get_free_idx(fdisk_dev_t *dev, int *rindex)
1022{
1023 link_t *link;
1024 fdisk_part_t *part;
1025 int nidx;
1026
1027 link = list_first(&dev->pri_idx);
1028 nidx = 1;
1029 while (link != NULL) {
1030 part = list_get_instance(link, fdisk_part_t, lpri_idx);
1031 if (part->index > nidx)
1032 break;
1033 nidx = part->index + 1;
1034 link = list_next(link, &dev->pri_idx);
1035 }
1036
1037 if (nidx > 4 /* XXXX actual number of slots*/) {
1038 return ELIMIT;
1039 }
1040
1041 *rindex = nidx;
1042 return EOK;
1043}
1044
1045/** Get free range of blocks.
1046 *
1047 * Get free range of blocks of at least the specified size (first fit).
1048 */
1049static int fdisk_part_get_free_range(fdisk_dev_t *dev, aoff64_t nblocks,
1050 fdisk_spc_t spc, aoff64_t *rblock0, aoff64_t *rnblocks)
1051{
1052 fdisk_free_range_t fr;
1053 uint64_t b0;
1054 uint64_t nb;
1055
1056 fdisk_free_range_first(dev, spc, &fr);
1057 do {
1058 if (fdisk_free_range_get(&fr, &b0, &nb)) {
1059 printf("free range: [%" PRIu64 ",+%" PRIu64 "]\n",
1060 b0, nb);
1061 if (nb >= nblocks) {
1062 printf("accepted.\n");
1063 *rblock0 = b0;
1064 *rnblocks = nb;
1065 return EOK;
1066 }
1067 }
1068 } while (fdisk_free_range_next(&fr));
1069
1070 /* No conforming free range found */
1071 return ENOSPC;
1072}
1073
1074/** Get largest free range of blocks.
1075 *
1076 * Get free range of blocks of the maximum size.
1077 */
1078static int fdisk_part_get_max_free_range(fdisk_dev_t *dev, fdisk_spc_t spc,
1079 aoff64_t *rblock0, aoff64_t *rnblocks)
1080{
1081 fdisk_free_range_t fr;
1082 uint64_t b0;
1083 uint64_t nb;
1084 uint64_t best_b0;
1085 uint64_t best_nb;
1086
1087 best_b0 = best_nb = 0;
1088 fdisk_free_range_first(dev, spc, &fr);
1089 do {
1090 if (fdisk_free_range_get(&fr, &b0, &nb)) {
1091 if (nb > best_nb) {
1092 best_b0 = b0;
1093 best_nb = nb;
1094 }
1095 }
1096 } while (fdisk_free_range_next(&fr));
1097
1098 if (best_nb == 0)
1099 return ENOSPC;
1100
1101 *rblock0 = best_b0;
1102 *rnblocks = best_nb;
1103 return EOK;
1104}
1105
1106/** Prepare new partition specification for VBD. */
1107static int fdisk_part_spec_prepare(fdisk_dev_t *dev, fdisk_part_spec_t *pspec,
1108 vbd_part_spec_t *vpspec)
1109{
1110 uint64_t cbytes;
1111 aoff64_t req_blocks;
1112 aoff64_t fblock0;
1113 aoff64_t fnblocks;
1114 aoff64_t hdrb;
1115 uint64_t block_size;
1116 label_pcnt_t pcnt;
1117 unsigned i;
1118 int index;
1119 int rc;
1120
1121 printf("fdisk_part_spec_prepare() - dev=%p pspec=%p vpspec=%p\n", dev, pspec,
1122 vpspec);
1123 printf("fdisk_part_spec_prepare() - block size\n");
1124 block_size = dev->dinfo.block_size;
1125 printf("fdisk_part_spec_prepare() - cbytes\n");
1126 cbytes = pspec->capacity.value;
1127 printf("fdisk_part_spec_prepare() - cunit\n");
1128 for (i = 0; i < pspec->capacity.cunit; i++)
1129 cbytes = cbytes * 1000;
1130
1131 printf("fdisk_part_spec_prepare() - req_blocks block_size=%zu\n",
1132 block_size);
1133 req_blocks = (cbytes + block_size - 1) / block_size;
1134 req_blocks = fdisk_ba_align_up(dev, req_blocks);
1135
1136 pcnt = -1;
1137
1138 switch (pspec->fstype) {
1139 case fs_exfat:
1140 pcnt = lpc_exfat;
1141 break;
1142 case fs_fat:
1143 pcnt = lpc_fat32; /* XXX Detect FAT12/16 vs FAT32 */
1144 break;
1145 case fs_minix:
1146 pcnt = lpc_minix;
1147 break;
1148 case fs_ext4:
1149 pcnt = lpc_ext4;
1150 break;
1151 }
1152
1153 if (pcnt < 0)
1154 return EINVAL;
1155
1156 printf("fdisk_part_spec_prepare() - switch\n");
1157 switch (pspec->pkind) {
1158 case lpk_primary:
1159 case lpk_extended:
1160 printf("fdisk_part_spec_prepare() - pri/ext\n");
1161 rc = fdisk_part_get_free_idx(dev, &index);
1162 if (rc != EOK)
1163 return EIO;
1164
1165 printf("fdisk_part_spec_prepare() - get free range\n");
1166 rc = fdisk_part_get_free_range(dev, req_blocks, spc_pri,
1167 &fblock0, &fnblocks);
1168 if (rc != EOK)
1169 return EIO;
1170
1171 printf("fdisk_part_spec_prepare() - memset\n");
1172 memset(vpspec, 0, sizeof(vbd_part_spec_t));
1173 vpspec->index = index;
1174 vpspec->block0 = fblock0;
1175 vpspec->nblocks = req_blocks;
1176 vpspec->pkind = pspec->pkind;
1177 break;
1178 case lpk_logical:
1179 printf("fdisk_part_spec_prepare() - log\n");
1180 hdrb = max(1, dev->align);
1181 rc = fdisk_part_get_free_range(dev, hdrb + req_blocks, spc_log,
1182 &fblock0, &fnblocks);
1183 if (rc != EOK)
1184 return EIO;
1185
1186 memset(vpspec, 0, sizeof(vbd_part_spec_t));
1187 vpspec->hdr_blocks = hdrb;
1188 vpspec->block0 = fblock0 + hdrb;
1189 vpspec->nblocks = req_blocks;
1190 vpspec->pkind = lpk_logical;
1191 break;
1192 }
1193
1194 if (pspec->pkind != lpk_extended) {
1195 rc = vbd_suggest_ptype(dev->fdisk->vbd, dev->sid, pcnt,
1196 &vpspec->ptype);
1197 if (rc != EOK)
1198 return EIO;
1199 }
1200
1201 return EOK;
1202}
1203
1204static int fdisk_update_dev_info(fdisk_dev_t *dev)
1205{
1206 int rc;
1207 size_t align_bytes;
1208 uint64_t avail_cap;
1209
1210 rc = vbd_disk_info(dev->fdisk->vbd, dev->sid, &dev->dinfo);
1211 if (rc != EOK)
1212 return EIO;
1213
1214 /* Capacity available for partition in bytes */
1215 avail_cap = dev->dinfo.anblocks * dev->dinfo.block_size;
1216
1217 /* Determine optimum alignment */
1218 align_bytes = 1024 * 1024; /* 1 MiB */
1219 while (align_bytes > 1 && avail_cap / align_bytes < 256) {
1220 align_bytes = align_bytes / 16;
1221 }
1222
1223 dev->align = align_bytes / dev->dinfo.block_size;
1224 if (dev->align < 1)
1225 dev->align = 1;
1226 return EOK;
1227}
1228
1229static uint64_t fdisk_ba_align_up(fdisk_dev_t *dev, uint64_t ba)
1230{
1231 return ((ba + dev->align - 1) / dev->align) * dev->align;
1232}
1233
1234static uint64_t fdisk_ba_align_down(fdisk_dev_t *dev, uint64_t ba)
1235{
1236 return ba - (ba % dev->align);
1237}
1238
1239static void fdisk_free_range_first(fdisk_dev_t *dev, fdisk_spc_t spc,
1240 fdisk_free_range_t *fr)
1241{
1242 link_t *link;
1243
1244 fr->dev = dev;
1245 fr->spc = spc;
1246
1247 if (fr->spc == spc_pri) {
1248 /* Primary partitions */
1249 fr->b0 = fdisk_ba_align_up(fr->dev, fr->dev->dinfo.ablock0);
1250
1251 link = list_first(&dev->pri_ba);
1252 if (link != NULL)
1253 fr->npart = list_get_instance(link, fdisk_part_t, lpri_ba);
1254 else
1255 fr->npart = NULL;
1256 } else { /* fr->spc == spc_log */
1257 /* Logical partitions */
1258 fr->b0 = fdisk_ba_align_up(fr->dev, fr->dev->ext_part->block0);
1259
1260 link = list_first(&dev->log_ba);
1261 if (link != NULL)
1262 fr->npart = list_get_instance(link, fdisk_part_t, llog_ba);
1263 else
1264 fr->npart = NULL;
1265 }
1266}
1267
1268static bool fdisk_free_range_next(fdisk_free_range_t *fr)
1269{
1270 link_t *link;
1271
1272 if (fr->npart == NULL)
1273 return false;
1274
1275 fr->b0 = fdisk_ba_align_up(fr->dev, fr->npart->block0 +
1276 fr->npart->nblocks);
1277
1278 if (fr->spc == spc_pri) {
1279 /* Primary partitions */
1280 link = list_next(&fr->npart->lpri_ba, &fr->dev->pri_ba);
1281 if (link != NULL)
1282 fr->npart = list_get_instance(link, fdisk_part_t, lpri_ba);
1283 else
1284 fr->npart = NULL;
1285 } else { /* fr->spc == spc_log */
1286 /* Logical partitions */
1287 link = list_next(&fr->npart->llog_ba, &fr->dev->log_ba);
1288 if (link != NULL)
1289 fr->npart = list_get_instance(link, fdisk_part_t, llog_ba);
1290 else
1291 fr->npart = NULL;
1292 }
1293
1294 return true;
1295}
1296
1297static bool fdisk_free_range_get(fdisk_free_range_t *fr,
1298 aoff64_t *b0, aoff64_t *nb)
1299{
1300 aoff64_t b1;
1301
1302 if (fr->npart != NULL) {
1303 b1 = fdisk_ba_align_down(fr->dev, fr->npart->block0);
1304 } else {
1305 if (fr->spc == spc_pri) {
1306 b1 = fdisk_ba_align_down(fr->dev,
1307 fr->dev->dinfo.ablock0 + fr->dev->dinfo.anblocks);
1308 } else { /* fr->spc == spc_log */
1309 b1 = fdisk_ba_align_down(fr->dev,
1310 fr->dev->ext_part->block0 + fr->dev->ext_part->nblocks);
1311 }
1312 }
1313
1314 if (b1 < fr->b0)
1315 return false;
1316
1317 *b0 = fr->b0;
1318 *nb = b1 - fr->b0;
1319
1320 return true;
1321}
1322
1323/** @}
1324 */
Note: See TracBrowser for help on using the repository browser.