source: mainline/uspace/lib/mbr/libmbr.c@ 271e24a

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

hdisk - testing libmbr

  • Property mode set to 100644
File size: 12.3 KB
Line 
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 <ipc/bd.h>
36#include <async.h>
37#include <stdio.h>
38#include <block.h>
39#include <errno.h>
40#include <stdlib.h>
41#include <assert.h>
42#include <byteorder.h>
43
44#include "libmbr.h"
45
46static br_block_t * alloc_br(void);
47static int decode_part(pt_entry_t * src, mbr_part_t * trgt, uint32_t base);
48static int decode_logical(mbr_t * mbr, mbr_partitions_t * p, mbr_part_t * ext);
49static void encode_part(mbr_part_t * src, pt_entry_t * trgt, uint32_t base);
50
51/** Read MBR from specific device
52 * @param dev_handle device to read MBR from
53 *
54 * @return mbr record on success, NULL on error
55 */
56mbr_t * mbr_read_mbr(service_id_t dev_handle)
57{
58 int rc;
59
60 mbr_t * mbr = malloc(sizeof(mbr_t));
61 if (mbr == NULL) {
62 return NULL;
63 }
64
65 rc = block_init(EXCHANGE_ATOMIC, dev_handle, 512);
66 if (rc != EOK) {
67 return NULL;
68 }
69
70 rc = block_read_direct(dev_handle, 0, 1, &(mbr->raw_data));
71 if (rc != EOK) {
72 block_fini(dev_handle);
73 return NULL;
74 }
75
76 block_fini(dev_handle);
77
78 mbr->device = dev_handle;
79 //mbr->partitions = NULL;
80
81 return mbr;
82}
83
84/** Write mbr to disk
85 * @param mbr MBR to be written
86 * @param dev_handle device handle to write MBR to (may be different
87 * from the device in 'mbr')
88 *
89 * @return 0 on success, -1 on block_init error, -2 on write error
90 */
91int mbr_write_mbr(mbr_t * mbr, service_id_t dev_handle)
92{
93 int rc;
94
95 rc = block_init(EXCHANGE_ATOMIC, dev_handle, 512);
96 if (rc != EOK) {
97 return rc;
98 }
99
100 rc = block_write_direct(dev_handle, 0, 1, &(mbr->raw_data));
101 block_fini(dev_handle);
102 if (rc != EOK) {
103 return rc;
104 }
105
106 return 0;
107}
108
109/** Decide whether this is an actual MBR or a Protective MBR from GPT
110 *
111 * @param mbr the actual MBR to decide upon
112 *
113 * @return 1 if MBR, 0 if GPT
114 */
115int mbr_is_mbr(mbr_t * mbr)
116{
117 return (mbr->raw_data.pte[0].ptype != PT_GPT) ? 1 : 0;
118}
119
120/** Parse partitions from MBR
121 * @param mbr MBR to be parsed
122 *
123 * @return linked list of partitions or NULL on error
124 */
125mbr_partitions_t * mbr_read_partitions(mbr_t * mbr)
126{
127 int rc, i;
128 mbr_part_t * p;
129 mbr_part_t * ext = NULL;
130 mbr_partitions_t * parts;
131
132 if (mbr == NULL)
133 return NULL;
134
135 parts = mbr_alloc_partitions();
136 if (parts == NULL) {
137 return NULL;
138 }
139
140 // Generate the primary partitions
141 for (i = 0; i < N_PRIMARY; ++i) {
142 if (mbr->raw_data.pte[i].ptype == PT_UNUSED)
143 continue;
144
145 p = malloc(sizeof(mbr_part_t));
146 if (p == NULL) {
147 printf(LIBMBR_NAME ": Error on memory allocation.\n");
148 free(p);
149 mbr_free_partitions(parts);
150 return NULL;
151 }
152 list_append(&(p->link), &(parts->list));
153 p->ebr = NULL;
154 if (decode_part(&(mbr->raw_data.pte[i]), p, 0))
155 ext = p;
156 }
157
158 // Fill in the primary partitions and generate logical ones, if any
159 rc = decode_logical(mbr, parts, ext);
160 if (rc != EOK) {
161 printf(LIBMBR_NAME ": Error occured during decoding the MBR.\n" \
162 LIBMBR_NAME ": Partition list may be incomplete.\n");
163 }
164
165 return parts;
166}
167
168/** Write MBR and partitions to device
169 * @param parts partition list to be written
170 * @param mbr MBR to be written with 'parts' partitions
171 * @param dev_handle device to write the data to
172 *
173 * @return returns EOK on succes, specific error code otherwise
174 */
175int mbr_write_partitions(mbr_partitions_t * parts, mbr_t * mbr, service_id_t dev_handle)
176{
177 bool logical = false;
178 int i = 0;
179 int rc;
180 mbr_part_t * p;
181 mbr_part_t * ext = (parts->l_extended == NULL) ? NULL
182 : list_get_instance(parts->l_extended, mbr_part_t, link);
183
184 br_block_t * last_ebr = NULL;
185
186
187 rc = block_init(EXCHANGE_ATOMIC, dev_handle, 512);
188 if (rc != EOK) {
189 return rc;
190 }
191
192 if (ext == NULL)
193 goto no_extended;
194
195 aoff64_t addr = ext->start_addr;
196 mbr_part_t * prev_part = NULL;
197
198 list_foreach(parts->list, it) {
199 p = list_get_instance(it, mbr_part_t, link);
200 if (mbr_get_flag(p, ST_LOGIC)) {
201 // writing logical partition
202
203 if (p->ebr == NULL) {
204 p->ebr = alloc_br();
205 if (p->ebr == NULL)
206 {
207 rc = ENOMEM;
208 goto end;
209 }
210 }
211
212
213 encode_part(p, &(p->ebr->pte[0]), addr);
214 if (prev_part != NULL) {
215 encode_part(p, &(prev_part->ebr->pte[1]), ext->start_addr);
216 rc = block_write_direct(dev_handle, p->start_addr, 1, prev_part->ebr);
217 if (rc != EOK)
218 goto end;
219 }
220
221 addr = p->start_addr;
222 prev_part = p;
223 } else {
224 // writing primary partition
225 if (i >= 4) {
226 rc = EINVAL;
227 goto end;
228 }
229
230 encode_part(p, &(mbr->raw_data.pte[i]), 0);
231
232 ++i;
233 }
234 }
235
236 /* If there was an extended but no logical, we should overwrite
237 * the space where the first logical's EBR would have been. There
238 * might be some garbage from the past.
239 */
240
241 last_ebr = prev_part->ebr;
242
243 if (!logical)
244 {
245 last_ebr = alloc_br();
246 if (last_ebr == NULL) {
247 rc = ENOMEM;
248 goto end;
249 }
250
251 last_ebr->pte[0].ptype = PT_UNUSED;
252 }
253
254
255 encode_part(NULL, &(last_ebr->pte[1]), 0);
256 rc = block_write_direct(dev_handle, addr, 1, last_ebr);
257
258 if (!logical)
259 {
260 free(last_ebr);
261 }
262
263 if (rc != EOK)
264 goto end;
265
266 goto skip;
267
268no_extended:
269
270 list_foreach(parts->list, it) {
271 p = list_get_instance(it, mbr_part_t, link);
272 if (mbr_get_flag(p, ST_LOGIC)) {
273 // extended does not exist, fail
274 return EINVAL;
275 } else {
276 // writing primary partition
277 if (i >= 4)
278 return EINVAL;
279
280 encode_part(p, &(mbr->raw_data.pte[i]), 0);
281
282 ++i;
283 }
284 }
285
286skip:
287 rc = block_write_direct(dev_handle, 0, 1, &(mbr->raw_data));
288 if (rc != EOK)
289 goto end;
290
291 /*
292 for (i = 0; i < N_PRIMARY; ++i) {
293 encode_part(&(p->partition), &(mbr->raw_data.pte[i]), 0);
294 if (p->type == PT_EXTENDED)
295 ext = p;
296
297 //p = list_get_instance(p->link.next, mbr_partitions_t, link);
298 p = p->next;
299 }
300
301 rc = block_write_direct(dev_handle, 0, 1, &(mbr->raw_data));
302 if (rc != EOK) {
303 block_fini(dev_handle);
304 return rc;
305 }
306
307 //writing logical partitions
308
309 if (p == NULL && ext != NULL) {
310 //we need an empty EBR to rewrite the old EBR on disk, if we need to delete it
311 br_block_t * temp_ebr = alloc_br();
312 if (temp_ebr == NULL) {
313 block_fini(dev_handle);
314 return ENOMEM;
315 }
316
317 temp_ebr->pte[0].ptype = PT_UNUSED;
318 encode_part(NULL, &(temp_ebr->pte[1]), 0);
319 rc = block_write_direct(dev_handle, ext->start_addr, 1, temp_ebr);
320 free(temp_ebr);
321 block_fini(dev_handle);
322 return rc;
323 }
324
325 if (p != NULL && ext == NULL) {
326 block_fini(dev_handle);
327 //no extended but one or more logical? EINVAL to the rescue!
328 return EINVAL;
329 }
330
331 aoff64_t addr = ext->start_addr;
332
333 while (p != NULL) {
334 if (p->type == PT_UNUSED) {
335 p = p->next;
336 continue;
337 }
338 //encode_part(p, &(p->ebr->pte[0]), p->start_addr - 63 * 512);
339 encode_part(p, &(p->ebr->pte[0]), addr);
340 encode_part(p->next, &(p->ebr->pte[1]), ext->start_addr);
341
342 rc = block_write_direct(dev_handle, p->start_addr, 1, p->ebr);
343 if (rc != EOK) {
344 block_fini(dev_handle);
345 return rc;
346 }
347 addr = p->start_addr;
348 p = p->next;
349 }*/
350
351 rc = EOK;
352
353end:
354 block_fini(dev_handle);
355
356 return rc;
357}
358
359/** mbr_part_t constructor */
360mbr_part_t * mbr_alloc_partition(void)
361{
362 mbr_part_t * p = malloc(sizeof(mbr_part_t));
363 if (p == NULL) {
364 return NULL;
365 }
366 link_initialize(&(p->link));
367 p->ebr = NULL;
368 p->type = 0;
369 p->status = 0;
370 p->start_addr = 0;
371 p->length = 0;
372
373 return p;
374}
375
376mbr_partitions_t * mbr_alloc_partitions(void)
377{
378 mbr_partitions_t * parts = malloc(sizeof(mbr_partitions_t));
379 if (parts == NULL) {
380 return NULL;
381 }
382
383 list_initialize(&(parts->list));
384
385 return parts;
386}
387
388/** Add partition */
389int mbr_add_partition(mbr_partitions_t * parts, mbr_part_t * partition)
390{
391 list_append(&(partition->link), &(parts->list));
392 return EOK;
393}
394
395/** Remove partition */
396int mbr_remove_partition(mbr_partitions_t * parts, size_t idx)
397{
398 link_t * l = list_nth(&(parts->list), idx);
399 list_remove(l);
400 mbr_part_t * p = list_get_instance(l, mbr_part_t, link);
401 mbr_free_partition(p);
402
403 return EOK;
404}
405
406/** mbr_part_t destructor */
407void mbr_free_partition(mbr_part_t * p)
408{
409 if (p->ebr != NULL)
410 free(p->ebr);
411 free(p);
412}
413
414/** Get flag bool value */
415int mbr_get_flag(mbr_part_t * p, MBR_FLAGS flag)
416{
417 return (p->status & (1 << flag)) ? 1 : 0;
418}
419
420/** Set a specifig status flag to a value */
421void mbr_set_flag(mbr_part_t * p, MBR_FLAGS flag, bool value)
422{
423 uint8_t status = p->status;
424
425 if (value)
426 status = status | (1 << flag);
427 else
428 status = status ^ (status & (1 << flag));
429
430 p->status = status;
431}
432
433/** Just a wrapper for free() */
434void mbr_free_mbr(mbr_t * mbr)
435{
436 free(mbr);
437}
438
439/** Free partition list
440 *
441 * @param parts partition list to be freed
442 */
443void mbr_free_partitions(mbr_partitions_t * parts)
444{
445 list_foreach_safe(parts->list, cur_link, next) {
446 mbr_part_t * p = list_get_instance(cur_link, mbr_part_t, link);
447 list_remove(cur_link);
448 mbr_free_partition(p);
449 }
450}
451
452// Internal functions follow //
453
454static br_block_t * alloc_br()
455{
456 br_block_t * br = malloc(sizeof(br_block_t));
457 if (br == NULL)
458 return NULL;
459
460 br->media_id = 0;
461 br->pad0 = 0;
462 br->signature = host2uint16_t_le(BR_SIGNATURE);
463
464 return br;
465}
466
467/** Parse partition entry to mbr_part_t */
468static int decode_part(pt_entry_t * src, mbr_part_t * trgt, uint32_t base)
469{
470 trgt->type = src->ptype;
471
472 /* Checking only 0x80; otherwise writing will fix to 0x00 */
473 //trgt->bootable = (src->status == B_ACTIVE) ? true : false;
474 mbr_set_flag(trgt, ST_BOOT, (src->status == B_ACTIVE) ? true : false);
475
476 trgt->start_addr = uint32_t_le2host(src->first_lba) + base;
477 trgt->length = uint32_t_le2host(src->length);
478
479 return (src->ptype == PT_EXTENDED) ? 1 : 0;
480}
481
482/** Parse MBR contents to mbr_part_t list
483 * parameter 'p' is allocated for only used primary partitions
484 */
485static int decode_logical(mbr_t * mbr, mbr_partitions_t * parts, mbr_part_t * ext)
486{
487 int rc;
488 mbr_part_t * p;
489
490 if (mbr == NULL || parts == NULL)
491 return EINVAL;
492
493
494 if (ext == NULL)
495 return EOK;
496
497
498 uint32_t addr = ext->start_addr;
499 //uint32_t base = ext->start_addr;
500 br_block_t * ebr;
501
502 rc = block_init(EXCHANGE_ATOMIC, mbr->device, 512);
503 if (rc != EOK)
504 return rc;
505
506 do {
507 ebr = alloc_br();
508 if (ebr == NULL) {
509 return ENOMEM;
510 }
511
512 rc = block_read_direct(mbr->device, addr, 1, ebr);
513 if (rc != EOK) {
514 return rc;
515 }
516
517 //FIXME: is this the right way?
518 if (uint16_t_le2host(ebr->signature) != BR_SIGNATURE) {
519 return EINVAL;
520 }
521
522 p = mbr_alloc_partition();
523
524 decode_part(&(ebr->pte[0]), p, addr);
525 mbr_set_flag(p, ST_LOGIC, true);
526 p->ebr = ebr;
527 mbr_add_partition(parts, p);
528
529 //TODO: Check this code
530 addr = ebr->pte[1].first_lba + ext->start_addr;
531 } while (ebr->pte[1].ptype != PT_UNUSED);
532
533
534 block_fini(mbr->device);
535
536 return EOK;
537}
538
539/** Convert mbr_part_t to pt_entry_t */
540static void encode_part(mbr_part_t * src, pt_entry_t * trgt, uint32_t base)
541{
542 if (src != NULL) {
543 trgt->status = mbr_get_flag(src, ST_BOOT) ? B_ACTIVE : B_INACTIVE;
544 trgt->ptype = src->type;
545 trgt->first_lba = host2uint32_t_le(src->start_addr - base + 63); //63 sectors skipped
546 trgt->length = host2uint32_t_le(src->length - 64); //63 + 1 (EBR)
547 } else {
548 trgt->status = 0;
549 trgt->first_chs[0] = 0;
550 trgt->first_chs[1] = 0;
551 trgt->first_chs[2] = 0;
552 trgt->ptype = 0;
553 trgt->last_chs[0] = 0;
554 trgt->last_chs[1] = 0;
555 trgt->last_chs[2] = 0;
556 trgt->first_lba = 0;
557 trgt->length = 0;
558 }
559}
560
Note: See TracBrowser for help on using the repository browser.