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

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

Use errno_t in all uspace and kernel code.

Change type of every variable, parameter and return value that holds an
<errno.h> constant to either errno_t (the usual case), or sys_errno_t
(some places in kernel). This is for the purpose of self-documentation,
as well as for type-checking with a bit of type definition hackery.

Although this is a massive commit, it is a simple text replacement, and thus
is very easy to verify. Simply do the following:

`
git checkout <this commit's hash>
git reset HEAD
git add .
tools/srepl '\berrno_t\b' int
git add .
tools/srepl '\bsys_errno_t\b' sysarg_t
git reset
git diff
`

While this doesn't ensure that the replacements are correct, it does ensure
that the commit doesn't do anything except those replacements. Since errno_t
is typedef'd to int in the usual case (and sys_errno_t to sysarg_t), even if
incorrect, this commit cannot change behavior.

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