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

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

Fix vertical spacing with new Ccheck revision.

  • 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
435static errno_t mbr_get_info(label_t *label, label_info_t *linfo)
436{
437 memset(linfo, 0, sizeof(label_info_t));
438 linfo->ltype = lt_mbr;
439
440 /* We support extended partitions */
441 linfo->flags = lf_ext_supp;
442
443 /** Can create primary if there is a free slot */
444 if (list_count(&label->pri_parts) < mbr_nprimary)
445 linfo->flags |= lf_can_create_pri;
446 /* Can create extended if there is a free slot and no extended */
447 if ((linfo->flags & lf_can_create_pri) != 0 && label->ext_part == NULL)
448 linfo->flags |= lf_can_create_ext;
449 /* Can create logical if there is an extended partition */
450 if (label->ext_part != NULL)
451 linfo->flags |= lf_can_create_log;
452 /* Can delete partition */
453 if (mbr_can_delete_part(label))
454 linfo->flags |= lf_can_delete_part;
455 /* Can modify partition */
456 if (mbr_can_modify_part(label))
457 linfo->flags |= lf_can_modify_part;
458
459 linfo->ablock0 = label->ablock0;
460 linfo->anblocks = label->anblocks;
461
462 return EOK;
463}
464
465static label_part_t *mbr_part_first(label_t *label)
466{
467 link_t *link;
468
469 link = list_first(&label->parts);
470 if (link == NULL)
471 return NULL;
472
473 return list_get_instance(link, label_part_t, lparts);
474}
475
476static label_part_t *mbr_part_next(label_part_t *part)
477{
478 link_t *link;
479
480 link = list_next(&part->lparts, &part->label->parts);
481 if (link == NULL)
482 return NULL;
483
484 return list_get_instance(link, label_part_t, lparts);
485}
486
487static label_part_t *mbr_log_part_first(label_t *label)
488{
489 link_t *link;
490
491 link = list_first(&label->log_parts);
492 if (link == NULL)
493 return NULL;
494
495 return list_get_instance(link, label_part_t, llog);
496}
497
498static label_part_t *mbr_log_part_next(label_part_t *part)
499{
500 link_t *link;
501
502 link = list_next(&part->llog, &part->label->log_parts);
503 if (link == NULL)
504 return NULL;
505
506 return list_get_instance(link, label_part_t, llog);
507}
508
509static label_part_t *mbr_log_part_prev(label_part_t *part)
510{
511 link_t *link;
512
513 link = list_prev(&part->llog, &part->label->log_parts);
514 if (link == NULL)
515 return NULL;
516
517 return list_get_instance(link, label_part_t, llog);
518}
519
520static label_part_t *mbr_pri_part_first(label_t *label)
521{
522 link_t *link;
523
524 link = list_first(&label->pri_parts);
525 if (link == NULL)
526 return NULL;
527
528 return list_get_instance(link, label_part_t, lpri);
529}
530
531static label_part_t *mbr_pri_part_next(label_part_t *part)
532{
533 link_t *link;
534
535 link = list_next(&part->lpri, &part->label->pri_parts);
536 if (link == NULL)
537 return NULL;
538
539 return list_get_instance(link, label_part_t, lpri);
540}
541
542static void mbr_part_get_info(label_part_t *part, label_part_info_t *pinfo)
543{
544 pinfo->index = part->index;
545 pinfo->block0 = part->block0;
546 pinfo->nblocks = part->nblocks;
547
548 if (link_used(&part->llog))
549 pinfo->pkind = lpk_logical;
550 else if (part->ptype.t.num == mbr_pt_extended)
551 pinfo->pkind = lpk_extended;
552 else
553 pinfo->pkind = lpk_primary;
554}
555
556static errno_t mbr_part_create(label_t *label, label_part_spec_t *pspec,
557 label_part_t **rpart)
558{
559 label_part_t *part;
560 label_part_t *prev;
561 label_part_t *next;
562 mbr_pte_t pte;
563 errno_t rc;
564
565 if (pspec->ptype.fmt != lptf_num)
566 return EINVAL;
567
568 part = calloc(1, sizeof(label_part_t));
569 if (part == NULL)
570 return ENOMEM;
571
572 /* XXX Check if index is used */
573 part->label = label;
574 part->index = pspec->index;
575 part->block0 = pspec->block0;
576 part->nblocks = pspec->nblocks;
577 part->hdr_blocks = pspec->hdr_blocks;
578
579 switch (pspec->pkind) {
580 case lpk_primary:
581 part->ptype = pspec->ptype;
582 break;
583 case lpk_extended:
584 part->ptype.fmt = lptf_num;
585 part->ptype.t.num = mbr_pt_extended;
586 if (pspec->ptype.t.num != 0) {
587 rc = EINVAL;
588 goto error;
589 }
590 if (label->ext_part != NULL) {
591 rc = EEXIST;
592 goto error;
593 }
594 break;
595 case lpk_logical:
596 part->ptype = pspec->ptype;
597 if (pspec->index != 0) {
598 rc = EINVAL;
599 goto error;
600 }
601 break;
602 }
603
604 if (pspec->pkind != lpk_logical) {
605 /* Primary or extended partition */
606
607 /* Verify index is within bounds and free */
608 rc = mbr_check_free_idx(label, pspec->index);
609 if (rc != EOK) {
610 rc = EINVAL;
611 goto error;
612 }
613
614 /* Verify range is within bounds and free */
615 rc = mbr_check_free_pri_range(label, pspec->block0, pspec->nblocks);
616 if (rc != EOK) {
617 rc = EINVAL;
618 goto error;
619 }
620
621 if (pspec->hdr_blocks != 0) {
622 rc = EINVAL;
623 goto error;
624 }
625
626 rc = mbr_part_to_pte(part, &pte);
627 if (rc != EOK) {
628 rc = EINVAL;
629 goto error;
630 }
631
632 rc = mbr_pte_update(label, &pte, pspec->index - 1);
633 if (rc != EOK) {
634 rc = EIO;
635 goto error;
636 }
637
638 if (pspec->pkind == lpk_extended) {
639 label->ext_part = part;
640
641 /* Create EBR for empty partition chain */
642 rc = mbr_ebr_create(label, NULL);
643 if (rc != EOK) {
644 label->ext_part = NULL;
645 rc = EIO;
646 goto error;
647 }
648 }
649
650 list_append(&part->lparts, &label->parts);
651 list_append(&part->lpri, &label->pri_parts);
652 } else {
653 /* Verify range is within bounds and free */
654 rc = mbr_check_free_log_range(label, pspec->hdr_blocks,
655 pspec->block0, pspec->nblocks);
656 if (rc != EOK) {
657 rc = EINVAL;
658 goto error;
659 }
660
661 /* Logical partition */
662 rc = mbr_log_part_insert(label, part);
663 if (rc != EOK)
664 goto error;
665
666 /* Create EBR for new partition */
667 rc = mbr_ebr_create(label, part);
668 if (rc != EOK)
669 goto error;
670
671 prev = mbr_log_part_prev(part);
672 if (prev != NULL) {
673 /* Update 'next' PTE in EBR of previous partition */
674 rc = mbr_ebr_update_next(label, prev);
675 if (rc != EOK) {
676 goto error;
677 }
678 } else {
679 /* New partition is now the first one */
680 next = mbr_log_part_next(part);
681 if (next != NULL) {
682 /*
683 * Create new, relocated EBR for the former
684 * first partition
685 */
686 next->hdr_blocks = pspec->hdr_blocks;
687 rc = mbr_ebr_create(label, next);
688 if (rc != EOK)
689 goto error;
690 }
691 }
692
693 /* This also sets index for the new partition. */
694 mbr_update_log_indices(label);
695 }
696
697 *rpart = part;
698 return EOK;
699error:
700 free(part);
701 return rc;
702}
703
704static errno_t mbr_part_destroy(label_part_t *part)
705{
706 mbr_pte_t pte;
707 label_part_t *prev;
708 label_part_t *next;
709 uint64_t ep_b0;
710 errno_t rc;
711
712 if (link_used(&part->lpri)) {
713 /* Primary/extended partition */
714
715 /* Prepare unused partition table entry */
716 mbr_unused_pte(&pte);
717
718 /* Modify partition table */
719 rc = mbr_pte_update(part->label, &pte, part->index - 1);
720 if (rc != EOK)
721 return EIO;
722
723 /* If it was the extended partition, clear ext. part. pointer */
724 if (part == part->label->ext_part)
725 part->label->ext_part = NULL;
726
727 list_remove(&part->lpri);
728 } else {
729 /* Logical partition */
730
731 prev = mbr_log_part_prev(part);
732 if (prev != NULL) {
733 /* Update next link in previous EBR */
734 list_remove(&part->llog);
735
736 rc = mbr_ebr_update_next(part->label, prev);
737 if (rc != EOK) {
738 /* Roll back */
739 list_insert_after(&part->llog, &prev->llog);
740 return EIO;
741 }
742
743 /* Delete EBR */
744 rc = mbr_ebr_delete(part->label, part);
745 if (rc != EOK)
746 return EIO;
747 } else {
748 next = mbr_log_part_next(part);
749 list_remove(&part->llog);
750
751 if (next != NULL) {
752 /*
753 * Relocate next partition's EBR to the beginning
754 * of the extended partition. This also
755 * overwrites the EBR of the former first
756 * partition.
757 */
758
759 /* First block of extended partition */
760 ep_b0 = part->label->ext_part->block0;
761
762 next->hdr_blocks = next->block0 - ep_b0;
763
764 rc = mbr_ebr_create(part->label, next);
765 if (rc != EOK) {
766 list_prepend(&part->llog, &part->label->log_parts);
767 return EIO;
768 }
769 } else {
770 /* Delete EBR */
771 rc = mbr_ebr_delete(part->label, part);
772 if (rc != EOK)
773 return EIO;
774 }
775 }
776
777 /* Update indices */
778 mbr_update_log_indices(part->label);
779 }
780
781 list_remove(&part->lparts);
782 free(part);
783 return EOK;
784}
785
786static errno_t mbr_suggest_ptype(label_t *label, label_pcnt_t pcnt,
787 label_ptype_t *ptype)
788{
789 ptype->fmt = lptf_num;
790 ptype->t.num = 0;
791
792 switch (pcnt) {
793 case lpc_exfat:
794 ptype->t.num = mbr_pt_ms_advanced;
795 break;
796 case lpc_ext4:
797 ptype->t.num = mbr_pt_linux;
798 break;
799 case lpc_fat12_16:
800 ptype->t.num = mbr_pt_fat16_lba;
801 break;
802 case lpc_fat32:
803 ptype->t.num = mbr_pt_fat32_lba;
804 break;
805 case lpc_minix:
806 ptype->t.num = mbr_pt_minix;
807 break;
808 }
809
810 if (ptype->t.num == 0)
811 return EINVAL;
812
813 return EOK;
814}
815
816/** Determine if two block address ranges overlap. */
817static bool mbr_overlap(uint64_t a0, uint64_t an, uint64_t b0, uint64_t bn)
818{
819 return !(a0 + an <= b0 || b0 + bn <= a0);
820}
821
822/** Verify that the specified index is valid and free. */
823static errno_t mbr_check_free_idx(label_t *label, int index)
824{
825 label_part_t *part;
826
827 if (index < 1 || index > label->pri_entries)
828 return EINVAL;
829
830 part = mbr_pri_part_first(label);
831 while (part != NULL) {
832 if (part->index == index)
833 return EEXIST;
834 part = mbr_pri_part_next(part);
835 }
836
837 return EOK;
838}
839
840static errno_t mbr_check_free_pri_range(label_t *label, uint64_t block0,
841 uint64_t nblocks)
842{
843 label_part_t *part;
844
845 if (block0 < label->ablock0)
846 return EINVAL;
847 if (block0 + nblocks > label->ablock0 + label->anblocks)
848 return EINVAL;
849
850 part = mbr_pri_part_first(label);
851 while (part != NULL) {
852 if (mbr_overlap(block0, nblocks, part->block0, part->nblocks))
853 return EEXIST;
854 part = mbr_pri_part_next(part);
855 }
856
857 return EOK;
858}
859
860static errno_t mbr_check_free_log_range(label_t *label, uint64_t hdr_blocks,
861 uint64_t block0, uint64_t nblocks)
862{
863 label_part_t *part;
864
865 if (block0 - hdr_blocks < label->ext_part->block0)
866 return EINVAL;
867 if (block0 + nblocks > label->ext_part->block0 + label->ext_part->nblocks)
868 return EINVAL;
869
870 part = mbr_log_part_first(label);
871 while (part != NULL) {
872 if (mbr_overlap(block0 - hdr_blocks, nblocks + hdr_blocks,
873 part->block0 - part->hdr_blocks, part->nblocks + part->hdr_blocks))
874 return EEXIST;
875 part = mbr_log_part_next(part);
876 }
877
878 return EOK;
879}
880
881static void mbr_unused_pte(mbr_pte_t *pte)
882{
883 memset(pte, 0, sizeof(mbr_pte_t));
884}
885
886static errno_t mbr_part_to_pte(label_part_t *part, mbr_pte_t *pte)
887{
888 if ((part->block0 >> 32) != 0)
889 return EINVAL;
890 if ((part->nblocks >> 32) != 0)
891 return EINVAL;
892 if ((part->ptype.t.num >> 8) != 0)
893 return EINVAL;
894
895 memset(pte, 0, sizeof(mbr_pte_t));
896 pte->ptype = part->ptype.t.num;
897 pte->first_lba = host2uint32_t_le(part->block0);
898 pte->length = host2uint32_t_le(part->nblocks);
899 return EOK;
900}
901
902static errno_t mbr_pte_to_part(label_t *label, mbr_pte_t *pte, int index)
903{
904 label_part_t *part;
905 uint32_t block0;
906 uint32_t nblocks;
907
908 block0 = uint32_t_le2host(pte->first_lba);
909 nblocks = uint32_t_le2host(pte->length);
910
911 /* See UEFI specification 2.0 section 5.2.1 Legacy Master Boot Record */
912 if (pte->ptype == mbr_pt_unused || nblocks == 0)
913 return EOK;
914
915 part = calloc(1, sizeof(label_part_t));
916 if (part == NULL)
917 return ENOMEM;
918
919 part->ptype.fmt = lptf_num;
920 part->ptype.t.num = pte->ptype;
921 part->index = index;
922 part->block0 = block0;
923 part->nblocks = nblocks;
924
925 /*
926 * TODO: Verify
927 * - partition must reside on disk
928 * - partition must not overlap any other partition
929 */
930
931 part->label = label;
932 list_append(&part->lparts, &label->parts);
933 list_append(&part->lpri, &label->pri_parts);
934
935 if (pte->ptype == mbr_pt_extended)
936 label->ext_part = part;
937 return EOK;
938}
939
940static errno_t mbr_pte_to_log_part(label_t *label, uint64_t ebr_b0,
941 mbr_pte_t *pte)
942{
943 label_part_t *part;
944 uint32_t block0;
945 uint32_t nblocks;
946 size_t nlparts;
947
948 block0 = ebr_b0 + uint32_t_le2host(pte->first_lba);
949 nblocks = uint32_t_le2host(pte->length);
950
951 if (pte->ptype == mbr_pt_unused || nblocks == 0)
952 return EOK;
953
954 part = calloc(1, sizeof(label_part_t));
955 if (part == NULL)
956 return ENOMEM;
957
958 nlparts = list_count(&label->log_parts);
959
960 part->ptype.fmt = lptf_num;
961 part->ptype.t.num = pte->ptype;
962 part->index = mbr_nprimary + 1 + nlparts;
963 part->block0 = block0;
964 part->nblocks = nblocks;
965 part->hdr_blocks = block0 - ebr_b0;
966
967 part->label = label;
968 list_append(&part->lparts, &label->parts);
969 list_append(&part->llog, &label->log_parts);
970
971 return EOK;
972}
973
974static void mbr_log_part_to_ptes(label_part_t *part, mbr_pte_t *pthis,
975 mbr_pte_t *pnext)
976{
977 label_part_t *next;
978 uint64_t ep_b0;
979 uint64_t totsize;
980
981 /* First block of extended partition */
982 ep_b0 = part->label->ext_part->block0;
983
984 assert(link_used(&part->llog));
985 assert(part->block0 >= ep_b0);
986 assert(part->hdr_blocks <= part->block0 - ep_b0);
987
988 /* 'This' EBR entry */
989 if (pthis != NULL) {
990 memset(pthis, 0, sizeof(mbr_pte_t));
991 pthis->ptype = part->ptype.t.num;
992 pthis->first_lba = host2uint32_t_le(part->hdr_blocks);
993 pthis->length = host2uint32_t_le(part->nblocks);
994 }
995
996 /* 'Next' EBR entry */
997 if (pnext != NULL) {
998 next = mbr_log_part_next(part);
999
1000 memset(pnext, 0, sizeof(mbr_pte_t));
1001 if (next != NULL) {
1002 /* Total size of EBR + partition */
1003 totsize = next->hdr_blocks + next->nblocks;
1004
1005 pnext->ptype = mbr_pt_extended;
1006 pnext->first_lba = host2uint32_t_le(next->block0 -
1007 next->hdr_blocks - ep_b0);
1008 pnext->length = host2uint32_t_le(totsize);
1009 }
1010 }
1011}
1012
1013/** Update partition table entry at specified index.
1014 *
1015 * Replace partition entry at index @a index with the contents of
1016 * @a pte.
1017 */
1018static errno_t mbr_pte_update(label_t *label, mbr_pte_t *pte, int index)
1019{
1020 mbr_br_block_t *br;
1021 errno_t rc;
1022
1023 br = calloc(1, label->block_size);
1024 if (br == NULL)
1025 return ENOMEM;
1026
1027 rc = label->bd.ops->read(label->bd.arg, mbr_ba, 1, br);
1028 if (rc != EOK) {
1029 rc = EIO;
1030 goto error;
1031 }
1032
1033 br->pte[index] = *pte;
1034
1035 rc = label->bd.ops->write(label->bd.arg, mbr_ba, 1, br);
1036 if (rc != EOK) {
1037 rc = EIO;
1038 goto error;
1039 }
1040
1041 free(br);
1042 return EOK;
1043error:
1044 free(br);
1045 return rc;
1046}
1047
1048/** Insert logical partition into logical partition list. */
1049static errno_t mbr_log_part_insert(label_t *label, label_part_t *part)
1050{
1051 label_part_t *cur;
1052
1053 cur = mbr_log_part_first(label);
1054 while (cur != NULL) {
1055 if (cur->block0 + cur->nblocks > part->block0)
1056 break;
1057 cur = mbr_log_part_next(part);
1058 }
1059
1060 if (cur != NULL)
1061 list_insert_before(&part->llog, &cur->llog);
1062 else
1063 list_append(&part->llog, &label->log_parts);
1064
1065 return EOK;
1066}
1067
1068/** Create EBR for partition.
1069 *
1070 * @param label Label
1071 * @param part Partition for which to create EBR or @c NULL to create
1072 * EBR for empty partition chain
1073 * @return EOK on success or non-zero error code
1074 */
1075static errno_t mbr_ebr_create(label_t *label, label_part_t *part)
1076{
1077 mbr_br_block_t *br;
1078 uint64_t ba;
1079 errno_t rc;
1080
1081 br = calloc(1, label->block_size);
1082 if (br == NULL)
1083 return ENOMEM;
1084
1085 if (part != NULL) {
1086 ba = part->block0 - part->hdr_blocks;
1087 mbr_log_part_to_ptes(part, &br->pte[mbr_ebr_pte_this],
1088 &br->pte[mbr_ebr_pte_next]);
1089 } else {
1090 ba = label->ext_part->block0;
1091 }
1092
1093 br->signature = host2uint16_t_le(mbr_br_signature);
1094
1095 rc = label->bd.ops->write(label->bd.arg, ba, 1, br);
1096 if (rc != EOK) {
1097 rc = EIO;
1098 goto error;
1099 }
1100
1101 free(br);
1102 return EOK;
1103error:
1104 free(br);
1105 return rc;
1106}
1107
1108static errno_t mbr_ebr_delete(label_t *label, label_part_t *part)
1109{
1110 mbr_br_block_t *br;
1111 uint64_t ba;
1112 errno_t rc;
1113
1114 br = calloc(1, label->block_size);
1115 if (br == NULL)
1116 return ENOMEM;
1117
1118 ba = part->block0 - part->hdr_blocks;
1119
1120 rc = label->bd.ops->write(label->bd.arg, ba, 1, br);
1121 if (rc != EOK) {
1122 rc = EIO;
1123 goto error;
1124 }
1125
1126 free(br);
1127 return EOK;
1128error:
1129 free(br);
1130 return rc;
1131}
1132
1133/** Update 'next' PTE in EBR of partition. */
1134static errno_t mbr_ebr_update_next(label_t *label, label_part_t *part)
1135{
1136 mbr_br_block_t *br;
1137 uint64_t ba;
1138 uint16_t sgn;
1139 errno_t rc;
1140
1141 ba = part->block0 - part->hdr_blocks;
1142
1143 br = calloc(1, label->block_size);
1144 if (br == NULL)
1145 return ENOMEM;
1146
1147 rc = label->bd.ops->read(label->bd.arg, ba, 1, br);
1148 if (rc != EOK) {
1149 rc = EIO;
1150 goto error;
1151 }
1152
1153 /* Verify boot record signature */
1154 sgn = uint16_t_le2host(br->signature);
1155 if (sgn != mbr_br_signature) {
1156 rc = EIO;
1157 goto error;
1158 }
1159
1160 mbr_log_part_to_ptes(part, NULL, &br->pte[mbr_ebr_pte_next]);
1161
1162 rc = label->bd.ops->write(label->bd.arg, ba, 1, br);
1163 if (rc != EOK) {
1164 rc = EIO;
1165 goto error;
1166 }
1167
1168 free(br);
1169 return EOK;
1170error:
1171 free(br);
1172 return rc;
1173}
1174
1175/** Update indices of logical partitions.
1176 *
1177 * Logical partition indices are unstable, i.e. they can change during
1178 * the lifetime of a logical partition. Since the index corresponds to the
1179 * position of the partition in order of block address, anytime a partition
1180 * is created or deleted, indices of all partitions at higher addresses
1181 * change.
1182 */
1183static void mbr_update_log_indices(label_t *label)
1184{
1185 label_part_t *part;
1186 int idx;
1187
1188 idx = mbr_nprimary + 1;
1189
1190 part = mbr_log_part_first(label);
1191 while (part != NULL) {
1192 part->index = idx++;
1193 part = mbr_log_part_next(part);
1194 }
1195}
1196
1197/** @}
1198 */
Note: See TracBrowser for help on using the repository browser.