source: mainline/uspace/lib/gpt/libgpt.c@ d617050

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

mbr && gpt fix

  • Property mode set to 100644
File size: 14.9 KB
RevLine 
[cbd64057]1/*
[5beb1ff]2 * Copyright (c) 2011, 2012, 2013 Dominik Taborsky
[cbd64057]3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libgpt
30 * @{
31 */
32/** @file
33 */
34
35/* TODO:
36 * This implementation only supports fixed size partition entries. Specification
37 * requires otherwise, though. Use void * array and casting to achieve that.
38 */
39
[7570e800]40#include <ipc/bd.h>
41#include <async.h>
42#include <stdio.h>
43#include <block.h>
[cbd64057]44#include <errno.h>
[7570e800]45#include <stdlib.h>
46#include <assert.h>
47#include <byteorder.h>
48#include <checksum.h>
[cbd64057]49#include <mem.h>
50
51#include "libgpt.h"
52
[7570e800]53static int load_and_check_header(service_id_t handle, aoff64_t addr, size_t b_size, gpt_header_t * header);
[271e24a]54static gpt_partitions_t * alloc_part_array(uint32_t num);
55static int extend_part_array(gpt_partitions_t * p);
56static int reduce_part_array(gpt_partitions_t * p);
[7570e800]57static long long nearest_larger_int(double a);
[d617050]58static int gpt_memcmp(const void * a, const void * b, size_t len);
[7570e800]59
[cbd64057]60/** Read GPT from specific device
61 * @param dev_handle device to read GPT from
[d617050]62 *
[cbd64057]63 * @return GPT record on success, NULL on error
64 */
65gpt_t * gpt_read_gpt_header(service_id_t dev_handle)
66{
67 int rc;
68 size_t b_size;
[d617050]69
[cbd64057]70 rc = block_get_bsize(dev_handle, &b_size);
71 if (rc != EOK) {
72 errno = rc;
73 return NULL;
74 }
[d617050]75
[cbd64057]76 gpt_t * gpt = malloc(sizeof(gpt_t));
77 if (gpt == NULL) {
78 errno = ENOMEM;
79 return NULL;
80 }
[d617050]81
[7570e800]82 gpt->raw_data = malloc(b_size);// We might need only sizeof(gpt_header_t),
[d617050]83 if (gpt == NULL) { // but we should follow specs and have
[cbd64057]84 free(gpt); // zeroes through all the rest of the block
85 errno = ENOMEM;
86 return NULL;
87 }
[d617050]88
89
[7570e800]90 rc = load_and_check_header(dev_handle, GPT_HDR_BA, b_size, gpt->raw_data);
[cbd64057]91 if (rc == EBADCHECKSUM || rc == EINVAL) {
92 aoff64_t n_blocks;
93 rc = block_get_nblocks(dev_handle, &n_blocks);
94 if (rc != EOK) {
95 errno = rc;
96 goto fail;
97 }
[d617050]98
[7570e800]99 rc = load_and_check_header(dev_handle, n_blocks - 1, b_size, gpt->raw_data);
[cbd64057]100 if (rc == EBADCHECKSUM || rc == EINVAL) {
101 errno = rc;
102 goto fail;
103 }
104 }
[d617050]105
[7570e800]106 gpt->device = dev_handle;
[d617050]107
[cbd64057]108 return gpt;
109
110fail:
111 gpt_free_gpt(gpt);
112 return NULL;
113}
114
115/** Write GPT header to device
116 * @param header GPT header to be written
117 * @param dev_handle device handle to write the data to
[d617050]118 *
[cbd64057]119 * @return 0 on success, libblock error code otherwise
[d617050]120 *
[30440ed]121 * Note: Firstly write partitions (if changed), then gpt header.
[cbd64057]122 */
[7570e800]123int gpt_write_gpt_header(gpt_t * gpt, service_id_t dev_handle)
[cbd64057]124{
125 int rc;
126 size_t b_size;
[d617050]127
[7570e800]128 gpt->raw_data->header_crc32 = 0;
129 gpt->raw_data->header_crc32 = compute_crc32((uint8_t *) gpt->raw_data,
130 uint32_t_le2host(gpt->raw_data->header_size));
[d617050]131
[cbd64057]132 rc = block_get_bsize(dev_handle, &b_size);
133 if (rc != EOK)
134 return rc;
[d617050]135
[cbd64057]136 rc = block_init(EXCHANGE_ATOMIC, dev_handle, b_size);
137 if (rc != EOK)
138 return rc;
[d617050]139
[cbd64057]140 /* Write to main GPT header location */
[7570e800]141 rc = block_write_direct(dev_handle, GPT_HDR_BA, GPT_HDR_BS, gpt->raw_data);
[cbd64057]142 if (rc != EOK)
143 block_fini(dev_handle);
144 return rc;
[d617050]145
[cbd64057]146 aoff64_t n_blocks;
147 rc = block_get_nblocks(dev_handle, &n_blocks);
148 if (rc != EOK)
149 return rc;
[d617050]150
[cbd64057]151 /* Write to backup GPT header location */
[7570e800]152 //FIXME: those idiots thought it would be cool to have these fields in reverse order...
153 rc = block_write_direct(dev_handle, n_blocks - 1, GPT_HDR_BS, gpt->raw_data);
[cbd64057]154 block_fini(dev_handle);
155 if (rc != EOK)
156 return rc;
[d617050]157
[cbd64057]158 return 0;
159}
160
161/** Parse partitions from GPT
162 * @param gpt GPT to be parsed
[d617050]163 *
[cbd64057]164 * @return partition linked list pointer or NULL on error
165 * error code is stored in errno
166 */
[271e24a]167gpt_partitions_t * gpt_read_partitions(gpt_t * gpt)
[cbd64057]168{
[7570e800]169 int rc;
170 unsigned int i;
[271e24a]171 gpt_partitions_t * res;
[30440ed]172 uint32_t fill = uint32_t_le2host(gpt->raw_data->fillries);
[7570e800]173 uint32_t ent_size = uint32_t_le2host(gpt->raw_data->entry_size);
174 uint64_t ent_lba = uint64_t_le2host(gpt->raw_data->entry_lba);
[d617050]175
[30440ed]176 res = alloc_part_array(fill);
[cbd64057]177 if (res == NULL) {
178 //errno = ENOMEM; // already set in alloc_part_array()
179 return NULL;
180 }
[d617050]181
[cbd64057]182 /* We can limit comm_size like this:
183 * - we don't need more bytes
184 * - the size of GPT partition entry can be different to 128 bytes */
[7570e800]185 rc = block_init(EXCHANGE_SERIALIZE, gpt->device, sizeof(gpt_entry_t));
[cbd64057]186 if (rc != EOK) {
187 gpt_free_partitions(res);
188 errno = rc;
189 return NULL;
190 }
[d617050]191
[cbd64057]192 size_t block_size;
193 rc = block_get_bsize(gpt->device, &block_size);
194 if (rc != EOK) {
195 gpt_free_partitions(res);
196 errno = rc;
197 return NULL;
198 }
[d617050]199
[cbd64057]200 //size_t bufpos = 0;
201 //size_t buflen = 0;
202 aoff64_t pos = ent_lba * block_size;
[d617050]203
[cbd64057]204 /* Now we read just sizeof(gpt_entry_t) bytes for each entry from the device.
205 * Hopefully, this does not bypass cache (no mention in libblock.c),
206 * and also allows us to have variable partition entry size (but we
207 * will always read just sizeof(gpt_entry_t) bytes - hopefully they
208 * don't break backward compatibility) */
[30440ed]209 for (i = 0; i < fill; ++i) {
[cbd64057]210 //FIXME: this does bypass cache...
211 rc = block_read_bytes_direct(gpt->device, pos, sizeof(gpt_entry_t), res->part_array + i);
212 //FIXME: but seqread() is just too complex...
213 //rc = block_seqread(gpt->device, &bufpos, &buflen, &pos, res->part_array[i], sizeof(gpt_entry_t));
214 pos += ent_size;
[d617050]215
[cbd64057]216 if (rc != EOK) {
217 gpt_free_partitions(res);
218 errno = rc;
219 return NULL;
220 }
221 }
[d617050]222
[7570e800]223 /* FIXME: so far my boasting about variable partition entry size
224 * will not work. The CRC32 checksums will be different.
225 * This can't be fixed easily - we'd have to run the checksum
226 * on all of the partition entry array.
227 */
[30440ed]228 uint32_t crc = compute_crc32((uint8_t *) res->part_array, res->fill * sizeof(gpt_entry_t));
[d617050]229
[7570e800]230 if(uint32_t_le2host(gpt->raw_data->pe_array_crc32) != crc)
[cbd64057]231 {
232 gpt_free_partitions(res);
233 errno = EBADCHECKSUM;
234 return NULL;
235 }
[d617050]236
[cbd64057]237 return res;
238}
239
240/** Write GPT and partitions to device
241 * @param parts partition list to be written
[7570e800]242 * @param header GPT header belonging to the 'parts' partitions
[cbd64057]243 * @param dev_handle device to write the data to
[d617050]244 *
[cbd64057]245 * @return returns EOK on succes, specific error code otherwise
246 */
[271e24a]247int gpt_write_partitions(gpt_partitions_t * parts, gpt_t * gpt, service_id_t dev_handle)
[cbd64057]248{
[7570e800]249 int rc;
[cbd64057]250 size_t b_size;
[d617050]251
[30440ed]252 gpt->raw_data->pe_array_crc32 = compute_crc32((uint8_t *) parts->part_array, parts->fill * gpt->raw_data->entry_size);
[d617050]253
[cbd64057]254 rc = block_get_bsize(dev_handle, &b_size);
255 if (rc != EOK)
256 return rc;
[d617050]257
[cbd64057]258 rc = block_init(EXCHANGE_ATOMIC, dev_handle, b_size);
259 if (rc != EOK)
260 return rc;
[d617050]261
[cbd64057]262 /* Write to main GPT partition array location */
[d617050]263 rc = block_write_direct(dev_handle, uint64_t_le2host(gpt->raw_data->entry_lba),
264 nearest_larger_int((uint64_t_le2host(gpt->raw_data->entry_size) * parts->fill) / b_size),
[cbd64057]265 parts->part_array);
266 if (rc != EOK)
267 block_fini(dev_handle);
268 return rc;
[d617050]269
[cbd64057]270 aoff64_t n_blocks;
271 rc = block_get_nblocks(dev_handle, &n_blocks);
272 if (rc != EOK)
273 return rc;
[d617050]274
[cbd64057]275 /* Write to backup GPT partition array location */
276 //rc = block_write_direct(dev_handle, n_blocks - 1, GPT_HDR_BS, header->raw_data);
277 block_fini(dev_handle);
278 if (rc != EOK)
279 return rc;
[d617050]280
281
[30440ed]282 return gpt_write_gpt_header(gpt, dev_handle);
[cbd64057]283}
284
[30440ed]285/** Alloc new partition
[d617050]286 *
[30440ed]287 * @param parts partition table to carry new partition
[d617050]288 *
[30440ed]289 * @return returns pointer to the new partition or NULL on ENOMEM
[d617050]290 *
[30440ed]291 * Note: use either gpt_alloc_partition or gpt_add_partition. The first
[d617050]292 * returns a pointer to write your data to, the second copies the data
[30440ed]293 * (and does not free the memory).
294 */
295gpt_part_t * gpt_alloc_partition(gpt_partitions_t * parts)
[7570e800]296{
[30440ed]297 if (parts->fill == parts->arr_size) {
298 if (extend_part_array(parts) == -1)
299 return NULL;
300 }
[d617050]301
[30440ed]302 return parts->part_array + parts->fill++;
303}
304
305/** Copy partition into partition array
[d617050]306 *
[30440ed]307 * @param parts target partition array
308 * @param partition source partition to copy
[d617050]309 *
[30440ed]310 * @return -1 on error, 0 otherwise
[d617050]311 *
[30440ed]312 * Note: use either gpt_alloc_partition or gpt_add_partition. The first
[d617050]313 * returns a pointer to write your data to, the second copies the data
[30440ed]314 * (and does not free the memory).
315 */
316int gpt_add_partition(gpt_partitions_t * parts, gpt_part_t * partition)
317{
318 if (parts->fill == parts->arr_size) {
319 if (extend_part_array(parts) == -1)
[d617050]320 return ENOMEM;
[30440ed]321 }
[271e24a]322 extend_part_array(parts);
[d617050]323 return EOK;;
[7570e800]324}
325
[30440ed]326/** Remove partition from array
[d617050]327 *
[30440ed]328 * @param idx index of the partition to remove
[d617050]329 *
[30440ed]330 * @return -1 on error, 0 otherwise
[d617050]331 *
[30440ed]332 * Note: even if it fails, the partition still gets removed. Only
333 * reducing the array failed.
334 */
335int gpt_remove_partition(gpt_partitions_t * parts, size_t idx)
[7570e800]336{
[30440ed]337 if (idx != parts->fill - 1) {
[d617050]338 memcpy(parts->part_array + idx, parts->part_array + parts->fill - 1, sizeof(gpt_entry_t));
[30440ed]339 parts->fill -= 1;
340 }
[d617050]341
[30440ed]342 if (parts->fill < (parts->arr_size / 2) - GPT_IGNORE_FILL_NUM) {
343 if (reduce_part_array(parts) == -1)
344 return -1;
345 }
[d617050]346
[30440ed]347 return 0;
[7570e800]348}
349
[cbd64057]350/** free() GPT header including gpt->header_lba */
351void gpt_free_gpt(gpt_t * gpt)
352{
[7570e800]353 free(gpt->raw_data);
[cbd64057]354 free(gpt);
355}
356
357/** Free partition list
[d617050]358 *
[cbd64057]359 * @param parts partition list to be freed
360 */
[271e24a]361void gpt_free_partitions(gpt_partitions_t * parts)
[cbd64057]362{
363 free(parts->part_array);
364 free(parts);
365}
366
[30440ed]367/** Get partition type by linear search
368 * (hopefully this doesn't get slow)
369 */
370size_t gpt_get_part_type(gpt_part_t * p)
371{
372 size_t i;
373 for (i = 0; gpt_ptypes[i].guid != NULL; i++) {
[d617050]374 if (gpt_memcmp(p->part_type, gpt_ptypes[i].guid, 16) == 0) {
[30440ed]375 break;
376 }
377 }
378 return i;
379}
380
[cbd64057]381/** Set partition type
382 * @param p partition to be set
383 * @param type partition type to set
[d617050]384 * - see our fine selection at gpt_ptypes to choose from
[cbd64057]385 */
[d617050]386void gpt_set_part_type(gpt_part_t * p, size_t type)
[cbd64057]387{
388 /* Beware: first 3 blocks are byteswapped! */
[d617050]389 p->part_type[3] = gpt_ptypes[type].guid[0];
390 p->part_type[2] = gpt_ptypes[type].guid[1];
391 p->part_type[1] = gpt_ptypes[type].guid[2];
392 p->part_type[0] = gpt_ptypes[type].guid[3];
393
394 p->part_type[5] = gpt_ptypes[type].guid[4];
395 p->part_type[4] = gpt_ptypes[type].guid[5];
396
397 p->part_type[7] = gpt_ptypes[type].guid[6];
398 p->part_type[6] = gpt_ptypes[type].guid[7];
399
400 p->part_type[8] = gpt_ptypes[type].guid[8];
401 p->part_type[9] = gpt_ptypes[type].guid[9];
402 p->part_type[10] = gpt_ptypes[type].guid[10];
403 p->part_type[11] = gpt_ptypes[type].guid[11];
404 p->part_type[12] = gpt_ptypes[type].guid[12];
405 p->part_type[13] = gpt_ptypes[type].guid[13];
406 p->part_type[14] = gpt_ptypes[type].guid[14];
407 p->part_type[15] = gpt_ptypes[type].guid[15];
408}
409
410/** Get partition starting LBA */
411uint64_t gpt_get_start_lba(gpt_part_t * p)
412{
413 return uint64_t_le2host(p->start_lba);
414}
415
416/** Set partition starting LBA */
417void gpt_set_start_lba(gpt_part_t * p, uint64_t start)
418{
419 p->start_lba = host2uint64_t_le(start);
420}
421
422/** Get partition ending LBA */
423uint64_t gpt_get_end_lba(gpt_part_t * p)
424{
425 return uint64_t_le2host(p->end_lba);
[cbd64057]426}
427
[d617050]428/** Set partition ending LBA */
429void gpt_set_end_lba(gpt_part_t * p, uint64_t end)
[30440ed]430{
[d617050]431 p->end_lba = host2uint64_t_le(end);
432}
433
434
435unsigned char * gpt_get_part_name(gpt_part_t * p)
436{
437 return p->part_name;
[30440ed]438}
439
[cbd64057]440/** Copy partition name */
[d617050]441void gpt_set_part_name(gpt_part_t * p, char * name[], size_t length)
[cbd64057]442{
[30440ed]443 if (length >= 72)
444 length = 71;
[d617050]445
[cbd64057]446 memcpy(p->part_name, name, length);
[30440ed]447 p->part_name[length] = '\0';
448}
449
450/** Get partition attribute */
[d617050]451bool gpt_get_flag(gpt_part_t * p, GPT_ATTR flag)
[30440ed]452{
[d617050]453 return (p->attributes & (((uint64_t) 1) << flag)) ? 1 : 0;
[30440ed]454}
455
456/** Set partition attribute */
[d617050]457void gpt_set_flag(gpt_part_t * p, GPT_ATTR flag, bool value)
[30440ed]458{
[d617050]459 uint64_t attr = p->attributes;
[30440ed]460
461 if (value)
462 attr = attr | (((uint64_t) 1) << flag);
463 else
464 attr = attr ^ (attr & (((uint64_t) 1) << flag));
465
[d617050]466 p->attributes = attr;
[cbd64057]467}
468
469// Internal functions follow //
470
[7570e800]471static int load_and_check_header(service_id_t dev_handle, aoff64_t addr, size_t b_size, gpt_header_t * header)
[cbd64057]472{
[7570e800]473 int rc;
[d617050]474
[7570e800]475 rc = block_init(EXCHANGE_ATOMIC, dev_handle, b_size);
[cbd64057]476 if (rc != EOK)
477 return rc;
[d617050]478
[7570e800]479 rc = block_read_direct(dev_handle, addr, GPT_HDR_BS, header);
480 block_fini(dev_handle);
[cbd64057]481 if (rc != EOK)
482 return rc;
[d617050]483
484
[7570e800]485 unsigned int i;
[cbd64057]486 /* Check the EFI signature */
487 for (i = 0; i < 8; ++i) {
[7570e800]488 if (header->efi_signature[i] != efi_signature[i])
[cbd64057]489 return EINVAL;
490 }
[d617050]491
[cbd64057]492 /* Check the CRC32 of the header */
[7570e800]493 uint32_t crc = header->header_crc32;
494 header->header_crc32 = 0;
495 if (crc != compute_crc32((uint8_t *) header, header->header_size))
[cbd64057]496 return EBADCHECKSUM;
497 else
[7570e800]498 header->header_crc32 = crc;
[d617050]499
[cbd64057]500 /* Check for zeroes in the rest of the block */
501 for (i = sizeof(gpt_header_t); i < b_size; ++i) {
[7570e800]502 if (((uint8_t *) header)[i] != 0)
503 return EINVAL;
[cbd64057]504 }
[d617050]505
[cbd64057]506 return EOK;
507}
508
[271e24a]509static gpt_partitions_t * alloc_part_array(uint32_t num)
[cbd64057]510{
[271e24a]511 gpt_partitions_t * res = malloc(sizeof(gpt_partitions_t));
[cbd64057]512 if (res == NULL) {
513 errno = ENOMEM;
514 return NULL;
515 }
[d617050]516
[cbd64057]517 uint32_t size = num > GPT_BASE_PART_NUM ? num : GPT_BASE_PART_NUM;
518 res->part_array = malloc(size * sizeof(gpt_entry_t));
[7570e800]519 if (res->part_array == NULL) {
[cbd64057]520 free(res);
521 errno = ENOMEM;
522 return NULL;
523 }
[d617050]524
[30440ed]525 res->fill = num;
[cbd64057]526 res->arr_size = size;
[d617050]527
[cbd64057]528 return res;
529}
530
[271e24a]531static int extend_part_array(gpt_partitions_t * p)
[cbd64057]532{
533 unsigned int nsize = p->arr_size * 2;
534 gpt_entry_t * tmp = malloc(nsize * sizeof(gpt_entry_t));
535 if(tmp == NULL) {
536 errno = ENOMEM;
537 return -1;
538 }
[d617050]539
[30440ed]540 memcpy(tmp, p->part_array, p->fill);
[cbd64057]541 free(p->part_array);
542 p->part_array = tmp;
543 p->arr_size = nsize;
[d617050]544
[cbd64057]545 return 0;
546}
547
[271e24a]548static int reduce_part_array(gpt_partitions_t * p)
[cbd64057]549{
550 if(p->arr_size > GPT_MIN_PART_NUM) {
551 unsigned int nsize = p->arr_size / 2;
552 gpt_entry_t * tmp = malloc(nsize * sizeof(gpt_entry_t));
553 if(tmp == NULL) {
554 errno = ENOMEM;
555 return -1;
556 }
[d617050]557
[30440ed]558 memcpy(tmp, p->part_array, p->fill < nsize ? p->fill : nsize);
[cbd64057]559 free(p->part_array);
560 p->part_array = tmp;
561 p->arr_size = nsize;
562 }
[d617050]563
[cbd64057]564 return 0;
565}
566
567//FIXME: replace this with a library call, if it exists
568static long long nearest_larger_int(double a)
569{
570 if ((long long) a == a) {
571 return (long long) a;
572 }
[d617050]573
[cbd64057]574 return ((long long) a) + 1;
575}
576
[d617050]577static int gpt_memcmp(const void * a, const void * b, size_t len)
578{
579 size_t i;
580 int diff;
581 const unsigned char * x = a;
582 const unsigned char * y = b;
583
584 for (i = 0; i < len; i++) {
585 diff = (int)*(x++) - (int)*(y++);
586 if (diff != 0) {
587 return diff;
588 }
589 }
590 return 0;
591}
592
[cbd64057]593
594
595
Note: See TracBrowser for help on using the repository browser.