gpttool

A tool to create GUID Partition Tables
git clone http://frotz.net/git/gpttool.git
Log | Files | Refs

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 }