source: mainline/uspace/lib/gpt/libgpt.c@ 30440ed

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

save progress

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