source: mainline/uspace/lib/gpt/libgpt.c@ 5beb1ff

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

minor fixes

  • Property mode set to 100644
File size: 11.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);
54static gpt_parts_t * alloc_part_array(uint32_t num);
55static int extend_part_array(gpt_parts_t * p);
56static int reduce_part_array(gpt_parts_t * p);
57static long long nearest_larger_int(double a);
58
[cbd64057]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
[7570e800]81 gpt->raw_data = malloc(b_size);// We might need only sizeof(gpt_header_t),
[cbd64057]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
[7570e800]89 rc = load_and_check_header(dev_handle, GPT_HDR_BA, b_size, gpt->raw_data);
[cbd64057]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
[7570e800]98 rc = load_and_check_header(dev_handle, n_blocks - 1, b_size, gpt->raw_data);
[cbd64057]99 if (rc == EBADCHECKSUM || rc == EINVAL) {
100 errno = rc;
101 goto fail;
102 }
103 }
104
[7570e800]105 gpt->device = dev_handle;
106
[cbd64057]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 */
[7570e800]120int gpt_write_gpt_header(gpt_t * gpt, service_id_t dev_handle)
[cbd64057]121{
122 int rc;
123 size_t b_size;
124
[7570e800]125 gpt->raw_data->header_crc32 = 0;
126 gpt->raw_data->header_crc32 = compute_crc32((uint8_t *) gpt->raw_data,
127 uint32_t_le2host(gpt->raw_data->header_size));
[cbd64057]128
129 rc = block_get_bsize(dev_handle, &b_size);
130 if (rc != EOK)
131 return rc;
132
133 rc = block_init(EXCHANGE_ATOMIC, dev_handle, b_size);
134 if (rc != EOK)
135 return rc;
136
137 /* Write to main GPT header location */
[7570e800]138 rc = block_write_direct(dev_handle, GPT_HDR_BA, GPT_HDR_BS, gpt->raw_data);
[cbd64057]139 if (rc != EOK)
140 block_fini(dev_handle);
141 return rc;
142
143 aoff64_t n_blocks;
144 rc = block_get_nblocks(dev_handle, &n_blocks);
145 if (rc != EOK)
146 return rc;
147
148 /* Write to backup GPT header location */
[7570e800]149 //FIXME: those idiots thought it would be cool to have these fields in reverse order...
150 rc = block_write_direct(dev_handle, n_blocks - 1, GPT_HDR_BS, gpt->raw_data);
[cbd64057]151 block_fini(dev_handle);
152 if (rc != EOK)
153 return rc;
154
155 return 0;
156}
157
158/** Parse partitions from GPT
159 * @param gpt GPT to be parsed
160 *
161 * @return partition linked list pointer or NULL on error
162 * error code is stored in errno
163 */
164gpt_parts_t * gpt_read_partitions(gpt_t * gpt)
165{
[7570e800]166 int rc;
167 unsigned int i;
[cbd64057]168 gpt_parts_t * res;
[7570e800]169 uint32_t num_ent = uint32_t_le2host(gpt->raw_data->num_entries);
170 uint32_t ent_size = uint32_t_le2host(gpt->raw_data->entry_size);
171 uint64_t ent_lba = uint64_t_le2host(gpt->raw_data->entry_lba);
[cbd64057]172
173 res = alloc_part_array(num_ent);
174 if (res == NULL) {
175 //errno = ENOMEM; // already set in alloc_part_array()
176 return NULL;
177 }
178
179 /* We can limit comm_size like this:
180 * - we don't need more bytes
181 * - the size of GPT partition entry can be different to 128 bytes */
[7570e800]182 rc = block_init(EXCHANGE_SERIALIZE, gpt->device, sizeof(gpt_entry_t));
[cbd64057]183 if (rc != EOK) {
184 gpt_free_partitions(res);
185 errno = rc;
186 return NULL;
187 }
188
189 size_t block_size;
190 rc = block_get_bsize(gpt->device, &block_size);
191 if (rc != EOK) {
192 gpt_free_partitions(res);
193 errno = rc;
194 return NULL;
195 }
196
197 //size_t bufpos = 0;
198 //size_t buflen = 0;
199 aoff64_t pos = ent_lba * block_size;
200
201 /* Now we read just sizeof(gpt_entry_t) bytes for each entry from the device.
202 * Hopefully, this does not bypass cache (no mention in libblock.c),
203 * and also allows us to have variable partition entry size (but we
204 * will always read just sizeof(gpt_entry_t) bytes - hopefully they
205 * don't break backward compatibility) */
206 for (i = 0; i < num_ent; ++i) {
207 //FIXME: this does bypass cache...
208 rc = block_read_bytes_direct(gpt->device, pos, sizeof(gpt_entry_t), res->part_array + i);
209 //FIXME: but seqread() is just too complex...
210 //rc = block_seqread(gpt->device, &bufpos, &buflen, &pos, res->part_array[i], sizeof(gpt_entry_t));
211 pos += ent_size;
212
213 if (rc != EOK) {
214 gpt_free_partitions(res);
215 errno = rc;
216 return NULL;
217 }
218 }
219
[7570e800]220 /* FIXME: so far my boasting about variable partition entry size
221 * will not work. The CRC32 checksums will be different.
222 * This can't be fixed easily - we'd have to run the checksum
223 * on all of the partition entry array.
224 */
225 uint32_t crc = compute_crc32((uint8_t *) res->part_array, res->num_ent * sizeof(gpt_entry_t));
[cbd64057]226
[7570e800]227 if(uint32_t_le2host(gpt->raw_data->pe_array_crc32) != crc)
[cbd64057]228 {
229 gpt_free_partitions(res);
230 errno = EBADCHECKSUM;
231 return NULL;
232 }
233
234 return res;
235}
236
237/** Write GPT and partitions to device
238 * @param parts partition list to be written
[7570e800]239 * @param header GPT header belonging to the 'parts' partitions
[cbd64057]240 * @param dev_handle device to write the data to
241 *
242 * @return returns EOK on succes, specific error code otherwise
243 */
[7570e800]244int gpt_write_partitions(gpt_parts_t * parts, gpt_t * gpt, service_id_t dev_handle)
[cbd64057]245{
[7570e800]246 int rc;
[cbd64057]247 size_t b_size;
248
[7570e800]249 gpt->raw_data->pe_array_crc32 = compute_crc32((uint8_t *) parts->part_array, parts->num_ent * gpt->raw_data->entry_size);
[cbd64057]250
251 rc = block_get_bsize(dev_handle, &b_size);
252 if (rc != EOK)
253 return rc;
254
255 rc = block_init(EXCHANGE_ATOMIC, dev_handle, b_size);
256 if (rc != EOK)
257 return rc;
258
259 /* Write to main GPT partition array location */
[7570e800]260 rc = block_write_direct(dev_handle, uint64_t_le2host(gpt->raw_data->entry_lba),
261 nearest_larger_int((uint64_t_le2host(gpt->raw_data->entry_size) * parts->num_ent) / b_size),
[cbd64057]262 parts->part_array);
263 if (rc != EOK)
264 block_fini(dev_handle);
265 return rc;
266
267 aoff64_t n_blocks;
268 rc = block_get_nblocks(dev_handle, &n_blocks);
269 if (rc != EOK)
270 return rc;
271
272 /* Write to backup GPT partition array location */
273 //rc = block_write_direct(dev_handle, n_blocks - 1, GPT_HDR_BS, header->raw_data);
274 block_fini(dev_handle);
275 if (rc != EOK)
276 return rc;
277
278
[7570e800]279 gpt_write_gpt_header(gpt, dev_handle);
[cbd64057]280
281 return 0;
282
283}
284
[7570e800]285gpt_parts_t * gpt_add_partition(gpt_parts_t * parts, g_part_t * partition)
286{
287
288}
289
290gpt_parts_t * gpt_remove_partition(gpt_parts_t * parts, int idx)
291{
292
293}
294
[cbd64057]295/** free() GPT header including gpt->header_lba */
296void gpt_free_gpt(gpt_t * gpt)
297{
[7570e800]298 free(gpt->raw_data);
[cbd64057]299 free(gpt);
300}
301
302/** Free partition list
303 *
304 * @param parts partition list to be freed
305 */
306void gpt_free_partitions(gpt_parts_t * parts)
307{
308 free(parts->part_array);
309 free(parts);
310}
311
312/** Set partition type
313 * @param p partition to be set
314 * @param type partition type to set
315 * - see gpt_ptypes to choose from
316 *
317 */
318void gpt_set_part_type(g_part_t * p, int type)
319{
320 /* Beware: first 3 blocks are byteswapped! */
[7570e800]321 p->raw_data.part_type[3] = gpt_ptypes[type].guid[0];
322 p->raw_data.part_type[2] = gpt_ptypes[type].guid[1];
323 p->raw_data.part_type[1] = gpt_ptypes[type].guid[2];
324 p->raw_data.part_type[0] = gpt_ptypes[type].guid[3];
325
326 p->raw_data.part_type[5] = gpt_ptypes[type].guid[4];
327 p->raw_data.part_type[4] = gpt_ptypes[type].guid[5];
328
329 p->raw_data.part_type[7] = gpt_ptypes[type].guid[6];
330 p->raw_data.part_type[6] = gpt_ptypes[type].guid[7];
331
332 p->raw_data.part_type[8] = gpt_ptypes[type].guid[8];
333 p->raw_data.part_type[9] = gpt_ptypes[type].guid[9];
334 p->raw_data.part_type[10] = gpt_ptypes[type].guid[10];
335 p->raw_data.part_type[11] = gpt_ptypes[type].guid[11];
336 p->raw_data.part_type[12] = gpt_ptypes[type].guid[12];
337 p->raw_data.part_type[13] = gpt_ptypes[type].guid[13];
338 p->raw_data.part_type[14] = gpt_ptypes[type].guid[14];
339 p->raw_data.part_type[15] = gpt_ptypes[type].guid[15];
[cbd64057]340}
341
342/** Copy partition name */
[7570e800]343void gpt_set_part_name(gpt_entry_t * p, char * name[], size_t length)
[cbd64057]344{
345 memcpy(p->part_name, name, length);
346}
347
348// Internal functions follow //
349
[7570e800]350static int load_and_check_header(service_id_t dev_handle, aoff64_t addr, size_t b_size, gpt_header_t * header)
[cbd64057]351{
[7570e800]352 int rc;
353
354 rc = block_init(EXCHANGE_ATOMIC, dev_handle, b_size);
[cbd64057]355 if (rc != EOK)
356 return rc;
357
[7570e800]358 rc = block_read_direct(dev_handle, addr, GPT_HDR_BS, header);
359 block_fini(dev_handle);
[cbd64057]360 if (rc != EOK)
361 return rc;
362
363
[7570e800]364 unsigned int i;
[cbd64057]365 /* Check the EFI signature */
366 for (i = 0; i < 8; ++i) {
[7570e800]367 if (header->efi_signature[i] != efi_signature[i])
[cbd64057]368 return EINVAL;
369 }
370
371 /* Check the CRC32 of the header */
[7570e800]372 uint32_t crc = header->header_crc32;
373 header->header_crc32 = 0;
374 if (crc != compute_crc32((uint8_t *) header, header->header_size))
[cbd64057]375 return EBADCHECKSUM;
376 else
[7570e800]377 header->header_crc32 = crc;
[cbd64057]378
379 /* Check for zeroes in the rest of the block */
380 for (i = sizeof(gpt_header_t); i < b_size; ++i) {
[7570e800]381 if (((uint8_t *) header)[i] != 0)
382 return EINVAL;
[cbd64057]383 }
[7570e800]384
[cbd64057]385 return EOK;
386}
387
388static gpt_parts_t * alloc_part_array(uint32_t num)
389{
390 gpt_parts_t * res = malloc(sizeof(gpt_parts_t));
391 if (res == NULL) {
392 errno = ENOMEM;
393 return NULL;
394 }
395
396 uint32_t size = num > GPT_BASE_PART_NUM ? num : GPT_BASE_PART_NUM;
397 res->part_array = malloc(size * sizeof(gpt_entry_t));
[7570e800]398 if (res->part_array == NULL) {
[cbd64057]399 free(res);
400 errno = ENOMEM;
401 return NULL;
402 }
403
404 res->num_ent = num;
405 res->arr_size = size;
406
407 return res;
408}
409
410static int extend_part_array(gpt_parts_t * p)
411{
412 unsigned int nsize = p->arr_size * 2;
413 gpt_entry_t * tmp = malloc(nsize * sizeof(gpt_entry_t));
414 if(tmp == NULL) {
415 errno = ENOMEM;
416 return -1;
417 }
418
419 memcpy(tmp, p->part_array, p->num_ent);
420 free(p->part_array);
421 p->part_array = tmp;
422 p->arr_size = nsize;
423
424 return 0;
425}
426
427static int reduce_part_array(gpt_parts_t * p)
428{
429 if(p->arr_size > GPT_MIN_PART_NUM) {
430 unsigned int nsize = p->arr_size / 2;
431 gpt_entry_t * tmp = malloc(nsize * sizeof(gpt_entry_t));
432 if(tmp == NULL) {
433 errno = ENOMEM;
434 return -1;
435 }
436
437 memcpy(tmp, p->part_array, p->num_ent < nsize ? p->num_ent : nsize);
438 free(p->part_array);
439 p->part_array = tmp;
440 p->arr_size = nsize;
441 }
442
443 return 0;
444}
445
446//FIXME: replace this with a library call, if it exists
447static long long nearest_larger_int(double a)
448{
449 if ((long long) a == a) {
450 return (long long) a;
451 }
452
453 return ((long long) a) + 1;
454}
455
456
457
458
Note: See TracBrowser for help on using the repository browser.