source: mainline/uspace/lib/mbr/libmbr.c@ 8c95dff

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8c95dff was 8c95dff, checked in by Dominik Taborsky (AT DOT) <brembyseznamcz>, 12 years ago

various bugfixes

  • Property mode set to 100644
File size: 21.7 KB
RevLine 
[d3a92c87]1/*
[5beb1ff]2 * Copyright (c) 2011, 2012, 2013 Dominik Taborsky
[d3a92c87]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.
[271e24a]14 * - The LIBMBR_NAME of the author may not be used to endorse or promote products
[d3a92c87]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 libmbr
30 * @{
31 */
32/** @file MBR extraxtion library
33 */
34
35#include <async.h>
[8f6c7785]36#include <assert.h>
[d3a92c87]37#include <block.h>
[8f6c7785]38#include <byteorder.h>
[d3a92c87]39#include <errno.h>
[8f6c7785]40#include <ipc/bd.h>
41#include <mem.h>
42#include <stdio.h>
[d3a92c87]43#include <stdlib.h>
[dc76f4a]44#include <str_error.h>
[d3a92c87]45
46#include "libmbr.h"
47
48static br_block_t * alloc_br(void);
[a2aa81cb]49static int decode_part(pt_entry_t *, mbr_part_t *, uint32_t);
50static int decode_logical(mbr_label_t *, mbr_part_t *);
51static void encode_part(mbr_part_t *, pt_entry_t *, uint32_t, bool);
[dc76f4a]52static bool check_overlap(mbr_part_t *, mbr_part_t *);
53static bool check_encaps(mbr_part_t *, mbr_part_t *);
54static bool check_preceeds(mbr_part_t *, mbr_part_t *);
55static mbr_err_val mbr_add_primary(mbr_label_t *, mbr_part_t *);
56static mbr_err_val mbr_add_logical(mbr_label_t *, mbr_part_t *);
[a2aa81cb]57
58/** Allocate and initialize mbr_label_t structure */
59mbr_label_t * mbr_alloc_label(void)
60{
61 mbr_label_t *label = malloc(sizeof(mbr_label_t));
62 if (label == NULL)
63 return NULL;
64
65 label->mbr = NULL;
66 label->parts = NULL;
67 label->device = 0;
68
69 return label;
70}
71
[8c95dff]72void mbr_set_device(mbr_label_t *label, service_id_t dev_handle)
73{
74 label->device = dev_handle;
75}
76
[a2aa81cb]77/** Free mbr_label_t structure */
78void mbr_free_label(mbr_label_t *label)
79{
80 if (label->mbr != NULL)
81 mbr_free_mbr(label->mbr);
82
83 if (label->parts != NULL)
84 mbr_free_partitions(label->parts);
85
86 free(label);
87}
[d3a92c87]88
[700f89e]89/** Allocate memory for mbr_t */
[9bda5d90]90mbr_t * mbr_alloc_mbr(void)
[700f89e]91{
[9bda5d90]92 return malloc(sizeof(mbr_t));
[700f89e]93}
[8f6c7785]94
[d3a92c87]95/** Read MBR from specific device
[a2aa81cb]96 * @param label label to write data to
97 * @param dev_handle device to read MBR from
[d3a92c87]98 *
[a2aa81cb]99 * @return EOK on success, error code on error
[d3a92c87]100 */
[a2aa81cb]101int mbr_read_mbr(mbr_label_t *label, service_id_t dev_handle)
[44c4886]102{
[d3a92c87]103 int rc;
[a2aa81cb]104
105 if (label->mbr == NULL) {
106 label->mbr = mbr_alloc_mbr();
107 if (label->mbr == NULL) {
108 return ENOMEM;
109 }
[d3a92c87]110 }
111
112 rc = block_init(EXCHANGE_ATOMIC, dev_handle, 512);
[a2aa81cb]113 if (rc != EOK)
114 return rc;
[d3a92c87]115
[a2aa81cb]116 rc = block_read_direct(dev_handle, 0, 1, &(label->mbr->raw_data));
[d3a92c87]117 block_fini(dev_handle);
[a2aa81cb]118 if (rc != EOK)
119 return rc;
[d3a92c87]120
[a2aa81cb]121 label->device = dev_handle;
[d3a92c87]122
[a2aa81cb]123 return EOK;
[d3a92c87]124}
125
126/** Write mbr to disk
[a2aa81cb]127 * @param label MBR to be written
[d3a92c87]128 * @param dev_handle device handle to write MBR to (may be different
129 * from the device in 'mbr')
130 *
[700f89e]131 * @return 0 on success, otherwise libblock error code
[d3a92c87]132 */
[a2aa81cb]133int mbr_write_mbr(mbr_label_t *label, service_id_t dev_handle)
[d3a92c87]134{
135 int rc;
136
137 rc = block_init(EXCHANGE_ATOMIC, dev_handle, 512);
138 if (rc != EOK) {
139 return rc;
140 }
141
[a2aa81cb]142 rc = block_write_direct(dev_handle, 0, 1, &(label->mbr->raw_data));
[d3a92c87]143 block_fini(dev_handle);
144 if (rc != EOK) {
145 return rc;
146 }
147
[a2aa81cb]148 return EOK;
[d3a92c87]149}
150
151/** Decide whether this is an actual MBR or a Protective MBR from GPT
152 *
153 * @param mbr the actual MBR to decide upon
154 *
155 * @return 1 if MBR, 0 if GPT
156 */
[a2aa81cb]157int mbr_is_mbr(mbr_label_t *label)
[d3a92c87]158{
[a2aa81cb]159 return (label->mbr->raw_data.pte[0].ptype != PT_GPT) ? 1 : 0;
[d3a92c87]160}
161
[a2aa81cb]162/** Parse partitions from MBR, freeing previous partitions if any
163 * NOTE: it is assumed mbr_read_mbr(label) was called before.
164 * @param label MBR to be parsed
[d3a92c87]165 *
[a2aa81cb]166 * @return linked list of partitions or NULL on error
[d3a92c87]167 */
[a2aa81cb]168int mbr_read_partitions(mbr_label_t *label)
[d3a92c87]169{
[a2aa81cb]170 if (label == NULL || label->mbr == NULL)
171 return EINVAL;
[8f6c7785]172
[a2aa81cb]173 int rc, rc_ext;
174 unsigned int i;
175 mbr_part_t *p;
176 mbr_part_t *ext = NULL;
[6e8e4e19]177
[a2aa81cb]178 if (label->parts != NULL)
179 mbr_free_partitions(label->parts);
[6e8e4e19]180
[a2aa81cb]181 label->parts = mbr_alloc_partitions();
182 if (label->parts == NULL) {
183 return ENOMEM;
[d3a92c87]184 }
[6e8e4e19]185
[a2aa81cb]186 /* Generate the primary partitions */
[d3a92c87]187 for (i = 0; i < N_PRIMARY; ++i) {
[a2aa81cb]188 if (label->mbr->raw_data.pte[i].ptype == PT_UNUSED)
[d3a92c87]189 continue;
[6e8e4e19]190
[8f6c7785]191 p = mbr_alloc_partition();
[d3a92c87]192 if (p == NULL) {
[271e24a]193 printf(LIBMBR_NAME ": Error on memory allocation.\n");
[a2aa81cb]194 mbr_free_partitions(label->parts);
195 return ENOMEM;
[d3a92c87]196 }
[6e8e4e19]197
[a2aa81cb]198 rc_ext = decode_part(&(label->mbr->raw_data.pte[i]), p, 0);
[8f6c7785]199 mbr_set_flag(p, ST_LOGIC, false);
[a2aa81cb]200 rc = mbr_add_partition(label, p);
[8f6c7785]201 if (rc != ERR_OK) {
202 printf(LIBMBR_NAME ": Error occured during decoding the MBR. (%d)\n" \
[a2aa81cb]203 LIBMBR_NAME ": MBR is invalid.\n", rc);
204 mbr_free_partitions(label->parts);
205 return EINVAL;
[8f6c7785]206 }
[6e8e4e19]207
[8f6c7785]208 if (rc_ext) {
[d3a92c87]209 ext = p;
[6e8e4e19]210 label->parts->l_extended = &p->link;
[30440ed]211 }
[d3a92c87]212 }
[6e8e4e19]213
[a2aa81cb]214 /* Fill in the primary partitions and generate logical ones, if any */
215 rc = decode_logical(label, ext);
[d3a92c87]216 if (rc != EOK) {
[6e8e4e19]217 printf(LIBMBR_NAME ": Error during decoding logical partitions: %d - %s.\n" \
218 LIBMBR_NAME ": Partition list may be incomplete.\n", rc, str_error(rc));
[a2aa81cb]219 return rc;
[d3a92c87]220 }
[6e8e4e19]221
[a2aa81cb]222 return EOK;
[d3a92c87]223}
224
225/** Write MBR and partitions to device
[a2aa81cb]226 * @param label label to write
227 * @param dev_handle device to write the data to
[d3a92c87]228 *
[a2aa81cb]229 * @return returns EOK on succes, specific error code otherwise
[d3a92c87]230 */
[a2aa81cb]231int mbr_write_partitions(mbr_label_t *label, service_id_t dev_handle)
[d3a92c87]232{
[1c8bfe8]233 if (label->parts == NULL)
234 return EOK;
235
236 if (label->mbr == NULL)
237 label->mbr = mbr_alloc_mbr();
238
[d3a92c87]239 int i = 0;
240 int rc;
[a2aa81cb]241 mbr_part_t *p;
242 mbr_part_t *ext = (label->parts->l_extended == NULL) ? NULL
243 : list_get_instance(label->parts->l_extended, mbr_part_t, link);
[8f6c7785]244
[d3a92c87]245 rc = block_init(EXCHANGE_ATOMIC, dev_handle, 512);
246 if (rc != EOK) {
[700f89e]247 printf(LIBMBR_NAME ": Error while initializing libblock: %d - %s.\n", rc, str_error(rc));
[d3a92c87]248 return rc;
249 }
[8f6c7785]250
[a2aa81cb]251 link_t *l = label->parts->list.head.next;
[8f6c7785]252
[a2aa81cb]253 /* Encoding primary partitions */
[6e8e4e19]254 for (i = 0; i < N_PRIMARY; i++) {
[a2aa81cb]255 p = list_get_instance(l, mbr_part_t, link);
256 encode_part(p, &(label->mbr->raw_data.pte[i]), 0, false);
[d617050]257 l = l->next;
258 }
[8f6c7785]259
[8c95dff]260 label->mbr->raw_data.signature = host2uint16_t_le(BR_SIGNATURE);
261
[a2aa81cb]262 /* Writing MBR */
263 rc = block_write_direct(dev_handle, 0, 1, &(label->mbr->raw_data));
[d617050]264 if (rc != EOK) {
[700f89e]265 printf(LIBMBR_NAME ": Error while writing MBR : %d - %s.\n", rc, str_error(rc));
[d617050]266 goto end;
267 }
[8f6c7785]268
[6e8e4e19]269 if (ext == NULL) {
270 rc = EOK;
[700f89e]271 goto end;
[6e8e4e19]272 }
[8f6c7785]273
[30440ed]274 uint32_t base = ext->start_addr;
[6e8e4e19]275 mbr_part_t *prev_p;
[700f89e]276
277 /* Note for future changes: Some thought has been put into design
278 * and implementation. If you don't have to change it, don't. Other
279 * designs have been tried, this came out as the least horror with
[8c95dff]280 * as much power over it as you can get. */
[700f89e]281
[a2aa81cb]282 /* Encoding and writing first logical partition */
283 if (l != &(label->parts->list.head)) {
[d617050]284 p = list_get_instance(l, mbr_part_t, link);
[8f6c7785]285 p->ebr_addr = base;
286 encode_part(p, &(p->ebr->pte[0]), base, false);
[700f89e]287 l = l->next;
288 } else {
289 /* If there was an extended but no logical, we should overwrite
290 * the space where the first logical's EBR would have been. There
291 * might be some garbage from the past. */
292 br_block_t * tmp = alloc_br();
293 rc = block_write_direct(dev_handle, base, 1, tmp);
[d617050]294 if (rc != EOK) {
[700f89e]295 printf(LIBMBR_NAME ": Error while writing EBR: %d - %s.\n", rc, str_error(rc));
[d617050]296 goto end;
[700f89e]297 }
298 free(tmp);
[6e8e4e19]299 rc = EOK;
[700f89e]300 goto end;
301 }
[8f6c7785]302
303 prev_p = p;
304
[6e8e4e19]305 /* Check EBR addresses
306 * This piece of code saves previous EBR placements from other
307 * software. But if our user modifies the logical partition chain,
308 * we have to fix those placements if needed.*/
309 link_t *l_ebr = l;
310 link_t *l_iter;
311 mbr_part_t *tmp = mbr_alloc_partition();
312 tmp->length = 1;
313 while (l_ebr != &(label->parts->list.head)) {
314 p = list_get_instance(l_ebr, mbr_part_t, link);
315 tmp->start_addr = p->ebr_addr;
316
317 l_iter = l;
318 while (l_iter != &(label->parts->list.head)) {
319 /* Checking whether EBR address makes sense. If not, we take a guess.
320 * So far this is simple, we just take the first preceeding sector.
321 * Fdisk always reserves at least 2048 sectors (1MiB), so it can have
322 * the EBR aligned as well as the partition itself. Parted reserves
323 * minimum one sector, like we do.
324 *
325 * Note that we know there is at least one sector free from previous checks.
326 * Also note that the user can set ebr_addr to their liking (if it's valid). */
327 if (p->ebr_addr < base || p->ebr_addr >= base + ext->length ||
328 check_overlap(tmp, list_get_instance(l_iter, mbr_part_t, link))) {
329 p->ebr_addr = p->start_addr - 1;
330 break;
331 }
332
333 l_iter = l_iter->next;
334 }
335
336 l_ebr = l_ebr->next;
337 }
338 mbr_free_partition(tmp);
339
[a2aa81cb]340 /* Encoding and writing logical partitions */
341 while (l != &(label->parts->list.head)) {
[d617050]342 p = list_get_instance(l, mbr_part_t, link);
[8f6c7785]343
344
345 encode_part(p, &(p->ebr->pte[0]), p->ebr_addr, false);
346 encode_part(p, &(prev_p->ebr->pte[1]), base, true);
347
348 rc = block_write_direct(dev_handle, prev_p->ebr_addr, 1, prev_p->ebr);
[d617050]349 if (rc != EOK) {
[700f89e]350 printf(LIBMBR_NAME ": Error while writing EBR: %d - %s.\n", rc, str_error(rc));
[d617050]351 goto end;
352 }
[8f6c7785]353
354 prev_p = p;
[d617050]355 l = l->next;
356 }
[8f6c7785]357
[a2aa81cb]358 /* write the last EBR */
[8f6c7785]359 encode_part(NULL, &(prev_p->ebr->pte[1]), 0, false);
360 rc = block_write_direct(dev_handle, prev_p->ebr_addr, 1, prev_p->ebr);
361 if (rc != EOK) {
[700f89e]362 printf(LIBMBR_NAME ": Error while writing EBR: %d - %s.\n", rc, str_error(rc));
[8f6c7785]363 goto end;
364 }
365
[d3a92c87]366 rc = EOK;
[8f6c7785]367
[d3a92c87]368end:
369 block_fini(dev_handle);
[8f6c7785]370
[d3a92c87]371 return rc;
372}
373
[271e24a]374/** mbr_part_t constructor */
375mbr_part_t * mbr_alloc_partition(void)
[d3a92c87]376{
[a2aa81cb]377 mbr_part_t *p = malloc(sizeof(mbr_part_t));
[d3a92c87]378 if (p == NULL) {
379 return NULL;
380 }
[700f89e]381
[d3a92c87]382 link_initialize(&(p->link));
383 p->ebr = NULL;
[a2aa81cb]384 p->type = PT_UNUSED;
[d3a92c87]385 p->status = 0;
386 p->start_addr = 0;
387 p->length = 0;
[8f6c7785]388 p->ebr_addr = 0;
[a2aa81cb]389
[d3a92c87]390 return p;
391}
392
[700f89e]393/** mbr_partitions_t constructor */
[271e24a]394mbr_partitions_t * mbr_alloc_partitions(void)
[d3a92c87]395{
[a2aa81cb]396 mbr_partitions_t *parts = malloc(sizeof(mbr_partitions_t));
[d3a92c87]397 if (parts == NULL) {
398 return NULL;
399 }
[a2aa81cb]400
[d3a92c87]401 list_initialize(&(parts->list));
[30440ed]402 parts->n_primary = 0;
403 parts->n_logical = 0;
404 parts->l_extended = NULL;
[a2aa81cb]405
406 /* add blank primary partitions */
407 int i;
408 mbr_part_t *p;
409 for (i = 0; i < N_PRIMARY; ++i) {
410 p = mbr_alloc_partition();
411 if (p == NULL) {
412 mbr_free_partitions(parts);
413 return NULL;
414 }
415 list_append(&(p->link), &(parts->list));
416 }
417
[d3a92c87]418
419 return parts;
420}
421
[d617050]422/** Add partition
423 * Performs checks, sorts the list.
[700f89e]424 *
[a2aa81cb]425 * @param label label to add to
[700f89e]426 * @param p partition to add
427 *
428 * @return ERR_OK (0) on success, other MBR_ERR_VAL otherwise
[d617050]429 */
[a2aa81cb]430mbr_err_val mbr_add_partition(mbr_label_t *label, mbr_part_t *p)
[d3a92c87]431{
[6e8e4e19]432 int rc1, rc2;
[a2aa81cb]433 aoff64_t nblocks;
[6e8e4e19]434
435 rc1 = block_init(EXCHANGE_ATOMIC, label->device, 512);
436 if (rc1 != EOK && rc1 != EEXIST) {
437 printf(LIBMBR_NAME ": Error during libblock init: %d - %s.\n", rc1, str_error(rc1));
[a2aa81cb]438 return ERR_LIBBLOCK;
439 }
[6e8e4e19]440
441 rc2 = block_get_nblocks(label->device, &nblocks);
442
443 if (rc1 != EEXIST)
444 block_fini(label->device);
445
446 if (rc2 != EOK) {
447 printf(LIBMBR_NAME ": Error while getting number of blocks: %d - %s.\n", rc2, str_error(rc2));
[a2aa81cb]448 return ERR_LIBBLOCK;
449 }
[6e8e4e19]450
451 if ((aoff64_t) p->start_addr + p->length > nblocks)
452 return ERR_OUT_BOUNDS;
453
454 if (label->parts == NULL) {
455 label->parts = mbr_alloc_partitions();
456 if (label->parts == NULL)
457 return ENOMEM; //FIXME! merge mbr_err_val into errno.h
458 }
459
460 if (mbr_get_flag(p, ST_LOGIC))
[a2aa81cb]461 /* adding logical partition */
[6e8e4e19]462 return mbr_add_logical(label, p);
463 else
[a2aa81cb]464 /* adding primary */
[6e8e4e19]465 return mbr_add_primary(label, p);
[d3a92c87]466}
467
[700f89e]468/** Remove partition
469 * Removes partition by index, indexed from zero. When removing extended
[a2aa81cb]470 * partition, all logical partitions get removed as well.
[700f89e]471 *
[a2aa81cb]472 * @param label label to remove from
[700f89e]473 * @param idx index of the partition to remove
474 *
475 * @return EOK on success, EINVAL if idx invalid
476 */
[a2aa81cb]477int mbr_remove_partition(mbr_label_t *label, size_t idx)
[d3a92c87]478{
[a2aa81cb]479 link_t *l = list_nth(&(label->parts->list), idx);
[700f89e]480 if (l == NULL)
481 return EINVAL;
482
[a2aa81cb]483 mbr_part_t *p;
[700f89e]484
[a2aa81cb]485 /* If we're removing an extended partition, remove all logical as well */
486 if (l == label->parts->l_extended) {
487 label->parts->l_extended = NULL;
[700f89e]488
[a2aa81cb]489 link_t *it = l->next;
490 link_t *next_it;
491 while (it != &(label->parts->list.head)) {
[700f89e]492 next_it = it->next;
493
494 p = list_get_instance(it, mbr_part_t, link);
495 if (mbr_get_flag(p, ST_LOGIC)) {
496 list_remove(it);
[a2aa81cb]497 label->parts->n_logical -= 1;
[700f89e]498 mbr_free_partition(p);
499 }
500
501 it = next_it;
502 }
503
[30440ed]504 }
[700f89e]505
[a2aa81cb]506 /* Remove the partition itself */
[700f89e]507 p = list_get_instance(l, mbr_part_t, link);
[a2aa81cb]508 if (mbr_get_flag(p, ST_LOGIC)) {
509 label->parts->n_logical -= 1;
510 list_remove(l);
511 mbr_free_partition(p);
512 } else {
513 /* Cannot remove primary - it would break ordering, just zero it */
514 label->parts->n_primary -= 1;
515 p->type = 0;
516 p->status = 0;
517 p->start_addr = 0;
518 p->length = 0;
519 p->ebr_addr = 0;
520 }
[700f89e]521
[271e24a]522 return EOK;
[d3a92c87]523}
524
[271e24a]525/** mbr_part_t destructor */
[a2aa81cb]526void mbr_free_partition(mbr_part_t *p)
[d3a92c87]527{
528 if (p->ebr != NULL)
529 free(p->ebr);
530 free(p);
531}
532
533/** Get flag bool value */
[a2aa81cb]534int mbr_get_flag(mbr_part_t *p, MBR_FLAGS flag)
[d3a92c87]535{
536 return (p->status & (1 << flag)) ? 1 : 0;
537}
538
539/** Set a specifig status flag to a value */
[a2aa81cb]540void mbr_set_flag(mbr_part_t *p, MBR_FLAGS flag, bool value)
[d3a92c87]541{
[6e8e4e19]542 uint16_t status = p->status;
[d3a92c87]543
544 if (value)
545 status = status | (1 << flag);
546 else
547 status = status ^ (status & (1 << flag));
548
549 p->status = status;
550}
551
[6e8e4e19]552/** Get next aligned address */
[8f6c7785]553uint32_t mbr_get_next_aligned(uint32_t addr, unsigned int alignment)
554{
555 uint32_t div = addr / alignment;
556 return (div + 1) * alignment;
557}
558
[6e8e4e19]559list_t * mbr_get_list(mbr_label_t *label)
560{
561 if (label->parts != NULL)
562 return &(label->parts->list);
563 else
564 return NULL;
565}
566
567mbr_part_t * mbr_get_first_partition(mbr_label_t *label)
568{
569 list_t *list = mbr_get_list(label);
570 if (list != NULL && !list_empty(list))
571 return list_get_instance(list->head.next, mbr_part_t, link);
572 else
573 return NULL;
574}
575
576mbr_part_t * mbr_get_next_partition(mbr_label_t *label, mbr_part_t *p)
577{
578 list_t *list = mbr_get_list(label);
579 if (list != NULL && &(p->link) != list_last(list))
580 return list_get_instance(p->link.next, mbr_part_t, link);
581 else
582 return NULL;
583}
584
[d3a92c87]585/** Just a wrapper for free() */
[a2aa81cb]586void mbr_free_mbr(mbr_t *mbr)
[d3a92c87]587{
588 free(mbr);
589}
590
591/** Free partition list
592 *
593 * @param parts partition list to be freed
594 */
[a2aa81cb]595void mbr_free_partitions(mbr_partitions_t *parts)
[d3a92c87]596{
597 list_foreach_safe(parts->list, cur_link, next) {
[a2aa81cb]598 mbr_part_t *p = list_get_instance(cur_link, mbr_part_t, link);
[8559fa0]599 list_remove(cur_link);
[d3a92c87]600 mbr_free_partition(p);
601 }
[d617050]602
[30440ed]603 free(parts);
[d3a92c87]604}
605
[0435fe41]606/* Internal functions follow */
[d3a92c87]607
[a2aa81cb]608static br_block_t *alloc_br()
[d3a92c87]609{
[a2aa81cb]610 br_block_t *br = malloc(sizeof(br_block_t));
[d3a92c87]611 if (br == NULL)
612 return NULL;
[8f6c7785]613
614 memset(br, 0, 512);
[d3a92c87]615 br->signature = host2uint16_t_le(BR_SIGNATURE);
[8f6c7785]616
[d3a92c87]617 return br;
618}
619
[30440ed]620/** Parse partition entry to mbr_part_t
621 * @return returns 1, if extended partition, 0 otherwise
622 * */
[a2aa81cb]623static int decode_part(pt_entry_t *src, mbr_part_t *trgt, uint32_t base)
[d3a92c87]624{
625 trgt->type = src->ptype;
626
[6e8e4e19]627 trgt->status = (trgt->status & 0xFF00) | (uint16_t) src->status;
[d3a92c87]628
629 trgt->start_addr = uint32_t_le2host(src->first_lba) + base;
630 trgt->length = uint32_t_le2host(src->length);
631
632 return (src->ptype == PT_EXTENDED) ? 1 : 0;
633}
634
[30440ed]635/** Parse MBR contents to mbr_part_t list */
[a2aa81cb]636static int decode_logical(mbr_label_t *label, mbr_part_t * ext)
[d3a92c87]637{
638 int rc;
[a2aa81cb]639 mbr_part_t *p;
[d3a92c87]640
641 if (ext == NULL)
642 return EOK;
643
[30440ed]644 uint32_t base = ext->start_addr;
645 uint32_t addr = base;
[a2aa81cb]646 br_block_t *ebr;
[8f6c7785]647
[a2aa81cb]648 rc = block_init(EXCHANGE_ATOMIC, label->device, 512);
[d3a92c87]649 if (rc != EOK)
650 return rc;
[8f6c7785]651
[30440ed]652 ebr = alloc_br();
653 if (ebr == NULL) {
654 rc = ENOMEM;
655 goto end;
656 }
[8f6c7785]657
[a2aa81cb]658 rc = block_read_direct(label->device, addr, 1, ebr);
[30440ed]659 if (rc != EOK) {
660 goto free_ebr_end;
661 }
[8f6c7785]662
[30440ed]663 if (uint16_t_le2host(ebr->signature) != BR_SIGNATURE) {
664 rc = EINVAL;
665 goto free_ebr_end;
666 }
[8f6c7785]667
[30440ed]668 if (ebr->pte[0].ptype == PT_UNUSED) {
669 rc = EOK;
670 goto free_ebr_end;
671 }
[8f6c7785]672
[30440ed]673 p = mbr_alloc_partition();
674 if (p == NULL) {
675 rc = ENOMEM;
676 goto free_ebr_end;
677 }
[8f6c7785]678
[30440ed]679 decode_part(&(ebr->pte[0]), p, base);
680 mbr_set_flag(p, ST_LOGIC, true);
681 p->ebr = ebr;
[8f6c7785]682 p->ebr_addr = addr;
[a2aa81cb]683 rc = mbr_add_partition(label, p);
[700f89e]684 if (rc != ERR_OK)
[8f6c7785]685 return EINVAL;
686
[30440ed]687 addr = uint32_t_le2host(ebr->pte[1].first_lba) + base;
[8f6c7785]688
[30440ed]689 while (ebr->pte[1].ptype != PT_UNUSED) {
[6e8e4e19]690
[d3a92c87]691 ebr = alloc_br();
692 if (ebr == NULL) {
[30440ed]693 rc = ENOMEM;
694 goto end;
[d3a92c87]695 }
[8f6c7785]696
[a2aa81cb]697 rc = block_read_direct(label->device, addr, 1, ebr);
[d3a92c87]698 if (rc != EOK) {
[30440ed]699 goto free_ebr_end;
[d3a92c87]700 }
[8f6c7785]701
[d3a92c87]702 if (uint16_t_le2host(ebr->signature) != BR_SIGNATURE) {
[30440ed]703 rc = EINVAL;
704 goto free_ebr_end;
[d3a92c87]705 }
[8f6c7785]706
[d3a92c87]707 p = mbr_alloc_partition();
[30440ed]708 if (p == NULL) {
709 rc = ENOMEM;
710 goto free_ebr_end;
711 }
[8f6c7785]712
713 decode_part(&(ebr->pte[0]), p, addr);
[d3a92c87]714 mbr_set_flag(p, ST_LOGIC, true);
715 p->ebr = ebr;
[8f6c7785]716 p->ebr_addr = addr;
[a2aa81cb]717 rc = mbr_add_partition(label, p);
[700f89e]718 if (rc != ERR_OK)
[8f6c7785]719 return EINVAL;
720
[30440ed]721 addr = uint32_t_le2host(ebr->pte[1].first_lba) + base;
722 }
[8f6c7785]723
[30440ed]724 rc = EOK;
[8f6c7785]725 goto end;
726
[30440ed]727free_ebr_end:
728 free(ebr);
[8f6c7785]729
[30440ed]730end:
[a2aa81cb]731 block_fini(label->device);
[8f6c7785]732
[30440ed]733 return rc;
[d3a92c87]734}
735
[271e24a]736/** Convert mbr_part_t to pt_entry_t */
[8f6c7785]737static void encode_part(mbr_part_t * src, pt_entry_t * trgt, uint32_t base, bool ebr)
[d3a92c87]738{
739 if (src != NULL) {
[a2aa81cb]740 trgt->status = (uint8_t) (src->status & 0xFF);
741 /* ingoring CHS */
742 trgt->first_chs[0] = 0xFE;
743 trgt->first_chs[1] = 0xFF;
744 trgt->first_chs[2] = 0xFF;
745 trgt->last_chs[0] = 0xFE;
746 trgt->last_chs[1] = 0xFF;
747 trgt->last_chs[2] = 0xFF;
[0435fe41]748 if (ebr) { /* encoding reference to EBR */
[8f6c7785]749 trgt->ptype = PT_EXTENDED_LBA;
750 trgt->first_lba = host2uint32_t_le(src->ebr_addr - base);
751 trgt->length = host2uint32_t_le(src->length + src->start_addr - src->ebr_addr);
[0435fe41]752 } else { /* encoding reference to partition */
[8f6c7785]753 trgt->ptype = src->type;
754 trgt->first_lba = host2uint32_t_le(src->start_addr - base);
755 trgt->length = host2uint32_t_le(src->length);
756 }
[6e8e4e19]757
758 if (trgt->ptype == PT_UNUSED)
759 memset(trgt, 0, sizeof(pt_entry_t));
[d3a92c87]760 } else {
[6e8e4e19]761 memset(trgt, 0, sizeof(pt_entry_t));
[d3a92c87]762 }
763}
764
[700f89e]765/** Check whether two partitions overlap
766 *
[dc76f4a]767 * @return true/false
[700f89e]768 */
[dc76f4a]769static bool check_overlap(mbr_part_t * p1, mbr_part_t * p2)
[d617050]770{
[6e8e4e19]771 if (p1->start_addr < p2->start_addr && p1->start_addr + p1->length <= p2->start_addr) {
[dc76f4a]772 return false;
[6e8e4e19]773 } else if (p1->start_addr > p2->start_addr && p2->start_addr + p2->length <= p1->start_addr) {
[dc76f4a]774 return false;
[d617050]775 }
776
[dc76f4a]777 return true;
[d617050]778}
779
[700f89e]780/** Check whether one partition encapsulates the other
781 *
[dc76f4a]782 * @return true/false
[700f89e]783 */
[dc76f4a]784static bool check_encaps(mbr_part_t * inner, mbr_part_t * outer)
[d617050]785{
786 if (inner->start_addr <= outer->start_addr || outer->start_addr + outer->length <= inner->start_addr) {
[dc76f4a]787 return false;
[d617050]788 } else if (outer->start_addr + outer->length < inner->start_addr + inner->length) {
[dc76f4a]789 return false;
[d617050]790 }
791
[dc76f4a]792 return true;
[d617050]793}
794
[700f89e]795/** Check whether one partition preceeds the other
796 *
[dc76f4a]797 * @return true/false
[700f89e]798 */
[dc76f4a]799static bool check_preceeds(mbr_part_t * preceeder, mbr_part_t * precedee)
[d617050]800{
801 return preceeder->start_addr < precedee->start_addr;
802}
803
[6e8e4e19]804mbr_err_val mbr_add_primary(mbr_label_t *label, mbr_part_t *p)
805{
806 if (label->parts->n_primary == 4) {
807 return ERR_PRIMARY_FULL;
808 }
809
810 /* Check if partition makes space for MBR itself. */
811 if (p->start_addr == 0) {
812 return ERR_OUT_BOUNDS;
813 }
814
815 /* if it's extended, is there any other one? */
816 if ((p->type == PT_EXTENDED || p->type == PT_EXTENDED_LBA) && label->parts->l_extended != NULL) {
817 return ERR_EXTENDED_PRESENT;
818 }
819
820 /* find a place and add it */
821 mbr_part_t *iter;
822 mbr_part_t *empty = NULL;
823 mbr_part_foreach(label, iter) {
824 if (iter->type == PT_UNUSED) {
825 if (empty == NULL)
826 empty = iter;
827 } else if (check_overlap(p, iter))
828 return ERR_OVERLAP;
829 }
830
831 list_insert_after(&(p->link), &(empty->link));
832 list_remove(&(empty->link));
833 free(empty);
834
835 label->parts->n_primary += 1;
836
837 if (p->type == PT_EXTENDED || p->type == PT_EXTENDED_LBA)
838 label->parts->l_extended = &(p->link);
839
840 return EOK;
841}
842
843mbr_err_val mbr_add_logical(mbr_label_t *label, mbr_part_t *p)
844{
845 /* is there any extended partition? */
846 if (label->parts->l_extended == NULL)
847 return ERR_NO_EXTENDED;
848
849 /* is the logical partition inside the extended one? */
850 mbr_part_t *ext = list_get_instance(label->parts->l_extended, mbr_part_t, link);
851 if (!check_encaps(p, ext))
852 return ERR_OUT_BOUNDS;
853
854 /* find a place for the new partition in a sorted linked list */
855 bool first_logical = true;
856 mbr_part_t *iter;
[0435fe41]857 mbr_part_foreach (label, iter) {
[6e8e4e19]858 if (mbr_get_flag(iter, ST_LOGIC)) {
859 if (check_overlap(p, iter))
860 return ERR_OVERLAP;
861 if (check_preceeds(iter, p)) {
862 /* checking if there's at least one sector of space preceeding */
863 if ((iter->start_addr + iter->length) >= p->start_addr - 1)
864 return ERR_NO_EBR;
865 } else if (first_logical){
866 /* First logical partition's EBR is before every other
867 * logical partition. Thus we don't check if this partition
868 * leaves enough space for it. */
869 first_logical = false;
870 } else {
871 /* checking if there's at least one sector of space following (for following partitions's EBR) */
872 if ((p->start_addr + p->length) >= iter->start_addr - 1)
873 return ERR_NO_EBR;
874 }
875 }
876 }
877
878 /* alloc EBR if it's not already there */
879 if (p->ebr == NULL) {
880 p->ebr = alloc_br();
881 if (p->ebr == NULL) {
882 return ERR_NOMEM;
883 }
884 }
885
886 /* add it */
887 list_append(&(p->link), &(label->parts->list));
888 label->parts->n_logical += 1;
889
890 return EOK;
891}
[8f6c7785]892
[d617050]893
894
Note: See TracBrowser for help on using the repository browser.