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

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

Break liblabel dependency on libblock.

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