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