source: mainline/uspace/lib/label/src/mbr.c@ efa3136

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

Modifying mount point for a partition.

  • Property mode set to 100644
File size: 25.9 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 liblabel
30 * @{
31 */
32/**
33 * @file Master Boot Record label
34 */
35
36#include <byteorder.h>
37#include <errno.h>
38#include <mem.h>
39#include <stdlib.h>
40
41#include "std/fat.h"
42#include "std/mbr.h"
43#include "mbr.h"
44
45static errno_t mbr_open(label_bd_t *, label_t **);
46static errno_t mbr_open_ext(label_t *);
47static errno_t mbr_create(label_bd_t *, label_t **);
48static void mbr_close(label_t *);
49static errno_t mbr_destroy(label_t *);
50static errno_t mbr_get_info(label_t *, label_info_t *);
51static label_part_t *mbr_part_first(label_t *);
52static label_part_t *mbr_part_next(label_part_t *);
53static void mbr_part_get_info(label_part_t *, label_part_info_t *);
54static errno_t mbr_part_create(label_t *, label_part_spec_t *, label_part_t **);
55static errno_t mbr_part_destroy(label_part_t *);
56static errno_t mbr_suggest_ptype(label_t *, label_pcnt_t, label_ptype_t *);
57
58static errno_t mbr_check_free_idx(label_t *, int);
59static errno_t mbr_check_free_pri_range(label_t *, uint64_t, uint64_t);
60static errno_t mbr_check_free_log_range(label_t *, uint64_t, uint64_t, uint64_t);
61
62static void mbr_unused_pte(mbr_pte_t *);
63static errno_t mbr_part_to_pte(label_part_t *, mbr_pte_t *);
64static errno_t mbr_pte_to_part(label_t *, mbr_pte_t *, int);
65static errno_t mbr_pte_to_log_part(label_t *, uint64_t, mbr_pte_t *);
66static void mbr_log_part_to_ptes(label_part_t *, mbr_pte_t *, mbr_pte_t *);
67static errno_t mbr_pte_update(label_t *, mbr_pte_t *, int);
68static errno_t mbr_log_part_insert(label_t *, label_part_t *);
69static errno_t mbr_ebr_create(label_t *, label_part_t *);
70static errno_t mbr_ebr_delete(label_t *, label_part_t *);
71static errno_t mbr_ebr_update_next(label_t *, label_part_t *);
72static void mbr_update_log_indices(label_t *);
73
74label_ops_t mbr_label_ops = {
75 .open = mbr_open,
76 .create = mbr_create,
77 .close = mbr_close,
78 .destroy = mbr_destroy,
79 .get_info = mbr_get_info,
80 .part_first = mbr_part_first,
81 .part_next = mbr_part_next,
82 .part_get_info = mbr_part_get_info,
83 .part_create = mbr_part_create,
84 .part_destroy = mbr_part_destroy,
85 .suggest_ptype = mbr_suggest_ptype
86};
87
88static errno_t mbr_open(label_bd_t *bd, label_t **rlabel)
89{
90 label_t *label = NULL;
91 mbr_br_block_t *mbr = NULL;
92 fat_bs_t *bs;
93 mbr_pte_t *eptr;
94 uint16_t sgn;
95 size_t bsize;
96 aoff64_t nblocks;
97 uint32_t entry;
98 errno_t rc;
99
100 rc = bd->ops->get_bsize(bd->arg, &bsize);
101 if (rc != EOK) {
102 rc = EIO;
103 goto error;
104 }
105
106 rc = bd->ops->get_nblocks(bd->arg, &nblocks);
107 if (rc != EOK) {
108 rc = EIO;
109 goto error;
110 }
111
112 if (bsize < 512 || (bsize % 512) != 0) {
113 rc = EINVAL;
114 goto error;
115 }
116
117 if (nblocks < mbr_ablock0) {
118 rc = EINVAL;
119 goto error;
120 }
121
122 mbr = calloc(1, bsize);
123 if (mbr == NULL) {
124 rc = ENOMEM;
125 goto error;
126 }
127
128 bs = (fat_bs_t *)mbr;
129
130 rc = bd->ops->read(bd->arg, mbr_ba, 1, mbr);
131 if (rc != EOK) {
132 rc = EIO;
133 goto error;
134 }
135
136 label = calloc(1, sizeof(label_t));
137 if (label == NULL)
138 return ENOMEM;
139
140 list_initialize(&label->parts);
141 list_initialize(&label->pri_parts);
142 list_initialize(&label->log_parts);
143
144 /* Verify boot record signature */
145 sgn = uint16_t_le2host(mbr->signature);
146 if (sgn != mbr_br_signature) {
147 rc = EIO;
148 goto error;
149 }
150
151 /*
152 * We can't really tell whether this is an MBR. Make sure
153 * this is not actually the BR of a 12/16-bit FAT file system
154 */
155 if (bs->type[0] == 'F' && bs->type[1] == 'A' && bs->type[2] == 'T') {
156 rc = EIO;
157 goto error;
158 }
159
160 /*
161 * Or a 32-bit FAT file system
162 */
163 if (bs->fat32.type[0] == 'F' && bs->fat32.type[1] == 'A' &&
164 bs->fat32.type[2] == 'T') {
165 rc = EIO;
166 goto error;
167 }
168
169 label->ext_part = NULL;
170 for (entry = 0; entry < mbr_nprimary; entry++) {
171 eptr = &mbr->pte[entry];
172 rc = mbr_pte_to_part(label, eptr, entry + 1);
173 if (rc != EOK)
174 goto error;
175 }
176
177 free(mbr);
178 mbr = NULL;
179
180 label->ops = &mbr_label_ops;
181 label->ltype = lt_mbr;
182 label->bd = *bd;
183 label->block_size = bsize;
184 label->ablock0 = mbr_ablock0;
185 label->anblocks = nblocks - mbr_ablock0;
186 label->pri_entries = mbr_nprimary;
187
188 if (label->ext_part != NULL) {
189 /* Open extended partition */
190 rc = mbr_open_ext(label);
191 if (rc != EOK)
192 goto error;
193 }
194
195 *rlabel = label;
196 return EOK;
197error:
198 free(mbr);
199 mbr_close(label);
200 return rc;
201}
202
203/** Open extended partition */
204static errno_t mbr_open_ext(label_t *label)
205{
206 mbr_br_block_t *ebr = NULL;
207 mbr_pte_t *ethis;
208 mbr_pte_t *enext;
209 uint64_t ebr_b0;
210 uint64_t ebr_nblocks_min;
211 uint64_t ebr_nblocks_max;
212 uint64_t pebr_b0;
213 uint64_t pebr_nblocks;
214 uint64_t pb0;
215 uint64_t pnblocks;
216 uint64_t ep_b0;
217 errno_t rc;
218
219 ebr = calloc(1, label->block_size);
220 if (ebr == NULL) {
221 rc = ENOMEM;
222 goto error;
223 }
224
225 /* First block of extended partition */
226 ep_b0 = label->ext_part->block0;
227
228 /* First block of current EBR */
229 ebr_b0 = label->ext_part->block0;
230
231 /*
232 * We don't have bounds for the first EBR, so for purpose of
233 * verification let's say it contains at least one block and
234 * at most all blocks from the extended partition.
235 */
236 ebr_nblocks_min = 1;
237 ebr_nblocks_max = label->ext_part->nblocks;
238
239 while (true) {
240 /* Read EBR */
241 rc = label->bd.ops->read(label->bd.arg, ebr_b0, 1, ebr);
242 if (rc != EOK) {
243 rc = EIO;
244 goto error;
245 }
246
247 ethis = &ebr->pte[mbr_ebr_pte_this];
248 enext = &ebr->pte[mbr_ebr_pte_next];
249
250 pb0 = ebr_b0 + uint32_t_le2host(ethis->first_lba);
251 pnblocks = uint32_t_le2host(ethis->length);
252
253 if (ethis->ptype == mbr_pt_unused || pnblocks == 0)
254 break;
255
256 /* Verify partition lies within the range of EBR */
257 if (pb0 + pnblocks > ebr_b0 + ebr_nblocks_max) {
258 rc = EIO;
259 goto error;
260 }
261
262 /* Create partition structure */
263 rc = mbr_pte_to_log_part(label, ebr_b0, ethis);
264 if (rc != EOK) {
265 rc = EIO;
266 goto error;
267 }
268
269 /* Save previous EBR range */
270 pebr_b0 = ebr_b0;
271 pebr_nblocks = ebr_nblocks_min;
272
273 /* Proceed to next EBR */
274 ebr_b0 = ep_b0 + uint32_t_le2host(enext->first_lba);
275 ebr_nblocks_min = uint32_t_le2host(enext->length);
276 ebr_nblocks_max = ebr_nblocks_min;
277
278 if (enext->ptype == mbr_pt_unused || ebr_nblocks_min == 0)
279 break;
280
281 /* Verify next EBR does not overlap this EBR */
282 if (ebr_b0 < pebr_b0 + pebr_nblocks) {
283 rc = EIO;
284 goto error;
285 }
286
287 /* Verify next EBR does not extend beyond end of label */
288 if (ebr_b0 + ebr_nblocks_max > label->ablock0 + label->anblocks) {
289 rc = EIO;
290 goto error;
291 }
292 }
293
294 free(ebr);
295 return EOK;
296error:
297 /* Note that logical partitions need to be deleted by caller */
298 free(ebr);
299 return rc;
300}
301
302static errno_t mbr_create(label_bd_t *bd, label_t **rlabel)
303{
304 label_t *label = NULL;
305 mbr_br_block_t *mbr = NULL;
306 aoff64_t nblocks;
307 size_t bsize;
308 int i;
309 errno_t rc;
310
311 rc = bd->ops->get_bsize(bd->arg, &bsize);
312 if (rc != EOK) {
313 rc = EIO;
314 goto error;
315 }
316
317 rc = bd->ops->get_nblocks(bd->arg, &nblocks);
318 if (rc != EOK) {
319 rc = EIO;
320 goto error;
321 }
322
323 mbr = calloc(1, bsize);
324 if (mbr == NULL) {
325 rc = ENOMEM;
326 goto error;
327 }
328
329 label = calloc(1, sizeof(label_t));
330 if (label == NULL)
331 return ENOMEM;
332
333 list_initialize(&label->parts);
334 list_initialize(&label->pri_parts);
335 list_initialize(&label->log_parts);
336
337 mbr->media_id = 0;
338 mbr->pad0 = 0;
339 for (i = 0; i < mbr_nprimary; i++)
340 mbr_unused_pte(&mbr->pte[i]);
341 mbr->signature = host2uint16_t_le(mbr_br_signature);
342
343 rc = bd->ops->write(bd->arg, mbr_ba, 1, mbr);
344 if (rc != EOK) {
345 rc = EIO;
346 goto error;
347 }
348
349 free(mbr);
350 mbr = NULL;
351
352 label->ops = &mbr_label_ops;
353 label->ltype = lt_mbr;
354 label->block_size = bsize;
355 label->bd = *bd;
356 label->ablock0 = mbr_ablock0;
357 label->anblocks = nblocks - mbr_ablock0;
358 label->pri_entries = mbr_nprimary;
359 label->ext_part = NULL;
360
361 *rlabel = label;
362 return EOK;
363error:
364 free(mbr);
365 free(label);
366 return rc;
367}
368
369static void mbr_close(label_t *label)
370{
371 label_part_t *part;
372
373 if (label == NULL)
374 return;
375
376 part = mbr_part_first(label);
377 while (part != NULL) {
378 list_remove(&part->lparts);
379 if (link_used(&part->lpri))
380 list_remove(&part->lpri);
381 if (link_used(&part->llog))
382 list_remove(&part->llog);
383 free(part);
384
385 part = mbr_part_first(label);
386 }
387
388 free(label);
389}
390
391static errno_t mbr_destroy(label_t *label)
392{
393 mbr_br_block_t *mbr = NULL;
394 label_part_t *part;
395 errno_t rc;
396
397 part = mbr_part_first(label);
398 if (part != NULL) {
399 rc = ENOTEMPTY;
400 goto error;
401 }
402
403 mbr = calloc(1, label->block_size);
404 if (mbr == NULL) {
405 rc = ENOMEM;
406 goto error;
407 }
408
409 rc = label->bd.ops->write(label->bd.arg, mbr_ba, 1, mbr);
410 if (rc != EOK) {
411 rc = EIO;
412 goto error;
413 }
414
415 free(mbr);
416 mbr = NULL;
417
418 free(label);
419 return EOK;
420error:
421 free(mbr);
422 return rc;
423}
424
425static bool mbr_can_delete_part(label_t *label)
426{
427 return list_count(&label->parts) > 0;
428}
429
430static bool mbr_can_modify_part(label_t *label)
431{
432 return list_count(&label->parts) > 0;
433}
434
435
436static errno_t mbr_get_info(label_t *label, label_info_t *linfo)
437{
438 memset(linfo, 0, sizeof(label_info_t));
439 linfo->ltype = lt_mbr;
440
441 /* We support extended partitions */
442 linfo->flags = lf_ext_supp;
443
444 /** Can create primary if there is a free slot */
445 if (list_count(&label->pri_parts) < mbr_nprimary)
446 linfo->flags |= lf_can_create_pri;
447 /* Can create extended if there is a free slot and no extended */
448 if ((linfo->flags & lf_can_create_pri) != 0 && label->ext_part == NULL)
449 linfo->flags |= lf_can_create_ext;
450 /* Can create logical if there is an extended partition */
451 if (label->ext_part != NULL)
452 linfo->flags |= lf_can_create_log;
453 /* Can delete partition */
454 if (mbr_can_delete_part(label))
455 linfo->flags |= lf_can_delete_part;
456 /* Can modify partition */
457 if (mbr_can_modify_part(label))
458 linfo->flags |= lf_can_modify_part;
459
460 linfo->ablock0 = label->ablock0;
461 linfo->anblocks = label->anblocks;
462
463 return EOK;
464}
465
466static label_part_t *mbr_part_first(label_t *label)
467{
468 link_t *link;
469
470 link = list_first(&label->parts);
471 if (link == NULL)
472 return NULL;
473
474 return list_get_instance(link, label_part_t, lparts);
475}
476
477static label_part_t *mbr_part_next(label_part_t *part)
478{
479 link_t *link;
480
481 link = list_next(&part->lparts, &part->label->parts);
482 if (link == NULL)
483 return NULL;
484
485 return list_get_instance(link, label_part_t, lparts);
486}
487
488static label_part_t *mbr_log_part_first(label_t *label)
489{
490 link_t *link;
491
492 link = list_first(&label->log_parts);
493 if (link == NULL)
494 return NULL;
495
496 return list_get_instance(link, label_part_t, llog);
497}
498
499static label_part_t *mbr_log_part_next(label_part_t *part)
500{
501 link_t *link;
502
503 link = list_next(&part->llog, &part->label->log_parts);
504 if (link == NULL)
505 return NULL;
506
507 return list_get_instance(link, label_part_t, llog);
508}
509
510static label_part_t *mbr_log_part_prev(label_part_t *part)
511{
512 link_t *link;
513
514 link = list_prev(&part->llog, &part->label->log_parts);
515 if (link == NULL)
516 return NULL;
517
518 return list_get_instance(link, label_part_t, llog);
519}
520
521static label_part_t *mbr_pri_part_first(label_t *label)
522{
523 link_t *link;
524
525 link = list_first(&label->pri_parts);
526 if (link == NULL)
527 return NULL;
528
529 return list_get_instance(link, label_part_t, lpri);
530}
531
532static label_part_t *mbr_pri_part_next(label_part_t *part)
533{
534 link_t *link;
535
536 link = list_next(&part->lpri, &part->label->pri_parts);
537 if (link == NULL)
538 return NULL;
539
540 return list_get_instance(link, label_part_t, lpri);
541}
542
543static void mbr_part_get_info(label_part_t *part, label_part_info_t *pinfo)
544{
545 pinfo->index = part->index;
546 pinfo->block0 = part->block0;
547 pinfo->nblocks = part->nblocks;
548
549 if (link_used(&part->llog))
550 pinfo->pkind = lpk_logical;
551 else if (part->ptype.t.num == mbr_pt_extended)
552 pinfo->pkind = lpk_extended;
553 else
554 pinfo->pkind = lpk_primary;
555}
556
557static errno_t mbr_part_create(label_t *label, label_part_spec_t *pspec,
558 label_part_t **rpart)
559{
560 label_part_t *part;
561 label_part_t *prev;
562 label_part_t *next;
563 mbr_pte_t pte;
564 errno_t rc;
565
566 if (pspec->ptype.fmt != lptf_num)
567 return EINVAL;
568
569 part = calloc(1, sizeof(label_part_t));
570 if (part == NULL)
571 return ENOMEM;
572
573 /* XXX Check if index is used */
574 part->label = label;
575 part->index = pspec->index;
576 part->block0 = pspec->block0;
577 part->nblocks = pspec->nblocks;
578 part->hdr_blocks = pspec->hdr_blocks;
579
580 switch (pspec->pkind) {
581 case lpk_primary:
582 part->ptype = pspec->ptype;
583 break;
584 case lpk_extended:
585 part->ptype.fmt = lptf_num;
586 part->ptype.t.num = mbr_pt_extended;
587 if (pspec->ptype.t.num != 0) {
588 rc = EINVAL;
589 goto error;
590 }
591 if (label->ext_part != NULL) {
592 rc = EEXIST;
593 goto error;
594 }
595 break;
596 case lpk_logical:
597 part->ptype = pspec->ptype;
598 if (pspec->index != 0) {
599 rc = EINVAL;
600 goto error;
601 }
602 break;
603 }
604
605 if (pspec->pkind != lpk_logical) {
606 /* Primary or extended partition */
607
608 /* Verify index is within bounds and free */
609 rc = mbr_check_free_idx(label, pspec->index);
610 if (rc != EOK) {
611 rc = EINVAL;
612 goto error;
613 }
614
615 /* Verify range is within bounds and free */
616 rc = mbr_check_free_pri_range(label, pspec->block0, pspec->nblocks);
617 if (rc != EOK) {
618 rc = EINVAL;
619 goto error;
620 }
621
622 if (pspec->hdr_blocks != 0) {
623 rc = EINVAL;
624 goto error;
625 }
626
627 rc = mbr_part_to_pte(part, &pte);
628 if (rc != EOK) {
629 rc = EINVAL;
630 goto error;
631 }
632
633 rc = mbr_pte_update(label, &pte, pspec->index - 1);
634 if (rc != EOK) {
635 rc = EIO;
636 goto error;
637 }
638
639 if (pspec->pkind == lpk_extended) {
640 label->ext_part = part;
641
642 /* Create EBR for empty partition chain */
643 rc = mbr_ebr_create(label, NULL);
644 if (rc != EOK) {
645 label->ext_part = NULL;
646 rc = EIO;
647 goto error;
648 }
649 }
650
651 list_append(&part->lparts, &label->parts);
652 list_append(&part->lpri, &label->pri_parts);
653 } else {
654 /* Verify range is within bounds and free */
655 rc = mbr_check_free_log_range(label, pspec->hdr_blocks,
656 pspec->block0, pspec->nblocks);
657 if (rc != EOK) {
658 rc = EINVAL;
659 goto error;
660 }
661
662 /* Logical partition */
663 rc = mbr_log_part_insert(label, part);
664 if (rc != EOK)
665 goto error;
666
667 /* Create EBR for new partition */
668 rc = mbr_ebr_create(label, part);
669 if (rc != EOK)
670 goto error;
671
672 prev = mbr_log_part_prev(part);
673 if (prev != NULL) {
674 /* Update 'next' PTE in EBR of previous partition */
675 rc = mbr_ebr_update_next(label, prev);
676 if (rc != EOK) {
677 goto error;
678 }
679 } else {
680 /* New partition is now the first one */
681 next = mbr_log_part_next(part);
682 if (next != NULL) {
683 /*
684 * Create new, relocated EBR for the former
685 * first partition
686 */
687 next->hdr_blocks = pspec->hdr_blocks;
688 rc = mbr_ebr_create(label, next);
689 if (rc != EOK)
690 goto error;
691 }
692 }
693
694 /* This also sets index for the new partition. */
695 mbr_update_log_indices(label);
696 }
697
698 *rpart = part;
699 return EOK;
700error:
701 free(part);
702 return rc;
703}
704
705static errno_t mbr_part_destroy(label_part_t *part)
706{
707 mbr_pte_t pte;
708 label_part_t *prev;
709 label_part_t *next;
710 uint64_t ep_b0;
711 errno_t rc;
712
713 if (link_used(&part->lpri)) {
714 /* Primary/extended partition */
715
716 /* Prepare unused partition table entry */
717 mbr_unused_pte(&pte);
718
719 /* Modify partition table */
720 rc = mbr_pte_update(part->label, &pte, part->index - 1);
721 if (rc != EOK)
722 return EIO;
723
724 /* If it was the extended partition, clear ext. part. pointer */
725 if (part == part->label->ext_part)
726 part->label->ext_part = NULL;
727
728 list_remove(&part->lpri);
729 } else {
730 /* Logical partition */
731
732 prev = mbr_log_part_prev(part);
733 if (prev != NULL) {
734 /* Update next link in previous EBR */
735 list_remove(&part->llog);
736
737 rc = mbr_ebr_update_next(part->label, prev);
738 if (rc != EOK) {
739 /* Roll back */
740 list_insert_after(&part->llog, &prev->llog);
741 return EIO;
742 }
743
744 /* Delete EBR */
745 rc = mbr_ebr_delete(part->label, part);
746 if (rc != EOK)
747 return EIO;
748 } else {
749 next = mbr_log_part_next(part);
750 list_remove(&part->llog);
751
752 if (next != NULL) {
753 /*
754 * Relocate next partition's EBR to the beginning
755 * of the extended partition. This also
756 * overwrites the EBR of the former first
757 * partition.
758 */
759
760 /* First block of extended partition */
761 ep_b0 = part->label->ext_part->block0;
762
763 next->hdr_blocks = next->block0 - ep_b0;
764
765 rc = mbr_ebr_create(part->label, next);
766 if (rc != EOK) {
767 list_prepend(&part->llog, &part->label->log_parts);
768 return EIO;
769 }
770 } else {
771 /* Delete EBR */
772 rc = mbr_ebr_delete(part->label, part);
773 if (rc != EOK)
774 return EIO;
775 }
776 }
777
778 /* Update indices */
779 mbr_update_log_indices(part->label);
780 }
781
782 list_remove(&part->lparts);
783 free(part);
784 return EOK;
785}
786
787static errno_t mbr_suggest_ptype(label_t *label, label_pcnt_t pcnt,
788 label_ptype_t *ptype)
789{
790 ptype->fmt = lptf_num;
791 ptype->t.num = 0;
792
793 switch (pcnt) {
794 case lpc_exfat:
795 ptype->t.num = mbr_pt_ms_advanced;
796 break;
797 case lpc_ext4:
798 ptype->t.num = mbr_pt_linux;
799 break;
800 case lpc_fat12_16:
801 ptype->t.num = mbr_pt_fat16_lba;
802 break;
803 case lpc_fat32:
804 ptype->t.num = mbr_pt_fat32_lba;
805 break;
806 case lpc_minix:
807 ptype->t.num = mbr_pt_minix;
808 break;
809 }
810
811 if (ptype->t.num == 0)
812 return EINVAL;
813
814 return EOK;
815}
816
817/** Determine if two block address ranges overlap. */
818static bool mbr_overlap(uint64_t a0, uint64_t an, uint64_t b0, uint64_t bn)
819{
820 return !(a0 + an <= b0 || b0 + bn <= a0);
821}
822
823/** Verify that the specified index is valid and free. */
824static errno_t mbr_check_free_idx(label_t *label, int index)
825{
826 label_part_t *part;
827
828 if (index < 1 || index > label->pri_entries)
829 return EINVAL;
830
831 part = mbr_pri_part_first(label);
832 while (part != NULL) {
833 if (part->index == index)
834 return EEXIST;
835 part = mbr_pri_part_next(part);
836 }
837
838 return EOK;
839}
840
841static errno_t mbr_check_free_pri_range(label_t *label, uint64_t block0,
842 uint64_t nblocks)
843{
844 label_part_t *part;
845
846 if (block0 < label->ablock0)
847 return EINVAL;
848 if (block0 + nblocks > label->ablock0 + label->anblocks)
849 return EINVAL;
850
851 part = mbr_pri_part_first(label);
852 while (part != NULL) {
853 if (mbr_overlap(block0, nblocks, part->block0, part->nblocks))
854 return EEXIST;
855 part = mbr_pri_part_next(part);
856 }
857
858 return EOK;
859}
860
861static errno_t mbr_check_free_log_range(label_t *label, uint64_t hdr_blocks,
862 uint64_t block0, uint64_t nblocks)
863{
864 label_part_t *part;
865
866 if (block0 - hdr_blocks < label->ext_part->block0)
867 return EINVAL;
868 if (block0 + nblocks > label->ext_part->block0 + label->ext_part->nblocks)
869 return EINVAL;
870
871 part = mbr_log_part_first(label);
872 while (part != NULL) {
873 if (mbr_overlap(block0 - hdr_blocks, nblocks + hdr_blocks,
874 part->block0 - part->hdr_blocks, part->nblocks + part->hdr_blocks))
875 return EEXIST;
876 part = mbr_log_part_next(part);
877 }
878
879 return EOK;
880}
881
882
883static void mbr_unused_pte(mbr_pte_t *pte)
884{
885 memset(pte, 0, sizeof(mbr_pte_t));
886}
887
888static errno_t mbr_part_to_pte(label_part_t *part, mbr_pte_t *pte)
889{
890 if ((part->block0 >> 32) != 0)
891 return EINVAL;
892 if ((part->nblocks >> 32) != 0)
893 return EINVAL;
894 if ((part->ptype.t.num >> 8) != 0)
895 return EINVAL;
896
897 memset(pte, 0, sizeof(mbr_pte_t));
898 pte->ptype = part->ptype.t.num;
899 pte->first_lba = host2uint32_t_le(part->block0);
900 pte->length = host2uint32_t_le(part->nblocks);
901 return EOK;
902}
903
904static errno_t mbr_pte_to_part(label_t *label, mbr_pte_t *pte, int index)
905{
906 label_part_t *part;
907 uint32_t block0;
908 uint32_t nblocks;
909
910 block0 = uint32_t_le2host(pte->first_lba);
911 nblocks = uint32_t_le2host(pte->length);
912
913 /* See UEFI specification 2.0 section 5.2.1 Legacy Master Boot Record */
914 if (pte->ptype == mbr_pt_unused || nblocks == 0)
915 return EOK;
916
917 part = calloc(1, sizeof(label_part_t));
918 if (part == NULL)
919 return ENOMEM;
920
921 part->ptype.fmt = lptf_num;
922 part->ptype.t.num = pte->ptype;
923 part->index = index;
924 part->block0 = block0;
925 part->nblocks = nblocks;
926
927 /*
928 * TODO: Verify
929 * - partition must reside on disk
930 * - partition must not overlap any other partition
931 */
932
933 part->label = label;
934 list_append(&part->lparts, &label->parts);
935 list_append(&part->lpri, &label->pri_parts);
936
937 if (pte->ptype == mbr_pt_extended)
938 label->ext_part = part;
939 return EOK;
940}
941
942static errno_t mbr_pte_to_log_part(label_t *label, uint64_t ebr_b0,
943 mbr_pte_t *pte)
944{
945 label_part_t *part;
946 uint32_t block0;
947 uint32_t nblocks;
948 size_t nlparts;
949
950 block0 = ebr_b0 + uint32_t_le2host(pte->first_lba);
951 nblocks = uint32_t_le2host(pte->length);
952
953 if (pte->ptype == mbr_pt_unused || nblocks == 0)
954 return EOK;
955
956 part = calloc(1, sizeof(label_part_t));
957 if (part == NULL)
958 return ENOMEM;
959
960 nlparts = list_count(&label->log_parts);
961
962 part->ptype.fmt = lptf_num;
963 part->ptype.t.num = pte->ptype;
964 part->index = mbr_nprimary + 1 + nlparts;
965 part->block0 = block0;
966 part->nblocks = nblocks;
967 part->hdr_blocks = block0 - ebr_b0;
968
969 part->label = label;
970 list_append(&part->lparts, &label->parts);
971 list_append(&part->llog, &label->log_parts);
972
973 return EOK;
974}
975
976static void mbr_log_part_to_ptes(label_part_t *part, mbr_pte_t *pthis,
977 mbr_pte_t *pnext)
978{
979 label_part_t *next;
980 uint64_t ep_b0;
981 uint64_t totsize;
982
983 /* First block of extended partition */
984 ep_b0 = part->label->ext_part->block0;
985
986 assert(link_used(&part->llog));
987 assert(part->block0 >= ep_b0);
988 assert(part->hdr_blocks <= part->block0 - ep_b0);
989
990 /* 'This' EBR entry */
991 if (pthis != NULL) {
992 memset(pthis, 0, sizeof(mbr_pte_t));
993 pthis->ptype = part->ptype.t.num;
994 pthis->first_lba = host2uint32_t_le(part->hdr_blocks);
995 pthis->length = host2uint32_t_le(part->nblocks);
996 }
997
998 /* 'Next' EBR entry */
999 if (pnext != NULL) {
1000 next = mbr_log_part_next(part);
1001
1002 memset(pnext, 0, sizeof(mbr_pte_t));
1003 if (next != NULL) {
1004 /* Total size of EBR + partition */
1005 totsize = next->hdr_blocks + next->nblocks;
1006
1007 pnext->ptype = mbr_pt_extended;
1008 pnext->first_lba = host2uint32_t_le(next->block0 -
1009 next->hdr_blocks - ep_b0);
1010 pnext->length = host2uint32_t_le(totsize);
1011 }
1012 }
1013}
1014
1015/** Update partition table entry at specified index.
1016 *
1017 * Replace partition entry at index @a index with the contents of
1018 * @a pte.
1019 */
1020static errno_t mbr_pte_update(label_t *label, mbr_pte_t *pte, int index)
1021{
1022 mbr_br_block_t *br;
1023 errno_t rc;
1024
1025 br = calloc(1, label->block_size);
1026 if (br == NULL)
1027 return ENOMEM;
1028
1029 rc = label->bd.ops->read(label->bd.arg, mbr_ba, 1, br);
1030 if (rc != EOK) {
1031 rc = EIO;
1032 goto error;
1033 }
1034
1035 br->pte[index] = *pte;
1036
1037 rc = label->bd.ops->write(label->bd.arg, mbr_ba, 1, br);
1038 if (rc != EOK) {
1039 rc = EIO;
1040 goto error;
1041 }
1042
1043 free(br);
1044 return EOK;
1045error:
1046 free(br);
1047 return rc;
1048}
1049
1050/** Insert logical partition into logical partition list. */
1051static errno_t mbr_log_part_insert(label_t *label, label_part_t *part)
1052{
1053 label_part_t *cur;
1054
1055 cur = mbr_log_part_first(label);
1056 while (cur != NULL) {
1057 if (cur->block0 + cur->nblocks > part->block0)
1058 break;
1059 cur = mbr_log_part_next(part);
1060 }
1061
1062 if (cur != NULL)
1063 list_insert_before(&part->llog, &cur->llog);
1064 else
1065 list_append(&part->llog, &label->log_parts);
1066
1067 return EOK;
1068}
1069
1070/** Create EBR for partition.
1071 *
1072 * @param label Label
1073 * @param part Partition for which to create EBR or @c NULL to create
1074 * EBR for empty partition chain
1075 * @return EOK on success or non-zero error code
1076 */
1077static errno_t mbr_ebr_create(label_t *label, label_part_t *part)
1078{
1079 mbr_br_block_t *br;
1080 uint64_t ba;
1081 errno_t rc;
1082
1083 br = calloc(1, label->block_size);
1084 if (br == NULL)
1085 return ENOMEM;
1086
1087 if (part != NULL) {
1088 ba = part->block0 - part->hdr_blocks;
1089 mbr_log_part_to_ptes(part, &br->pte[mbr_ebr_pte_this],
1090 &br->pte[mbr_ebr_pte_next]);
1091 } else {
1092 ba = label->ext_part->block0;
1093 }
1094
1095 br->signature = host2uint16_t_le(mbr_br_signature);
1096
1097 rc = label->bd.ops->write(label->bd.arg, ba, 1, br);
1098 if (rc != EOK) {
1099 rc = EIO;
1100 goto error;
1101 }
1102
1103 free(br);
1104 return EOK;
1105error:
1106 free(br);
1107 return rc;
1108}
1109
1110static errno_t mbr_ebr_delete(label_t *label, label_part_t *part)
1111{
1112 mbr_br_block_t *br;
1113 uint64_t ba;
1114 errno_t rc;
1115
1116 br = calloc(1, label->block_size);
1117 if (br == NULL)
1118 return ENOMEM;
1119
1120 ba = part->block0 - part->hdr_blocks;
1121
1122 rc = label->bd.ops->write(label->bd.arg, ba, 1, br);
1123 if (rc != EOK) {
1124 rc = EIO;
1125 goto error;
1126 }
1127
1128 free(br);
1129 return EOK;
1130error:
1131 free(br);
1132 return rc;
1133}
1134
1135/** Update 'next' PTE in EBR of partition. */
1136static errno_t mbr_ebr_update_next(label_t *label, label_part_t *part)
1137{
1138 mbr_br_block_t *br;
1139 uint64_t ba;
1140 uint16_t sgn;
1141 errno_t rc;
1142
1143 ba = part->block0 - part->hdr_blocks;
1144
1145 br = calloc(1, label->block_size);
1146 if (br == NULL)
1147 return ENOMEM;
1148
1149 rc = label->bd.ops->read(label->bd.arg, ba, 1, br);
1150 if (rc != EOK) {
1151 rc = EIO;
1152 goto error;
1153 }
1154
1155 /* Verify boot record signature */
1156 sgn = uint16_t_le2host(br->signature);
1157 if (sgn != mbr_br_signature) {
1158 rc = EIO;
1159 goto error;
1160 }
1161
1162 mbr_log_part_to_ptes(part, NULL, &br->pte[mbr_ebr_pte_next]);
1163
1164 rc = label->bd.ops->write(label->bd.arg, ba, 1, br);
1165 if (rc != EOK) {
1166 rc = EIO;
1167 goto error;
1168 }
1169
1170 free(br);
1171 return EOK;
1172error:
1173 free(br);
1174 return rc;
1175}
1176
1177/** Update indices of logical partitions.
1178 *
1179 * Logical partition indices are unstable, i.e. they can change during
1180 * the lifetime of a logical partition. Since the index corresponds to the
1181 * position of the partition in order of block address, anytime a partition
1182 * is created or deleted, indices of all partitions at higher addresses
1183 * change.
1184 */
1185static void mbr_update_log_indices(label_t *label)
1186{
1187 label_part_t *part;
1188 int idx;
1189
1190 idx = mbr_nprimary + 1;
1191
1192 part = mbr_log_part_first(label);
1193 while (part != NULL) {
1194 part->index = idx++;
1195 part = mbr_log_part_next(part);
1196 }
1197}
1198
1199/** @}
1200 */
Note: See TracBrowser for help on using the repository browser.