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