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