source: mainline/uspace/lib/mbr/libmbr.c@ 0c322fa

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0c322fa was 6453e306, checked in by Martin Decky <martin@…>, 12 years ago

basic code review and coding style cleanup

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