source: mainline/uspace/lib/fdisk/src/fdisk.c@ 03661d19

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

Handle simplified capacity entry.

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