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

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

Removing debug printfs

  • Property mode set to 100644
File size: 23.1 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>
[0435fe41]50#include <sys/typefmt.h>
[8c95dff]51#include <mbr.h>
[0435fe41]52
[cbd64057]53
54#include "libgpt.h"
55
[dc76f4a]56static int load_and_check_header(service_id_t, aoff64_t, size_t, gpt_header_t *);
57static gpt_partitions_t * alloc_part_array(uint32_t);
[1c8bfe8]58static int extend_part_array(gpt_partitions_t *);
59static int reduce_part_array(gpt_partitions_t *);
60static uint8_t get_byte(const char *);
[dc76f4a]61static bool check_overlap(gpt_part_t *, gpt_part_t *);
[2b55edb]62static bool check_encaps(gpt_part_t *, uint64_t, uint64_t);
[44c4886]63
64/** Allocate memory for gpt label */
65gpt_label_t * gpt_alloc_label(void)
66{
67 gpt_label_t *label = malloc(sizeof(gpt_label_t));
68 if (label == NULL)
69 return NULL;
70
[dc76f4a]71 /* This is necessary so that gpt_part_foreach does not segfault */
72 label->parts = gpt_alloc_partitions();
73 if (label == NULL) {
74 free(label);
75 return NULL;
76 }
77
[44c4886]78 label->gpt = NULL;
[dc76f4a]79
[44c4886]80 label->device = 0;
81
82 return label;
83}
84
85/** Free gpt_label_t structure */
86void gpt_free_label(gpt_label_t *label)
87{
88 if (label->gpt != NULL)
89 gpt_free_gpt(label->gpt);
90
91 if (label->parts != NULL)
92 gpt_free_partitions(label->parts);
93
94 free(label);
95}
[7570e800]96
[700f89e]97/** Allocate memory for gpt header */
[44c4886]98gpt_t * gpt_alloc_header(size_t size)
[700f89e]99{
[44c4886]100 gpt_t *gpt = malloc(sizeof(gpt_t));
101 if (gpt == NULL)
102 return NULL;
103
[dc76f4a]104 /*
105 * We might need only sizeof(gpt_header_t), but we should follow
106 * specs and have zeroes through all the rest of the block
107 */
[44c4886]108 size_t final_size = size > sizeof(gpt_header_t) ? size : sizeof(gpt_header_t);
109 gpt->header = malloc(final_size);
110 if (gpt->header == NULL) {
111 free(gpt);
112 return NULL;
113 }
114
[8c95dff]115 /* Enter some sane defaults. */
[44c4886]116 memset(gpt->header, 0, final_size);
[8c95dff]117 memcpy(gpt->header->efi_signature, efi_signature, 8);
118 memcpy(gpt->header->revision, revision, 4);
119 gpt->header->header_size = host2uint32_t_le(final_size);
120 gpt->header->entry_lba = host2uint64_t_le((uint64_t) 2);
121 gpt->header->entry_size = host2uint32_t_le(sizeof(gpt_entry_t));
122
[44c4886]123
124 return gpt;
125}
126
127/** free() GPT header including gpt->header_lba */
128void gpt_free_gpt(gpt_t *gpt)
129{
130 free(gpt->header);
131 free(gpt);
[700f89e]132}
133
[cbd64057]134/** Read GPT from specific device
[44c4886]135 * @param label label structure to fill
136 * @param dev_handle device to read GPT from
[d617050]137 *
[44c4886]138 * @return EOK on success, errorcode on error
[cbd64057]139 */
[44c4886]140int gpt_read_header(gpt_label_t *label, service_id_t dev_handle)
[cbd64057]141{
142 int rc;
143 size_t b_size;
[8f6c7785]144
145 rc = block_init(EXCHANGE_ATOMIC, dev_handle, 512);
146 if (rc != EOK)
[8559fa0]147 goto fail;
[8f6c7785]148
[cbd64057]149 rc = block_get_bsize(dev_handle, &b_size);
[44c4886]150 if (rc != EOK)
[8559fa0]151 goto fini_fail;
[8f6c7785]152
[44c4886]153 if (label->gpt == NULL) {
154 label->gpt = gpt_alloc_header(b_size);
[8559fa0]155 if (label->gpt == NULL) {
156 rc = ENOMEM;
157 goto fini_fail;
158 }
[cbd64057]159 }
[8f6c7785]160
[44c4886]161 rc = load_and_check_header(dev_handle, GPT_HDR_BA, b_size, label->gpt->header);
[cbd64057]162 if (rc == EBADCHECKSUM || rc == EINVAL) {
163 aoff64_t n_blocks;
164 rc = block_get_nblocks(dev_handle, &n_blocks);
[44c4886]165 if (rc != EOK)
[8559fa0]166 goto free_fail;
[d617050]167
[44c4886]168 rc = load_and_check_header(dev_handle, n_blocks - 1, b_size, label->gpt->header);
169 if (rc == EBADCHECKSUM || rc == EINVAL)
[8559fa0]170 goto free_fail;
[cbd64057]171 }
[8f6c7785]172
[44c4886]173 label->device = dev_handle;
[8f6c7785]174 block_fini(dev_handle);
[44c4886]175 return EOK;
[8f6c7785]176
[8559fa0]177free_fail:
[44c4886]178 gpt_free_gpt(label->gpt);
179 label->gpt = NULL;
[8559fa0]180fini_fail:
181 block_fini(dev_handle);
182fail:
[44c4886]183 return rc;
[cbd64057]184}
185
186/** Write GPT header to device
[44c4886]187 * @param label GPT label header to be written
188 * @param dev_handle device handle to write the data to
[d617050]189 *
[44c4886]190 * @return EOK on success, libblock error code otherwise
[d617050]191 *
[44c4886]192 * Note: Firstly write partitions (if modified), then gpt header.
[cbd64057]193 */
[44c4886]194int gpt_write_header(gpt_label_t *label, service_id_t dev_handle)
[cbd64057]195{
196 int rc;
197 size_t b_size;
[dc76f4a]198
199 /* The comm_size argument (the last one) is ignored */
200 rc = block_init(EXCHANGE_ATOMIC, dev_handle, 4096);
[1c8bfe8]201 if (rc != EOK && rc != EEXIST)
[cbd64057]202 return rc;
[dc76f4a]203
[8f6c7785]204 rc = block_get_bsize(dev_handle, &b_size);
[cbd64057]205 if (rc != EOK)
206 return rc;
[dc76f4a]207
[cbd64057]208 aoff64_t n_blocks;
209 rc = block_get_nblocks(dev_handle, &n_blocks);
[44c4886]210 if (rc != EOK) {
211 block_fini(dev_handle);
[cbd64057]212 return rc;
[44c4886]213 }
[dc76f4a]214
215 uint64_t tmp;
216
[8c95dff]217 gpt_set_random_uuid(label->gpt->header->disk_guid);
218
[dc76f4a]219 /* Prepare the backup header */
220 label->gpt->header->alternate_lba = label->gpt->header->my_lba;
221 label->gpt->header->my_lba = host2uint64_t_le(n_blocks - 1);
222
223 tmp = label->gpt->header->entry_lba;
224 label->gpt->header->entry_lba = host2uint64_t_le(n_blocks -
225 (uint32_t_le2host(label->gpt->header->fillries) * sizeof(gpt_entry_t))
226 / b_size - 1);
227
228 label->gpt->header->header_crc32 = 0;
229 label->gpt->header->header_crc32 = host2uint32_t_le(
230 compute_crc32((uint8_t *) label->gpt->header,
231 uint32_t_le2host(label->gpt->header->header_size)));
232
[cbd64057]233 /* Write to backup GPT header location */
[44c4886]234 rc = block_write_direct(dev_handle, n_blocks - 1, GPT_HDR_BS, label->gpt->header);
[dc76f4a]235 if (rc != EOK) {
236 block_fini(dev_handle);
237 return rc;
238 }
239
240
241 /* Prepare the main header */
242 label->gpt->header->entry_lba = tmp;
243
244 tmp = label->gpt->header->alternate_lba;
245 label->gpt->header->alternate_lba = label->gpt->header->my_lba;
246 label->gpt->header->my_lba = tmp;
247
248 label->gpt->header->header_crc32 = 0;
249 label->gpt->header->header_crc32 = host2uint32_t_le(
250 compute_crc32((uint8_t *) label->gpt->header,
251 uint32_t_le2host(label->gpt->header->header_size)));
252
253 /* Write to main GPT header location */
254 rc = block_write_direct(dev_handle, GPT_HDR_BA, GPT_HDR_BS, label->gpt->header);
[cbd64057]255 if (rc != EOK)
256 return rc;
[dc76f4a]257
[8c95dff]258 /* Write Protective MBR */
259 br_block_t mbr;
260 memset(&mbr, 0, 512);
261 memset(mbr.pte[0].first_chs, 1, 3);
262 mbr.pte[0].ptype = 0xEE;
263 memset(mbr.pte[0].last_chs, 0xFF, 3);
264 mbr.pte[0].first_lba = host2uint32_t_le(1);
265 mbr.pte[0].length = 0xFFFFFFFF;
266 mbr.signature = host2uint16_t_le(BR_SIGNATURE);
267
268 rc = block_write_direct(dev_handle, 0, 1, &mbr);
269 block_fini(dev_handle);
270 if (rc != EOK)
271 return rc;
[dc76f4a]272
[cbd64057]273 return 0;
274}
275
[700f89e]276/** Alloc partition array */
[44c4886]277gpt_partitions_t * gpt_alloc_partitions()
[700f89e]278{
[dc76f4a]279 return alloc_part_array(GPT_MIN_PART_NUM);
[700f89e]280}
281
[cbd64057]282/** Parse partitions from GPT
[44c4886]283 * @param label GPT label to be parsed
[d617050]284 *
[44c4886]285 * @return EOK on success, errorcode otherwise
[cbd64057]286 */
[44c4886]287int gpt_read_partitions(gpt_label_t *label)
[cbd64057]288{
[7570e800]289 int rc;
290 unsigned int i;
[c3cbbb2]291 uint32_t fillries = uint32_t_le2host(label->gpt->header->fillries);
[44c4886]292 uint32_t ent_size = uint32_t_le2host(label->gpt->header->entry_size);
293 uint64_t ent_lba = uint64_t_le2host(label->gpt->header->entry_lba);
294
295 if (label->parts == NULL) {
[c3cbbb2]296 label->parts = alloc_part_array(fillries);
[44c4886]297 if (label->parts == NULL) {
298 return ENOMEM;
299 }
[cbd64057]300 }
[d617050]301
[8559fa0]302 /* comm_size is ignored */
[44c4886]303 rc = block_init(EXCHANGE_SERIALIZE, label->device, sizeof(gpt_entry_t));
304 if (rc != EOK)
305 goto fail;
[d617050]306
[cbd64057]307 size_t block_size;
[44c4886]308 rc = block_get_bsize(label->device, &block_size);
309 if (rc != EOK)
[8559fa0]310 goto fini_fail;
[d617050]311
[cbd64057]312 aoff64_t pos = ent_lba * block_size;
[d617050]313
[dc76f4a]314 /*
315 * Now we read just sizeof(gpt_entry_t) bytes for each entry from the device.
[cbd64057]316 * Hopefully, this does not bypass cache (no mention in libblock.c),
317 * and also allows us to have variable partition entry size (but we
318 * will always read just sizeof(gpt_entry_t) bytes - hopefully they
[dc76f4a]319 * don't break backward compatibility)
320 */
[c3cbbb2]321 for (i = 0; i < fillries; ++i) {
[dc76f4a]322 /*FIXME: this does bypass cache... */
[44c4886]323 rc = block_read_bytes_direct(label->device, pos, sizeof(gpt_entry_t), label->parts->part_array + i);
[dc76f4a]324 /*
325 * FIXME: but seqread() is just too complex...
326 * rc = block_seqread(gpt->device, &bufpos, &buflen, &pos, res->part_array[i], sizeof(gpt_entry_t));
327 */
[cbd64057]328 pos += ent_size;
[d617050]329
[44c4886]330 if (rc != EOK)
[8559fa0]331 goto fini_fail;
[cbd64057]332 }
[2b55edb]333
[8559fa0]334 uint32_t crc = compute_crc32((uint8_t *) label->parts->part_array,
[2b55edb]335 fillries * ent_size);
[d617050]336
[0435fe41]337 if (uint32_t_le2host(label->gpt->header->pe_array_crc32) != crc)
[cbd64057]338 {
[44c4886]339 rc = EBADCHECKSUM;
[8559fa0]340 goto fini_fail;
[cbd64057]341 }
[8559fa0]342
343 block_fini(label->device);
[44c4886]344 return EOK;
345
[8559fa0]346fini_fail:
347 block_fini(label->device);
348
[44c4886]349fail:
350 gpt_free_partitions(label->parts);
351 label->parts = NULL;
352 return rc;
[cbd64057]353}
354
355/** Write GPT and partitions to device
[9bdfde73]356 * Note: also writes the header.
[44c4886]357 * @param label label to write
358 * @param dev_handle device to write the data to
[d617050]359 *
[44c4886]360 * @return returns EOK on succes, errorcode otherwise
[cbd64057]361 */
[44c4886]362int gpt_write_partitions(gpt_label_t *label, service_id_t dev_handle)
[cbd64057]363{
[7570e800]364 int rc;
[cbd64057]365 size_t b_size;
[44c4886]366
367 /* comm_size of 4096 is ignored */
368 rc = block_init(EXCHANGE_ATOMIC, dev_handle, 4096);
[1c8bfe8]369 if (rc != EOK && rc != EEXIST)
[cbd64057]370 return rc;
[1c8bfe8]371
[8f6c7785]372 rc = block_get_bsize(dev_handle, &b_size);
[cbd64057]373 if (rc != EOK)
[44c4886]374 goto fail;
[1c8bfe8]375
[cbd64057]376 aoff64_t n_blocks;
377 rc = block_get_nblocks(dev_handle, &n_blocks);
378 if (rc != EOK)
[44c4886]379 goto fail;
[1c8bfe8]380
[8c95dff]381 /* When we're creating a new label from scratch, we need to fill
382 * the header with sensible defaults. */
383 if (label->gpt == NULL) {
384 label->gpt = gpt_alloc_header(b_size);
385 }
386
387 uint32_t e_size = uint32_t_le2host(label->gpt->header->entry_size);
388 size_t fillries = label->parts->fill > GPT_MIN_PART_NUM ? label->parts->fill : GPT_MIN_PART_NUM;
[f4a47e52]389
[8c95dff]390 if (e_size != sizeof(gpt_entry_t))
391 return ENOTSUP;
392
[2b55edb]393 label->gpt->header->fillries = host2uint32_t_le(fillries);
[dc76f4a]394 uint64_t arr_blocks = (fillries * sizeof(gpt_entry_t)) / b_size;
[493b881]395 uint64_t gpt_space = arr_blocks + GPT_HDR_BS + 1; /* +1 for Protective MBR */
396 label->gpt->header->first_usable_lba = host2uint64_t_le(gpt_space);
397 label->gpt->header->last_usable_lba = host2uint64_t_le(n_blocks - gpt_space - 1);
[f4a47e52]398
[2b55edb]399 /* Perform checks */
[0435fe41]400 gpt_part_foreach (label, p) {
[2b55edb]401 if (gpt_get_part_type(p) == GPT_PTE_UNUSED)
402 continue;
403
[493b881]404 if (!check_encaps(p, n_blocks, gpt_space)) {
[2b55edb]405 rc = ERANGE;
406 goto fail;
407 }
408
[0435fe41]409 gpt_part_foreach (label, q) {
[2b55edb]410 if (p == q)
411 continue;
412
413 if (gpt_get_part_type(p) != GPT_PTE_UNUSED) {
414 if (check_overlap(p, q)) {
415 rc = ERANGE;
416 goto fail;
417 }
418 }
419 }
420 }
[f4a47e52]421
[2b55edb]422 label->gpt->header->pe_array_crc32 = host2uint32_t_le(compute_crc32(
423 (uint8_t *) label->parts->part_array,
424 fillries * e_size));
[dc76f4a]425
[f4a47e52]426
[cbd64057]427 /* Write to backup GPT partition array location */
[dc76f4a]428 rc = block_write_direct(dev_handle, n_blocks - arr_blocks - 1,
429 arr_blocks, label->parts->part_array);
[cbd64057]430 if (rc != EOK)
[44c4886]431 goto fail;
[f4a47e52]432
[1c8bfe8]433 /* Write to main GPT partition array location */
434 rc = block_write_direct(dev_handle, uint64_t_le2host(label->gpt->header->entry_lba),
[dc76f4a]435 arr_blocks, label->parts->part_array);
[1c8bfe8]436 if (rc != EOK)
437 goto fail;
[f4a47e52]438
[44c4886]439 return gpt_write_header(label, dev_handle);
440
441fail:
442 block_fini(dev_handle);
443 return rc;
[cbd64057]444}
445
[30440ed]446/** Alloc new partition
[d617050]447 *
[44c4886]448 * @return returns pointer to the new partition or NULL
[d617050]449 *
[44c4886]450 * Note: use either gpt_alloc_partition or gpt_get_partition.
451 * This returns a memory block (zero-filled) and needs gpt_add_partition()
452 * to be called to insert it into a partition array.
[1c8bfe8]453 * Requires you to call gpt_free_partition afterwards.
[44c4886]454 */
455gpt_part_t * gpt_alloc_partition(void)
456{
457 gpt_part_t *p = malloc(sizeof(gpt_part_t));
458 if (p == NULL)
459 return NULL;
460
461 memset(p, 0, sizeof(gpt_part_t));
462
463 return p;
464}
465
466/** Alloc new partition already inside the label
467 *
468 * @param label label to carry new partition
[d617050]469 *
[44c4886]470 * @return returns pointer to the new partition or NULL on ENOMEM
[d617050]471 *
[44c4886]472 * Note: use either gpt_alloc_partition or gpt_get_partition.
[1c8bfe8]473 * This one returns a pointer to the first empty structure already
474 * inside the array, so don't call gpt_add_partition() afterwards.
[44c4886]475 * This is the one you will usually want.
[30440ed]476 */
[44c4886]477gpt_part_t * gpt_get_partition(gpt_label_t *label)
[7570e800]478{
[1c8bfe8]479 gpt_part_t *p;
480
[c3cbbb2]481
[1c8bfe8]482 /* Find the first empty entry */
483 do {
484 if (label->parts->fill == label->parts->arr_size) {
485 if (extend_part_array(label->parts) == -1)
486 return NULL;
487 }
488
489 p = label->parts->part_array + label->parts->fill++;
490
491 } while (gpt_get_part_type(p) != GPT_PTE_UNUSED);
492
493 return p;
494}
[d617050]495
[1c8bfe8]496/** Get partition already inside the label
497 *
498 * @param label label to carrying the partition
499 * @param idx index of the partition
500 *
501 * @return returns pointer to the partition
502 * or NULL when out of range
503 *
504 * Note: For new partitions use either gpt_alloc_partition or
505 * gpt_get_partition unless you want a partition at a specific place.
506 * This returns a pointer to a structure already inside the array,
507 * so don't call gpt_add_partition() afterwards.
508 * This function is handy when you want to change already existing
509 * partition or to simply write somewhere in the middle. This works only
510 * for indexes smaller than either 128 or the actual number of filled
511 * entries.
512 */
513gpt_part_t * gpt_get_partition_at(gpt_label_t *label, size_t idx)
514{
515 return NULL;
516
517 if (idx >= GPT_MIN_PART_NUM && idx >= label->parts->fill)
518 return NULL;
519
520 return label->parts->part_array + idx;
[30440ed]521}
522
523/** Copy partition into partition array
[d617050]524 *
[44c4886]525 * @param parts target label
[30440ed]526 * @param partition source partition to copy
[d617050]527 *
[30440ed]528 * @return -1 on error, 0 otherwise
[d617050]529 *
[44c4886]530 * Note: for use with gpt_alloc_partition() only. You will get
531 * duplicates with gpt_get_partition().
[9bdfde73]532 * Note: does not call gpt_free_partition()!
[30440ed]533 */
[44c4886]534int gpt_add_partition(gpt_label_t *label, gpt_part_t *partition)
[30440ed]535{
[dc76f4a]536 gpt_part_t *p;
537 /* Find the first empty entry */
538 do {
539 if (label->parts->fill == label->parts->arr_size) {
540 if (extend_part_array(label->parts) == -1)
541 return ENOMEM;
542 }
543
544 p = label->parts->part_array + label->parts->fill++;
545
546 } while (gpt_get_part_type(p) != GPT_PTE_UNUSED);
[44c4886]547
[9bdfde73]548
[dc76f4a]549 memcpy(p, partition, sizeof(gpt_entry_t));
550
[9bdfde73]551
[44c4886]552 return EOK;
[7570e800]553}
554
[30440ed]555/** Remove partition from array
[44c4886]556 * @param label label to remove from
557 * @param idx index of the partition to remove
[d617050]558 *
[44c4886]559 * @return EOK on success, ENOMEM on array reduction failure
[d617050]560 *
[30440ed]561 * Note: even if it fails, the partition still gets removed. Only
562 * reducing the array failed.
563 */
[44c4886]564int gpt_remove_partition(gpt_label_t *label, size_t idx)
[7570e800]565{
[dc76f4a]566 if (idx >= label->parts->arr_size)
[1c8bfe8]567 return EINVAL;
[44c4886]568
[dc76f4a]569 /*
570 * FIXME!
[1c8bfe8]571 * If we allow blank spots, we break the array. If we have more than
572 * 128 partitions in the array and then remove something from
[dc76f4a]573 * the first 128 partitions, we would forget to write the last one.
574 */
[1c8bfe8]575 memset(label->parts->part_array + idx, 0, sizeof(gpt_entry_t));
576
[dc76f4a]577 if (label->parts->fill > idx)
578 label->parts->fill = idx;
[1c8bfe8]579
[dc76f4a]580 /*
581 * FIXME! HOPEFULLY FIXED.
[1c8bfe8]582 * We cannot reduce the array so simply. We may have some partitions
[dc76f4a]583 * there since we allow blank spots.
584 */
[9bdfde73]585 gpt_part_t * p;
[dc76f4a]586
587 if (label->parts->fill > GPT_MIN_PART_NUM &&
588 label->parts->fill < (label->parts->arr_size / 2) - GPT_IGNORE_FILL_NUM) {
[9bdfde73]589 for (p = gpt_get_partition_at(label, label->parts->arr_size / 2);
590 p < label->parts->part_array + label->parts->arr_size; ++p) {
591 if (gpt_get_part_type(p) != GPT_PTE_UNUSED)
592 return EOK;
593 }
594
[44c4886]595 if (reduce_part_array(label->parts) == ENOMEM)
596 return ENOMEM;
[30440ed]597 }
[d617050]598
[44c4886]599 return EOK;
[cbd64057]600}
601
602/** Free partition list
[d617050]603 *
[cbd64057]604 * @param parts partition list to be freed
605 */
[271e24a]606void gpt_free_partitions(gpt_partitions_t * parts)
[cbd64057]607{
608 free(parts->part_array);
609 free(parts);
610}
611
[30440ed]612/** Get partition type by linear search
613 * (hopefully this doesn't get slow)
614 */
615size_t gpt_get_part_type(gpt_part_t * p)
616{
617 size_t i;
[1c8bfe8]618
[30440ed]619 for (i = 0; gpt_ptypes[i].guid != NULL; i++) {
[1c8bfe8]620 if (p->part_type[3] == get_byte(gpt_ptypes[i].guid +0) &&
621 p->part_type[2] == get_byte(gpt_ptypes[i].guid +2) &&
622 p->part_type[1] == get_byte(gpt_ptypes[i].guid +4) &&
623 p->part_type[0] == get_byte(gpt_ptypes[i].guid +6) &&
624
625 p->part_type[5] == get_byte(gpt_ptypes[i].guid +8) &&
626 p->part_type[4] == get_byte(gpt_ptypes[i].guid +10) &&
627
628 p->part_type[7] == get_byte(gpt_ptypes[i].guid +12) &&
629 p->part_type[6] == get_byte(gpt_ptypes[i].guid +14) &&
630
631 p->part_type[8] == get_byte(gpt_ptypes[i].guid +16) &&
632 p->part_type[9] == get_byte(gpt_ptypes[i].guid +18) &&
633 p->part_type[10] == get_byte(gpt_ptypes[i].guid +20) &&
634 p->part_type[11] == get_byte(gpt_ptypes[i].guid +22) &&
635 p->part_type[12] == get_byte(gpt_ptypes[i].guid +24) &&
636 p->part_type[13] == get_byte(gpt_ptypes[i].guid +26) &&
637 p->part_type[14] == get_byte(gpt_ptypes[i].guid +28) &&
638 p->part_type[15] == get_byte(gpt_ptypes[i].guid +30))
639 break;
[30440ed]640 }
[1c8bfe8]641
[30440ed]642 return i;
643}
644
[cbd64057]645/** Set partition type
646 * @param p partition to be set
647 * @param type partition type to set
[d617050]648 * - see our fine selection at gpt_ptypes to choose from
[cbd64057]649 */
[d617050]650void gpt_set_part_type(gpt_part_t * p, size_t type)
[cbd64057]651{
652 /* Beware: first 3 blocks are byteswapped! */
[8c95dff]653 p->part_type[3] = get_byte(gpt_ptypes[type].guid +0);
654 p->part_type[2] = get_byte(gpt_ptypes[type].guid +2);
655 p->part_type[1] = get_byte(gpt_ptypes[type].guid +4);
656 p->part_type[0] = get_byte(gpt_ptypes[type].guid +6);
657
658 p->part_type[5] = get_byte(gpt_ptypes[type].guid +8);
659 p->part_type[4] = get_byte(gpt_ptypes[type].guid +10);
660
661 p->part_type[7] = get_byte(gpt_ptypes[type].guid +12);
662 p->part_type[6] = get_byte(gpt_ptypes[type].guid +14);
663
664 p->part_type[8] = get_byte(gpt_ptypes[type].guid +16);
665 p->part_type[9] = get_byte(gpt_ptypes[type].guid +18);
666 p->part_type[10] = get_byte(gpt_ptypes[type].guid +20);
667 p->part_type[11] = get_byte(gpt_ptypes[type].guid +22);
668 p->part_type[12] = get_byte(gpt_ptypes[type].guid +24);
669 p->part_type[13] = get_byte(gpt_ptypes[type].guid +26);
670 p->part_type[14] = get_byte(gpt_ptypes[type].guid +28);
671 p->part_type[15] = get_byte(gpt_ptypes[type].guid +30);
[d617050]672}
673
674/** Get partition starting LBA */
675uint64_t gpt_get_start_lba(gpt_part_t * p)
676{
677 return uint64_t_le2host(p->start_lba);
678}
679
680/** Set partition starting LBA */
681void gpt_set_start_lba(gpt_part_t * p, uint64_t start)
682{
683 p->start_lba = host2uint64_t_le(start);
684}
685
686/** Get partition ending LBA */
687uint64_t gpt_get_end_lba(gpt_part_t * p)
688{
689 return uint64_t_le2host(p->end_lba);
[cbd64057]690}
691
[d617050]692/** Set partition ending LBA */
693void gpt_set_end_lba(gpt_part_t * p, uint64_t end)
[30440ed]694{
[d617050]695 p->end_lba = host2uint64_t_le(end);
696}
697
[44c4886]698/** Get partition name */
[d617050]699unsigned char * gpt_get_part_name(gpt_part_t * p)
700{
701 return p->part_name;
[30440ed]702}
703
[cbd64057]704/** Copy partition name */
[1c8bfe8]705void gpt_set_part_name(gpt_part_t *p, char *name, size_t length)
[cbd64057]706{
[30440ed]707 if (length >= 72)
708 length = 71;
[d617050]709
[cbd64057]710 memcpy(p->part_name, name, length);
[30440ed]711 p->part_name[length] = '\0';
712}
713
714/** Get partition attribute */
[d617050]715bool gpt_get_flag(gpt_part_t * p, GPT_ATTR flag)
[30440ed]716{
[d617050]717 return (p->attributes & (((uint64_t) 1) << flag)) ? 1 : 0;
[30440ed]718}
719
720/** Set partition attribute */
[d617050]721void gpt_set_flag(gpt_part_t * p, GPT_ATTR flag, bool value)
[30440ed]722{
[d617050]723 uint64_t attr = p->attributes;
[30440ed]724
725 if (value)
726 attr = attr | (((uint64_t) 1) << flag);
727 else
728 attr = attr ^ (attr & (((uint64_t) 1) << flag));
729
[d617050]730 p->attributes = attr;
[cbd64057]731}
732
[dc76f4a]733/** Generate a new pseudo-random UUID
734 * @param uuid Pointer to the UUID to overwrite.
735 */
[c3cbbb2]736void gpt_set_random_uuid(uint8_t * uuid)
737{
[0435fe41]738 srandom((unsigned int) (size_t) uuid);
[c3cbbb2]739
740 unsigned int i;
741 for (i = 0; i < 16/sizeof(long int); ++i)
742 ((long int *)uuid)[i] = random();
743
744}
745
[dc76f4a]746/** Get next aligned address */
747uint64_t gpt_get_next_aligned(uint64_t addr, unsigned int alignment)
748{
749 uint64_t div = addr / alignment;
750 return (div + 1) * alignment;
751}
752
753/* Internal functions follow */
[cbd64057]754
[7570e800]755static int load_and_check_header(service_id_t dev_handle, aoff64_t addr, size_t b_size, gpt_header_t * header)
[cbd64057]756{
[7570e800]757 int rc;
[d617050]758
[7570e800]759 rc = block_read_direct(dev_handle, addr, GPT_HDR_BS, header);
[cbd64057]760 if (rc != EOK)
761 return rc;
[d617050]762
[7570e800]763 unsigned int i;
[cbd64057]764 /* Check the EFI signature */
765 for (i = 0; i < 8; ++i) {
[7570e800]766 if (header->efi_signature[i] != efi_signature[i])
[cbd64057]767 return EINVAL;
768 }
[d617050]769
[cbd64057]770 /* Check the CRC32 of the header */
[7570e800]771 uint32_t crc = header->header_crc32;
772 header->header_crc32 = 0;
773 if (crc != compute_crc32((uint8_t *) header, header->header_size))
[cbd64057]774 return EBADCHECKSUM;
775 else
[7570e800]776 header->header_crc32 = crc;
[d617050]777
[cbd64057]778 /* Check for zeroes in the rest of the block */
779 for (i = sizeof(gpt_header_t); i < b_size; ++i) {
[7570e800]780 if (((uint8_t *) header)[i] != 0)
781 return EINVAL;
[cbd64057]782 }
[d617050]783
[cbd64057]784 return EOK;
785}
786
[271e24a]787static gpt_partitions_t * alloc_part_array(uint32_t num)
[cbd64057]788{
[271e24a]789 gpt_partitions_t * res = malloc(sizeof(gpt_partitions_t));
[cbd64057]790 if (res == NULL) {
791 errno = ENOMEM;
792 return NULL;
793 }
[dc76f4a]794
[cbd64057]795 uint32_t size = num > GPT_BASE_PART_NUM ? num : GPT_BASE_PART_NUM;
796 res->part_array = malloc(size * sizeof(gpt_entry_t));
[7570e800]797 if (res->part_array == NULL) {
[cbd64057]798 free(res);
799 errno = ENOMEM;
800 return NULL;
801 }
[dc76f4a]802
803 memset(res->part_array, 0, size * sizeof(gpt_entry_t));
804
[c3cbbb2]805 res->fill = 0;
806 res->arr_size = num;
[d617050]807
[cbd64057]808 return res;
809}
810
[271e24a]811static int extend_part_array(gpt_partitions_t * p)
[cbd64057]812{
[c3cbbb2]813 size_t nsize = p->arr_size * 2;
[cbd64057]814 gpt_entry_t * tmp = malloc(nsize * sizeof(gpt_entry_t));
[0435fe41]815 if (tmp == NULL) {
[cbd64057]816 errno = ENOMEM;
817 return -1;
818 }
[c3cbbb2]819
820 memcpy(tmp, p->part_array, p->fill * sizeof(gpt_entry_t));
[cbd64057]821 free(p->part_array);
822 p->part_array = tmp;
823 p->arr_size = nsize;
[d617050]824
[cbd64057]825 return 0;
826}
827
[271e24a]828static int reduce_part_array(gpt_partitions_t * p)
[cbd64057]829{
[0435fe41]830 if (p->arr_size > GPT_MIN_PART_NUM) {
[cbd64057]831 unsigned int nsize = p->arr_size / 2;
[44c4886]832 nsize = nsize > GPT_MIN_PART_NUM ? nsize : GPT_MIN_PART_NUM;
[cbd64057]833 gpt_entry_t * tmp = malloc(nsize * sizeof(gpt_entry_t));
[0435fe41]834 if (tmp == NULL)
[44c4886]835 return ENOMEM;
[d617050]836
[dc76f4a]837 memcpy(tmp, p->part_array, p->fill < nsize ? p->fill : nsize);
[cbd64057]838 free(p->part_array);
839 p->part_array = tmp;
840 p->arr_size = nsize;
841 }
[d617050]842
[cbd64057]843 return 0;
844}
845
[dc76f4a]846/* Parse a byte from a string in hexadecimal
847 * i.e., "FF" => 255
848 */
[1c8bfe8]849static uint8_t get_byte(const char * c)
[d617050]850{
[1c8bfe8]851 uint8_t val = 0;
852 char hex[3] = {*c, *(c+1), 0};
853
854 errno = str_uint8_t(hex, NULL, 16, false, &val);
855 return val;
[d617050]856}
857
[dc76f4a]858static bool check_overlap(gpt_part_t * p1, gpt_part_t * p2)
[9bdfde73]859{
[493b881]860 if (gpt_get_start_lba(p1) < gpt_get_start_lba(p2) && gpt_get_end_lba(p1) < gpt_get_start_lba(p2)) {
[dc76f4a]861 return false;
[493b881]862 } else if (gpt_get_start_lba(p1) > gpt_get_start_lba(p2) && gpt_get_end_lba(p2) < gpt_get_start_lba(p1)) {
[dc76f4a]863 return false;
[9bdfde73]864 }
[cbd64057]865
[dc76f4a]866 return true;
[9bdfde73]867}
[cbd64057]868
[2b55edb]869static bool check_encaps(gpt_part_t *p, uint64_t n_blocks, uint64_t first_lba)
870{
[493b881]871 /*
872 * We allow "<=" in the second expression because it lacks MBR so
873 * it's by 1 block smaller.
874 */
875 if (gpt_get_start_lba(p) >= first_lba && gpt_get_end_lba(p) <= n_blocks - first_lba)
[2b55edb]876 return true;
877
878 return false;
879}
Note: See TracBrowser for help on using the repository browser.