source: mainline/uspace/lib/fdisk/src/fdisk.c@ 9e53406

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

libfdisk should not depend on libblock nor should it access the block devices directly.

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