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

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

Most of extended (but not logical) partition support.

  • Property mode set to 100644
File size: 15.3 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_part_spec_prepare(fdisk_dev_t *, fdisk_part_spec_t *,
61 vbd_part_spec_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 rc = vol_get_disks(fdisk->vol, &svcs, &count);
131 if (rc != EOK) {
132 rc = EIO;
133 goto error;
134 }
135
136 for (i = 0; i < count; i++) {
137 info = calloc(1, sizeof(fdisk_dev_info_t));
138 if (info == NULL) {
139 rc = ENOMEM;
140 goto error;
141 }
142
143 info->svcid = svcs[i];
144 info->devlist = devlist;
145 list_append(&info->ldevlist, &devlist->devinfos);
146 }
147
148 *rdevlist = devlist;
149 free(svcs);
150 return EOK;
151error:
152 free(svcs);
153 fdisk_dev_list_free(devlist);
154 return rc;
155}
156
157void fdisk_dev_list_free(fdisk_dev_list_t *devlist)
158{
159 fdisk_dev_info_t *info;
160
161 if (devlist == NULL)
162 return;
163
164 while (!list_empty(&devlist->devinfos)) {
165 info = list_get_instance(list_first(
166 &devlist->devinfos), fdisk_dev_info_t,
167 ldevlist);
168
169 list_remove(&info->ldevlist);
170 fdisk_dev_info_delete(info);
171 }
172
173 free(devlist);
174}
175
176fdisk_dev_info_t *fdisk_dev_first(fdisk_dev_list_t *devlist)
177{
178 if (list_empty(&devlist->devinfos))
179 return NULL;
180
181 return list_get_instance(list_first(&devlist->devinfos),
182 fdisk_dev_info_t, ldevlist);
183}
184
185fdisk_dev_info_t *fdisk_dev_next(fdisk_dev_info_t *devinfo)
186{
187 link_t *lnext;
188
189 lnext = list_next(&devinfo->ldevlist,
190 &devinfo->devlist->devinfos);
191 if (lnext == NULL)
192 return NULL;
193
194 return list_get_instance(lnext, fdisk_dev_info_t,
195 ldevlist);
196}
197
198void fdisk_dev_info_get_svcid(fdisk_dev_info_t *info, service_id_t *rsid)
199{
200 *rsid = info->svcid;
201}
202
203int fdisk_dev_info_get_svcname(fdisk_dev_info_t *info, char **rname)
204{
205 char *name;
206 int rc;
207
208 if (info->svcname == NULL) {
209 rc = loc_service_get_name(info->svcid,
210 &info->svcname);
211 if (rc != EOK)
212 return rc;
213 }
214
215 name = str_dup(info->svcname);
216 if (name == NULL)
217 return ENOMEM;
218
219 *rname = name;
220 return EOK;
221}
222
223int fdisk_dev_info_capacity(fdisk_dev_info_t *info, fdisk_cap_t *cap)
224{
225 size_t bsize;
226 aoff64_t nblocks;
227 int rc;
228
229 if (!info->blk_inited) {
230 rc = block_init(EXCHANGE_SERIALIZE, info->svcid, 2048);
231 if (rc != EOK)
232 return rc;
233
234 info->blk_inited = true;
235 }
236
237 rc = block_get_bsize(info->svcid, &bsize);
238 if (rc != EOK)
239 return EIO;
240
241 rc = block_get_nblocks(info->svcid, &nblocks);
242 if (rc != EOK)
243 return EIO;
244
245 cap->value = bsize * nblocks;
246 cap->cunit = cu_byte;
247
248 return EOK;
249}
250
251static int fdisk_part_add(fdisk_dev_t *dev, vbd_part_id_t partid,
252 fdisk_part_t **rpart)
253{
254 fdisk_part_t *part, *p;
255 vbd_part_info_t pinfo;
256 link_t *link;
257 int rc;
258
259 part = calloc(1, sizeof(fdisk_part_t));
260 if (part == NULL)
261 return ENOMEM;
262
263 rc = vbd_part_get_info(dev->fdisk->vbd, partid, &pinfo);
264 if (rc != EOK) {
265 rc = EIO;
266 goto error;
267 }
268
269 part->dev = dev;
270 part->index = pinfo.index;
271 part->block0 = pinfo.block0;
272 part->nblocks = pinfo.nblocks;
273 part->pkind = pinfo.pkind;
274
275 /* Insert to list by block address */
276 link = list_first(&dev->parts_ba);
277 while (link != NULL) {
278 p = list_get_instance(link, fdisk_part_t, ldev_ba);
279 if (p->block0 > part->block0) {
280 list_insert_before(&part->ldev_ba, &p->ldev_ba);
281 break;
282 }
283
284 link = list_next(link, &dev->parts_ba);
285 }
286
287 if (link == NULL)
288 list_append(&part->ldev_ba, &dev->parts_ba);
289
290 /* Insert to list by index */
291 link = list_first(&dev->parts_idx);
292 while (link != NULL) {
293 p = list_get_instance(link, fdisk_part_t, ldev_idx);
294 if (p->index > part->index) {
295 list_insert_before(&part->ldev_idx, &p->ldev_idx);
296 break;
297 }
298
299 link = list_next(link, &dev->parts_idx);
300 }
301
302 if (link == NULL)
303 list_append(&part->ldev_idx, &dev->parts_idx);
304
305 part->capacity.cunit = cu_byte;
306 part->capacity.value = part->nblocks * dev->dinfo.block_size;
307 part->part_id = partid;
308
309 if (rpart != NULL)
310 *rpart = part;
311 return EOK;
312error:
313 free(part);
314 return rc;
315}
316
317
318int fdisk_dev_open(fdisk_t *fdisk, service_id_t sid, fdisk_dev_t **rdev)
319{
320 vol_disk_info_t vinfo;
321 fdisk_dev_t *dev = NULL;
322 service_id_t *psids = NULL;
323 size_t nparts, i;
324 int rc;
325
326 dev = calloc(1, sizeof(fdisk_dev_t));
327 if (dev == NULL)
328 return ENOMEM;
329
330 dev->fdisk = fdisk;
331 dev->sid = sid;
332 list_initialize(&dev->parts_idx);
333 list_initialize(&dev->parts_ba);
334
335 rc = vol_disk_info(fdisk->vol, sid, &vinfo);
336 if (rc != EOK) {
337 rc = EIO;
338 goto error;
339 }
340
341 dev->dcnt = vinfo.dcnt;
342
343 if (dev->dcnt != dc_label)
344 goto done;
345
346 printf("get label info\n");
347 rc = vbd_disk_info(fdisk->vbd, sid, &dev->dinfo);
348 if (rc != EOK) {
349 printf("failed\n");
350 rc = EIO;
351 goto error;
352 }
353
354 printf("block size: %zu\n", dev->dinfo.block_size);
355 printf("get partitions\n");
356 rc = vbd_label_get_parts(fdisk->vbd, sid, &psids, &nparts);
357 if (rc != EOK) {
358 printf("failed\n");
359 rc = EIO;
360 goto error;
361 }
362 printf("OK\n");
363
364 printf("found %zu partitions.\n", nparts);
365 for (i = 0; i < nparts; i++) {
366 printf("add partition sid=%zu\n", psids[i]);
367 rc = fdisk_part_add(dev, psids[i], NULL);
368 if (rc != EOK) {
369 printf("failed\n");
370 goto error;
371 }
372 printf("OK\n");
373 }
374
375 free(psids);
376done:
377 *rdev = dev;
378 return EOK;
379error:
380 fdisk_dev_close(dev);
381 return rc;
382}
383
384void fdisk_dev_close(fdisk_dev_t *dev)
385{
386 if (dev == NULL)
387 return;
388
389 /* XXX Clean up partitions */
390 free(dev);
391}
392
393int fdisk_dev_get_svcname(fdisk_dev_t *dev, char **rname)
394{
395 char *name;
396 int rc;
397
398 rc = loc_service_get_name(dev->sid, &name);
399 if (rc != EOK)
400 return rc;
401
402 *rname = name;
403 return EOK;
404}
405
406int fdisk_dev_capacity(fdisk_dev_t *dev, fdisk_cap_t *cap)
407{
408 size_t bsize;
409 aoff64_t nblocks;
410 int rc;
411
412 rc = block_init(EXCHANGE_SERIALIZE, dev->sid, 2048);
413 if (rc != EOK)
414 return rc;
415
416 rc = block_get_bsize(dev->sid, &bsize);
417 if (rc != EOK)
418 return EIO;
419
420 rc = block_get_nblocks(dev->sid, &nblocks);
421 if (rc != EOK)
422 return EIO;
423
424 block_fini(dev->sid);
425
426 cap->value = bsize * nblocks;
427 cap->cunit = cu_byte;
428
429 return EOK;
430}
431
432int fdisk_label_get_info(fdisk_dev_t *dev, fdisk_label_info_t *info)
433{
434 vol_disk_info_t vinfo;
435 int rc;
436
437 rc = vol_disk_info(dev->fdisk->vol, dev->sid, &vinfo);
438 if (rc != EOK) {
439 rc = EIO;
440 goto error;
441 }
442
443 info->dcnt = vinfo.dcnt;
444 info->ltype = vinfo.ltype;
445 info->flags = vinfo.flags;
446 return EOK;
447error:
448 return rc;
449}
450
451int fdisk_label_create(fdisk_dev_t *dev, label_type_t ltype)
452{
453 return vol_label_create(dev->fdisk->vol, dev->sid, ltype);
454}
455
456int fdisk_label_destroy(fdisk_dev_t *dev)
457{
458 fdisk_part_t *part;
459 int rc;
460
461 part = fdisk_part_first(dev);
462 while (part != NULL) {
463 (void) fdisk_part_destroy(part); /* XXX */
464 part = fdisk_part_first(dev);
465 }
466
467 rc = vol_disk_empty(dev->fdisk->vol, dev->sid);
468 if (rc != EOK)
469 return EIO;
470
471 dev->dcnt = dc_empty;
472 return EOK;
473}
474
475fdisk_part_t *fdisk_part_first(fdisk_dev_t *dev)
476{
477 link_t *link;
478
479 link = list_first(&dev->parts_ba);
480 if (link == NULL)
481 return NULL;
482
483 return list_get_instance(link, fdisk_part_t, ldev_ba);
484}
485
486fdisk_part_t *fdisk_part_next(fdisk_part_t *part)
487{
488 link_t *link;
489
490 link = list_next(&part->ldev_ba, &part->dev->parts_ba);
491 if (link == NULL)
492 return NULL;
493
494 return list_get_instance(link, fdisk_part_t, ldev_ba);
495}
496
497int fdisk_part_get_info(fdisk_part_t *part, fdisk_part_info_t *info)
498{
499 info->capacity = part->capacity;
500 info->fstype = part->fstype;
501 info->pkind = part->pkind;
502 return EOK;
503}
504
505int fdisk_part_get_max_avail(fdisk_dev_t *dev, fdisk_cap_t *cap)
506{
507 return EOK;
508}
509
510int fdisk_part_create(fdisk_dev_t *dev, fdisk_part_spec_t *pspec,
511 fdisk_part_t **rpart)
512{
513 fdisk_part_t *part;
514 vbd_part_spec_t vpspec;
515 vbd_part_id_t partid;
516 int rc;
517
518 printf("fdisk_part_create()\n");
519
520 rc = fdisk_part_spec_prepare(dev, pspec, &vpspec);
521 if (rc != EOK)
522 return EIO;
523
524 printf("fdisk_part_create() - call vbd_part_create\n");
525 rc = vbd_part_create(dev->fdisk->vbd, dev->sid, &vpspec, &partid);
526 if (rc != EOK)
527 return EIO;
528
529 printf("fdisk_part_create() - call fdisk_part_add\n");
530 rc = fdisk_part_add(dev, partid, &part);
531 if (rc != EOK) {
532 /* Try rolling back */
533 (void) vbd_part_delete(dev->fdisk->vbd, partid);
534 return EIO;
535 }
536
537 printf("fdisk_part_create() - done\n");
538 part->fstype = pspec->fstype;
539 part->capacity = pspec->capacity;
540
541 if (rpart != NULL)
542 *rpart = part;
543 return EOK;
544}
545
546int fdisk_part_destroy(fdisk_part_t *part)
547{
548 int rc;
549
550 rc = vbd_part_delete(part->dev->fdisk->vbd, part->part_id);
551 if (rc != EOK)
552 return EIO;
553
554 list_remove(&part->ldev_ba);
555 list_remove(&part->ldev_idx);
556 free(part);
557 return EOK;
558}
559
560void fdisk_pspec_init(fdisk_part_spec_t *pspec)
561{
562 memset(pspec, 0, sizeof(fdisk_part_spec_t));
563}
564
565int fdisk_cap_format(fdisk_cap_t *cap, char **rstr)
566{
567 int rc;
568 const char *sunit;
569
570 sunit = NULL;
571
572 if (cap->cunit < 0 || cap->cunit >= CU_LIMIT)
573 assert(false);
574
575 sunit = cu_str[cap->cunit];
576 rc = asprintf(rstr, "%" PRIu64 " %s", cap->value, sunit);
577 if (rc < 0)
578 return ENOMEM;
579
580 return EOK;
581}
582
583int fdisk_cap_parse(const char *str, fdisk_cap_t *cap)
584{
585 char *eptr;
586 char *p;
587 unsigned long val;
588 int i;
589
590 val = strtoul(str, &eptr, 10);
591
592 while (*eptr == ' ')
593 ++eptr;
594
595 if (*eptr == '\0') {
596 cap->cunit = cu_byte;
597 } else {
598 for (i = 0; i < CU_LIMIT; i++) {
599 if (str_lcasecmp(eptr, cu_str[i],
600 str_length(cu_str[i])) == 0) {
601 p = eptr + str_size(cu_str[i]);
602 while (*p == ' ')
603 ++p;
604 if (*p == '\0')
605 goto found;
606 }
607 }
608
609 return EINVAL;
610found:
611 cap->cunit = i;
612 }
613
614 cap->value = val;
615 return EOK;
616}
617
618int fdisk_ltype_format(label_type_t ltype, char **rstr)
619{
620 const char *sltype;
621 char *s;
622
623 sltype = NULL;
624 switch (ltype) {
625 case lt_mbr:
626 sltype = "MBR";
627 break;
628 case lt_gpt:
629 sltype = "GPT";
630 break;
631 }
632
633 s = str_dup(sltype);
634 if (s == NULL)
635 return ENOMEM;
636
637 *rstr = s;
638 return EOK;
639}
640
641int fdisk_fstype_format(fdisk_fstype_t fstype, char **rstr)
642{
643 const char *sfstype;
644 char *s;
645
646 sfstype = NULL;
647 switch (fstype) {
648 case fdfs_none:
649 sfstype = "None";
650 break;
651 case fdfs_unknown:
652 sfstype = "Unknown";
653 break;
654 case fdfs_exfat:
655 sfstype = "ExFAT";
656 break;
657 case fdfs_fat:
658 sfstype = "FAT";
659 break;
660 case fdfs_minix:
661 sfstype = "MINIX";
662 break;
663 case fdfs_ext4:
664 sfstype = "Ext4";
665 break;
666 }
667
668 s = str_dup(sfstype);
669 if (s == NULL)
670 return ENOMEM;
671
672 *rstr = s;
673 return EOK;
674}
675
676int fdisk_pkind_format(label_pkind_t pkind, char **rstr)
677{
678 const char *spkind;
679 char *s;
680
681 spkind = NULL;
682 switch (pkind) {
683 case lpk_primary:
684 spkind = "Primary";
685 break;
686 case lpk_extended:
687 spkind = "Extended";
688 break;
689 case lpk_logical:
690 spkind = "Logical";
691 break;
692 }
693
694 s = str_dup(spkind);
695 if (s == NULL)
696 return ENOMEM;
697
698 *rstr = s;
699 return EOK;
700}
701
702/** Get free partition index. */
703static int fdisk_part_get_free_idx(fdisk_dev_t *dev, int *rindex)
704{
705 link_t *link;
706 fdisk_part_t *part;
707 int nidx;
708
709 link = list_first(&dev->parts_idx);
710 nidx = 1;
711 while (link != NULL) {
712 part = list_get_instance(link, fdisk_part_t, ldev_idx);
713 if (part->index > nidx)
714 break;
715 nidx = part->index + 1;
716 link = list_next(link, &dev->parts_idx);
717 }
718
719 if (nidx > 4 /* XXXX actual number of slots*/) {
720 return ELIMIT;
721 }
722
723 *rindex = nidx;
724 return EOK;
725}
726
727/** Get free range of blocks.
728 *
729 * Get free range of blocks of at least the specified size (first fit).
730 */
731static int fdisk_part_get_free_range(fdisk_dev_t *dev, aoff64_t nblocks,
732 aoff64_t *rblock0, aoff64_t *rnblocks)
733{
734 link_t *link;
735 fdisk_part_t *part;
736 uint64_t avail;
737 int nba;
738
739 link = list_first(&dev->parts_ba);
740 nba = dev->dinfo.ablock0;
741 while (link != NULL) {
742 part = list_get_instance(link, fdisk_part_t, ldev_ba);
743 if (part->block0 - nba >= nblocks)
744 break;
745 nba = part->block0 + part->nblocks;
746 link = list_next(link, &dev->parts_ba);
747 }
748
749 if (link != NULL) {
750 /* Free range before a partition */
751 avail = part->block0 - nba;
752 } else {
753 /* Free range at the end */
754 avail = dev->dinfo.ablock0 + dev->dinfo.anblocks - nba;
755
756 /* Verify that the range is large enough */
757 if (avail < nblocks)
758 return ELIMIT;
759 }
760
761 *rblock0 = nba;
762 *rnblocks = avail;
763 return EOK;
764}
765
766/** Prepare new partition specification for VBD. */
767static int fdisk_part_spec_prepare(fdisk_dev_t *dev, fdisk_part_spec_t *pspec,
768 vbd_part_spec_t *vpspec)
769{
770 uint64_t cbytes;
771 aoff64_t req_blocks;
772 aoff64_t fblock0;
773 aoff64_t fnblocks;
774 uint64_t block_size;
775 unsigned i;
776 int index;
777 int rc;
778
779// pspec->fstype
780 printf("fdisk_part_spec_prepare()\n");
781 block_size = dev->dinfo.block_size;
782 cbytes = pspec->capacity.value;
783 for (i = 0; i < pspec->capacity.cunit; i++)
784 cbytes = cbytes * 1000;
785
786 req_blocks = (cbytes + block_size - 1) / block_size;
787
788 rc = fdisk_part_get_free_idx(dev, &index);
789 if (rc != EOK)
790 return EIO;
791
792 rc = fdisk_part_get_free_range(dev, req_blocks, &fblock0, &fnblocks);
793 if (rc != EOK)
794 return EIO;
795
796 memset(vpspec, 0, sizeof(vbd_part_spec_t));
797 vpspec->index = index;
798 vpspec->block0 = fblock0;
799 vpspec->nblocks = req_blocks;
800 vpspec->pkind = pspec->pkind;
801 if (pspec->pkind != lpk_extended)
802 vpspec->ptype = 42;
803
804 return EOK;
805}
806
807/** @}
808 */
Note: See TracBrowser for help on using the repository browser.