source: mainline/uspace/lib/gpt/libgpt.c@ 8f6c7785

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

logical write functional

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