Home | History | Annotate | Download | only in commands
      1 /*
      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 #include <inttypes.h>
     46 
     47 #include "partitions.h"
     48 #include "debug.h"
     49 #include "utils.h"
     50 #include "protocol.h"
     51 
     52 #define BLKRRPART  _IO(0x12,95) /* re-read partition table */
     53 #define BLKSSZGET  _IO(0x12,104)
     54 
     55 #define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
     56 #define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
     57 #define ALIGN_DOWN(x, y) ((y) * ((x) / (y)))
     58 
     59 
     60 const uint8_t partition_type_uuid[16] = {
     61     0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44,
     62     0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7,
     63 };
     64 
     65 //TODO: There is assumption that we are using little endian
     66 
     67 static void GPT_entry_clear(struct GPT_entry_raw *entry)
     68 {
     69     memset(entry, 0, sizeof(*entry));
     70 }
     71 
     72 /*
     73  * returns mapped location to choosen area
     74  * mapped_ptr is pointer to whole area mapped (it can be bigger then requested)
     75  */
     76 int gpt_mmap(struct GPT_mapping *mapping, uint64_t location, int size, int fd)
     77 {
     78     unsigned int location_diff = location & ~PAGE_MASK;
     79 
     80     mapping->size = ALIGN(size + location_diff, PAGE_SIZE);
     81 
     82     uint64_t sz = get_file_size64(fd);
     83     if (sz < size + location) {
     84         D(ERR, "the location of mapping area is outside of the device size %" PRId64, sz);
     85         return 1;
     86     }
     87     location = ALIGN_DOWN(location, PAGE_SIZE);
     88 
     89     mapping->map_ptr = mmap64(NULL, mapping->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, location);
     90 
     91     if (mapping->map_ptr == MAP_FAILED) {
     92         mapping->ptr = MAP_FAILED;
     93         D(ERR, "map failed: %s", strerror(errno));
     94         return 1;
     95     }
     96 
     97     mapping->ptr = (void *)((char *) mapping->map_ptr + location_diff);
     98     return 0;
     99 }
    100 
    101 void gpt_unmap(struct GPT_mapping *mapping) {
    102     munmap(mapping->map_ptr, mapping->size);
    103 }
    104 
    105 
    106 #define LBA_ADDR(table, value)   ((uint64_t) (table)->sector_size * (value))
    107 
    108 int GPT_map_from_content(struct GPT_entry_table *table, const struct GPT_content *content)
    109 {
    110 
    111     // Mapping header
    112     if (gpt_mmap(&table->header_map, LBA_ADDR(table, content->header.current_lba),
    113                  table->sector_size, table->fd)) {
    114         D(ERR, "unable to map header:%s\n", strerror(errno));
    115         goto error_header;
    116     }
    117 
    118     table->header = (struct GPT_header *) table->header_map.ptr;
    119 
    120     table->partition_table_size = ROUND_UP(content->header.entries_count * sizeof(*table->entries),
    121                                            table->sector_size);
    122 
    123     // Mapping entry table
    124     if (gpt_mmap(&table->entries_map, LBA_ADDR(table, content->header.entries_lba),
    125                  table->partition_table_size, table->fd)) {
    126         D(ERR, "unable to map entries");
    127         goto error_signature;
    128     }
    129 
    130     table->entries = (struct GPT_entry_raw *) table->entries_map.ptr;
    131 
    132     // Mapping secondary header
    133     if (gpt_mmap(&table->sec_header_map, LBA_ADDR(table, content->header.backup_lba),
    134                  table->sector_size, table->fd)) {
    135         D(ERR, "unable to map backup gpt header");
    136         goto error_sec_header;
    137     }
    138 
    139     // Mapping secondary entries table
    140     if (gpt_mmap(&table->sec_entries_map,
    141                  LBA_ADDR(table, content->header.backup_lba) - table->partition_table_size,
    142                  table->partition_table_size, table->fd)) {
    143         D(ERR, "unable to map secondary gpt table");
    144         goto error_sec_entries;
    145     }
    146 
    147     table->second_header = (struct GPT_header *) table->sec_header_map.ptr;
    148     table->second_entries = (struct GPT_entry_raw *) table->sec_entries_map.ptr;
    149     table->second_valid = strcmp("EFI PART", (char *) table->second_header->signature) == 0;
    150 
    151     return 0;
    152 
    153 error_sec_entries:
    154     gpt_unmap(&table->sec_header_map);
    155 error_sec_header:
    156     gpt_unmap(&table->entries_map);
    157 error_signature:
    158     gpt_unmap(&table->header_map);
    159 error_header:
    160     return 1;
    161 }
    162 
    163 int GPT_map(struct GPT_entry_table *table, unsigned header_lba)
    164 {
    165     struct GPT_content content;
    166     struct GPT_mapping mapping;
    167     struct GPT_header *header;
    168 
    169     if (gpt_mmap(&mapping, LBA_ADDR(table, header_lba), table->sector_size, table->fd)) {
    170         D(ERR, "unable to map header: %s", strerror(errno));
    171         goto error_header;
    172     }
    173 
    174     header = (struct GPT_header *) mapping.ptr;
    175 
    176     if (strcmp("EFI PART", (char *) header->signature)) {
    177         D(ERR, "GPT entry not valid");
    178         goto error_signature;
    179     }
    180 
    181     content.header = *header;
    182 
    183     gpt_unmap(&mapping);
    184 
    185     return GPT_map_from_content(table, &content);
    186 
    187 error_signature:
    188     gpt_unmap(&table->header_map);
    189 error_header:
    190     return 1;
    191 }
    192 
    193 struct GPT_entry_table* GPT_get_device(const char *path, unsigned header_lba)
    194 {
    195     struct GPT_entry_table *table;
    196     size_t sector_bytes;
    197 
    198     table = (struct GPT_entry_table *) malloc(sizeof(*table));
    199     table->fd = open(path, O_RDWR);
    200 
    201     if (table->fd < 0) {
    202         D(ERR, "unable to open file %s:%s\n", path, strerror(errno));
    203         return NULL;
    204     }
    205 
    206     if (!ioctl(table->fd, BLKSSZGET, &sector_bytes)) {
    207         table->sector_size = (unsigned) sector_bytes;
    208         D(INFO, "Got sector size %d", table->sector_size);
    209     } else {
    210         D(WARN, "unable to get sector size, assuming 512");
    211         table->sector_size = 512;
    212     }
    213 
    214     if (GPT_map(table, header_lba)) {
    215         D(ERR, "Could not map gpt");
    216         return NULL;
    217     }
    218 
    219     return table;
    220 }
    221 
    222 static struct GPT_entry_table* GPT_get_from_content(const char *path, const struct GPT_content *content)
    223 {
    224     struct GPT_entry_table *table;
    225     size_t sector_bytes;
    226 
    227     table = (struct GPT_entry_table *) malloc(sizeof(*table));
    228     table->fd = open(path, O_RDWR);
    229 
    230     if (table->fd < 0) {
    231         D(ERR, "unable to open file %s:%s\n", path, strerror(errno));
    232         return NULL;
    233     }
    234 
    235     if (!ioctl(table->fd, BLKSSZGET, &sector_bytes)) {
    236         table->sector_size = (unsigned) sector_bytes;
    237         D(INFO, "Got sector size %d", table->sector_size);
    238     } else {
    239         D(WARN, "unable to get sector size %s, assuming 512", strerror(errno));
    240         table->sector_size = 512;
    241     }
    242 
    243     if (GPT_map_from_content(table, content)) {
    244         D(ERR, "Could not map gpt");
    245         return NULL;
    246     }
    247 
    248     return table;
    249 }
    250 
    251 
    252 void GPT_release_device(struct GPT_entry_table *table)
    253 {
    254     gpt_unmap(&table->header_map);
    255     gpt_unmap(&table->entries_map);
    256     gpt_unmap(&table->sec_header_map);
    257     gpt_unmap(&table->sec_entries_map);
    258     close(table->fd);
    259     free(table);
    260 }
    261 
    262 static int GPT_check_overlap(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
    263 static int GPT_check_overlap_except(struct GPT_entry_table *table,
    264                                     struct GPT_entry_raw *entry,
    265                                     struct GPT_entry_raw *exclude);
    266 
    267 void GPT_edit_entry(struct GPT_entry_table *table,
    268                     struct GPT_entry_raw *old_entry,
    269                     struct GPT_entry_raw *new_entry)
    270 {
    271     struct GPT_entry_raw *current_entry = GPT_get_pointer(table, old_entry);
    272 
    273     if (GPT_check_overlap_except(table, new_entry, current_entry)) {
    274         D(ERR, "Couldn't add overlaping partition");
    275         return;
    276     }
    277 
    278     if (current_entry == NULL) {
    279         D(ERR, "Couldn't find entry");
    280         return;
    281     }
    282 
    283     *current_entry = *new_entry;
    284 }
    285 
    286 int GPT_delete_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
    287 {
    288     struct GPT_entry_raw *raw = GPT_get_pointer(table, entry);
    289 
    290     if (raw == NULL) {
    291         D(ERR, "could not find entry");
    292         return 1;
    293     }
    294     D(DEBUG, "Deleting gpt entry '%s'\n", raw->partition_guid);
    295 
    296     // Entry in the middle of table may become empty
    297     GPT_entry_clear(raw);
    298 
    299     return 0;
    300 }
    301 
    302 void GPT_add_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
    303 {
    304     unsigned i;
    305     int inserted = 0;
    306     if (GPT_check_overlap(table, entry)) {
    307         D(ERR, "Couldn't add overlaping partition");
    308         return;
    309     }
    310 
    311     if (GPT_get_pointer(table, entry) != NULL) {
    312         D(WARN, "Add entry fault, this entry already exists");
    313         return;
    314     }
    315 
    316     struct GPT_entry_raw *entries = table->entries;
    317 
    318     for (i = 0; i < table->header->entries_count; ++i) {
    319         if (!entries[i].type_guid[0]) {
    320             inserted = 1;
    321             D(DEBUG, "inserting");
    322             memcpy(&entries[i], entry, sizeof(entries[i]));
    323             break;
    324         }
    325     }
    326 
    327     if (!inserted) {
    328         D(ERR, "Unable to find empty partion entry");
    329     }
    330 }
    331 
    332 struct GPT_entry_raw *GPT_get_pointer_by_UTFname(struct GPT_entry_table *table, const uint16_t *name);
    333 
    334 struct GPT_entry_raw *GPT_get_pointer(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
    335 {
    336     if (entry->partition_guid[0] != 0)
    337         return GPT_get_pointer_by_guid(table, (const char *) entry->partition_guid);
    338     else if (entry->name[0] != 0)
    339         return GPT_get_pointer_by_UTFname(table, entry->name);
    340 
    341     D(WARN, "Name or guid needed to find entry");
    342     return NULL;
    343 }
    344 
    345 struct GPT_entry_raw *GPT_get_pointer_by_guid(struct GPT_entry_table *table, const char *name)
    346 {
    347     int current = (int) table->header->entries_count;
    348 
    349     for (current = current - 1; current >= 0; --current) {
    350         if (strncmp((char *) name,
    351                     (char *) table->entries[current].partition_guid, 16) == 0) {
    352                 return &table->entries[current];
    353         }
    354     }
    355 
    356     return NULL;
    357 }
    358 
    359 int strncmp_UTF16_char(const uint16_t *s1, const char *s2, size_t n)
    360 {
    361     if (n == 0)
    362         return (0);
    363     do {
    364         if (((*s1) & 127) != *s2++)
    365             return (((unsigned char) ((*s1) & 127)) - *(unsigned char *)--s2);
    366         if (*s1++ == 0)
    367             break;
    368     } while (--n != 0);
    369     return (0);
    370 }
    371 
    372 int strncmp_UTF16(const uint16_t *s1, const uint16_t *s2, size_t n)
    373 {
    374     if (n == 0)
    375         return (0);
    376     do {
    377         if ((*s1) != *s2++)
    378             return (*s1 - *--s2);
    379         if (*s1++ == 0)
    380             break;
    381     } while (--n != 0);
    382     return (0);
    383 }
    384 
    385 struct GPT_entry_raw *GPT_get_pointer_by_name(struct GPT_entry_table *table, const char *name)
    386 {
    387     int count = (int) table->header->entries_count;
    388     int current;
    389 
    390     for (current = 0; current < count; ++current) {
    391         if (strncmp_UTF16_char(table->entries[current].name,
    392                          (char *) name, 16) == 0) {
    393                     return &table->entries[current];
    394         }
    395     }
    396 
    397     return NULL;
    398 }
    399 
    400 struct GPT_entry_raw *GPT_get_pointer_by_UTFname(struct GPT_entry_table *table, const uint16_t *name)
    401 {
    402     int count = (int) table->header->entries_count;
    403     int current;
    404 
    405     for (current = 0; current < count; ++current) {
    406         if (strncmp_UTF16(table->entries[current].name,
    407                           name, GPT_NAMELEN) == 0) {
    408                 return &table->entries[current];
    409         }
    410     }
    411 
    412     return NULL;
    413 }
    414 
    415 void GPT_sync(struct GPT_entry_table *table)
    416 {
    417     uint32_t crc;
    418 
    419     //calculate crc32
    420     crc = crc32(0, Z_NULL, 0);
    421     crc = crc32(crc, (void*) table->entries, table->header->entries_count * sizeof(*table->entries));
    422     table->header->partition_array_checksum = crc;
    423 
    424     table->header->header_checksum = 0;
    425     crc = crc32(0, Z_NULL, 0);
    426     crc = crc32(crc, (void*) table->header, table->header->header_size);
    427     table->header->header_checksum = crc;
    428 
    429     //sync secondary partion
    430     if (table->second_valid) {
    431         memcpy((void *)table->second_entries, (void *) table->entries, table->partition_table_size);
    432         memcpy((void *)table->second_header, (void *)table->header, sizeof(*table->header));
    433     }
    434 
    435     if(!ioctl(table->fd, BLKRRPART, NULL)) {
    436         D(WARN, "Unable to force kernel to refresh partition table");
    437     }
    438 }
    439 
    440 void GPT_to_UTF16(uint16_t *to, const char *from, int n)
    441 {
    442     int i;
    443     for (i = 0; i < (n - 1) && (to[i] = from[i]) != '\0'; ++i);
    444     to[i] = '\0';
    445 }
    446 
    447 void GPT_from_UTF16(char *to, const uint16_t *from, int n)
    448 {
    449     int i;
    450     for (i = 0; i < (n - 1) && (to[i] = from[i] & 127) != '\0'; ++i);
    451     to[i] = '\0';
    452 }
    453 
    454 static int GPT_check_overlap_except(struct GPT_entry_table *table,
    455                                     struct GPT_entry_raw *entry,
    456                                     struct GPT_entry_raw *exclude) {
    457     int current = (int) table->header->entries_count;
    458     int dontcheck;
    459     struct GPT_entry_raw *current_entry;
    460     if (entry->last_lba < entry->first_lba) {
    461         D(WARN, "Start address have to be less than end address");
    462         return 1;
    463     }
    464 
    465     for (current = current - 1; current >= 0; --current) {
    466         current_entry = &table->entries[current];
    467         dontcheck = strncmp((char *) entry->partition_guid,
    468                            (char *) current_entry->partition_guid , 16) == 0;
    469         dontcheck |= current_entry->type_guid[0] == 0;
    470         dontcheck |= current_entry == exclude;
    471 
    472         if (!dontcheck && ((entry->last_lba >= current_entry->first_lba &&
    473                             entry->first_lba < current_entry->last_lba ))) {
    474             return 1;
    475         }
    476     }
    477 
    478     return 0;
    479 }
    480 
    481 static int GPT_check_overlap(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
    482 {
    483     return GPT_check_overlap_except(table, entry, NULL);
    484 }
    485 
    486 static char *get_key_value(char *ptr, char **key, char **value)
    487 {
    488     *key = ptr;
    489     ptr = strchr(ptr, '=');
    490 
    491     if (ptr == NULL)
    492         return NULL;
    493 
    494     *ptr++ = '\0';
    495     *value = ptr;
    496     ptr = strchr(ptr, ';');
    497 
    498     if (ptr == NULL)
    499         ptr = *value + strlen(*value);
    500     else
    501         *ptr = '\0';
    502 
    503     *key = strip(*key);
    504     *value = strip(*value);
    505 
    506     return ptr;
    507 }
    508 
    509 //TODO: little endian?
    510 static int add_key_value(const char *key, const char *value, struct GPT_entry_raw *entry)
    511 {
    512     char *endptr;
    513     if (!strcmp(key, "type")) {
    514         strncpy((char *) entry->type_guid, value, 16);
    515         entry->type_guid[15] = 0;
    516     }
    517     else if (!strcmp(key, "guid")) {
    518         strncpy((char *) entry->partition_guid, value, 16);
    519         entry->type_guid[15] = 0;
    520     }
    521     else if (!strcmp(key, "firstlba")) {
    522         entry->first_lba = strtoul(value, &endptr, 10);
    523         if (*endptr != '\0') goto error;
    524     }
    525     else if (!strcmp(key, "lastlba")) {
    526         entry->last_lba = strtoul(value, &endptr, 10);
    527         if (*endptr != '\0') goto error;
    528     }
    529     else if (!strcmp(key, "flags")) {
    530         entry->flags = strtoul(value, &endptr, 16);
    531         if (*endptr != '\0') goto error;
    532     }
    533     else if (!strcmp(key, "name")) {
    534         GPT_to_UTF16(entry->name, value, GPT_NAMELEN);
    535     }
    536     else {
    537         goto error;
    538     }
    539 
    540     return 0;
    541 
    542 error:
    543     D(ERR, "Could not find key or parse value: %s,%s", key, value);
    544     return 1;
    545 }
    546 
    547 int GPT_parse_entry(char *string, struct GPT_entry_raw *entry)
    548 {
    549     char *ptr = string;
    550     char *key, *value;
    551 
    552     while ((ptr = get_key_value(ptr, &key, &value)) != NULL) {
    553         if (add_key_value(key, value, entry)) {
    554             D(WARN, "key or value not valid: %s %s", key, value);
    555             return 1;
    556         }
    557     }
    558 
    559     return 0;
    560 }
    561 
    562 void entry_set_guid(int n, uint8_t *guid)
    563 {
    564     int fd;
    565     fd = open("/dev/urandom", O_RDONLY);
    566     read(fd, guid, 16);
    567     close(fd);
    568 
    569     //rfc4122
    570     guid[8] = (guid[8] & 0x3F) | 0x80;
    571     guid[7] = (guid[7] & 0x0F) | 0x40;
    572 }
    573 
    574 void GPT_default_content(struct GPT_content *content, struct GPT_entry_table *table)
    575 {
    576     if (table != NULL) {
    577         memcpy(&content->header, table->header, sizeof(content->header));
    578         content->header.header_size = sizeof(content->header);
    579         content->header.entry_size = sizeof(struct GPT_entry_raw);
    580     }
    581     else {
    582         D(WARN, "Could not locate old gpt table, using default values");
    583         memset(&content->header, 0, sizeof(content->header) / sizeof(int));
    584         content->header = (struct GPT_header) {
    585             .revision = 0x10000,
    586             .header_size = sizeof(content->header),
    587             .header_checksum = 0,
    588             .reserved_zeros = 0,
    589             .current_lba = 1,
    590             .backup_lba = 1,
    591             .entry_size = sizeof(struct GPT_entry_raw),
    592             .partition_array_checksum = 0
    593         };
    594         strncpy((char *)content->header.signature, "EFI PART", 8);
    595         strncpy((char *)content->header.disk_guid, "ANDROID MMC DISK", 16);
    596     }
    597 }
    598 
    599 static int get_config_uint64(cnode *node, uint64_t *ptr, const char *name)
    600 {
    601     const char *tmp;
    602     uint64_t val;
    603     char *endptr;
    604     if ((tmp = config_str(node, name, NULL))) {
    605         val = strtoull(tmp, &endptr, 10);
    606         if (*endptr != '\0') {
    607             D(WARN, "Value for %s is not a number: %s", name, tmp);
    608             return 1;
    609         }
    610         *ptr = val;
    611         return 0;
    612     }
    613     return 1;
    614 }
    615 
    616 static int get_config_string(cnode *node, char *ptr, int max_len, const char *name)
    617 {
    618     size_t begin, end;
    619     const char *value = config_str(node, name, NULL);
    620     if (!value)
    621         return -1;
    622 
    623     begin = strcspn(value, "\"") + 1;
    624     end = strcspn(&value[begin], "\"");
    625 
    626     if ((int) end > max_len) {
    627         D(WARN, "Identifier \"%s\" too long", value);
    628         return -1;
    629     }
    630 
    631     strncpy(ptr, &value[begin], end);
    632     if((int) end < max_len)
    633         ptr[end] = 0;
    634     return 0;
    635 }
    636 
    637 static void GPT_parse_header(cnode *node, struct GPT_content *content)
    638 {
    639     get_config_uint64(node, &content->header.current_lba, "header_lba");
    640     get_config_uint64(node, &content->header.backup_lba, "backup_lba");
    641     get_config_uint64(node, &content->header.first_usable_lba, "first_lba");
    642     get_config_uint64(node, &content->header.last_usable_lba, "last_lba");
    643     get_config_uint64(node, &content->header.entries_lba, "entries_lba");
    644     get_config_string(node, (char *) content->header.disk_guid, 16, "guid");
    645 }
    646 
    647 static int GPT_parse_partitions(cnode *node, struct GPT_content *content)
    648 {
    649     cnode *current;
    650     int i;
    651     uint64_t partition_size;
    652     struct GPT_entry_raw *entry;
    653     for (i = 0, current = node->first_child; current; current = current->next, ++i) {
    654         entry = &content->entries[i];
    655         entry_set_guid(i, content->entries[i].partition_guid);
    656         memcpy(&content->entries[i].type_guid, partition_type_uuid, 16);
    657         if (get_config_uint64(current, &entry->first_lba, "first_lba")) {
    658             D(ERR, "first_lba not specified");
    659             return 1;
    660         }
    661         if (get_config_uint64(current, &partition_size, "partition_size")) {
    662             D(ERR, "partition_size not specified");
    663             return 1;
    664         }
    665         if (config_str(current, "system", NULL)) {
    666             entry->flags |= GPT_FLAG_SYSTEM;
    667         }
    668         if (config_str(current, "bootable", NULL)) {
    669             entry->flags |= GPT_FLAG_BOOTABLE;
    670         }
    671         if (config_str(current, "readonly", NULL)) {
    672             entry->flags |= GPT_FLAG_READONLY;
    673         }
    674         if (config_str(current, "automount", NULL)) {
    675             entry->flags |= GPT_FLAG_DOAUTOMOUNT;
    676         }
    677 
    678         get_config_uint64(current, &content->entries[i].flags, "flags");
    679         content->entries[i].last_lba = content->entries[i].first_lba + partition_size - 1;
    680         GPT_to_UTF16(content->entries[i].name, current->name, 16);
    681     }
    682     return 0;
    683 }
    684 
    685 static inline int cnode_count(cnode *node)
    686 {
    687     int i;
    688     cnode *current;
    689     for (i = 0, current = node->first_child; current; current = current->next, ++i)
    690         ;
    691     return i;
    692 }
    693 
    694 
    695 static int GPT_parse_cnode(cnode *root, struct GPT_content *content)
    696 {
    697     cnode *partnode;
    698 
    699     if (!(partnode = config_find(root, "partitions"))) {
    700         D(ERR, "Could not find partition table");
    701         return 0;
    702     }
    703 
    704     GPT_parse_header(root, content);
    705 
    706     content->header.entries_count = cnode_count(partnode);
    707     content->entries = malloc(content->header.entries_count * sizeof(struct GPT_entry_raw));
    708 
    709     if (GPT_parse_partitions(partnode, content)) {
    710         D(ERR, "Could not parse partitions");
    711         return 0;
    712     }
    713 
    714     return 1;
    715 }
    716 
    717 int GPT_parse_file(int fd, struct GPT_content *content)
    718 {
    719     char *data;
    720     int size;
    721     int ret;
    722     cnode *root = config_node("", "");
    723 
    724     size = get_file_size(fd);
    725     data = (char *) mmap(NULL, size + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
    726 
    727     if (data == NULL) {
    728         if (size == 0)
    729             D(ERR, "config file empty");
    730         else
    731             D(ERR, "Out of memory");
    732         return 0;
    733     }
    734 
    735     data[size - 1] = 0;
    736     config_load(root, data);
    737 
    738     if (root->first_child == NULL) {
    739         D(ERR, "Could not read config file");
    740         return 0;
    741     }
    742 
    743     ret = GPT_parse_cnode(root, content);
    744     munmap(data, size);
    745     return ret;
    746 }
    747 
    748 void GPT_release_content(struct GPT_content *content)
    749 {
    750     free(content->entries);
    751 }
    752 
    753 int GPT_write_content(const char *device, struct GPT_content *content)
    754 {
    755     struct GPT_entry_table *maptable;
    756 
    757     maptable = GPT_get_from_content(device, content);
    758     if (maptable == NULL) {
    759         D(ERR, "could not map device");
    760         return 0;
    761     }
    762 
    763     memcpy(maptable->header, &content->header, sizeof(*maptable->header));
    764     memcpy(maptable->entries, content->entries,
    765            content->header.entries_count * sizeof(*maptable->entries));
    766 
    767     GPT_sync(maptable);
    768     GPT_release_device(maptable);
    769 
    770     return 1;
    771 }
    772 
    773