source: mainline/uspace/lib/gpt/libgpt.c@ 700f89e

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

libmbr semifinal, hdisk enhancements

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