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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 7d3a4ac was f7f2c7f, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 5 years ago

Avoid memory leak in mbr_create function

  • 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 rc = ENOMEM;
332 goto error;
333 }
334
335 list_initialize(&label->parts);
336 list_initialize(&label->pri_parts);
337 list_initialize(&label->log_parts);
338
339 mbr->media_id = 0;
340 mbr->pad0 = 0;
341 for (i = 0; i < mbr_nprimary; i++)
342 mbr_unused_pte(&mbr->pte[i]);
343 mbr->signature = host2uint16_t_le(mbr_br_signature);
344
345 rc = bd->ops->write(bd->arg, mbr_ba, 1, mbr);
346 if (rc != EOK) {
347 rc = EIO;
348 goto error;
349 }
350
351 free(mbr);
352 mbr = NULL;
353
354 label->ops = &mbr_label_ops;
355 label->ltype = lt_mbr;
356 label->block_size = bsize;
357 label->bd = *bd;
358 label->ablock0 = mbr_ablock0;
359 label->anblocks = nblocks - mbr_ablock0;
360 label->pri_entries = mbr_nprimary;
361 label->ext_part = NULL;
362
363 *rlabel = label;
364 return EOK;
365error:
366 free(mbr);
367 free(label);
368 return rc;
369}
370
371static void mbr_close(label_t *label)
372{
373 label_part_t *part;
374
375 if (label == NULL)
376 return;
377
378 part = mbr_part_first(label);
379 while (part != NULL) {
380 list_remove(&part->lparts);
381 if (link_used(&part->lpri))
382 list_remove(&part->lpri);
383 if (link_used(&part->llog))
384 list_remove(&part->llog);
385 free(part);
386
387 part = mbr_part_first(label);
388 }
389
390 free(label);
391}
392
393static errno_t mbr_destroy(label_t *label)
394{
395 mbr_br_block_t *mbr = NULL;
396 label_part_t *part;
397 errno_t rc;
398
399 part = mbr_part_first(label);
400 if (part != NULL) {
401 rc = ENOTEMPTY;
402 goto error;
403 }
404
405 mbr = calloc(1, label->block_size);
406 if (mbr == NULL) {
407 rc = ENOMEM;
408 goto error;
409 }
410
411 rc = label->bd.ops->write(label->bd.arg, mbr_ba, 1, mbr);
412 if (rc != EOK) {
413 rc = EIO;
414 goto error;
415 }
416
417 free(mbr);
418 mbr = NULL;
419
420 free(label);
421 return EOK;
422error:
423 free(mbr);
424 return rc;
425}
426
427static bool mbr_can_delete_part(label_t *label)
428{
429 return list_count(&label->parts) > 0;
430}
431
432static bool mbr_can_modify_part(label_t *label)
433{
434 return list_count(&label->parts) > 0;
435}
436
437static errno_t mbr_get_info(label_t *label, label_info_t *linfo)
438{
439 memset(linfo, 0, sizeof(label_info_t));
440 linfo->ltype = lt_mbr;
441
442 /* We support extended partitions */
443 linfo->flags = lf_ext_supp;
444
445 /** Can create primary if there is a free slot */
446 if (list_count(&label->pri_parts) < mbr_nprimary)
447 linfo->flags |= lf_can_create_pri;
448 /* Can create extended if there is a free slot and no extended */
449 if ((linfo->flags & lf_can_create_pri) != 0 && label->ext_part == NULL)
450 linfo->flags |= lf_can_create_ext;
451 /* Can create logical if there is an extended partition */
452 if (label->ext_part != NULL)
453 linfo->flags |= lf_can_create_log;
454 /* Can delete partition */
455 if (mbr_can_delete_part(label))
456 linfo->flags |= lf_can_delete_part;
457 /* Can modify partition */
458 if (mbr_can_modify_part(label))
459 linfo->flags |= lf_can_modify_part;
460
461 linfo->ablock0 = label->ablock0;
462 linfo->anblocks = label->anblocks;
463
464 return EOK;
465}
466
467static label_part_t *mbr_part_first(label_t *label)
468{
469 link_t *link;
470
471 link = list_first(&label->parts);
472 if (link == NULL)
473 return NULL;
474
475 return list_get_instance(link, label_part_t, lparts);
476}
477
478static label_part_t *mbr_part_next(label_part_t *part)
479{
480 link_t *link;
481
482 link = list_next(&part->lparts, &part->label->parts);
483 if (link == NULL)
484 return NULL;
485
486 return list_get_instance(link, label_part_t, lparts);
487}
488
489static label_part_t *mbr_log_part_first(label_t *label)
490{
491 link_t *link;
492
493 link = list_first(&label->log_parts);
494 if (link == NULL)
495 return NULL;
496
497 return list_get_instance(link, label_part_t, llog);
498}
499
500static label_part_t *mbr_log_part_next(label_part_t *part)
501{
502 link_t *link;
503
504 link = list_next(&part->llog, &part->label->log_parts);
505 if (link == NULL)
506 return NULL;
507
508 return list_get_instance(link, label_part_t, llog);
509}
510
511static label_part_t *mbr_log_part_prev(label_part_t *part)
512{
513 link_t *link;
514
515 link = list_prev(&part->llog, &part->label->log_parts);
516 if (link == NULL)
517 return NULL;
518
519 return list_get_instance(link, label_part_t, llog);
520}
521
522static label_part_t *mbr_pri_part_first(label_t *label)
523{
524 link_t *link;
525
526 link = list_first(&label->pri_parts);
527 if (link == NULL)
528 return NULL;
529
530 return list_get_instance(link, label_part_t, lpri);
531}
532
533static label_part_t *mbr_pri_part_next(label_part_t *part)
534{
535 link_t *link;
536
537 link = list_next(&part->lpri, &part->label->pri_parts);
538 if (link == NULL)
539 return NULL;
540
541 return list_get_instance(link, label_part_t, lpri);
542}
543
544static void mbr_part_get_info(label_part_t *part, label_part_info_t *pinfo)
545{
546 pinfo->index = part->index;
547 pinfo->block0 = part->block0;
548 pinfo->nblocks = part->nblocks;
549
550 if (link_used(&part->llog))
551 pinfo->pkind = lpk_logical;
552 else if (part->ptype.t.num == mbr_pt_extended)
553 pinfo->pkind = lpk_extended;
554 else
555 pinfo->pkind = lpk_primary;
556}
557
558static errno_t mbr_part_create(label_t *label, label_part_spec_t *pspec,
559 label_part_t **rpart)
560{
561 label_part_t *part;
562 label_part_t *prev;
563 label_part_t *next;
564 mbr_pte_t pte;
565 errno_t rc;
566
567 if (pspec->ptype.fmt != lptf_num)
568 return EINVAL;
569
570 part = calloc(1, sizeof(label_part_t));
571 if (part == NULL)
572 return ENOMEM;
573
574 /* XXX Check if index is used */
575 part->label = label;
576 part->index = pspec->index;
577 part->block0 = pspec->block0;
578 part->nblocks = pspec->nblocks;
579 part->hdr_blocks = pspec->hdr_blocks;
580
581 switch (pspec->pkind) {
582 case lpk_primary:
583 part->ptype = pspec->ptype;
584 break;
585 case lpk_extended:
586 part->ptype.fmt = lptf_num;
587 part->ptype.t.num = mbr_pt_extended;
588 if (pspec->ptype.t.num != 0) {
589 rc = EINVAL;
590 goto error;
591 }
592 if (label->ext_part != NULL) {
593 rc = EEXIST;
594 goto error;
595 }
596 break;
597 case lpk_logical:
598 part->ptype = pspec->ptype;
599 if (pspec->index != 0) {
600 rc = EINVAL;
601 goto error;
602 }
603 break;
604 }
605
606 if (pspec->pkind != lpk_logical) {
607 /* Primary or extended partition */
608
609 /* Verify index is within bounds and free */
610 rc = mbr_check_free_idx(label, pspec->index);
611 if (rc != EOK) {
612 rc = EINVAL;
613 goto error;
614 }
615
616 /* Verify range is within bounds and free */
617 rc = mbr_check_free_pri_range(label, pspec->block0, pspec->nblocks);
618 if (rc != EOK) {
619 rc = EINVAL;
620 goto error;
621 }
622
623 if (pspec->hdr_blocks != 0) {
624 rc = EINVAL;
625 goto error;
626 }
627
628 rc = mbr_part_to_pte(part, &pte);
629 if (rc != EOK) {
630 rc = EINVAL;
631 goto error;
632 }
633
634 rc = mbr_pte_update(label, &pte, pspec->index - 1);
635 if (rc != EOK) {
636 rc = EIO;
637 goto error;
638 }
639
640 if (pspec->pkind == lpk_extended) {
641 label->ext_part = part;
642
643 /* Create EBR for empty partition chain */
644 rc = mbr_ebr_create(label, NULL);
645 if (rc != EOK) {
646 label->ext_part = NULL;
647 rc = EIO;
648 goto error;
649 }
650 }
651
652 list_append(&part->lparts, &label->parts);
653 list_append(&part->lpri, &label->pri_parts);
654 } else {
655 /* Verify range is within bounds and free */
656 rc = mbr_check_free_log_range(label, pspec->hdr_blocks,
657 pspec->block0, pspec->nblocks);
658 if (rc != EOK) {
659 rc = EINVAL;
660 goto error;
661 }
662
663 /* Logical partition */
664 rc = mbr_log_part_insert(label, part);
665 if (rc != EOK)
666 goto error;
667
668 /* Create EBR for new partition */
669 rc = mbr_ebr_create(label, part);
670 if (rc != EOK)
671 goto error;
672
673 prev = mbr_log_part_prev(part);
674 if (prev != NULL) {
675 /* Update 'next' PTE in EBR of previous partition */
676 rc = mbr_ebr_update_next(label, prev);
677 if (rc != EOK) {
678 goto error;
679 }
680 } else {
681 /* New partition is now the first one */
682 next = mbr_log_part_next(part);
683 if (next != NULL) {
684 /*
685 * Create new, relocated EBR for the former
686 * first partition
687 */
688 next->hdr_blocks = pspec->hdr_blocks;
689 rc = mbr_ebr_create(label, next);
690 if (rc != EOK)
691 goto error;
692 }
693 }
694
695 /* This also sets index for the new partition. */
696 mbr_update_log_indices(label);
697 }
698
699 *rpart = part;
700 return EOK;
701error:
702 free(part);
703 return rc;
704}
705
706static errno_t mbr_part_destroy(label_part_t *part)
707{
708 mbr_pte_t pte;
709 label_part_t *prev;
710 label_part_t *next;
711 uint64_t ep_b0;
712 errno_t rc;
713
714 if (link_used(&part->lpri)) {
715 /* Primary/extended partition */
716
717 /* Prepare unused partition table entry */
718 mbr_unused_pte(&pte);
719
720 /* Modify partition table */
721 rc = mbr_pte_update(part->label, &pte, part->index - 1);
722 if (rc != EOK)
723 return EIO;
724
725 /* If it was the extended partition, clear ext. part. pointer */
726 if (part == part->label->ext_part)
727 part->label->ext_part = NULL;
728
729 list_remove(&part->lpri);
730 } else {
731 /* Logical partition */
732
733 prev = mbr_log_part_prev(part);
734 if (prev != NULL) {
735 /* Update next link in previous EBR */
736 list_remove(&part->llog);
737
738 rc = mbr_ebr_update_next(part->label, prev);
739 if (rc != EOK) {
740 /* Roll back */
741 list_insert_after(&part->llog, &prev->llog);
742 return EIO;
743 }
744
745 /* Delete EBR */
746 rc = mbr_ebr_delete(part->label, part);
747 if (rc != EOK)
748 return EIO;
749 } else {
750 next = mbr_log_part_next(part);
751 list_remove(&part->llog);
752
753 if (next != NULL) {
754 /*
755 * Relocate next partition's EBR to the beginning
756 * of the extended partition. This also
757 * overwrites the EBR of the former first
758 * partition.
759 */
760
761 /* First block of extended partition */
762 ep_b0 = part->label->ext_part->block0;
763
764 next->hdr_blocks = next->block0 - ep_b0;
765
766 rc = mbr_ebr_create(part->label, next);
767 if (rc != EOK) {
768 list_prepend(&part->llog, &part->label->log_parts);
769 return EIO;
770 }
771 } else {
772 /* Delete EBR */
773 rc = mbr_ebr_delete(part->label, part);
774 if (rc != EOK)
775 return EIO;
776 }
777 }
778
779 /* Update indices */
780 mbr_update_log_indices(part->label);
781 }
782
783 list_remove(&part->lparts);
784 free(part);
785 return EOK;
786}
787
788static errno_t mbr_suggest_ptype(label_t *label, label_pcnt_t pcnt,
789 label_ptype_t *ptype)
790{
791 ptype->fmt = lptf_num;
792 ptype->t.num = 0;
793
794 switch (pcnt) {
795 case lpc_exfat:
796 ptype->t.num = mbr_pt_ms_advanced;
797 break;
798 case lpc_ext4:
799 ptype->t.num = mbr_pt_linux;
800 break;
801 case lpc_fat12_16:
802 ptype->t.num = mbr_pt_fat16_lba;
803 break;
804 case lpc_fat32:
805 ptype->t.num = mbr_pt_fat32_lba;
806 break;
807 case lpc_minix:
808 ptype->t.num = mbr_pt_minix;
809 break;
810 }
811
812 if (ptype->t.num == 0)
813 return EINVAL;
814
815 return EOK;
816}
817
818/** Determine if two block address ranges overlap. */
819static bool mbr_overlap(uint64_t a0, uint64_t an, uint64_t b0, uint64_t bn)
820{
821 return !(a0 + an <= b0 || b0 + bn <= a0);
822}
823
824/** Verify that the specified index is valid and free. */
825static errno_t mbr_check_free_idx(label_t *label, int index)
826{
827 label_part_t *part;
828
829 if (index < 1 || index > label->pri_entries)
830 return EINVAL;
831
832 part = mbr_pri_part_first(label);
833 while (part != NULL) {
834 if (part->index == index)
835 return EEXIST;
836 part = mbr_pri_part_next(part);
837 }
838
839 return EOK;
840}
841
842static errno_t mbr_check_free_pri_range(label_t *label, uint64_t block0,
843 uint64_t nblocks)
844{
845 label_part_t *part;
846
847 if (block0 < label->ablock0)
848 return EINVAL;
849 if (block0 + nblocks > label->ablock0 + label->anblocks)
850 return EINVAL;
851
852 part = mbr_pri_part_first(label);
853 while (part != NULL) {
854 if (mbr_overlap(block0, nblocks, part->block0, part->nblocks))
855 return EEXIST;
856 part = mbr_pri_part_next(part);
857 }
858
859 return EOK;
860}
861
862static errno_t mbr_check_free_log_range(label_t *label, uint64_t hdr_blocks,
863 uint64_t block0, uint64_t nblocks)
864{
865 label_part_t *part;
866
867 if (block0 - hdr_blocks < label->ext_part->block0)
868 return EINVAL;
869 if (block0 + nblocks > label->ext_part->block0 + label->ext_part->nblocks)
870 return EINVAL;
871
872 part = mbr_log_part_first(label);
873 while (part != NULL) {
874 if (mbr_overlap(block0 - hdr_blocks, nblocks + hdr_blocks,
875 part->block0 - part->hdr_blocks, part->nblocks + part->hdr_blocks))
876 return EEXIST;
877 part = mbr_log_part_next(part);
878 }
879
880 return EOK;
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.