source: mainline/uspace/lib/fdisk/src/fdisk.c@ 6bc542b

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

Allocate and create partition with libfdisk (except actual modification of on-disk label).

  • Property mode set to 100644
File size: 14.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_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
274 /* Insert to list by block address */
275 link = list_first(&dev->parts_ba);
276 while (link != NULL) {
277 p = list_get_instance(link, fdisk_part_t, ldev_ba);
278 if (p->block0 > part->block0) {
279 list_insert_before(&part->ldev_ba, &p->ldev_ba);
280 break;
281 }
282
283 link = list_next(link, &dev->parts_ba);
284 }
285
286 if (link == NULL)
287 list_append(&part->ldev_ba, &dev->parts_ba);
288
289 /* Insert to list by index */
290 link = list_first(&dev->parts_idx);
291 while (link != NULL) {
292 p = list_get_instance(link, fdisk_part_t, ldev_idx);
293 if (p->index > part->index) {
294 list_insert_before(&part->ldev_idx, &p->ldev_idx);
295 break;
296 }
297
298 link = list_next(link, &dev->parts_idx);
299 }
300
301 if (link == NULL)
302 list_append(&part->ldev_idx, &dev->parts_idx);
303
304 part->capacity.cunit = cu_byte;
305 part->capacity.value = part->nblocks * dev->dinfo.block_size;
306 part->part_id = partid;
307
308 if (rpart != NULL)
309 *rpart = part;
310 return EOK;
311error:
312 free(part);
313 return rc;
314}
315
316
317int fdisk_dev_open(fdisk_t *fdisk, service_id_t sid, fdisk_dev_t **rdev)
318{
319 fdisk_dev_t *dev = NULL;
320 service_id_t *psids = NULL;
321 size_t nparts, i;
322 int rc;
323
324 dev = calloc(1, sizeof(fdisk_dev_t));
325 if (dev == NULL)
326 return ENOMEM;
327
328 dev->fdisk = fdisk;
329 dev->sid = sid;
330 list_initialize(&dev->parts_idx);
331 list_initialize(&dev->parts_ba);
332
333 printf("get info\n");
334 rc = vbd_disk_info(fdisk->vbd, sid, &dev->dinfo);
335 if (rc != EOK) {
336 printf("failed\n");
337 rc = EIO;
338 goto error;
339 }
340
341 printf("block size: %zu\n", dev->dinfo.block_size);
342 printf("get partitions\n");
343 rc = vbd_label_get_parts(fdisk->vbd, sid, &psids, &nparts);
344 if (rc != EOK) {
345 printf("failed\n");
346 rc = EIO;
347 goto error;
348 }
349 printf("OK\n");
350
351 printf("found %zu partitions.\n", nparts);
352 for (i = 0; i < nparts; i++) {
353 printf("add partition sid=%zu\n", psids[i]);
354 rc = fdisk_part_add(dev, psids[i], NULL);
355 if (rc != EOK) {
356 printf("failed\n");
357 goto error;
358 }
359 printf("OK\n");
360 }
361
362 free(psids);
363 *rdev = dev;
364 return EOK;
365error:
366 fdisk_dev_close(dev);
367 return rc;
368}
369
370void fdisk_dev_close(fdisk_dev_t *dev)
371{
372 if (dev == NULL)
373 return;
374
375 /* XXX Clean up partitions */
376 free(dev);
377}
378
379int fdisk_dev_get_svcname(fdisk_dev_t *dev, char **rname)
380{
381 char *name;
382 int rc;
383
384 rc = loc_service_get_name(dev->sid, &name);
385 if (rc != EOK)
386 return rc;
387
388 *rname = name;
389 return EOK;
390}
391
392int fdisk_dev_capacity(fdisk_dev_t *dev, fdisk_cap_t *cap)
393{
394 size_t bsize;
395 aoff64_t nblocks;
396 int rc;
397
398 rc = block_init(EXCHANGE_SERIALIZE, dev->sid, 2048);
399 if (rc != EOK)
400 return rc;
401
402 rc = block_get_bsize(dev->sid, &bsize);
403 if (rc != EOK)
404 return EIO;
405
406 rc = block_get_nblocks(dev->sid, &nblocks);
407 if (rc != EOK)
408 return EIO;
409
410 block_fini(dev->sid);
411
412 cap->value = bsize * nblocks;
413 cap->cunit = cu_byte;
414
415 return EOK;
416}
417
418int fdisk_label_get_info(fdisk_dev_t *dev, fdisk_label_info_t *info)
419{
420 vol_disk_info_t vinfo;
421 int rc;
422
423 rc = vol_disk_info(dev->fdisk->vol, dev->sid, &vinfo);
424 if (rc != EOK) {
425 rc = EIO;
426 goto error;
427 }
428
429 info->dcnt = vinfo.dcnt;
430 info->ltype = vinfo.ltype;
431 return EOK;
432error:
433 return rc;
434}
435
436int fdisk_label_create(fdisk_dev_t *dev, label_type_t ltype)
437{
438 return vol_label_create(dev->fdisk->vol, dev->sid, ltype);
439}
440
441int fdisk_label_destroy(fdisk_dev_t *dev)
442{
443 fdisk_part_t *part;
444 int rc;
445
446 part = fdisk_part_first(dev);
447 while (part != NULL) {
448 (void) fdisk_part_destroy(part); /* XXX */
449 part = fdisk_part_first(dev);
450 }
451
452 rc = vol_disk_empty(dev->fdisk->vol, dev->sid);
453 if (rc != EOK)
454 return EIO;
455
456 dev->dcnt = dc_empty;
457 return EOK;
458}
459
460fdisk_part_t *fdisk_part_first(fdisk_dev_t *dev)
461{
462 link_t *link;
463
464 link = list_first(&dev->parts_ba);
465 if (link == NULL)
466 return NULL;
467
468 return list_get_instance(link, fdisk_part_t, ldev_ba);
469}
470
471fdisk_part_t *fdisk_part_next(fdisk_part_t *part)
472{
473 link_t *link;
474
475 link = list_next(&part->ldev_ba, &part->dev->parts_ba);
476 if (link == NULL)
477 return NULL;
478
479 return list_get_instance(link, fdisk_part_t, ldev_ba);
480}
481
482int fdisk_part_get_info(fdisk_part_t *part, fdisk_part_info_t *info)
483{
484 info->capacity = part->capacity;
485 info->fstype = part->fstype;
486 return EOK;
487}
488
489int fdisk_part_get_max_avail(fdisk_dev_t *dev, fdisk_cap_t *cap)
490{
491 return EOK;
492}
493
494int fdisk_part_create(fdisk_dev_t *dev, fdisk_part_spec_t *pspec,
495 fdisk_part_t **rpart)
496{
497 fdisk_part_t *part;
498 vbd_part_spec_t vpspec;
499 vbd_part_id_t partid;
500 int rc;
501
502 printf("fdisk_part_create()\n");
503
504 rc = fdisk_part_spec_prepare(dev, pspec, &vpspec);
505 if (rc != EOK)
506 return EIO;
507
508 printf("fdisk_part_create() - call vbd_part_create\n");
509 rc = vbd_part_create(dev->fdisk->vbd, dev->sid, &vpspec, &partid);
510 if (rc != EOK)
511 return EIO;
512
513 printf("fdisk_part_create() - call fdisk_part_add\n");
514 rc = fdisk_part_add(dev, partid, &part);
515 if (rc != EOK) {
516 /* Try rolling back */
517 (void) vbd_part_delete(dev->fdisk->vbd, partid);
518 return EIO;
519 }
520
521 printf("fdisk_part_create() - done\n");
522 part->fstype = pspec->fstype;
523 part->capacity = pspec->capacity;
524
525 if (rpart != NULL)
526 *rpart = part;
527 return EOK;
528}
529
530int fdisk_part_destroy(fdisk_part_t *part)
531{
532 int rc;
533
534 rc = vbd_part_delete(part->dev->fdisk->vbd, part->part_id);
535 if (rc != EOK)
536 return EIO;
537
538 list_remove(&part->ldev_ba);
539 list_remove(&part->ldev_idx);
540 free(part);
541 return EOK;
542}
543
544void fdisk_pspec_init(fdisk_part_spec_t *pspec)
545{
546 memset(pspec, 0, sizeof(fdisk_part_spec_t));
547}
548
549int fdisk_cap_format(fdisk_cap_t *cap, char **rstr)
550{
551 int rc;
552 const char *sunit;
553
554 sunit = NULL;
555
556 if (cap->cunit < 0 || cap->cunit >= CU_LIMIT)
557 assert(false);
558
559 sunit = cu_str[cap->cunit];
560 rc = asprintf(rstr, "%" PRIu64 " %s", cap->value, sunit);
561 if (rc < 0)
562 return ENOMEM;
563
564 return EOK;
565}
566
567int fdisk_cap_parse(const char *str, fdisk_cap_t *cap)
568{
569 char *eptr;
570 char *p;
571 unsigned long val;
572 int i;
573
574 val = strtoul(str, &eptr, 10);
575
576 while (*eptr == ' ')
577 ++eptr;
578
579 if (*eptr == '\0') {
580 cap->cunit = cu_byte;
581 } else {
582 for (i = 0; i < CU_LIMIT; i++) {
583 if (str_lcasecmp(eptr, cu_str[i],
584 str_length(cu_str[i])) == 0) {
585 p = eptr + str_size(cu_str[i]);
586 while (*p == ' ')
587 ++p;
588 if (*p == '\0')
589 goto found;
590 }
591 }
592
593 return EINVAL;
594found:
595 cap->cunit = i;
596 }
597
598 cap->value = val;
599 return EOK;
600}
601
602int fdisk_ltype_format(label_type_t ltype, char **rstr)
603{
604 const char *sltype;
605 char *s;
606
607 sltype = NULL;
608 switch (ltype) {
609 case lt_mbr:
610 sltype = "MBR";
611 break;
612 case lt_gpt:
613 sltype = "GPT";
614 break;
615 }
616
617 s = str_dup(sltype);
618 if (s == NULL)
619 return ENOMEM;
620
621 *rstr = s;
622 return EOK;
623}
624
625int fdisk_fstype_format(fdisk_fstype_t fstype, char **rstr)
626{
627 const char *sfstype;
628 char *s;
629
630 sfstype = NULL;
631 switch (fstype) {
632 case fdfs_none:
633 sfstype = "None";
634 break;
635 case fdfs_unknown:
636 sfstype = "Unknown";
637 break;
638 case fdfs_exfat:
639 sfstype = "ExFAT";
640 break;
641 case fdfs_fat:
642 sfstype = "FAT";
643 break;
644 case fdfs_minix:
645 sfstype = "MINIX";
646 break;
647 case fdfs_ext4:
648 sfstype = "Ext4";
649 break;
650 }
651
652 s = str_dup(sfstype);
653 if (s == NULL)
654 return ENOMEM;
655
656 *rstr = s;
657 return EOK;
658}
659
660/** Get free partition index. */
661static int fdisk_part_get_free_idx(fdisk_dev_t *dev, int *rindex)
662{
663 link_t *link;
664 fdisk_part_t *part;
665 int nidx;
666
667 link = list_first(&dev->parts_idx);
668 nidx = 1;
669 while (link != NULL) {
670 part = list_get_instance(link, fdisk_part_t, ldev_idx);
671 if (part->index > nidx)
672 break;
673 nidx = part->index;
674 link = list_next(link, &dev->parts_idx);
675 }
676
677 if (nidx > 4 /* XXXX actual number of slots*/) {
678 return ELIMIT;
679 }
680
681 *rindex = nidx;
682 return EOK;
683}
684
685/** Get free range of blocks.
686 *
687 * Get free range of blocks of at least the specified size (first fit).
688 */
689static int fdisk_part_get_free_range(fdisk_dev_t *dev, aoff64_t nblocks,
690 aoff64_t *rblock0, aoff64_t *rnblocks)
691{
692 link_t *link;
693 fdisk_part_t *part;
694 uint64_t avail;
695 int nba;
696
697 link = list_first(&dev->parts_ba);
698 nba = dev->dinfo.ablock0;
699 while (link != NULL) {
700 part = list_get_instance(link, fdisk_part_t, ldev_ba);
701 if (part->block0 - nba >= nblocks)
702 break;
703 nba = part->block0 + part->nblocks;
704 link = list_next(link, &dev->parts_ba);
705 }
706
707 if (link != NULL) {
708 /* Free range before a partition */
709 avail = part->block0 - nba;
710 } else {
711 /* Free range at the end */
712 avail = dev->dinfo.ablock0 + dev->dinfo.anblocks - nba;
713
714 /* Verify that the range is large enough */
715 if (avail < nblocks)
716 return ELIMIT;
717 }
718
719 *rblock0 = nba;
720 *rnblocks = avail;
721 return EOK;
722}
723
724/** Prepare new partition specification for VBD. */
725static int fdisk_part_spec_prepare(fdisk_dev_t *dev, fdisk_part_spec_t *pspec,
726 vbd_part_spec_t *vpspec)
727{
728 uint64_t cbytes;
729 aoff64_t req_blocks;
730 aoff64_t fblock0;
731 aoff64_t fnblocks;
732 uint64_t block_size;
733 unsigned i;
734 int index;
735 int rc;
736
737// pspec->fstype
738 printf("fdisk_part_spec_prepare()\n");
739 block_size = dev->dinfo.block_size;
740 cbytes = pspec->capacity.value;
741 for (i = 0; i < pspec->capacity.cunit; i++)
742 cbytes = cbytes * 1000;
743
744 req_blocks = (cbytes + block_size - 1) / block_size;
745
746 rc = fdisk_part_get_free_idx(dev, &index);
747 if (rc != EOK)
748 return EIO;
749
750 rc = fdisk_part_get_free_range(dev, req_blocks, &fblock0, &fnblocks);
751 if (rc != EOK)
752 return EIO;
753
754 vpspec->index = index;
755 vpspec->block0 = fblock0;
756 vpspec->nblocks = req_blocks;
757 vpspec->ptype = 42;
758 return EOK;
759}
760
761/** @}
762 */
Note: See TracBrowser for help on using the repository browser.