source: mainline/uspace/lib/fdisk/src/fdisk.c@ 0cebbac

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0cebbac was 63011c53, checked in by jzr <zarevucky.jiri@…>, 8 years ago

pcnt is unsigned, so it can't be used this way.

Detected by clang.

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