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

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

Initialize EBR for empty partition chain when creating extended partition. Fix logical partition size. When creating partition in liblabel, check index and block range are within bounds and free.

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