blob: 0e466fdc55085a113c81bf261d6447167595e734 [file] [log] [blame]
Bill Richardsonf1372d92010-06-11 09:15:55 -07001/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 *
5 * Utility for ChromeOS-specific GPT partitions, Please see corresponding .c
6 * files for more details.
7 */
8
9#include "cgpt.h"
10
11#include <errno.h>
12#include <fcntl.h>
13#include <getopt.h>
Bill Richardsonc4e92af2010-10-12 07:33:15 -070014#include <stdarg.h>
Bill Richardsonf1372d92010-06-11 09:15:55 -070015#include <stdint.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <sys/ioctl.h>
20#include <sys/mount.h>
21#include <sys/stat.h>
22#include <sys/types.h>
23#include <unistd.h>
Bill Richardsonf1372d92010-06-11 09:15:55 -070024
25#include "cgptlib_internal.h"
26#include "crc32.h"
27
Bill Richardsonf1372d92010-06-11 09:15:55 -070028void Error(const char *format, ...) {
29 va_list ap;
30 va_start(ap, format);
31 fprintf(stderr, "ERROR: %s %s: ", progname, command);
32 vfprintf(stderr, format, ap);
33 va_end(ap);
34}
35
36
37int CheckValid(const struct drive *drive) {
38 if ((drive->gpt.valid_headers != MASK_BOTH) ||
39 (drive->gpt.valid_entries != MASK_BOTH)) {
40 fprintf(stderr, "\nWARNING: one of the GPT header/entries is invalid, "
41 "please run '%s repair'\n", progname);
42 return CGPT_FAILED;
43 }
44 return CGPT_OK;
45}
46
47/* Loads sectors from 'fd'.
48 * *buf is pointed to an allocated memory when returned, and should be
49 * freed by cgpt_close().
50 *
51 * fd -- file descriptot.
52 * buf -- pointer to buffer pointer
53 * sector -- offset of starting sector (in sectors)
54 * sector_bytes -- bytes per sector
55 * sector_count -- number of sectors to load
56 *
57 * Returns CGPT_OK for successful. Aborts if any error occurs.
58 */
59static int Load(const int fd, uint8_t **buf,
60 const uint64_t sector,
61 const uint64_t sector_bytes,
62 const uint64_t sector_count) {
63 int count; /* byte count to read */
64 int nread;
65
Bill Richardsonc4e92af2010-10-12 07:33:15 -070066 require(buf);
67 if (!sector_count || !sector_bytes) {
68 Error("%s() failed at line %d: sector_count=%d, sector_bytes=%d\n",
69 __FUNCTION__, __LINE__, sector_count, sector_bytes);
70 return CGPT_FAILED;
71 }
72 /* Make sure that sector_bytes * sector_count doesn't roll over. */
73 if (sector_bytes > (UINT64_MAX / sector_count)) {
74 Error("%s() failed at line %d: sector_count=%d, sector_bytes=%d\n",
75 __FUNCTION__, __LINE__, sector_count, sector_bytes);
76 return CGPT_FAILED;
77 }
Bill Richardsonf1372d92010-06-11 09:15:55 -070078 count = sector_bytes * sector_count;
79 *buf = malloc(count);
Bill Richardsonc4e92af2010-10-12 07:33:15 -070080 require(*buf);
Bill Richardsonf1372d92010-06-11 09:15:55 -070081
Bill Richardsonc4e92af2010-10-12 07:33:15 -070082 if (-1 == lseek(fd, sector * sector_bytes, SEEK_SET)) {
83 Error("Can't lseek: %s\n", strerror(errno));
Bill Richardsonf1372d92010-06-11 09:15:55 -070084 goto error_free;
Bill Richardsonc4e92af2010-10-12 07:33:15 -070085 }
Bill Richardsonf1372d92010-06-11 09:15:55 -070086
87 nread = read(fd, *buf, count);
Bill Richardsonc4e92af2010-10-12 07:33:15 -070088 if (nread < count) {
89 Error("Can't read enough: %d, not %d\n", nread, count);
Bill Richardsonf1372d92010-06-11 09:15:55 -070090 goto error_free;
Bill Richardsonc4e92af2010-10-12 07:33:15 -070091 }
Bill Richardsonf1372d92010-06-11 09:15:55 -070092
93 return CGPT_OK;
94
95error_free:
96 free(*buf);
97 *buf = 0;
98 return CGPT_FAILED;
99}
100
101
102int ReadPMBR(struct drive *drive) {
103 if (-1 == lseek(drive->fd, 0, SEEK_SET))
104 return CGPT_FAILED;
105
106 int nread = read(drive->fd, &drive->pmbr, sizeof(struct pmbr));
107 if (nread != sizeof(struct pmbr))
108 return CGPT_FAILED;
109
110 return CGPT_OK;
111}
112
113int WritePMBR(struct drive *drive) {
114 if (-1 == lseek(drive->fd, 0, SEEK_SET))
115 return CGPT_FAILED;
116
117 int nwrote = write(drive->fd, &drive->pmbr, sizeof(struct pmbr));
118 if (nwrote != sizeof(struct pmbr))
119 return CGPT_FAILED;
120
121 return CGPT_OK;
122}
123
124/* Saves sectors to 'fd'.
125 *
126 * fd -- file descriptot.
127 * buf -- pointer to buffer
128 * sector -- starting sector offset
129 * sector_bytes -- bytes per sector
130 * sector_count -- number of sector to save
131 *
132 * Returns CGPT_OK for successful, CGPT_FAILED for failed.
133 */
134static int Save(const int fd, const uint8_t *buf,
135 const uint64_t sector,
136 const uint64_t sector_bytes,
137 const uint64_t sector_count) {
138 int count; /* byte count to write */
139 int nwrote;
140
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700141 require(buf);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700142 count = sector_bytes * sector_count;
143
144 if (-1 == lseek(fd, sector * sector_bytes, SEEK_SET))
145 return CGPT_FAILED;
146
147 nwrote = write(fd, buf, count);
148 if (nwrote < count)
149 return CGPT_FAILED;
150
151 return CGPT_OK;
152}
153
154
155// Opens a block device or file, loads raw GPT data from it.
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700156//
Bill Richardsonf1372d92010-06-11 09:15:55 -0700157// Returns CGPT_FAILED if any error happens.
158// Returns CGPT_OK if success and information are stored in 'drive'. */
159int DriveOpen(const char *drive_path, struct drive *drive) {
160 struct stat stat;
161
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700162 require(drive_path);
163 require(drive);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700164
165 // Clear struct for proper error handling.
166 memset(drive, 0, sizeof(struct drive));
167
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700168 drive->fd = open(drive_path, O_RDWR | O_LARGEFILE | O_NOFOLLOW);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700169 if (drive->fd == -1) {
170 Error("Can't open %s: %s\n", drive_path, strerror(errno));
171 return CGPT_FAILED;
172 }
173
174 if (fstat(drive->fd, &stat) == -1) {
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700175 Error("Can't fstat %s: %s\n", drive_path, strerror(errno));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700176 goto error_close;
177 }
178 if ((stat.st_mode & S_IFMT) != S_IFREG) {
179 if (ioctl(drive->fd, BLKGETSIZE64, &drive->size) < 0) {
180 Error("Can't read drive size from %s: %s\n", drive_path, strerror(errno));
181 goto error_close;
182 }
183 if (ioctl(drive->fd, BLKSSZGET, &drive->gpt.sector_bytes) < 0) {
184 Error("Can't read sector size from %s: %s\n",
185 drive_path, strerror(errno));
186 goto error_close;
187 }
188 } else {
189 drive->gpt.sector_bytes = 512; /* bytes */
190 drive->size = stat.st_size;
191 }
192 if (drive->size % drive->gpt.sector_bytes) {
193 Error("Media size (%llu) is not a multiple of sector size(%d)\n",
194 (long long unsigned int)drive->size, drive->gpt.sector_bytes);
195 goto error_close;
196 }
197 drive->gpt.drive_sectors = drive->size / drive->gpt.sector_bytes;
198
199 // Read the data.
200 if (CGPT_OK != Load(drive->fd, &drive->gpt.primary_header,
201 GPT_PMBR_SECTOR,
202 drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
203 goto error_close;
204 }
205 if (CGPT_OK != Load(drive->fd, &drive->gpt.secondary_header,
206 drive->gpt.drive_sectors - GPT_PMBR_SECTOR,
207 drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
208 goto error_close;
209 }
210 if (CGPT_OK != Load(drive->fd, &drive->gpt.primary_entries,
211 GPT_PMBR_SECTOR + GPT_HEADER_SECTOR,
212 drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
213 goto error_close;
214 }
215 if (CGPT_OK != Load(drive->fd, &drive->gpt.secondary_entries,
216 drive->gpt.drive_sectors - GPT_HEADER_SECTOR
217 - GPT_ENTRIES_SECTORS,
218 drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
219 goto error_close;
220 }
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700221
Bill Richardsonf1372d92010-06-11 09:15:55 -0700222 // We just load the data. Caller must validate it.
223 return CGPT_OK;
224
225error_close:
226 (void) DriveClose(drive, 0);
227 return CGPT_FAILED;
228}
229
230
231int DriveClose(struct drive *drive, int update_as_needed) {
232 int errors = 0;
233
234 if (update_as_needed) {
235 if (drive->gpt.modified & GPT_MODIFIED_HEADER1) {
236 if (CGPT_OK != Save(drive->fd, drive->gpt.primary_header,
237 GPT_PMBR_SECTOR,
238 drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
239 errors++;
240 Error("Cannot write primary header: %s\n", strerror(errno));
241 }
242 }
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700243
Bill Richardsonf1372d92010-06-11 09:15:55 -0700244 if (drive->gpt.modified & GPT_MODIFIED_HEADER2) {
245 if(CGPT_OK != Save(drive->fd, drive->gpt.secondary_header,
246 drive->gpt.drive_sectors - GPT_PMBR_SECTOR,
247 drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
248 errors++;
249 Error("Cannot write secondary header: %s\n", strerror(errno));
250 }
251 }
252 if (drive->gpt.modified & GPT_MODIFIED_ENTRIES1) {
253 if (CGPT_OK != Save(drive->fd, drive->gpt.primary_entries,
254 GPT_PMBR_SECTOR + GPT_HEADER_SECTOR,
255 drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
256 errors++;
257 Error("Cannot write primary entries: %s\n", strerror(errno));
258 }
259 }
260 if (drive->gpt.modified & GPT_MODIFIED_ENTRIES2) {
261 if (CGPT_OK != Save(drive->fd, drive->gpt.secondary_entries,
262 drive->gpt.drive_sectors - GPT_HEADER_SECTOR
263 - GPT_ENTRIES_SECTORS,
264 drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
265 errors++;
266 Error("Cannot write secondary entries: %s\n", strerror(errno));
267 }
268 }
269 }
270
271 close(drive->fd);
272
273 if (drive->gpt.primary_header)
274 free(drive->gpt.primary_header);
275 drive->gpt.primary_header = 0;
276 if (drive->gpt.primary_entries)
277 free(drive->gpt.primary_entries);
278 drive->gpt.primary_entries = 0;
279 if (drive->gpt.secondary_header)
280 free(drive->gpt.secondary_header);
281 drive->gpt.secondary_header = 0;
282 if (drive->gpt.secondary_entries)
283 free(drive->gpt.secondary_entries);
284 drive->gpt.secondary_entries = 0;
285
286 return errors ? CGPT_FAILED : CGPT_OK;
287}
288
289
Bill Richardsonf1372d92010-06-11 09:15:55 -0700290/* GUID conversion functions. Accepted format:
291 *
292 * "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
293 *
294 * Returns CGPT_OK if parsing is successful; otherwise CGPT_FAILED.
295 */
296int StrToGuid(const char *str, Guid *guid) {
297 uint32_t time_low;
298 uint16_t time_mid;
299 uint16_t time_high_and_version;
300 unsigned int chunk[11];
301
302 if (11 != sscanf(str, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
303 chunk+0,
304 chunk+1,
305 chunk+2,
306 chunk+3,
307 chunk+4,
308 chunk+5,
309 chunk+6,
310 chunk+7,
311 chunk+8,
312 chunk+9,
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700313 chunk+10)) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700314 printf("FAILED\n");
315 return CGPT_FAILED;
316 }
317
318 time_low = chunk[0] & 0xffffffff;
319 time_mid = chunk[1] & 0xffff;
320 time_high_and_version = chunk[2] & 0xffff;
321
322 guid->u.Uuid.time_low = htole32(time_low);
323 guid->u.Uuid.time_mid = htole16(time_mid);
324 guid->u.Uuid.time_high_and_version = htole16(time_high_and_version);
325
326 guid->u.Uuid.clock_seq_high_and_reserved = chunk[3] & 0xff;
327 guid->u.Uuid.clock_seq_low = chunk[4] & 0xff;
328 guid->u.Uuid.node[0] = chunk[5] & 0xff;
329 guid->u.Uuid.node[1] = chunk[6] & 0xff;
330 guid->u.Uuid.node[2] = chunk[7] & 0xff;
331 guid->u.Uuid.node[3] = chunk[8] & 0xff;
332 guid->u.Uuid.node[4] = chunk[9] & 0xff;
333 guid->u.Uuid.node[5] = chunk[10] & 0xff;
334
335 return CGPT_OK;
336}
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700337void GuidToStr(const Guid *guid, char *str, unsigned int buflen) {
338 require(buflen >= GUID_STRLEN);
339 require(snprintf(str, buflen,
340 "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
341 le32toh(guid->u.Uuid.time_low),
342 le16toh(guid->u.Uuid.time_mid),
343 le16toh(guid->u.Uuid.time_high_and_version),
344 guid->u.Uuid.clock_seq_high_and_reserved,
345 guid->u.Uuid.clock_seq_low,
346 guid->u.Uuid.node[0], guid->u.Uuid.node[1],
347 guid->u.Uuid.node[2], guid->u.Uuid.node[3],
348 guid->u.Uuid.node[4], guid->u.Uuid.node[5]) == GUID_STRLEN-1);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700349}
350
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700351/* Convert possibly unterminated UTF16 string to UTF8.
352 * Caller must prepare enough space for UTF8, which could be up to
Chris Sosae4171852010-11-19 05:59:53 -0800353 * twice the number of UTF16 chars plus the terminating '\0'.
354 * FIXME(wfrichar): The original implementation had security issues. As a
355 * temporary fix, I'm making this ONLY support ASCII codepoints. Bug 7542
356 * (http://code.google.com/p/chromium-os/issues/detail?id=7542) is filed to fix
357 * this.
Bill Richardsonf1372d92010-06-11 09:15:55 -0700358 */
Chris Sosae4171852010-11-19 05:59:53 -0800359void UTF16ToUTF8(const uint16_t *utf16, unsigned int maxinput,
360 uint8_t *utf8, unsigned int maxoutput)
Bill Richardsonf1372d92010-06-11 09:15:55 -0700361{
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700362 size_t s16idx, s8idx;
Chris Sosae4171852010-11-19 05:59:53 -0800363 uint32_t utfchar;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700364
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700365 if (!utf16 || !maxinput || !utf8 || !maxoutput)
Chris Sosae4171852010-11-19 05:59:53 -0800366 return;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700367
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700368 maxoutput--; /* plan for termination now */
369
370 for (s16idx = s8idx = 0;
371 s16idx < maxinput && utf16[s16idx] && maxoutput;
Chris Sosae4171852010-11-19 05:59:53 -0800372 s16idx++, maxoutput--) {
373 utfchar = le16toh(utf16[s16idx]);
374 utf8[s8idx++] = utfchar & 0x7F;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700375 }
376 utf8[s8idx++] = 0;
377}
378
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700379/* Convert UTF8 string to UTF16. The UTF8 string must be null-terminated.
380 * Caller must prepare enough space for UTF16, including a terminating 0x0000.
Chris Sosae4171852010-11-19 05:59:53 -0800381 * FIXME(wfrichar): The original implementation had security issues. As a
382 * temporary fix, I'm making this ONLY support ASCII codepoints. Bug 7542
383 * (http://code.google.com/p/chromium-os/issues/detail?id=7542) is filed to fix
384 * this.
Bill Richardsonf1372d92010-06-11 09:15:55 -0700385 */
Chris Sosae4171852010-11-19 05:59:53 -0800386void UTF8ToUTF16(const uint8_t *utf8, uint16_t *utf16, unsigned int maxoutput)
Bill Richardsonf1372d92010-06-11 09:15:55 -0700387{
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700388 size_t s16idx, s8idx;
Chris Sosae4171852010-11-19 05:59:53 -0800389 uint32_t utfchar;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700390
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700391 if (!utf8 || !utf16 || !maxoutput)
Chris Sosae4171852010-11-19 05:59:53 -0800392 return;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700393
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700394 maxoutput--; /* plan for termination */
395
396 for (s8idx = s16idx = 0;
397 utf8[s8idx] && maxoutput;
Chris Sosae4171852010-11-19 05:59:53 -0800398 s8idx++, maxoutput--) {
399 utfchar = utf8[s8idx];
400 utf16[s16idx++] = utfchar & 0x7F;
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700401 }
402 utf16[s16idx++] = 0;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700403}
404
405struct {
406 Guid type;
407 char *name;
408 char *description;
409} supported_types[] = {
410 {GPT_ENT_TYPE_CHROMEOS_KERNEL, "kernel", "ChromeOS kernel"},
411 {GPT_ENT_TYPE_CHROMEOS_ROOTFS, "rootfs", "ChromeOS rootfs"},
412 {GPT_ENT_TYPE_LINUX_DATA, "data", "Linux data"},
413 {GPT_ENT_TYPE_CHROMEOS_RESERVED, "reserved", "ChromeOS reserved"},
414 {GPT_ENT_TYPE_EFI, "efi", "EFI System Partition"},
415 {GPT_ENT_TYPE_UNUSED, "unused", "Unused (nonexistent) partition"},
416};
417
418/* Resolves human-readable GPT type.
419 * Returns CGPT_OK if found.
420 * Returns CGPT_FAILED if no known type found. */
421int ResolveType(const Guid *type, char *buf) {
422 int i;
423 for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
424 if (!memcmp(type, &supported_types[i].type, sizeof(Guid))) {
425 strcpy(buf, supported_types[i].description);
426 return CGPT_OK;
427 }
428 }
429 return CGPT_FAILED;
430}
431
432int SupportedType(const char *name, Guid *type) {
433 int i;
434 for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
435 if (!strcmp(name, supported_types[i].name)) {
436 memcpy(type, &supported_types[i].type, sizeof(Guid));
437 return CGPT_OK;
438 }
439 }
440 return CGPT_FAILED;
441}
442
443void PrintTypes(void) {
444 int i;
445 printf("The partition type may also be given as one of these aliases:\n\n");
446 for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
447 printf(" %-10s %s\n", supported_types[i].name,
448 supported_types[i].description);
449 }
450 printf("\n");
451}
452
453uint32_t GetNumberOfEntries(const GptData *gpt) {
454 GptHeader *header = 0;
455 if (gpt->valid_headers & MASK_PRIMARY)
456 header = (GptHeader*)gpt->primary_header;
457 else if (gpt->valid_headers & MASK_SECONDARY)
458 header = (GptHeader*)gpt->secondary_header;
459 else
460 return 0;
461 return header->number_of_entries;
462}
463
464static uint32_t GetSizeOfEntries(const GptData *gpt) {
465 GptHeader *header = 0;
466 if (gpt->valid_headers & MASK_PRIMARY)
467 header = (GptHeader*)gpt->primary_header;
468 else if (gpt->valid_headers & MASK_SECONDARY)
469 header = (GptHeader*)gpt->secondary_header;
470 else
471 return 0;
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700472 return header->size_of_entry;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700473}
474
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700475GptEntry *GetEntry(GptData *gpt, int secondary, uint32_t entry_index) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700476 uint8_t *entries;
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700477 uint32_t stride = GetSizeOfEntries(gpt);
478 require(stride);
479 require(entry_index < GetNumberOfEntries(gpt));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700480
481 if (secondary == PRIMARY) {
482 entries = gpt->primary_entries;
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800483 } else if (secondary == SECONDARY) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700484 entries = gpt->secondary_entries;
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800485 } else { /* ANY_VALID */
486 require(secondary == ANY_VALID);
487 if (gpt->valid_entries & MASK_PRIMARY) {
488 entries = gpt->primary_entries;
489 } else {
490 require(gpt->valid_entries & MASK_SECONDARY);
491 entries = gpt->secondary_entries;
492 }
Bill Richardsonf1372d92010-06-11 09:15:55 -0700493 }
494
495 return (GptEntry*)(&entries[stride * entry_index]);
496}
497
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700498void SetPriority(GptData *gpt, int secondary, uint32_t entry_index,
499 int priority) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700500 GptEntry *entry;
501 entry = GetEntry(gpt, secondary, entry_index);
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700502 require(priority >= 0 && priority <= CGPT_ATTRIBUTE_MAX_PRIORITY);
vbendebf7a45cc2010-06-21 08:44:16 -0700503 entry->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_PRIORITY_MASK;
504 entry->attrs.fields.gpt_att |= priority << CGPT_ATTRIBUTE_PRIORITY_OFFSET;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700505}
506
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700507int GetPriority(GptData *gpt, int secondary, uint32_t entry_index) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700508 GptEntry *entry;
509 entry = GetEntry(gpt, secondary, entry_index);
vbendebf7a45cc2010-06-21 08:44:16 -0700510 return (entry->attrs.fields.gpt_att & CGPT_ATTRIBUTE_PRIORITY_MASK) >>
511 CGPT_ATTRIBUTE_PRIORITY_OFFSET;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700512}
513
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700514void SetTries(GptData *gpt, int secondary, uint32_t entry_index, int tries) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700515 GptEntry *entry;
516 entry = GetEntry(gpt, secondary, entry_index);
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700517 require(tries >= 0 && tries <= CGPT_ATTRIBUTE_MAX_TRIES);
vbendebf7a45cc2010-06-21 08:44:16 -0700518 entry->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_TRIES_MASK;
519 entry->attrs.fields.gpt_att |= tries << CGPT_ATTRIBUTE_TRIES_OFFSET;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700520}
521
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700522int GetTries(GptData *gpt, int secondary, uint32_t entry_index) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700523 GptEntry *entry;
524 entry = GetEntry(gpt, secondary, entry_index);
vbendebf7a45cc2010-06-21 08:44:16 -0700525 return (entry->attrs.fields.gpt_att & CGPT_ATTRIBUTE_TRIES_MASK) >>
526 CGPT_ATTRIBUTE_TRIES_OFFSET;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700527}
528
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700529void SetSuccessful(GptData *gpt, int secondary, uint32_t entry_index,
530 int success) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700531 GptEntry *entry;
532 entry = GetEntry(gpt, secondary, entry_index);
533
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700534 require(success >= 0 && success <= CGPT_ATTRIBUTE_MAX_SUCCESSFUL);
vbendebf7a45cc2010-06-21 08:44:16 -0700535 entry->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_SUCCESSFUL_MASK;
536 entry->attrs.fields.gpt_att |= success << CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700537}
538
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700539int GetSuccessful(GptData *gpt, int secondary, uint32_t entry_index) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700540 GptEntry *entry;
541 entry = GetEntry(gpt, secondary, entry_index);
vbendebf7a45cc2010-06-21 08:44:16 -0700542 return (entry->attrs.fields.gpt_att & CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >>
Bill Richardsonf1372d92010-06-11 09:15:55 -0700543 CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
544}
545
546
547#define TOSTRING(A) #A
548const char *GptError(int errnum) {
549 const char *error_string[] = {
550 TOSTRING(GPT_SUCCESS),
551 TOSTRING(GPT_ERROR_NO_VALID_KERNEL),
552 TOSTRING(GPT_ERROR_INVALID_HEADERS),
553 TOSTRING(GPT_ERROR_INVALID_ENTRIES),
554 TOSTRING(GPT_ERROR_INVALID_SECTOR_SIZE),
555 TOSTRING(GPT_ERROR_INVALID_SECTOR_NUMBER),
556 TOSTRING(GPT_ERROR_INVALID_UPDATE_TYPE)
557 };
558 if (errnum < 0 || errnum >= ARRAY_COUNT(error_string))
559 return "<illegal value>";
560 return error_string[errnum];
561}
562
563/* Update CRC value if necessary. */
564void UpdateCrc(GptData *gpt) {
565 GptHeader *primary_header, *secondary_header;
566
567 primary_header = (GptHeader*)gpt->primary_header;
568 secondary_header = (GptHeader*)gpt->secondary_header;
569
570 if (gpt->modified & GPT_MODIFIED_ENTRIES1) {
571 primary_header->entries_crc32 =
572 Crc32(gpt->primary_entries, TOTAL_ENTRIES_SIZE);
573 }
574 if (gpt->modified & GPT_MODIFIED_ENTRIES2) {
575 secondary_header->entries_crc32 =
576 Crc32(gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
577 }
578 if (gpt->modified & GPT_MODIFIED_HEADER1) {
579 primary_header->header_crc32 = 0;
580 primary_header->header_crc32 = Crc32(
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800581 (const uint8_t *)primary_header, sizeof(GptHeader));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700582 }
583 if (gpt->modified & GPT_MODIFIED_HEADER2) {
584 secondary_header->header_crc32 = 0;
585 secondary_header->header_crc32 = Crc32(
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800586 (const uint8_t *)secondary_header, sizeof(GptHeader));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700587 }
588}
589/* Two headers are NOT bitwise identical. For example, my_lba pointers to header
590 * itself so that my_lba in primary and secondary is definitely different.
591 * Only the following fields should be identical.
592 *
593 * first_usable_lba
594 * last_usable_lba
595 * number_of_entries
596 * size_of_entry
597 * disk_uuid
598 *
599 * If any of above field are not matched, overwrite secondary with primary since
600 * we always trust primary.
601 * If any one of header is invalid, copy from another. */
602int IsSynonymous(const GptHeader* a, const GptHeader* b) {
603 if ((a->first_usable_lba == b->first_usable_lba) &&
604 (a->last_usable_lba == b->last_usable_lba) &&
605 (a->number_of_entries == b->number_of_entries) &&
606 (a->size_of_entry == b->size_of_entry) &&
607 (!memcmp(&a->disk_uuid, &b->disk_uuid, sizeof(Guid))))
608 return 1;
609 return 0;
610}
611
612/* Primary entries and secondary entries should be bitwise identical.
613 * If two entries tables are valid, compare them. If not the same,
614 * overwrites secondary with primary (primary always has higher priority),
615 * and marks secondary as modified.
616 * If only one is valid, overwrites invalid one.
617 * If all are invalid, does nothing.
618 * This function returns bit masks for GptData.modified field.
619 * Note that CRC is NOT re-computed in this function.
620 */
621uint8_t RepairEntries(GptData *gpt, const uint32_t valid_entries) {
622 if (valid_entries == MASK_BOTH) {
623 if (memcmp(gpt->primary_entries, gpt->secondary_entries,
624 TOTAL_ENTRIES_SIZE)) {
625 memcpy(gpt->secondary_entries, gpt->primary_entries, TOTAL_ENTRIES_SIZE);
626 return GPT_MODIFIED_ENTRIES2;
627 }
628 } else if (valid_entries == MASK_PRIMARY) {
629 memcpy(gpt->secondary_entries, gpt->primary_entries, TOTAL_ENTRIES_SIZE);
630 return GPT_MODIFIED_ENTRIES2;
631 } else if (valid_entries == MASK_SECONDARY) {
632 memcpy(gpt->primary_entries, gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
633 return GPT_MODIFIED_ENTRIES1;
634 }
635
636 return 0;
637}
638
639/* The above five fields are shared between primary and secondary headers.
640 * We can recover one header from another through copying those fields. */
641void CopySynonymousParts(GptHeader* target, const GptHeader* source) {
642 target->first_usable_lba = source->first_usable_lba;
643 target->last_usable_lba = source->last_usable_lba;
644 target->number_of_entries = source->number_of_entries;
645 target->size_of_entry = source->size_of_entry;
646 memcpy(&target->disk_uuid, &source->disk_uuid, sizeof(Guid));
647}
648
649/* This function repairs primary and secondary headers if possible.
650 * If both headers are valid (CRC32 is correct) but
651 * a) indicate inconsistent usable LBA ranges,
652 * b) inconsistent partition entry size and number,
653 * c) inconsistent disk_uuid,
654 * we will use the primary header to overwrite secondary header.
655 * If primary is invalid (CRC32 is wrong), then we repair it from secondary.
656 * If secondary is invalid (CRC32 is wrong), then we repair it from primary.
657 * This function returns the bitmasks for modified header.
658 * Note that CRC value is NOT re-computed in this function. UpdateCrc() will
659 * do it later.
660 */
661uint8_t RepairHeader(GptData *gpt, const uint32_t valid_headers) {
662 GptHeader *primary_header, *secondary_header;
663
664 primary_header = (GptHeader*)gpt->primary_header;
665 secondary_header = (GptHeader*)gpt->secondary_header;
666
667 if (valid_headers == MASK_BOTH) {
668 if (!IsSynonymous(primary_header, secondary_header)) {
669 CopySynonymousParts(secondary_header, primary_header);
670 return GPT_MODIFIED_HEADER2;
671 }
672 } else if (valid_headers == MASK_PRIMARY) {
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800673 memcpy(secondary_header, primary_header, sizeof(GptHeader));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700674 secondary_header->my_lba = gpt->drive_sectors - 1; /* the last sector */
675 secondary_header->alternate_lba = primary_header->my_lba;
676 secondary_header->entries_lba = secondary_header->my_lba -
677 GPT_ENTRIES_SECTORS;
678 return GPT_MODIFIED_HEADER2;
679 } else if (valid_headers == MASK_SECONDARY) {
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800680 memcpy(primary_header, secondary_header, sizeof(GptHeader));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700681 primary_header->my_lba = GPT_PMBR_SECTOR; /* the second sector on drive */
682 primary_header->alternate_lba = secondary_header->my_lba;
683 primary_header->entries_lba = primary_header->my_lba + GPT_HEADER_SECTOR;
684 return GPT_MODIFIED_HEADER1;
685 }
686
687 return 0;
688}
689
690
691int IsZero(const Guid *gp) {
692 return (0 == memcmp(gp, &guid_unused, sizeof(Guid)));
693}
694
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700695void PMBRToStr(struct pmbr *pmbr, char *str, unsigned int buflen) {
696 char buf[GUID_STRLEN];
Bill Richardsonf1372d92010-06-11 09:15:55 -0700697 if (IsZero(&pmbr->boot_guid)) {
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700698 require(snprintf(str, buflen, "PMBR") < buflen);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700699 } else {
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700700 GuidToStr(&pmbr->boot_guid, buf, sizeof(buf));
701 require(snprintf(str, buflen, "PMBR (Boot GUID: %s)", buf) < buflen);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700702 }
703}