blob: 3bfc2988f5006775d3a6ae69938f943dab9ffaad [file] [log] [blame]
Szymon Starzyckie160f812013-07-24 17:08:04 -07001/*
2 * Copyright (c) 2009-2013, Google Inc.
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 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google, Inc. nor the names of its contributors
15 * may be used to endorse or promote products derived from this
16 * software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <fcntl.h>
35#include <sys/mman.h>
36#include <sys/stat.h>
37#include <sys/types.h>
38#include <unistd.h>
39#include <endian.h>
40#include <zlib.h>
41#include <linux/hdreg.h>
42#include <sys/ioctl.h>
43#include <stdlib.h>
44#include <cutils/config_utils.h>
45
46#include "partitions.h"
47#include "debug.h"
48#include "utils.h"
49#include "protocol.h"
50
51#define MMAP2_SHIFT 12
52
53#define BLKRRPART _IO(0x12,95) /* re-read partition table */
54#define BLKSSZGET _IO(0x12,104)
55
56#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
57#define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
58#define ALIGN_DOWN(x, y) ((y) * ((x) / (y)))
59
60
61const uint16_t partition_type_uuid[16] = {
62 0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44,
63 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7,
64};
65
66//TODO: If both blocks are invalid should I leave everything to vendor (through libvendor)
67
68static void GPT_entry_clear(struct GPT_entry_raw *entry)
69{
70 memset(entry, 0, sizeof(*entry));
71}
72
73extern void* __mmap2(void *, size_t, int, int, int, off_t);
74
75static inline void *mmap64(void *addr, size_t length, int prot,
76 int flags, int fd, uint64_t offset) {
77 return __mmap2(addr, length, prot, flags, fd, offset >> MMAP2_SHIFT);
78}
79
80/*
81 * returns mapped location to choosen area
82 * mapped_ptr is pointer to whole area mapped (it can be bigger then requested)
83 */
84int gpt_mmap(struct GPT_mapping *mapping, uint64_t location, int size, int fd)
85{
86 unsigned int location_diff = location & ~PAGE_MASK;
87
88 mapping->size = ALIGN(size + location_diff, PAGE_SIZE);
89
90 uint64_t sz = get_file_size64(fd);
91 if (sz < size + location) {
92 D(ERR, "the location of mapping area is outside of the device size %lld", sz);
93 return 1;
94 }
95
96 mapping->map_ptr = mmap64(NULL, mapping->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, location);
97
98 if (mapping->map_ptr == MAP_FAILED) {
99 mapping->ptr = MAP_FAILED;
100 D(ERR, "map failed %d", (int) mapping->map_ptr);
101 return 1;
102 }
103
104 mapping->ptr = (void *)((char *) mapping->map_ptr + location_diff);
105 return 0;
106}
107
108void gpt_unmap(struct GPT_mapping *mapping) {
109 munmap(mapping->map_ptr, mapping->size);
110}
111
112
113#define LBA_ADDR(table, value) ((uint64_t) (table)->sector_size * (value))
114
115int GPT_map_from_content(struct GPT_entry_table *table, const struct GPT_content *content)
116{
117
118 // Mapping header
119 if (gpt_mmap(&table->header_map, LBA_ADDR(table, content->header.current_lba),
120 table->sector_size, table->fd)) {
121 D(ERR, "unable to map header:%s\n", strerror(errno));
122 goto error_header;
123 }
124
125 table->header = (struct GPT_header *) table->header_map.ptr;
126
127 table->partition_table_size = ROUND_UP(content->header.entries_count * sizeof(*table->entries),
128 table->sector_size);
129
130 // Mapping entry table
131 if (gpt_mmap(&table->entries_map, LBA_ADDR(table, content->header.entries_lba),
132 table->partition_table_size, table->fd)) {
133 D(ERR, "unable to map entries");
134 goto error_signature;
135 }
136
137 table->entries = (struct GPT_entry_raw *) table->entries_map.ptr;
138
139 // Mapping secondary header
140 if (gpt_mmap(&table->sec_header_map, LBA_ADDR(table, content->header.backup_lba),
141 table->sector_size, table->fd)) {
142 D(ERR, "unable to map backup gpt header");
143 goto error_sec_header;
144 }
145
146 // Mapping secondary entries table
147 if (gpt_mmap(&table->sec_entries_map,
148 LBA_ADDR(table, content->header.backup_lba) - table->partition_table_size,
149 table->partition_table_size, table->fd)) {
150 D(ERR, "unable to map secondary gpt table");
151 goto error_sec_entries;
152 }
153
154 table->second_header = (struct GPT_header *) table->sec_header_map.ptr;
155 table->second_entries = (struct GPT_entry_raw *) table->sec_entries_map.ptr;
156 table->second_valid = strcmp("EFI PART", (char *) table->second_header->signature) == 0;
157
158 return 0;
159
160error_sec_entries:
161 gpt_unmap(&table->sec_header_map);
162error_sec_header:
163 gpt_unmap(&table->entries_map);
164error_signature:
165 gpt_unmap(&table->header_map);
166error_header:
167 return 1;
168}
169
170int GPT_map(struct GPT_entry_table *table, unsigned header_lba)
171{
172 struct GPT_content content;
173 struct GPT_mapping mapping;
174 struct GPT_header *header;
175
176 if (gpt_mmap(&mapping, LBA_ADDR(table, header_lba), table->sector_size, table->fd)) {
177 D(ERR, "unable to map header: %s", strerror(errno));
178 goto error_header;
179 }
180
181 header = (struct GPT_header *) mapping.ptr;
182
183 if (strcmp("EFI PART", (char *) header->signature)) {
184 D(ERR, "GPT entry not valid");
185 goto error_signature;
186 }
187
188 content.header = *header;
189
190 gpt_unmap(&mapping);
191
192 return GPT_map_from_content(table, &content);
193
194error_signature:
195 gpt_unmap(&table->header_map);
196error_header:
197 return 1;
198}
199
200struct GPT_entry_table* GPT_get_device(const char *path, unsigned header_lba)
201{
202 struct GPT_entry_table *table;
203 size_t sector_bytes;
204
205 table = (struct GPT_entry_table *) malloc(sizeof(*table));
206 table->fd = open(path, O_RDWR);
207
208 if (table->fd < 0) {
209 D(ERR, "unable to open file %s:%s\n", path, strerror(errno));
210 return NULL;
211 }
212
213 if (!ioctl(table->fd, BLKSSZGET, &sector_bytes)) {
214 table->sector_size = (unsigned) sector_bytes;
215 D(INFO, "Got sector size %d", table->sector_size);
216 } else {
217 D(WARN, "unable to get sector size, assuming 512");
218 table->sector_size = 512;
219 }
220
221 if (GPT_map(table, header_lba)) {
222 D(ERR, "Could not map gpt");
223 return NULL;
224 }
225
226 return table;
227}
228
229static struct GPT_entry_table* GPT_get_from_content(const char *path, const struct GPT_content *content)
230{
231 struct GPT_entry_table *table;
232 size_t sector_bytes;
233
234 table = (struct GPT_entry_table *) malloc(sizeof(*table));
235 table->fd = open(path, O_RDWR);
236
237 if (table->fd < 0) {
238 D(ERR, "unable to open file %s:%s\n", path, strerror(errno));
239 return NULL;
240 }
241
242 if (!ioctl(table->fd, BLKSSZGET, &sector_bytes)) {
243 table->sector_size = (unsigned) sector_bytes;
244 D(INFO, "Got sector size %d", table->sector_size);
245 } else {
246 D(WARN, "unable to get sector size %s, assuming 512", strerror(errno));
247 table->sector_size = 512;
248 }
249
250 if (GPT_map_from_content(table, content)) {
251 D(ERR, "Could not map gpt");
252 return NULL;
253 }
254
255 return table;
256}
257
258
259void GPT_release_device(struct GPT_entry_table *table)
260{
261 gpt_unmap(&table->header_map);
262 gpt_unmap(&table->entries_map);
263 gpt_unmap(&table->sec_header_map);
264 gpt_unmap(&table->sec_entries_map);
265 close(table->fd);
266 free(table);
267}
268
269static int GPT_check_overlap(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
270static int GPT_check_overlap_except(struct GPT_entry_table *table,
271 struct GPT_entry_raw *entry,
272 struct GPT_entry_raw *exclude);
273
274void GPT_edit_entry(struct GPT_entry_table *table,
275 struct GPT_entry_raw *old_entry,
276 struct GPT_entry_raw *new_entry)
277{
278 struct GPT_entry_raw *current_entry = GPT_get_pointer(table, old_entry);
279
280 if (GPT_check_overlap_except(table, new_entry, current_entry)) {
281 D(ERR, "Couldn't add overlaping partition");
282 return;
283 }
284
285 if (current_entry == NULL) {
286 D(ERR, "Couldn't find entry");
287 return;
288 }
289
290 *current_entry = *new_entry;
291}
292
293int GPT_delete_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
294{
295 struct GPT_entry_raw *raw = GPT_get_pointer(table, entry);
296
297 if (raw == NULL) {
298 D(ERR, "could not find entry");
299 return 1;
300 }
301 D(DEBUG, "Deleting gpt entry '%s'\n", raw->partition_guid);
302
303 // This entry can be empty in the middle
304 GPT_entry_clear(raw);
305
306 return 0;
307}
308
309void GPT_add_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
310{
311 unsigned i;
312 int inserted = 0;
313 if (GPT_check_overlap(table, entry)) {
314 D(ERR, "Couldn't add overlaping partition");
315 return;
316 }
317
318 if (GPT_get_pointer(table, entry) != NULL) {
319 D(WARN, "Add entry fault, this entry already exists");
320 return;
321 }
322
323 struct GPT_entry_raw *entries = table->entries;
324
325 for (i = 0; i < table->header->entries_count; ++i) {
326 if (!entries[i].type_guid[0]) {
327 inserted = 1;
328 D(DEBUG, "inserting");
329 memcpy(&entries[i], entry, sizeof(entries[i]));
330 break;
331 }
332 }
333
334 if (!inserted) {
335 D(ERR, "Unable to find empty partion entry");
336 }
337}
338
339struct GPT_entry_raw *GPT_get_pointer_by_UTFname(struct GPT_entry_table *table, const uint16_t *name);
340
341struct GPT_entry_raw *GPT_get_pointer(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
342{
343 if (entry->partition_guid[0] != 0)
344 return GPT_get_pointer_by_guid(table, (const char *) entry->partition_guid);
345 else if (entry->name[0] != 0)
346 return GPT_get_pointer_by_UTFname(table, entry->name);
347
348 D(WARN, "Name or guid needed to find entry");
349 return NULL;
350}
351
352struct GPT_entry_raw *GPT_get_pointer_by_guid(struct GPT_entry_table *table, const char *name)
353{
354 int current = (int) table->header->entries_count;
355
356 for (current = current - 1; current >= 0; --current) {
357 if (strncmp((char *) name,
358 (char *) table->entries[current].partition_guid, 16) == 0) {
359 return &table->entries[current];
360 }
361 }
362
363 return NULL;
364}
365
366int strncmp_UTF16_char(const uint16_t *s1, const char *s2, size_t n)
367{
368 if (n == 0)
369 return (0);
370 do {
371 if (((*s1) & 127) != *s2++)
372 return (((unsigned char) ((*s1) & 127)) - *(unsigned char *)--s2);
373 if (*s1++ == 0)
374 break;
375 } while (--n != 0);
376 return (0);
377}
378
379int strncmp_UTF16(const uint16_t *s1, const uint16_t *s2, size_t n)
380{
381 if (n == 0)
382 return (0);
383 do {
384 if ((*s1) != *s2++)
385 return (*s1 - *--s2);
386 if (*s1++ == 0)
387 break;
388 } while (--n != 0);
389 return (0);
390}
391
392struct GPT_entry_raw *GPT_get_pointer_by_name(struct GPT_entry_table *table, const char *name)
393{
394 //TODO: reverse direction
395 int count = (int) table->header->entries_count;
396 int current;
397
398 for (current = 0; current < count; ++current) {
399 if (strncmp_UTF16_char(table->entries[current].name,
400 (char *) name, 16) == 0) {
401 return &table->entries[current];
402 }
403 }
404
405 return NULL;
406}
407
408struct GPT_entry_raw *GPT_get_pointer_by_UTFname(struct GPT_entry_table *table, const uint16_t *name)
409{
410 int count = (int) table->header->entries_count;
411 int current;
412
413 for (current = 0; current < count; ++current) {
414 if (strncmp_UTF16(table->entries[current].name,
415 name, GPT_NAMELEN) == 0) {
416 return &table->entries[current];
417 }
418 }
419
420 return NULL;
421}
422
423void GPT_sync(struct GPT_entry_table *table)
424{
425 uint32_t crc;
426
427 //calculate crc32
428 crc = crc32(0, Z_NULL, 0);
429 crc = crc32(crc, (void*) table->entries, table->header->entries_count * sizeof(*table->entries));
430 table->header->partition_array_checksum = crc;
431
432 table->header->header_checksum = 0;
433 crc = crc32(0, Z_NULL, 0);
434 crc = crc32(crc, (void*) table->header, sizeof(*table->header));
435 table->header->header_checksum = crc;
436
437 //sync secondary partion
438 if (table->second_valid) {
439 memcpy((void *)table->second_entries, (void *) table->entries, table->partition_table_size);
440 memcpy((void *)table->second_header, (void *)table->header, sizeof(*table->header));
441 }
442
443 if(!ioctl(table->fd, BLKRRPART, NULL)) {
444 D(WARN, "Unable to force kernel to refresh partition table");
445 }
446}
447
448void GPT_to_UTF16(uint16_t *to, const char *from, int n)
449{
450 int i;
451 for (i = 0; i < (n - 1) && (to[i] = from[i]) != '\0'; ++i);
452 to[i] = '\0';
453}
454
455void GPT_from_UTF16(char *to, const uint16_t *from, int n)
456{
457 int i;
458 for (i = 0; i < (n - 1) && (to[i] = from[i] & 127) != '\0'; ++i);
459 to[i] = '\0';
460}
461
462static int GPT_check_overlap_except(struct GPT_entry_table *table,
463 struct GPT_entry_raw *entry,
464 struct GPT_entry_raw *exclude) {
465 int current = (int) table->header->entries_count;
466 int dontcheck;
467 struct GPT_entry_raw *current_entry;
468 if (entry->last_lba < entry->first_lba) {
469 D(WARN, "Start address have to be less than end address");
470 return 1;
471 }
472
473 for (current = current - 1; current >= 0; --current) {
474 current_entry = &table->entries[current];
475 dontcheck = strncmp((char *) entry->partition_guid,
476 (char *) current_entry->partition_guid , 16) == 0;
477 dontcheck |= current_entry->type_guid[0] == 0;
478 dontcheck |= current_entry == exclude;
479
480 if (!dontcheck && ((entry->last_lba >= current_entry->first_lba &&
481 entry->first_lba < current_entry->last_lba ))) {
482 return 1;
483 }
484 }
485
486 return 0;
487}
488
489static int GPT_check_overlap(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
490{
491 return GPT_check_overlap_except(table, entry, NULL);
492}
493
494static char *get_key_value(char *ptr, char **key, char **value)
495{
496 *key = ptr;
497 ptr = strchr(ptr, '=');
498
499 if (ptr == NULL)
500 return NULL;
501
502 *ptr++ = '\0';
503 *value = ptr;
504 ptr = strchr(ptr, ';');
505
506 if (ptr == NULL)
507 ptr = *value + strlen(*value);
508 else
509 *ptr = '\0';
510
511 *key = strip(*key);
512 *value = strip(*value);
513
514 return ptr;
515}
516
517//TODO: little endian?
518static int add_key_value(const char *key, const char *value, struct GPT_entry_raw *entry)
519{
520 char *endptr;
521 if (!strcmp(key, "type")) {
522 strncpy((char *) entry->type_guid, value, 16);
523 entry->type_guid[15] = 0;
524 }
525 else if (!strcmp(key, "guid")) {
526 strncpy((char *) entry->partition_guid, value, 16);
527 entry->type_guid[15] = 0;
528 }
529 else if (!strcmp(key, "firstlba")) {
530 entry->first_lba = strtoul(value, &endptr, 10);
531 if (*endptr != '\0') goto error;
532 }
533 else if (!strcmp(key, "lastlba")) {
534 entry->last_lba = strtoul(value, &endptr, 10);
535 if (*endptr != '\0') goto error;
536 }
537 else if (!strcmp(key, "flags")) {
538 entry->flags = strtoul(value, &endptr, 10);
539 if (*endptr != '\0') goto error;
540 }
541 else if (!strcmp(key, "name")) {
542 GPT_to_UTF16(entry->name, value, GPT_NAMELEN);
543 }
544 else {
545 goto error;
546 }
547
548 return 0;
549
550error:
551 D(ERR, "Could not find key or parse value: %s,%s", key, value);
552 return 1;
553}
554
555int GPT_parse_entry(char *string, struct GPT_entry_raw *entry)
556{
557 char *ptr = string;
558 char *key, *value;
559
560 while ((ptr = get_key_value(ptr, &key, &value)) != NULL) {
561 if (add_key_value(key, value, entry)) {
562 D(WARN, "key or value not valid: %s %s", key, value);
563 return 1;
564 }
565 }
566
567 return 0;
568}
569
570static void entry_set_guid(int n, uint8_t *guid)
571{
572 guid[0] = (uint8_t) (n + 1);
573 int fd;
574 fd = open("/dev/urandom", O_RDONLY);
575 read(fd, &guid[1], 15);
576 close(fd);
577}
578
579void GPT_default_content(struct GPT_content *content, struct GPT_entry_table *table)
580{
581 if (table != NULL) {
582 memcpy(&content->header, table->header, sizeof(content->header));
583 }
584 else {
585 D(WARN, "Could not locate old gpt table, using default values");
586 memset(&content->header, 0, sizeof(content->header) / sizeof(int));
587 content->header = (struct GPT_header) {
588 .revision = 0x0100,
589 .header_size = sizeof(content->header),
590 .header_checksum = 0,
591 .reserved_zeros = 0,
592 .current_lba = 1,
593 .backup_lba = 1,
594 .entry_size = sizeof(struct GPT_entry_raw),
595 .partition_array_checksum = 0
596 };
597 strncpy((char *)content->header.signature, "EFI PART", 8);
598 entry_set_guid(0, content->header.disk_guid);
599 }
600}
601
602static int get_config_uint64(cnode *node, uint64_t *ptr, const char *name)
603{
604 const char *tmp;
605 uint64_t val;
606 char *endptr;
607 if ((tmp = config_str(node, name, NULL))) {
608 val = strtoull(tmp, &endptr, 10);
609 if (*endptr != '\0') {
610 D(WARN, "Value for %s is not a number: %s", name, tmp);
611 return 1;
612 }
613 *ptr = val;
614 return 0;
615 }
616 return 1;
617}
618
619static void GPT_parse_header(cnode *node, struct GPT_content *content)
620{
621 get_config_uint64(node, &content->header.current_lba, "header_lba");
622 get_config_uint64(node, &content->header.backup_lba, "backup_lba");
623 get_config_uint64(node, &content->header.first_usable_lba, "first_lba");
624 get_config_uint64(node, &content->header.last_usable_lba, "last_lba");
625 get_config_uint64(node, &content->header.entries_lba, "entries_lba");
626}
627
628static int GPT_parse_partitions(cnode *node, struct GPT_content *content)
629{
630 cnode *current;
631 int i;
632 uint64_t partition_size;
633 for (i = 0, current = node->first_child; current; current = current->next, ++i) {
634 entry_set_guid(i, content->entries[i].partition_guid);
635 memcpy(&content->entries[i].type_guid, partition_type_uuid, 16);
636 if (get_config_uint64(current, &content->entries[i].first_lba, "first_lba")) {
637 D(ERR, "first_lba not specified");
638 return 1;
639 }
640 if (get_config_uint64(current, &partition_size, "partition_size")) {
641 D(ERR, "partition_size not specified");
642 return 1;
643 }
644 get_config_uint64(current, &content->entries[i].flags, "flags");
645 content->entries[i].last_lba = content->entries[i].first_lba + partition_size - 1;
646 GPT_to_UTF16(content->entries[i].name, current->name, 16);
647 }
648 return 0;
649}
650
651static inline int cnode_count(cnode *node)
652{
653 int i;
654 cnode *current;
655 for (i = 0, current = node->first_child; current; current = current->next, ++i)
656 ;
657 return i;
658}
659
660
661static int GPT_parse_cnode(cnode *root, struct GPT_content *content)
662{
663 cnode *partnode;
664
665 if (!(partnode = config_find(root, "partitions"))) {
666 D(ERR, "Could not find partition table");
667 return 0;
668 }
669
670 GPT_parse_header(root, content);
671
672 content->header.entries_count = cnode_count(partnode);
673 content->entries = malloc(content->header.entries_count * sizeof(struct GPT_entry_raw));
674
675 if (GPT_parse_partitions(partnode, content)) {
676 D(ERR, "Could not parse partitions");
677 return 0;
678 }
679
680 return 1;
681}
682
683int GPT_parse_file(int fd, struct GPT_content *content)
684{
685 char *data;
686 int size;
687 int ret;
688 cnode *root = config_node("", "");
689
690 size = get_file_size(fd);
691 data = (char *) mmap(NULL, size + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
692
693 if (data == NULL) {
694 if (size == 0)
695 D(ERR, "config file empty");
696 else
697 D(ERR, "Out of memory");
698 return 0;
699 }
700
701 data[size - 1] = 0;
702 config_load(root, data);
703
704 if (root->first_child == NULL) {
705 D(ERR, "Could not read config file");
706 return 0;
707 }
708
709 ret = GPT_parse_cnode(root, content);
710 munmap(data, size);
711 return ret;
712}
713
714void GPT_release_content(struct GPT_content *content)
715{
716 free(content->entries);
717}
718
719int GPT_write_content(const char *device, struct GPT_content *content)
720{
721 struct GPT_entry_table *maptable;
722
723 maptable = GPT_get_from_content(device, content);
724 if (maptable == NULL) {
725 D(ERR, "could not map device");
726 return 0;
727 }
728
729 memcpy(maptable->header, &content->header, sizeof(*maptable->header));
730 memcpy(maptable->entries, content->entries,
731 content->header.entries_count * sizeof(*maptable->entries));
732
733 //GPT_sync(maptable);
734 GPT_release_device(maptable);
735
736 return 1;
737}
738