gpttool.c revision a360a77294600cd679d4ea69b0b24e217bd02a9e
1/* system/core/gpttool/gpttool.c 2** 3** Copyright 2011, The Android Open Source Project 4** 5** Licensed under the Apache License, Version 2.0 (the "License"); 6** you may not use this file except in compliance with the License. 7** You may obtain a copy of the License at 8** 9** http://www.apache.org/licenses/LICENSE-2.0 10** 11** Unless required by applicable law or agreed to in writing, software 12** distributed under the License is distributed on an "AS IS" BASIS, 13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14** See the License for the specific language governing permissions and 15** limitations under the License. 16*/ 17 18#include <stdio.h> 19#include <stdlib.h> 20#include <unistd.h> 21#include <string.h> 22#include <fcntl.h> 23 24#include <zlib.h> 25 26#include <linux/fs.h> 27 28typedef unsigned char u8; 29typedef unsigned short u16; 30typedef unsigned int u32; 31typedef unsigned long long u64; 32 33const u8 partition_type_uuid[16] = { 34 0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44, 35 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7, 36}; 37 38 39#define EFI_VERSION 0x00010000 40#define EFI_MAGIC "EFI PART" 41#define EFI_ENTRIES 128 42#define EFI_NAMELEN 36 43 44struct efi_header { 45 u8 magic[8]; 46 47 u32 version; 48 u32 header_sz; 49 50 u32 crc32; 51 u32 reserved; 52 53 u64 header_lba; 54 u64 backup_lba; 55 u64 first_lba; 56 u64 last_lba; 57 58 u8 volume_uuid[16]; 59 60 u64 entries_lba; 61 62 u32 entries_count; 63 u32 entries_size; 64 u32 entries_crc32; 65} __attribute__((packed)); 66 67struct efi_entry { 68 u8 type_uuid[16]; 69 u8 uniq_uuid[16]; 70 u64 first_lba; 71 u64 last_lba; 72 u64 attr; 73 u16 name[EFI_NAMELEN]; 74}; 75 76struct ptable { 77 u8 mbr[512]; 78 union { 79 struct efi_header header; 80 u8 block[512]; 81 }; 82 struct efi_entry entry[EFI_ENTRIES]; 83}; 84 85void get_uuid(u8 *uuid) 86{ 87 int fd; 88 fd = open("/dev/urandom", O_RDONLY); 89 read(fd, uuid, 16); 90 close(fd); 91} 92 93void init_mbr(u8 *mbr, u32 blocks) 94{ 95 mbr[0x1be] = 0x00; // nonbootable 96 mbr[0x1bf] = 0xFF; // bogus CHS 97 mbr[0x1c0] = 0xFF; 98 mbr[0x1c1] = 0xFF; 99 100 mbr[0x1c2] = 0xEE; // GPT partition 101 mbr[0x1c3] = 0xFF; // bogus CHS 102 mbr[0x1c4] = 0xFF; 103 mbr[0x1c5] = 0xFF; 104 105 mbr[0x1c6] = 0x01; // start 106 mbr[0x1c7] = 0x00; 107 mbr[0x1c8] = 0x00; 108 mbr[0x1c9] = 0x00; 109 110 memcpy(mbr + 0x1ca, &blocks, sizeof(u32)); 111 112 mbr[0x1fe] = 0x55; 113 mbr[0x1ff] = 0xaa; 114} 115 116int add_ptn(struct ptable *ptbl, u64 first, u64 last, const char *name) 117{ 118 struct efi_header *hdr = &ptbl->header; 119 struct efi_entry *entry = ptbl->entry; 120 unsigned n; 121 122 if (first < 34) { 123 fprintf(stderr,"partition '%s' overlaps partition table\n", name); 124 return -1; 125 } 126 127 if (last > hdr->last_lba) { 128 fprintf(stderr,"partition '%s' does not fit on disk\n", name); 129 return -1; 130 } 131 for (n = 0; n < EFI_ENTRIES; n++, entry++) { 132 if (entry->type_uuid[0]) 133 continue; 134 memcpy(entry->type_uuid, partition_type_uuid, 16); 135 get_uuid(entry->uniq_uuid); 136 entry->first_lba = first; 137 entry->last_lba = last; 138 for (n = 0; (n < EFI_NAMELEN) && *name; n++) 139 entry->name[n] = *name++; 140 return 0; 141 } 142 fprintf(stderr,"out of partition table entries\n"); 143 return -1; 144} 145 146int usage(void) 147{ 148 fprintf(stderr, 149 "usage: gpttool write <disk> [ <partition> ]*\n" 150 " gpttool read <disk>\n" 151 " gpttool test [ <partition> ]*\n" 152 "\n" 153 "partition: [<name>]:<size>[kmg] | @<file-of-partitions>\n" 154 ); 155 return 0; 156} 157 158void show(struct ptable *ptbl) 159{ 160 struct efi_entry *entry = ptbl->entry; 161 unsigned n, m; 162 char name[EFI_NAMELEN]; 163 164 fprintf(stderr,"ptn start block end block name\n"); 165 fprintf(stderr,"---- ------------- ------------- --------------------\n"); 166 167 for (n = 0; n < EFI_ENTRIES; n++, entry++) { 168 if (entry->type_uuid[0] == 0) 169 break; 170 for (m = 0; m < EFI_NAMELEN; m++) { 171 name[m] = entry->name[m] & 127; 172 } 173 name[m] = 0; 174 fprintf(stderr,"#%03d %13lld %13lld %s\n", 175 n + 1, entry->first_lba, entry->last_lba, name); 176 } 177} 178 179u64 find_next_lba(struct ptable *ptbl) 180{ 181 struct efi_entry *entry = ptbl->entry; 182 unsigned n; 183 u64 a = 0; 184 for (n = 0; n < EFI_ENTRIES; n++, entry++) { 185 if ((entry->last_lba + 1) > a) 186 a = entry->last_lba + 1; 187 } 188 return a; 189} 190 191u64 next_lba = 0; 192 193u64 parse_size(char *sz) 194{ 195 int l = strlen(sz); 196 u64 n = strtoull(sz, 0, 10); 197 if (l) { 198 switch(sz[l-1]){ 199 case 'k': 200 case 'K': 201 n *= 1024; 202 break; 203 case 'm': 204 case 'M': 205 n *= (1024 * 1024); 206 break; 207 case 'g': 208 case 'G': 209 n *= (1024 * 1024 * 1024); 210 break; 211 } 212 } 213 return n; 214} 215 216int parse_ptn(struct ptable *ptbl, char *x) 217{ 218 char *y = strchr(x, ':'); 219 u64 sz; 220 221 if (!y) { 222 fprintf(stderr,"invalid partition entry: %s\n", x); 223 return -1; 224 } 225 *y++ = 0; 226 227 if (*y == 0) { 228 sz = ptbl->header.last_lba - next_lba; 229 } else { 230 sz = parse_size(y); 231 if (sz & 511) { 232 fprintf(stderr,"partition size must be multiple of 512\n"); 233 return -1; 234 } 235 sz /= 512; 236 } 237 238 if (sz == 0) { 239 fprintf(stderr,"zero size partitions not allowed\n"); 240 return -1; 241 } 242 243 if (x[0] && add_ptn(ptbl, next_lba, next_lba + sz - 1, x)) 244 return -1; 245 246 next_lba = next_lba + sz; 247 return 0; 248} 249 250int main(int argc, char **argv) 251{ 252 struct ptable ptbl; 253 struct efi_entry *entry; 254 struct efi_header *hdr = &ptbl.header; 255 struct stat s; 256 u32 n; 257 u64 sz, blk; 258 int fd; 259 const char *device; 260 int real_disk = 0; 261 262 if (argc < 2) 263 return usage(); 264 265 if (!strcmp(argv[1], "write")) { 266 if (argc < 3) 267 return usage(); 268 device = argv[2]; 269 argc -= 2; 270 argv += 2; 271 real_disk = 1; 272 } else if (!strcmp(argv[1], "test")) { 273 argc -= 1; 274 argv += 1; 275 real_disk = 0; 276 sz = 2097152 * 16; 277 fprintf(stderr,"< simulating 16GB disk >\n\n"); 278 } else { 279 return usage(); 280 } 281 282 if (real_disk) { 283 if (!strcmp(device, "/dev/sda") || 284 !strcmp(device, "/dev/sdb")) { 285 fprintf(stderr,"error: refusing to partition sda or sdb\n"); 286 return -1; 287 } 288 289 fd = open(device, O_RDWR); 290 if (fd < 0) { 291 fprintf(stderr,"error: cannot open '%s'\n", device); 292 return -1; 293 } 294 if (ioctl(fd, BLKGETSIZE64, &sz)) { 295 fprintf(stderr,"error: cannot query block device size\n"); 296 return -1; 297 } 298 sz /= 512; 299 fprintf(stderr,"blocks %lld\n", sz); 300 } 301 302 memset(&ptbl, 0, sizeof(ptbl)); 303 304 init_mbr(ptbl.mbr, sz - 1); 305 306 memcpy(hdr->magic, EFI_MAGIC, sizeof(hdr->magic)); 307 hdr->version = EFI_VERSION; 308 hdr->header_sz = sizeof(struct efi_header); 309 hdr->header_lba = 1; 310 hdr->backup_lba = sz - 1; 311 hdr->first_lba = 34; 312 hdr->last_lba = sz - 1; 313 get_uuid(hdr->volume_uuid); 314 hdr->entries_lba = 2; 315 hdr->entries_count = 128; 316 hdr->entries_size = sizeof(struct efi_entry); 317 318 while (argc > 1) { 319 if (argv[1][0] == '@') { 320 char line[256], *p; 321 FILE *f; 322 f = fopen(argv[1] + 1, "r"); 323 if (!f) { 324 fprintf(stderr,"cannot read partitions from '%s\n", argv[1]); 325 return -1; 326 } 327 while (fgets(line, sizeof(line), f)) { 328 p = line + strlen(line); 329 while (p > line) { 330 p--; 331 if (*p > ' ') 332 break; 333 *p = 0; 334 } 335 p = line; 336 while (*p && (*p <= ' ')) 337 p++; 338 if (*p == '#') 339 continue; 340 if (*p == 0) 341 continue; 342 if (parse_ptn(&ptbl, p)) 343 return -1; 344 } 345 fclose(f); 346 } else { 347 if (parse_ptn(&ptbl, argv[1])) 348 return -1; 349 } 350 argc--; 351 argv++; 352 } 353 354 n = crc32(0, Z_NULL, 0); 355 n = crc32(n, (void*) ptbl.entry, sizeof(ptbl.entry)); 356 hdr->entries_crc32 = n; 357 358 n = crc32(0, Z_NULL, 0); 359 n = crc32(n, (void*) &ptbl.header, sizeof(ptbl.header)); 360 hdr->crc32 = n; 361 362 show(&ptbl); 363 364 if (real_disk) { 365 write(fd, &ptbl, sizeof(ptbl)); 366 fsync(fd); 367 368 if (ioctl(fd, BLKRRPART, 0)) { 369 fprintf(stderr,"could not re-read partition table\n"); 370 } 371 close(fd); 372 } 373 return 0; 374} 375