GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/fdisk/gpt.c Lines: 0 194 0.0 %
Date: 2016-12-06 Branches: 0 92 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: gpt.c,v 1.11 2016/03/28 16:55:09 mestre Exp $	*/
2
/*
3
 * Copyright (c) 2015 Markus Muller <mmu@grummel.net>
4
 * Copyright (c) 2015 Kenneth R Westerback <krw@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/param.h>	/* DEV_BSIZE */
20
#include <sys/disklabel.h>
21
#include <sys/dkio.h>
22
#include <sys/ioctl.h>
23
24
#include <errno.h>
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
#include <unistd.h>
29
#include <uuid.h>
30
31
#include "disk.h"
32
#include "misc.h"
33
#include "part.h"
34
#include "gpt.h"
35
36
#ifdef DEBUG
37
#define DPRINTF(x...)	printf(x)
38
#else
39
#define DPRINTF(x...)
40
#endif
41
42
struct gpt_header gh;
43
struct gpt_partition gp[NGPTPARTITIONS];
44
45
int
46
GPT_get_header(off_t where)
47
{
48
	char *secbuf;
49
	uint64_t partlastlba;
50
	int partspersec;
51
	uint32_t orig_gh_csum, new_gh_csum;
52
53
	secbuf = DISK_readsector(where);
54
	if (secbuf == 0)
55
		return (1);
56
57
	memcpy(&gh, secbuf, sizeof(struct gpt_header));
58
	free(secbuf);
59
60
	if (letoh64(gh.gh_sig) != GPTSIGNATURE) {
61
		DPRINTF("gpt signature: expected 0x%llx, got 0x%llx\n",
62
		    GPTSIGNATURE, letoh64(gh.gh_sig));
63
		return (1);
64
	}
65
66
	if (letoh32(gh.gh_rev) != GPTREVISION) {
67
		DPRINTF("gpt revision: expected 0x%x, got 0x%x\n",
68
		    GPTREVISION, letoh32(gh.gh_rev));
69
		return (1);
70
	}
71
72
	if (letoh64(gh.gh_lba_self) != where) {
73
		DPRINTF("gpt self lba: expected %lld, got %llu\n",
74
		    (long long)where, letoh64(gh.gh_lba_self));
75
		return (1);
76
	}
77
78
	if (letoh32(gh.gh_size) != GPTMINHDRSIZE) {
79
		DPRINTF("gpt header size: expected %u, got %u\n",
80
		    GPTMINHDRSIZE, letoh32(gh.gh_size));
81
		return (1);
82
	}
83
84
	if (letoh32(gh.gh_part_size) != GPTMINPARTSIZE) {
85
		DPRINTF("gpt partition size: expected %u, got %u\n",
86
		    GPTMINPARTSIZE, letoh32(gh.gh_part_size));
87
		return (1);
88
	}
89
90
	if (letoh32(gh.gh_part_num) > NGPTPARTITIONS) {
91
		DPRINTF("gpt partition count: expected <= %u, got %u\n",
92
		    NGPTPARTITIONS, letoh32(gh.gh_part_num));
93
		return (1);
94
	}
95
96
	orig_gh_csum = gh.gh_csum;
97
	gh.gh_csum = 0;
98
	new_gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size));
99
	gh.gh_csum = orig_gh_csum;
100
	if (letoh32(orig_gh_csum) != new_gh_csum) {
101
		DPRINTF("gpt header checksum: expected 0x%x, got 0x%x\n",
102
		    orig_gh_csum, new_gh_csum);
103
		return (1);
104
	}
105
106
	if (letoh64(gh.gh_lba_end) >= DL_GETDSIZE(&dl)) {
107
		DPRINTF("gpt last usable LBA: expected < %lld, got %llu\n",
108
		    DL_GETDSIZE(&dl), letoh64(gh.gh_lba_end));
109
		return (1);
110
	}
111
112
	if (letoh64(gh.gh_lba_start) >= letoh64(gh.gh_lba_end)) {
113
		DPRINTF("gpt first usable LBA: expected < %llu, got %llu\n",
114
		    letoh64(gh.gh_lba_start), letoh64(gh.gh_lba_start));
115
		return (1);
116
	}
117
118
	if (letoh64(gh.gh_part_lba) <= letoh64(gh.gh_lba_end) &&
119
	    letoh64(gh.gh_part_lba) >= letoh64(gh.gh_lba_start)) {
120
		DPRINTF("gpt partition table start lba: expected < %llu or "
121
		    "> %llu, got %llu\n", letoh64(gh.gh_lba_start),
122
		    letoh64(gh.gh_lba_end), letoh64(gh.gh_part_lba));
123
		return (1);
124
	}
125
126
	partspersec = dl.d_secsize / letoh32(gh.gh_part_size);
127
	partlastlba = letoh64(gh.gh_part_lba) +
128
	    ((letoh32(gh.gh_part_num) + partspersec - 1) / partspersec) - 1;
129
	if (partlastlba <= letoh64(gh.gh_lba_end) &&
130
	    partlastlba >= letoh64(gh.gh_lba_start)) {
131
		DPRINTF("gpt partition table last LBA: expected < %llu or "
132
		    "> %llu, got %llu\n", letoh64(gh.gh_lba_start),
133
		    letoh64(gh.gh_lba_end), partlastlba);
134
		return (1);
135
	}
136
137
	/*
138
	 * Other possible paranoia checks:
139
	 *	1) partition table starts before primary gpt lba.
140
	 *	2) partition table extends into lowest partition.
141
	 *	3) alt partition table starts before gh_lba_end.
142
	 */
143
	return (0);
144
}
145
146
int
147
GPT_get_partition_table(off_t where)
148
{
149
	ssize_t len;
150
	off_t off;
151
	int secs;
152
	uint32_t checksum, partspersec;
153
154
	DPRINTF("gpt partition table being read from LBA %llu\n",
155
	    letoh64(gh.gh_part_lba));
156
157
	partspersec = dl.d_secsize / letoh32(gh.gh_part_size);
158
	if (partspersec * letoh32(gh.gh_part_size) != dl.d_secsize) {
159
		DPRINTF("gpt partition table entry invalid size. %u\n",
160
		    letoh32(gh.gh_part_size));
161
		return (1);
162
	}
163
	secs = (letoh32(gh.gh_part_num) + partspersec - 1) / partspersec;
164
165
	memset(&gp, 0, sizeof(gp));
166
167
	where *= dl.d_secsize;
168
	off = lseek(disk.fd, where, SEEK_SET);
169
	if (off == -1) {
170
		DPRINTF("seek to gpt partition table @ sector %llu failed\n",
171
		    (unsigned long long)where / dl.d_secsize);
172
		return (1);
173
	}
174
	len = read(disk.fd, &gp, secs * dl.d_secsize);
175
	if (len == -1 || len != secs * dl.d_secsize) {
176
		DPRINTF("gpt partition table read failed.\n");
177
		return (1);
178
	}
179
180
	checksum = crc32((unsigned char *)&gp, letoh32(gh.gh_part_num) *
181
	    letoh32(gh.gh_part_size));
182
	if (checksum != letoh32(gh.gh_part_csum)) {
183
		DPRINTF("gpt partition table checksum: expected %x, got %x\n",
184
		    checksum, letoh32(gh.gh_part_csum));
185
		return (1);
186
	}
187
188
	return (0);
189
}
190
191
void
192
GPT_get_gpt(int which)
193
{
194
	int privalid, altvalid;
195
196
	/*
197
	 * primary header && primary partition table ||
198
	 * alt header && alt partition table
199
	 */
200
	privalid = GPT_get_header(GPTSECTOR);
201
	if (privalid == 0)
202
		privalid = GPT_get_partition_table(gh.gh_part_lba);
203
	if (which == 1 || (which == 0 && privalid == 0))
204
		return;
205
206
	/* No valid GPT found. Zap any artifacts. */
207
	memset(&gh, 0, sizeof(gh));
208
	memset(&gp, 0, sizeof(gp));
209
210
	altvalid = GPT_get_header(DL_GETDSIZE(&dl) - 1);
211
	if (altvalid == 0)
212
		altvalid = GPT_get_partition_table(gh.gh_part_lba);
213
	if (which == 2 || altvalid == 0)
214
		return;
215
216
	/* No valid GPT found. Zap any artifacts. */
217
	memset(&gh, 0, sizeof(gh));
218
	memset(&gp, 0, sizeof(gp));
219
}
220
221
void
222
GPT_print(char *units, int verbosity)
223
{
224
	const int secsize = unit_types[SECTORS].conversion;
225
	struct uuid guid;
226
	char *guidstr = NULL;
227
	double size;
228
	int i, u, status;
229
230
	u = unit_lookup(units);
231
	size = ((double)DL_GETDSIZE(&dl) * secsize) / unit_types[u].conversion;
232
	printf("Disk: %s       Usable LBA: %llu to %llu [%.0f ",
233
	    disk.name, letoh64(gh.gh_lba_start), letoh64(gh.gh_lba_end), size);
234
235
	if (u == SECTORS && secsize != DEV_BSIZE)
236
		printf("%d-byte ", secsize);
237
	printf("%s]\n", unit_types[u].lname);
238
239
	if (verbosity) {
240
		printf("GUID: ");
241
		uuid_dec_le(&gh.gh_guid, &guid);
242
		uuid_to_string(&guid, &guidstr, &status);
243
		if (status == uuid_s_ok)
244
			printf("%s\n", guidstr);
245
		else
246
			printf("<invalid header GUID>\n");
247
		free(guidstr);
248
	}
249
250
	GPT_print_parthdr(verbosity);
251
	for (i = 0; i < letoh32(gh.gh_part_num); i++) {
252
		if (uuid_is_nil(&gp[i].gp_type, NULL))
253
			continue;
254
		GPT_print_part(i, units, verbosity);
255
	}
256
257
}
258
259
void
260
GPT_print_parthdr(int verbosity)
261
{
262
	printf("   #: type                                "
263
	    " [       start:         size ]\n");
264
	if (verbosity)
265
		printf("      guid                                 name\n");
266
	printf("--------------------------------------------------------"
267
	    "----------------\n");
268
}
269
270
void
271
GPT_print_part(int n, char *units, int verbosity)
272
{
273
	struct uuid guid;
274
	const int secsize = unit_types[SECTORS].conversion;
275
	struct gpt_partition *partn = &gp[n];
276
	char *guidstr = NULL;
277
	double size;
278
	int u, status;
279
280
	uuid_dec_le(&partn->gp_type, &guid);
281
	u = unit_lookup(units);
282
	size = letoh64(partn->gp_lba_end) - letoh64(partn->gp_lba_start) + 1;
283
	size = (size * secsize) / unit_types[u].conversion;
284
	printf("%c%3d: %-36s [%12lld: %12.0f%s]\n",
285
	    (letoh64(partn->gp_attrs) & GPTDOSACTIVE)?'*':' ', n,
286
	    PRT_uuid_to_typename(&guid), letoh64(partn->gp_lba_start),
287
	    size, unit_types[u].abbr);
288
289
	if (verbosity) {
290
		uuid_dec_le(&partn->gp_guid, &guid);
291
		uuid_to_string(&guid, &guidstr, &status);
292
		if (status != uuid_s_ok)
293
			printf("      <invalid partition guid>             ");
294
		else
295
			printf("      %-36s ", guidstr);
296
		printf("%-36s\n", utf16le_to_string(partn->gp_name));
297
		free(guidstr);
298
	}
299
}
300
301
int
302
GPT_init(void)
303
{
304
	extern u_int32_t b_arg;
305
	const int secsize = unit_types[SECTORS].conversion;
306
	struct uuid guid;
307
	int needed;
308
	uint32_t status;
309
	const uint8_t gpt_uuid_efi_system[] = GPT_UUID_EFI_SYSTEM;
310
	const uint8_t gpt_uuid_openbsd[] = GPT_UUID_OPENBSD;
311
312
	memset(&gh, 0, sizeof(gh));
313
	memset(&gp, 0, sizeof(gp));
314
315
	needed = sizeof(gp) / secsize + 2;
316
	/* Start on 64 sector boundary */
317
	if (needed % 64)
318
		needed += (64 - (needed % 64));
319
320
	gh.gh_sig = htole64(GPTSIGNATURE);
321
	gh.gh_rev = htole32(GPTREVISION);
322
	gh.gh_size = htole32(GPTMINHDRSIZE);
323
	gh.gh_csum = 0;
324
	gh.gh_rsvd = 0;
325
	gh.gh_lba_self = htole64(1);
326
	gh.gh_lba_alt = htole64(DL_GETDSIZE(&dl) - 1);
327
	gh.gh_lba_start = htole64(needed);
328
	gh.gh_lba_end = htole64(DL_GETDSIZE(&dl) - needed);
329
	gh.gh_part_lba = htole64(2);
330
	gh.gh_part_num = htole32(NGPTPARTITIONS);
331
	gh.gh_part_size = htole32(GPTMINPARTSIZE);
332
333
	uuid_create(&guid, &status);
334
	if (status != uuid_s_ok)
335
		return (1);
336
	uuid_enc_le(&gh.gh_guid, &guid);
337
338
#if defined(__i386__) || defined(__amd64__)
339
	if (b_arg > 0) {
340
		/* Add an EFI system partition on i386/amd64. */
341
		uuid_dec_be(gpt_uuid_efi_system, &guid);
342
		uuid_enc_le(&gp[1].gp_type, &guid);
343
		uuid_create(&guid, &status);
344
		if (status != uuid_s_ok)
345
			return (1);
346
		uuid_enc_le(&gp[1].gp_guid, &guid);
347
		gp[1].gp_lba_start = gh.gh_lba_start;
348
		gp[1].gp_lba_end = htole64(letoh64(gh.gh_lba_start)+b_arg - 1);
349
		memcpy(gp[1].gp_name, string_to_utf16le("EFI System Area"),
350
		    sizeof(gp[1].gp_name));
351
	}
352
#endif
353
	uuid_dec_be(gpt_uuid_openbsd, &guid);
354
	uuid_enc_le(&gp[3].gp_type, &guid);
355
	uuid_create(&guid, &status);
356
	if (status != uuid_s_ok)
357
		return (1);
358
	uuid_enc_le(&gp[3].gp_guid, &guid);
359
	gp[3].gp_lba_start = gh.gh_lba_start;
360
#if defined(__i386__) || defined(__amd64__)
361
	if (b_arg > 0) {
362
		gp[3].gp_lba_start = htole64(letoh64(gp[3].gp_lba_start) +
363
		    b_arg);
364
		if (letoh64(gp[3].gp_lba_start) % 64)
365
			gp[3].gp_lba_start =
366
			    htole64(letoh64(gp[3].gp_lba_start) +
367
			    (64 - letoh64(gp[3].gp_lba_start) % 64));
368
	}
369
#endif
370
	gp[3].gp_lba_end = gh.gh_lba_end;
371
	memcpy(gp[3].gp_name, string_to_utf16le("OpenBSD Area"),
372
	    sizeof(gp[3].gp_name));
373
374
	gh.gh_part_csum = crc32((unsigned char *)&gp, sizeof(gp));
375
	gh.gh_csum = crc32((unsigned char *)&gh, sizeof(gh));
376
377
	return 0;
378
}
379
380
int
381
GPT_write(void)
382
{
383
	char *secbuf;
384
	const int secsize = unit_types[SECTORS].conversion;
385
	ssize_t len;
386
	off_t off;
387
	u_int64_t altgh, altgp;
388
389
	/* Assume we always write full-size partition table. XXX */
390
	altgh = DL_GETDSIZE(&dl) - 1;
391
	altgp = DL_GETDSIZE(&dl) - 1 - (sizeof(gp) / secsize);
392
393
	/*
394
	 * Place the new GPT header at the start of sectors 1 and
395
	 * DL_GETDSIZE(lp)-1 and write the sectors back.
396
	 */
397
	gh.gh_lba_self = htole64(1);
398
	gh.gh_lba_alt = htole64(altgh);
399
	gh.gh_part_lba = htole64(2);
400
	gh.gh_part_csum = crc32((unsigned char *)&gp, sizeof(gp));
401
	gh.gh_csum = 0;
402
	gh.gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size));
403
404
	secbuf = DISK_readsector(1);
405
	if (secbuf == NULL)
406
		return (-1);
407
408
	memcpy(secbuf, &gh, sizeof(gh));
409
	DISK_writesector(secbuf, 1);
410
	free(secbuf);
411
412
	gh.gh_lba_self = htole64(altgh);
413
	gh.gh_lba_alt = htole64(1);
414
	gh.gh_part_lba = htole64(altgp);
415
	gh.gh_csum = 0;
416
	gh.gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size));
417
418
	secbuf = DISK_readsector(altgh);
419
	if (secbuf == NULL)
420
		return (-1);
421
422
	memcpy(secbuf, &gh, sizeof(gh));
423
	DISK_writesector(secbuf, altgh);
424
	free(secbuf);
425
426
	/*
427
	 * Write partition table after primary header
428
	 * (i.e. at sector 1) and before alt header
429
	 * (i.e. ending in sector before alt header.
430
	 * XXX ALWAYS NGPTPARTITIONS!
431
	 * XXX ASSUME gp is multiple of sector size!
432
	 */
433
	off = lseek(disk.fd, secsize * 2, SEEK_SET);
434
	if (off == secsize * 2)
435
		len = write(disk.fd, &gp, sizeof(gp));
436
	else
437
		len = -1;
438
	if (len == -1 || len != sizeof(gp)) {
439
		errno = EIO;
440
		return (-1);
441
	}
442
443
	off = lseek(disk.fd, secsize * altgp, SEEK_SET);
444
	if (off == secsize * altgp)
445
		len = write(disk.fd, &gp, sizeof(gp));
446
	else
447
		len = -1;
448
449
	if (len == -1 || len != sizeof(gp)) {
450
		errno = EIO;
451
		return (-1);
452
	}
453
454
	/* Refresh in-kernel disklabel from the updated disk information. */
455
	ioctl(disk.fd, DIOCRLDINFO, 0);
456
457
	return (0);
458
}