gpttool.c (9353B)
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 <linux/fs.h> 25 #include <sys/stat.h> 26 #include <sys/ioctl.h> 27 28 #include <zlib.h> 29 30 typedef unsigned char u8; 31 typedef unsigned short u16; 32 typedef unsigned int u32; 33 typedef unsigned long long u64; 34 35 #define _crc32(ptr,len) crc32(crc32(0,Z_NULL,0),(void*)(ptr),len) 36 37 const u8 partition_type_uuid[16] = { 38 0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44, 39 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7, 40 }; 41 42 const u8 partition_type_efi[16] = { 43 0x28, 0x73, 0x2a, 0xc1, 0x1f, 0xf8, 0xd2, 0x11, 44 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b, 45 }; 46 47 const u8 partition_type_linux[16] = { 48 0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44, 49 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7, 50 }; 51 52 const u8 partition_type_swap[16] = { 53 0x6d, 0xfd, 0x57, 0x06, 0xab, 0xa4, 0xc4, 0x43, 54 0x84, 0xe5, 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f, 55 }; 56 57 #define EFI_VERSION 0x00010000 58 #define EFI_MAGIC "EFI PART" 59 #define EFI_ENTRIES 128 60 #define EFI_NAMELEN 36 61 62 struct efi_header { 63 u8 magic[8]; 64 65 u32 version; 66 u32 header_sz; 67 68 u32 crc32; 69 u32 reserved; 70 71 u64 header_lba; 72 u64 backup_lba; 73 u64 first_lba; 74 u64 last_lba; 75 76 u8 volume_uuid[16]; 77 78 u64 entries_lba; 79 80 u32 entries_count; 81 u32 entries_size; 82 u32 entries_crc32; 83 } __attribute__((packed)); 84 85 struct efi_entry { 86 u8 type_uuid[16]; 87 u8 uniq_uuid[16]; 88 u64 first_lba; 89 u64 last_lba; 90 u64 attr; 91 u16 name[EFI_NAMELEN]; 92 }; 93 94 struct ptable { 95 u8 mbr[512]; 96 union { 97 struct efi_header header; 98 u8 block[512]; 99 }; 100 struct efi_entry entry[EFI_ENTRIES]; 101 }; 102 103 void get_uuid(u8 *uuid) 104 { 105 int fd; 106 fd = open("/dev/urandom", O_RDONLY); 107 read(fd, uuid, 16); 108 close(fd); 109 } 110 111 void init_mbr(u8 *mbr, u32 blocks) 112 { 113 mbr[0x1be] = 0x00; // nonbootable 114 mbr[0x1bf] = 0x00; // bogus CHS 115 mbr[0x1c0] = 0x01; 116 mbr[0x1c1] = 0x00; 117 118 mbr[0x1c2] = 0xEE; // GPT partition 119 mbr[0x1c3] = 0xFE; // bogus CHS 120 mbr[0x1c4] = 0xFF; 121 mbr[0x1c5] = 0xFF; 122 123 mbr[0x1c6] = 0x01; // start 124 mbr[0x1c7] = 0x00; 125 mbr[0x1c8] = 0x00; 126 mbr[0x1c9] = 0x00; 127 128 memcpy(mbr + 0x1ca, &blocks, sizeof(u32)); 129 130 mbr[0x1fe] = 0x55; 131 mbr[0x1ff] = 0xaa; 132 } 133 134 int add_ptn(struct ptable *ptbl, u64 first, u64 last, const char *name, const u8 *type) 135 { 136 struct efi_header *hdr = &ptbl->header; 137 struct efi_entry *entry = ptbl->entry; 138 unsigned n; 139 140 if (first < 34) { 141 fprintf(stderr,"partition '%s' overlaps partition table\n", name); 142 return -1; 143 } 144 145 if (last > hdr->last_lba) { 146 fprintf(stderr,"partition '%s' does not fit on disk\n", name); 147 return -1; 148 } 149 for (n = 0; n < EFI_ENTRIES; n++, entry++) { 150 if (entry->type_uuid[0]) 151 continue; 152 memcpy(entry->type_uuid, type, 16); 153 get_uuid(entry->uniq_uuid); 154 entry->first_lba = first; 155 entry->last_lba = last; 156 for (n = 0; (n < EFI_NAMELEN) && *name; n++) 157 entry->name[n] = *name++; 158 return 0; 159 } 160 fprintf(stderr,"out of partition table entries\n"); 161 return -1; 162 } 163 164 int usage(void) 165 { 166 fprintf(stderr, 167 "usage: gpttool write <disk> [ <partition> ]*\n" 168 " gpttool read <disk>\n" 169 " gpttool test [ <partition> ]*\n" 170 "\n" 171 "partition: [<name>]:<size>[kmg][=linux|=swap|=efi]\n" 172 " or: @<file-of-partitions>\n" 173 ); 174 return 0; 175 } 176 177 void show(struct ptable *ptbl) 178 { 179 struct efi_entry *entry = ptbl->entry; 180 unsigned n, m; 181 char name[EFI_NAMELEN + 1]; 182 183 fprintf(stderr,"ptn start block end block name\n"); 184 fprintf(stderr,"---- ------------- ------------- --------------------\n"); 185 186 for (n = 0; n < EFI_ENTRIES; n++, entry++) { 187 if (entry->type_uuid[0] == 0) 188 break; 189 for (m = 0; m < EFI_NAMELEN; m++) { 190 name[m] = entry->name[m] & 127; 191 } 192 name[m] = 0; 193 fprintf(stderr,"#%03d %13lld %13lld %s\n", 194 n + 1, entry->first_lba, entry->last_lba, name); 195 } 196 } 197 198 u64 find_next_lba(struct ptable *ptbl) 199 { 200 struct efi_entry *entry = ptbl->entry; 201 unsigned n; 202 u64 a = 0; 203 for (n = 0; n < EFI_ENTRIES; n++, entry++) { 204 if ((entry->last_lba + 1) > a) 205 a = entry->last_lba + 1; 206 } 207 return a; 208 } 209 210 u64 next_lba = 0; 211 212 u64 parse_size(char *sz) 213 { 214 int l = strlen(sz); 215 u64 n = strtoull(sz, 0, 10); 216 if (l) { 217 switch(sz[l-1]){ 218 case 'k': 219 case 'K': 220 n *= 1024; 221 break; 222 case 'm': 223 case 'M': 224 n *= (1024 * 1024); 225 break; 226 case 'g': 227 case 'G': 228 n *= (1024 * 1024 * 1024); 229 break; 230 } 231 } 232 return n; 233 } 234 235 int parse_ptn(struct ptable *ptbl, char *x) 236 { 237 const u8 *type = partition_type_uuid; 238 char *y = strchr(x, ':'); 239 char *z; 240 u64 sz; 241 242 if (!y) { 243 fprintf(stderr,"invalid partition entry: %s\n", x); 244 return -1; 245 } 246 *y++ = 0; 247 248 z = strchr(y, '='); 249 if (z) { 250 *z++ = 0; 251 if (!strcmp(z, "efi")) { 252 type = partition_type_efi; 253 } else if (!strcmp(z, "linux")) { 254 type = partition_type_linux; 255 } else if (!strcmp(z, "swap")) { 256 type = partition_type_swap; 257 } 258 } 259 if (*y == 0) { 260 sz = ptbl->header.last_lba - next_lba; 261 } else { 262 sz = parse_size(y); 263 if (sz & 511) { 264 fprintf(stderr,"partition size must be multiple of 512\n"); 265 return -1; 266 } 267 sz /= 512; 268 } 269 270 if (sz == 0) { 271 fprintf(stderr,"zero size partitions not allowed\n"); 272 return -1; 273 } 274 275 if (x[0] && add_ptn(ptbl, next_lba, next_lba + sz - 1, x, type)) 276 return -1; 277 278 next_lba = next_lba + sz; 279 return 0; 280 } 281 282 void update_crc32(struct ptable *ptbl) { 283 u32 n; 284 285 286 n = _crc32((void*) ptbl->entry, sizeof(ptbl->entry)); 287 ptbl->header.entries_crc32 = n; 288 289 ptbl->header.crc32 = 0; 290 n = _crc32((void*) &ptbl->header, sizeof(ptbl->header)); 291 ptbl->header.crc32 = n; 292 } 293 294 int main(int argc, char **argv) 295 { 296 struct ptable ptbl; 297 struct efi_header *hdr = &ptbl.header; 298 struct stat s; 299 u64 sz; 300 int fd = -1; 301 const char *device; 302 int real_disk = 0; 303 304 if (argc < 2) 305 return usage(); 306 307 if (!strcmp(argv[1], "write")) { 308 if (argc < 3) 309 return usage(); 310 device = argv[2]; 311 argc -= 2; 312 argv += 2; 313 real_disk = 1; 314 } else if (!strcmp(argv[1], "test")) { 315 argc -= 1; 316 argv += 1; 317 real_disk = 0; 318 sz = 2097152 * 16; 319 fprintf(stderr,"< simulating 16GB disk >\n\n"); 320 } else { 321 return usage(); 322 } 323 324 if (real_disk) { 325 #if 0 326 if (!strcmp(device, "/dev/sda") || 327 !strcmp(device, "/dev/sdb")) { 328 fprintf(stderr,"error: refusing to partition sda or sdb\n"); 329 return -1; 330 } 331 #endif 332 fd = open(device, O_RDWR); 333 if (fd < 0) { 334 fprintf(stderr,"error: cannot open '%s'\n", device); 335 return -1; 336 } 337 if (fstat(fd, &s)) { 338 fprintf(stderr,"error: cannot stat '%s'\n", device); 339 return -1; 340 } 341 if (!S_ISBLK(s.st_mode) && !S_ISCHR(s.st_mode)) { 342 sz = s.st_size; 343 if (sz & 511) { 344 fprintf(stderr,"error: file size not multiple of 512\n"); 345 return -1; 346 } 347 } else if (ioctl(fd, BLKGETSIZE64, &sz)) { 348 fprintf(stderr,"error: cannot query block device size\n"); 349 return -1; 350 } 351 sz /= 512; 352 fprintf(stderr,"blocks %lld\n", sz); 353 } 354 355 memset(&ptbl, 0, sizeof(ptbl)); 356 357 init_mbr(ptbl.mbr, sz - 1); 358 359 memcpy(hdr->magic, EFI_MAGIC, sizeof(hdr->magic)); 360 hdr->version = EFI_VERSION; 361 hdr->header_sz = sizeof(struct efi_header); 362 hdr->header_lba = 1; 363 hdr->backup_lba = sz - 1; 364 hdr->first_lba = 34; 365 hdr->last_lba = sz - 33; 366 get_uuid(hdr->volume_uuid); 367 hdr->entries_lba = 2; 368 hdr->entries_count = 128; 369 hdr->entries_size = sizeof(struct efi_entry); 370 371 while (argc > 1) { 372 if (argv[1][0] == '@') { 373 char line[256], *p; 374 FILE *f; 375 f = fopen(argv[1] + 1, "r"); 376 if (!f) { 377 fprintf(stderr,"cannot read partitions from '%s\n", argv[1]); 378 return -1; 379 } 380 while (fgets(line, sizeof(line), f)) { 381 p = line + strlen(line); 382 while (p > line) { 383 p--; 384 if (*p > ' ') 385 break; 386 *p = 0; 387 } 388 p = line; 389 while (*p && (*p <= ' ')) 390 p++; 391 if (*p == '#') 392 continue; 393 if (*p == 0) 394 continue; 395 if (parse_ptn(&ptbl, p)) 396 return -1; 397 } 398 fclose(f); 399 } else { 400 if (parse_ptn(&ptbl, argv[1])) 401 return -1; 402 } 403 argc--; 404 argv++; 405 } 406 407 update_crc32(&ptbl); 408 409 show(&ptbl); 410 411 if (real_disk) { 412 u64 off = (sz - 33) * 512; 413 if (write(fd, &ptbl, sizeof(ptbl)) != sizeof(ptbl)) { 414 fprintf(stderr,"error writing primary partition table\n"); 415 return -1; 416 } 417 if (lseek(fd, off, SEEK_SET) != off) { 418 fprintf(stderr,"error seeking to secondary partition table\n"); 419 return -1; 420 } 421 422 /* reverse these for the secondary copy */ 423 hdr->header_lba = sz -1; 424 hdr->backup_lba = 1; 425 update_crc32(&ptbl); 426 427 if (write(fd, &ptbl.entry, sizeof(ptbl) - 1024) != (sizeof(ptbl) - 1024)) { 428 fprintf(stderr,"error writing secondary partition table\n"); 429 return -1; 430 } 431 if (write(fd, &ptbl.header, 512) != 512) { 432 fprintf(stderr,"error writing secondary partition header\n"); 433 return -1; 434 } 435 fsync(fd); 436 437 if (ioctl(fd, BLKRRPART, 0)) { 438 fprintf(stderr,"warning: could not re-read partition table\n"); 439 } 440 if (close(fd)) { 441 fprintf(stderr,"error writing data\n"); 442 return -1; 443 } 444 } 445 return 0; 446 }