Home | History | Annotate | Download | only in gpttool
      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