/* ** Copyright 2011, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <unistd.h> #include <zlib.h> #include <linux/fs.h> typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long long u64; const u8 partition_type_uuid[16] = { 0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44, 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7, }; #define EFI_VERSION 0x00010000 #define EFI_MAGIC "EFI PART" #define EFI_ENTRIES 128 #define EFI_NAMELEN 36 struct efi_header { u8 magic[8]; u32 version; u32 header_sz; u32 crc32; u32 reserved; u64 header_lba; u64 backup_lba; u64 first_lba; u64 last_lba; u8 volume_uuid[16]; u64 entries_lba; u32 entries_count; u32 entries_size; u32 entries_crc32; } __attribute__((packed)); struct efi_entry { u8 type_uuid[16]; u8 uniq_uuid[16]; u64 first_lba; u64 last_lba; u64 attr; u16 name[EFI_NAMELEN]; }; struct ptable { u8 mbr[512]; union { struct efi_header header; u8 block[512]; }; struct efi_entry entry[EFI_ENTRIES]; }; void get_uuid(u8 *uuid) { int fd; fd = open("/dev/urandom", O_RDONLY); read(fd, uuid, 16); close(fd); } void init_mbr(u8 *mbr, u32 blocks) { mbr[0x1be] = 0x00; // nonbootable mbr[0x1bf] = 0xFF; // bogus CHS mbr[0x1c0] = 0xFF; mbr[0x1c1] = 0xFF; mbr[0x1c2] = 0xEE; // GPT partition mbr[0x1c3] = 0xFF; // bogus CHS mbr[0x1c4] = 0xFF; mbr[0x1c5] = 0xFF; mbr[0x1c6] = 0x01; // start mbr[0x1c7] = 0x00; mbr[0x1c8] = 0x00; mbr[0x1c9] = 0x00; memcpy(mbr + 0x1ca, &blocks, sizeof(u32)); mbr[0x1fe] = 0x55; mbr[0x1ff] = 0xaa; } int add_ptn(struct ptable *ptbl, u64 first, u64 last, const char *name) { struct efi_header *hdr = &ptbl->header; struct efi_entry *entry = ptbl->entry; unsigned n; if (first < 34) { fprintf(stderr,"partition '%s' overlaps partition table\n", name); return -1; } if (last > hdr->last_lba) { fprintf(stderr,"partition '%s' does not fit on disk\n", name); return -1; } for (n = 0; n < EFI_ENTRIES; n++, entry++) { if (entry->type_uuid[0]) continue; memcpy(entry->type_uuid, partition_type_uuid, 16); get_uuid(entry->uniq_uuid); entry->first_lba = first; entry->last_lba = last; for (n = 0; (n < EFI_NAMELEN) && *name; n++) entry->name[n] = *name++; return 0; } fprintf(stderr,"out of partition table entries\n"); return -1; } int usage(void) { fprintf(stderr, "usage: gpttool write <disk> [ <partition> ]*\n" " gpttool read <disk>\n" " gpttool test [ <partition> ]*\n" "\n" "partition: [<name>]:<size>[kmg] | @<file-of-partitions>\n" ); return 0; } void show(struct ptable *ptbl) { struct efi_entry *entry = ptbl->entry; unsigned n, m; char name[EFI_NAMELEN + 1]; fprintf(stderr,"ptn start block end block name\n"); fprintf(stderr,"---- ------------- ------------- --------------------\n"); for (n = 0; n < EFI_ENTRIES; n++, entry++) { if (entry->type_uuid[0] == 0) break; for (m = 0; m < EFI_NAMELEN; m++) { name[m] = entry->name[m] & 127; } name[m] = 0; fprintf(stderr,"#%03d %13lld %13lld %s\n", n + 1, entry->first_lba, entry->last_lba, name); } } u64 find_next_lba(struct ptable *ptbl) { struct efi_entry *entry = ptbl->entry; unsigned n; u64 a = 0; for (n = 0; n < EFI_ENTRIES; n++, entry++) { if ((entry->last_lba + 1) > a) a = entry->last_lba + 1; } return a; } u64 next_lba = 0; u64 parse_size(char *sz) { int l = strlen(sz); u64 n = strtoull(sz, 0, 10); if (l) { switch(sz[l-1]){ case 'k': case 'K': n *= 1024; break; case 'm': case 'M': n *= (1024 * 1024); break; case 'g': case 'G': n *= (1024 * 1024 * 1024); break; } } return n; } int parse_ptn(struct ptable *ptbl, char *x) { char *y = strchr(x, ':'); u64 sz; if (!y) { fprintf(stderr,"invalid partition entry: %s\n", x); return -1; } *y++ = 0; if (*y == 0) { sz = ptbl->header.last_lba - next_lba; } else { sz = parse_size(y); if (sz & 511) { fprintf(stderr,"partition size must be multiple of 512\n"); return -1; } sz /= 512; } if (sz == 0) { fprintf(stderr,"zero size partitions not allowed\n"); return -1; } if (x[0] && add_ptn(ptbl, next_lba, next_lba + sz - 1, x)) return -1; next_lba = next_lba + sz; return 0; } int main(int argc, char **argv) { struct ptable ptbl; struct efi_header *hdr = &ptbl.header; u32 n; u64 sz; int fd; const char *device; int real_disk = 0; if (argc < 2) return usage(); if (!strcmp(argv[1], "write")) { if (argc < 3) return usage(); device = argv[2]; argc -= 2; argv += 2; real_disk = 1; } else if (!strcmp(argv[1], "test")) { argc -= 1; argv += 1; real_disk = 0; sz = 2097152 * 16; fprintf(stderr,"< simulating 16GB disk >\n\n"); } else { return usage(); } if (real_disk) { if (!strcmp(device, "/dev/sda") || !strcmp(device, "/dev/sdb")) { fprintf(stderr,"error: refusing to partition sda or sdb\n"); return -1; } fd = open(device, O_RDWR); if (fd < 0) { fprintf(stderr,"error: cannot open '%s'\n", device); return -1; } if (ioctl(fd, BLKGETSIZE64, &sz)) { fprintf(stderr,"error: cannot query block device size\n"); return -1; } sz /= 512; fprintf(stderr,"blocks %lld\n", sz); } memset(&ptbl, 0, sizeof(ptbl)); init_mbr(ptbl.mbr, sz - 1); memcpy(hdr->magic, EFI_MAGIC, sizeof(hdr->magic)); hdr->version = EFI_VERSION; hdr->header_sz = sizeof(struct efi_header); hdr->header_lba = 1; hdr->backup_lba = sz - 1; hdr->first_lba = 34; hdr->last_lba = sz - 1; get_uuid(hdr->volume_uuid); hdr->entries_lba = 2; hdr->entries_count = 128; hdr->entries_size = sizeof(struct efi_entry); while (argc > 1) { if (argv[1][0] == '@') { char line[256], *p; FILE *f; f = fopen(argv[1] + 1, "r"); if (!f) { fprintf(stderr,"cannot read partitions from '%s\n", argv[1]); return -1; } while (fgets(line, sizeof(line), f)) { p = line + strlen(line); while (p > line) { p--; if (*p > ' ') break; *p = 0; } p = line; while (*p && (*p <= ' ')) p++; if (*p == '#') continue; if (*p == 0) continue; if (parse_ptn(&ptbl, p)) return -1; } fclose(f); } else { if (parse_ptn(&ptbl, argv[1])) return -1; } argc--; argv++; } n = crc32(0, Z_NULL, 0); n = crc32(n, (void*) ptbl.entry, sizeof(ptbl.entry)); hdr->entries_crc32 = n; n = crc32(0, Z_NULL, 0); n = crc32(n, (void*) &ptbl.header, sizeof(ptbl.header)); hdr->crc32 = n; show(&ptbl); if (real_disk) { write(fd, &ptbl, sizeof(ptbl)); fsync(fd); if (ioctl(fd, BLKRRPART, 0)) { fprintf(stderr,"could not re-read partition table\n"); } close(fd); } return 0; }