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

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

GPT library - alpha status

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