blob: dec26d1210e1328e88c8678c65def1f2d95f34c5 [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
Bill Richardsonf1372d92010-06-11 09:15:55 -07009#include <errno.h>
10#include <fcntl.h>
11#include <getopt.h>
Bill Richardsonc4e92af2010-10-12 07:33:15 -070012#include <stdarg.h>
Bill Richardsonf1372d92010-06-11 09:15:55 -070013#include <stdint.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <sys/ioctl.h>
18#include <sys/mount.h>
19#include <sys/stat.h>
20#include <sys/types.h>
21#include <unistd.h>
Bill Richardsonf1372d92010-06-11 09:15:55 -070022
Bill Richardson0c3ba242013-03-29 11:09:30 -070023#include "cgpt.h"
Bill Richardsonf1372d92010-06-11 09:15:55 -070024#include "cgptlib_internal.h"
25#include "crc32.h"
Bill Richardson0c3ba242013-03-29 11:09:30 -070026#include "vboot_host.h"
Bill Richardsonf1372d92010-06-11 09:15:55 -070027
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 Richardson23429d32012-04-30 11:33:13 -0700156// mode should be O_RDONLY or O_RDWR
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700157//
Bill Richardsonf1372d92010-06-11 09:15:55 -0700158// Returns CGPT_FAILED if any error happens.
159// Returns CGPT_OK if success and information are stored in 'drive'. */
Bill Richardson23429d32012-04-30 11:33:13 -0700160int DriveOpen(const char *drive_path, struct drive *drive, int mode) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700161 struct stat stat;
162
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700163 require(drive_path);
164 require(drive);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700165
166 // Clear struct for proper error handling.
167 memset(drive, 0, sizeof(struct drive));
168
Bill Richardson23429d32012-04-30 11:33:13 -0700169 drive->fd = open(drive_path, mode | O_LARGEFILE | O_NOFOLLOW);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700170 if (drive->fd == -1) {
171 Error("Can't open %s: %s\n", drive_path, strerror(errno));
172 return CGPT_FAILED;
173 }
174
175 if (fstat(drive->fd, &stat) == -1) {
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700176 Error("Can't fstat %s: %s\n", drive_path, strerror(errno));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700177 goto error_close;
178 }
179 if ((stat.st_mode & S_IFMT) != S_IFREG) {
180 if (ioctl(drive->fd, BLKGETSIZE64, &drive->size) < 0) {
181 Error("Can't read drive size from %s: %s\n", drive_path, strerror(errno));
182 goto error_close;
183 }
184 if (ioctl(drive->fd, BLKSSZGET, &drive->gpt.sector_bytes) < 0) {
185 Error("Can't read sector size from %s: %s\n",
186 drive_path, strerror(errno));
187 goto error_close;
188 }
189 } else {
190 drive->gpt.sector_bytes = 512; /* bytes */
191 drive->size = stat.st_size;
192 }
193 if (drive->size % drive->gpt.sector_bytes) {
194 Error("Media size (%llu) is not a multiple of sector size(%d)\n",
195 (long long unsigned int)drive->size, drive->gpt.sector_bytes);
196 goto error_close;
197 }
198 drive->gpt.drive_sectors = drive->size / drive->gpt.sector_bytes;
199
200 // Read the data.
201 if (CGPT_OK != Load(drive->fd, &drive->gpt.primary_header,
202 GPT_PMBR_SECTOR,
203 drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
204 goto error_close;
205 }
206 if (CGPT_OK != Load(drive->fd, &drive->gpt.secondary_header,
207 drive->gpt.drive_sectors - GPT_PMBR_SECTOR,
208 drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
209 goto error_close;
210 }
211 if (CGPT_OK != Load(drive->fd, &drive->gpt.primary_entries,
212 GPT_PMBR_SECTOR + GPT_HEADER_SECTOR,
213 drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
214 goto error_close;
215 }
216 if (CGPT_OK != Load(drive->fd, &drive->gpt.secondary_entries,
217 drive->gpt.drive_sectors - GPT_HEADER_SECTOR
218 - GPT_ENTRIES_SECTORS,
219 drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
220 goto error_close;
221 }
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700222
Bill Richardsonf1372d92010-06-11 09:15:55 -0700223 // We just load the data. Caller must validate it.
224 return CGPT_OK;
225
226error_close:
227 (void) DriveClose(drive, 0);
228 return CGPT_FAILED;
229}
230
231
232int DriveClose(struct drive *drive, int update_as_needed) {
233 int errors = 0;
234
235 if (update_as_needed) {
236 if (drive->gpt.modified & GPT_MODIFIED_HEADER1) {
237 if (CGPT_OK != Save(drive->fd, drive->gpt.primary_header,
238 GPT_PMBR_SECTOR,
239 drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
240 errors++;
241 Error("Cannot write primary header: %s\n", strerror(errno));
242 }
243 }
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700244
Bill Richardsonf1372d92010-06-11 09:15:55 -0700245 if (drive->gpt.modified & GPT_MODIFIED_HEADER2) {
246 if(CGPT_OK != Save(drive->fd, drive->gpt.secondary_header,
247 drive->gpt.drive_sectors - GPT_PMBR_SECTOR,
248 drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
249 errors++;
250 Error("Cannot write secondary header: %s\n", strerror(errno));
251 }
252 }
253 if (drive->gpt.modified & GPT_MODIFIED_ENTRIES1) {
254 if (CGPT_OK != Save(drive->fd, drive->gpt.primary_entries,
255 GPT_PMBR_SECTOR + GPT_HEADER_SECTOR,
256 drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
257 errors++;
258 Error("Cannot write primary entries: %s\n", strerror(errno));
259 }
260 }
261 if (drive->gpt.modified & GPT_MODIFIED_ENTRIES2) {
262 if (CGPT_OK != Save(drive->fd, drive->gpt.secondary_entries,
263 drive->gpt.drive_sectors - GPT_HEADER_SECTOR
264 - GPT_ENTRIES_SECTORS,
265 drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
266 errors++;
267 Error("Cannot write secondary entries: %s\n", strerror(errno));
268 }
269 }
270 }
271
Louis Yung-Chieh Lo57cdad32013-01-16 11:52:17 +0800272 // Sync early! Only sync file descriptor here, and leave the whole system sync
273 // outside cgpt because whole system sync would trigger tons of disk accesses
274 // and timeout tests.
275 fsync(drive->fd);
276
Bill Richardsonf1372d92010-06-11 09:15:55 -0700277 close(drive->fd);
278
279 if (drive->gpt.primary_header)
280 free(drive->gpt.primary_header);
281 drive->gpt.primary_header = 0;
282 if (drive->gpt.primary_entries)
283 free(drive->gpt.primary_entries);
284 drive->gpt.primary_entries = 0;
285 if (drive->gpt.secondary_header)
286 free(drive->gpt.secondary_header);
287 drive->gpt.secondary_header = 0;
288 if (drive->gpt.secondary_entries)
289 free(drive->gpt.secondary_entries);
290 drive->gpt.secondary_entries = 0;
291
292 return errors ? CGPT_FAILED : CGPT_OK;
293}
294
295
Bill Richardsonf1372d92010-06-11 09:15:55 -0700296/* GUID conversion functions. Accepted format:
297 *
298 * "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
299 *
300 * Returns CGPT_OK if parsing is successful; otherwise CGPT_FAILED.
301 */
302int StrToGuid(const char *str, Guid *guid) {
303 uint32_t time_low;
304 uint16_t time_mid;
305 uint16_t time_high_and_version;
306 unsigned int chunk[11];
307
308 if (11 != sscanf(str, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
309 chunk+0,
310 chunk+1,
311 chunk+2,
312 chunk+3,
313 chunk+4,
314 chunk+5,
315 chunk+6,
316 chunk+7,
317 chunk+8,
318 chunk+9,
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700319 chunk+10)) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700320 printf("FAILED\n");
321 return CGPT_FAILED;
322 }
323
324 time_low = chunk[0] & 0xffffffff;
325 time_mid = chunk[1] & 0xffff;
326 time_high_and_version = chunk[2] & 0xffff;
327
328 guid->u.Uuid.time_low = htole32(time_low);
329 guid->u.Uuid.time_mid = htole16(time_mid);
330 guid->u.Uuid.time_high_and_version = htole16(time_high_and_version);
331
332 guid->u.Uuid.clock_seq_high_and_reserved = chunk[3] & 0xff;
333 guid->u.Uuid.clock_seq_low = chunk[4] & 0xff;
334 guid->u.Uuid.node[0] = chunk[5] & 0xff;
335 guid->u.Uuid.node[1] = chunk[6] & 0xff;
336 guid->u.Uuid.node[2] = chunk[7] & 0xff;
337 guid->u.Uuid.node[3] = chunk[8] & 0xff;
338 guid->u.Uuid.node[4] = chunk[9] & 0xff;
339 guid->u.Uuid.node[5] = chunk[10] & 0xff;
340
341 return CGPT_OK;
342}
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700343void GuidToStr(const Guid *guid, char *str, unsigned int buflen) {
344 require(buflen >= GUID_STRLEN);
345 require(snprintf(str, buflen,
346 "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
347 le32toh(guid->u.Uuid.time_low),
348 le16toh(guid->u.Uuid.time_mid),
349 le16toh(guid->u.Uuid.time_high_and_version),
350 guid->u.Uuid.clock_seq_high_and_reserved,
351 guid->u.Uuid.clock_seq_low,
352 guid->u.Uuid.node[0], guid->u.Uuid.node[1],
353 guid->u.Uuid.node[2], guid->u.Uuid.node[3],
354 guid->u.Uuid.node[4], guid->u.Uuid.node[5]) == GUID_STRLEN-1);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700355}
356
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700357/* Convert possibly unterminated UTF16 string to UTF8.
358 * Caller must prepare enough space for UTF8, which could be up to
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800359 * twice the byte length of UTF16 string plus the terminating '\0'.
360 * See the following table for encoding lengths.
361 *
362 * Code point UTF16 UTF8
363 * 0x0000-0x007F 2 bytes 1 byte
364 * 0x0080-0x07FF 2 bytes 2 bytes
365 * 0x0800-0xFFFF 2 bytes 3 bytes
366 * 0x10000-0x10FFFF 4 bytes 4 bytes
367 *
368 * This function uses a simple state meachine to convert UTF-16 char(s) to
369 * a code point. Once a code point is parsed out, the state machine throws
370 * out sequencial UTF-8 chars in one time.
371 *
372 * Return: CGPT_OK --- all character are converted successfully.
373 * CGPT_FAILED --- convert error, i.e. output buffer is too short.
Bill Richardsonf1372d92010-06-11 09:15:55 -0700374 */
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800375int UTF16ToUTF8(const uint16_t *utf16, unsigned int maxinput,
376 uint8_t *utf8, unsigned int maxoutput)
Bill Richardsonf1372d92010-06-11 09:15:55 -0700377{
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700378 size_t s16idx, s8idx;
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800379 uint32_t code_point = 0;
380 int code_point_ready = 1; // code point is ready to output.
381 int retval = CGPT_OK;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700382
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700383 if (!utf16 || !maxinput || !utf8 || !maxoutput)
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800384 return CGPT_FAILED;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700385
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700386 maxoutput--; /* plan for termination now */
387
388 for (s16idx = s8idx = 0;
389 s16idx < maxinput && utf16[s16idx] && maxoutput;
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800390 s16idx++) {
391 uint16_t codeunit = le16toh(utf16[s16idx]);
392
393 if (code_point_ready) {
394 if (codeunit >= 0xD800 && codeunit <= 0xDBFF) {
395 /* high surrogate, need the low surrogate. */
396 code_point_ready = 0;
397 code_point = (codeunit & 0x03FF) + 0x0040;
398 } else {
399 /* BMP char, output it. */
400 code_point = codeunit;
401 }
402 } else {
403 /* expect the low surrogate */
404 if (codeunit >= 0xDC00 && codeunit <= 0xDFFF) {
405 code_point = (code_point << 10) | (codeunit & 0x03FF);
406 code_point_ready = 1;
407 } else {
408 /* the second code unit is NOT the low surrogate. Unexpected. */
409 code_point_ready = 0;
410 retval = CGPT_FAILED;
411 break;
412 }
413 }
414
415 /* If UTF code point is ready, output it. */
416 if (code_point_ready) {
417 require(code_point <= 0x10FFFF);
418 if (code_point <= 0x7F && maxoutput >= 1) {
419 maxoutput -= 1;
420 utf8[s8idx++] = code_point & 0x7F;
421 } else if (code_point <= 0x7FF && maxoutput >= 2) {
422 maxoutput -= 2;
423 utf8[s8idx++] = 0xC0 | (code_point >> 6);
424 utf8[s8idx++] = 0x80 | (code_point & 0x3F);
425 } else if (code_point <= 0xFFFF && maxoutput >= 3) {
426 maxoutput -= 3;
427 utf8[s8idx++] = 0xE0 | (code_point >> 12);
428 utf8[s8idx++] = 0x80 | ((code_point >> 6) & 0x3F);
429 utf8[s8idx++] = 0x80 | (code_point & 0x3F);
430 } else if (code_point <= 0x10FFFF && maxoutput >= 4) {
431 maxoutput -= 4;
432 utf8[s8idx++] = 0xF0 | (code_point >> 18);
433 utf8[s8idx++] = 0x80 | ((code_point >> 12) & 0x3F);
434 utf8[s8idx++] = 0x80 | ((code_point >> 6) & 0x3F);
435 utf8[s8idx++] = 0x80 | (code_point & 0x3F);
436 } else {
437 /* buffer underrun */
438 retval = CGPT_FAILED;
439 break;
440 }
441 }
Bill Richardsonf1372d92010-06-11 09:15:55 -0700442 }
443 utf8[s8idx++] = 0;
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800444 return retval;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700445}
446
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700447/* Convert UTF8 string to UTF16. The UTF8 string must be null-terminated.
448 * Caller must prepare enough space for UTF16, including a terminating 0x0000.
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800449 * See the following table for encoding lengths. In any case, the caller
450 * just needs to prepare the byte length of UTF8 plus the terminating 0x0000.
451 *
452 * Code point UTF16 UTF8
453 * 0x0000-0x007F 2 bytes 1 byte
454 * 0x0080-0x07FF 2 bytes 2 bytes
455 * 0x0800-0xFFFF 2 bytes 3 bytes
456 * 0x10000-0x10FFFF 4 bytes 4 bytes
457 *
458 * This function converts UTF8 chars to a code point first. Then, convrts it
459 * to UTF16 code unit(s).
460 *
461 * Return: CGPT_OK --- all character are converted successfully.
462 * CGPT_FAILED --- convert error, i.e. output buffer is too short.
Bill Richardsonf1372d92010-06-11 09:15:55 -0700463 */
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800464int UTF8ToUTF16(const uint8_t *utf8, uint16_t *utf16, unsigned int maxoutput)
Bill Richardsonf1372d92010-06-11 09:15:55 -0700465{
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700466 size_t s16idx, s8idx;
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800467 uint32_t code_point = 0;
468 unsigned int expected_units = 1;
469 unsigned int decoded_units = 1;
470 int retval = CGPT_OK;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700471
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700472 if (!utf8 || !utf16 || !maxoutput)
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800473 return CGPT_FAILED;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700474
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700475 maxoutput--; /* plan for termination */
476
477 for (s8idx = s16idx = 0;
478 utf8[s8idx] && maxoutput;
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800479 s8idx++) {
480 uint8_t code_unit;
481 code_unit = utf8[s8idx];
482
483 if (expected_units != decoded_units) {
484 /* Trailing bytes of multi-byte character */
485 if ((code_unit & 0xC0) == 0x80) {
486 code_point = (code_point << 6) | (code_unit & 0x3F);
487 ++decoded_units;
488 } else {
489 /* Unexpected code unit. */
490 retval = CGPT_FAILED;
491 break;
492 }
493 } else {
494 /* parsing a new code point. */
495 decoded_units = 1;
496 if (code_unit <= 0x7F) {
497 code_point = code_unit;
498 expected_units = 1;
499 } else if (code_unit <= 0xBF) {
500 /* 0x80-0xBF must NOT be the heading byte unit of a new code point. */
501 retval = CGPT_FAILED;
502 break;
503 } else if (code_unit >= 0xC2 && code_unit <= 0xDF) {
504 code_point = code_unit & 0x1F;
505 expected_units = 2;
506 } else if (code_unit >= 0xE0 && code_unit <= 0xEF) {
507 code_point = code_unit & 0x0F;
508 expected_units = 3;
509 } else if (code_unit >= 0xF0 && code_unit <= 0xF4) {
510 code_point = code_unit & 0x07;
511 expected_units = 4;
512 } else {
513 /* illegal code unit: 0xC0-0xC1, 0xF5-0xFF */
514 retval = CGPT_FAILED;
515 break;
516 }
517 }
518
519 /* If no more unit is needed, output the UTF16 unit(s). */
520 if ((retval == CGPT_OK) &&
521 (expected_units == decoded_units)) {
522 /* Check if the encoding is the shortest possible UTF-8 sequence. */
523 switch (expected_units) {
524 case 2:
525 if (code_point <= 0x7F) retval = CGPT_FAILED;
526 break;
527 case 3:
528 if (code_point <= 0x7FF) retval = CGPT_FAILED;
529 break;
530 case 4:
531 if (code_point <= 0xFFFF) retval = CGPT_FAILED;
532 break;
533 }
534 if (retval == CGPT_FAILED) break; /* leave immediately */
535
536 if ((code_point <= 0xD7FF) ||
537 (code_point >= 0xE000 && code_point <= 0xFFFF)) {
538 utf16[s16idx++] = code_point;
539 maxoutput -= 1;
540 } else if (code_point >= 0x10000 && code_point <= 0x10FFFF &&
541 maxoutput >= 2) {
542 utf16[s16idx++] = 0xD800 | ((code_point >> 10) - 0x0040);
543 utf16[s16idx++] = 0xDC00 | (code_point & 0x03FF);
544 maxoutput -= 2;
545 } else {
546 /* Three possibilities fall into here. Both are failure cases.
547 * a. surrogate pair (non-BMP characters; 0xD800~0xDFFF)
548 * b. invalid code point > 0x10FFFF
549 * c. buffer underrun
550 */
551 retval = CGPT_FAILED;
552 break;
553 }
554 }
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700555 }
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800556
557 /* A null-terminator shows up before the UTF8 sequence ends. */
558 if (expected_units != decoded_units) {
559 retval = CGPT_FAILED;
560 }
561
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700562 utf16[s16idx++] = 0;
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800563 return retval;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700564}
565
Bill Richardson3430b322010-11-29 14:24:51 -0800566/* global types to compare against */
Gabe Black93cf15e2011-07-07 16:00:00 -0700567const Guid guid_chromeos_firmware = GPT_ENT_TYPE_CHROMEOS_FIRMWARE;
Bill Richardson3430b322010-11-29 14:24:51 -0800568const Guid guid_chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL;
569const Guid guid_chromeos_rootfs = GPT_ENT_TYPE_CHROMEOS_ROOTFS;
570const Guid guid_linux_data = GPT_ENT_TYPE_LINUX_DATA;
571const Guid guid_chromeos_reserved = GPT_ENT_TYPE_CHROMEOS_RESERVED;
572const Guid guid_efi = GPT_ENT_TYPE_EFI;
573const Guid guid_unused = GPT_ENT_TYPE_UNUSED;
574
575static struct {
576 const Guid *type;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700577 char *name;
578 char *description;
579} supported_types[] = {
Gabe Black93cf15e2011-07-07 16:00:00 -0700580 {&guid_chromeos_firmware, "firmware", "ChromeOS firmware"},
Bill Richardson3430b322010-11-29 14:24:51 -0800581 {&guid_chromeos_kernel, "kernel", "ChromeOS kernel"},
582 {&guid_chromeos_rootfs, "rootfs", "ChromeOS rootfs"},
583 {&guid_linux_data, "data", "Linux data"},
584 {&guid_chromeos_reserved, "reserved", "ChromeOS reserved"},
585 {&guid_efi, "efi", "EFI System Partition"},
586 {&guid_unused, "unused", "Unused (nonexistent) partition"},
Bill Richardsonf1372d92010-06-11 09:15:55 -0700587};
588
589/* Resolves human-readable GPT type.
590 * Returns CGPT_OK if found.
591 * Returns CGPT_FAILED if no known type found. */
592int ResolveType(const Guid *type, char *buf) {
593 int i;
594 for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
Bill Richardson3430b322010-11-29 14:24:51 -0800595 if (!memcmp(type, supported_types[i].type, sizeof(Guid))) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700596 strcpy(buf, supported_types[i].description);
597 return CGPT_OK;
598 }
599 }
600 return CGPT_FAILED;
601}
602
603int SupportedType(const char *name, Guid *type) {
604 int i;
605 for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
606 if (!strcmp(name, supported_types[i].name)) {
Bill Richardson3430b322010-11-29 14:24:51 -0800607 memcpy(type, supported_types[i].type, sizeof(Guid));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700608 return CGPT_OK;
609 }
610 }
611 return CGPT_FAILED;
612}
613
614void PrintTypes(void) {
615 int i;
616 printf("The partition type may also be given as one of these aliases:\n\n");
617 for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
618 printf(" %-10s %s\n", supported_types[i].name,
619 supported_types[i].description);
620 }
621 printf("\n");
622}
623
624uint32_t GetNumberOfEntries(const GptData *gpt) {
625 GptHeader *header = 0;
626 if (gpt->valid_headers & MASK_PRIMARY)
627 header = (GptHeader*)gpt->primary_header;
628 else if (gpt->valid_headers & MASK_SECONDARY)
629 header = (GptHeader*)gpt->secondary_header;
630 else
631 return 0;
632 return header->number_of_entries;
633}
634
635static uint32_t GetSizeOfEntries(const GptData *gpt) {
636 GptHeader *header = 0;
637 if (gpt->valid_headers & MASK_PRIMARY)
638 header = (GptHeader*)gpt->primary_header;
639 else if (gpt->valid_headers & MASK_SECONDARY)
640 header = (GptHeader*)gpt->secondary_header;
641 else
642 return 0;
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700643 return header->size_of_entry;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700644}
645
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700646GptEntry *GetEntry(GptData *gpt, int secondary, uint32_t entry_index) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700647 uint8_t *entries;
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700648 uint32_t stride = GetSizeOfEntries(gpt);
649 require(stride);
650 require(entry_index < GetNumberOfEntries(gpt));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700651
652 if (secondary == PRIMARY) {
653 entries = gpt->primary_entries;
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800654 } else if (secondary == SECONDARY) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700655 entries = gpt->secondary_entries;
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800656 } else { /* ANY_VALID */
657 require(secondary == ANY_VALID);
658 if (gpt->valid_entries & MASK_PRIMARY) {
659 entries = gpt->primary_entries;
660 } else {
661 require(gpt->valid_entries & MASK_SECONDARY);
662 entries = gpt->secondary_entries;
663 }
Bill Richardsonf1372d92010-06-11 09:15:55 -0700664 }
665
666 return (GptEntry*)(&entries[stride * entry_index]);
667}
668
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700669void SetPriority(GptData *gpt, int secondary, uint32_t entry_index,
670 int priority) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700671 GptEntry *entry;
672 entry = GetEntry(gpt, secondary, entry_index);
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700673 require(priority >= 0 && priority <= CGPT_ATTRIBUTE_MAX_PRIORITY);
vbendebf7a45cc2010-06-21 08:44:16 -0700674 entry->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_PRIORITY_MASK;
675 entry->attrs.fields.gpt_att |= priority << CGPT_ATTRIBUTE_PRIORITY_OFFSET;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700676}
677
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700678int GetPriority(GptData *gpt, int secondary, uint32_t entry_index) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700679 GptEntry *entry;
680 entry = GetEntry(gpt, secondary, entry_index);
vbendebf7a45cc2010-06-21 08:44:16 -0700681 return (entry->attrs.fields.gpt_att & CGPT_ATTRIBUTE_PRIORITY_MASK) >>
682 CGPT_ATTRIBUTE_PRIORITY_OFFSET;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700683}
684
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700685void SetTries(GptData *gpt, int secondary, uint32_t entry_index, int tries) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700686 GptEntry *entry;
687 entry = GetEntry(gpt, secondary, entry_index);
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700688 require(tries >= 0 && tries <= CGPT_ATTRIBUTE_MAX_TRIES);
vbendebf7a45cc2010-06-21 08:44:16 -0700689 entry->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_TRIES_MASK;
690 entry->attrs.fields.gpt_att |= tries << CGPT_ATTRIBUTE_TRIES_OFFSET;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700691}
692
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700693int GetTries(GptData *gpt, int secondary, uint32_t entry_index) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700694 GptEntry *entry;
695 entry = GetEntry(gpt, secondary, entry_index);
vbendebf7a45cc2010-06-21 08:44:16 -0700696 return (entry->attrs.fields.gpt_att & CGPT_ATTRIBUTE_TRIES_MASK) >>
697 CGPT_ATTRIBUTE_TRIES_OFFSET;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700698}
699
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700700void SetSuccessful(GptData *gpt, int secondary, uint32_t entry_index,
701 int success) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700702 GptEntry *entry;
703 entry = GetEntry(gpt, secondary, entry_index);
704
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700705 require(success >= 0 && success <= CGPT_ATTRIBUTE_MAX_SUCCESSFUL);
vbendebf7a45cc2010-06-21 08:44:16 -0700706 entry->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_SUCCESSFUL_MASK;
707 entry->attrs.fields.gpt_att |= success << CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700708}
709
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700710int GetSuccessful(GptData *gpt, int secondary, uint32_t entry_index) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700711 GptEntry *entry;
712 entry = GetEntry(gpt, secondary, entry_index);
vbendebf7a45cc2010-06-21 08:44:16 -0700713 return (entry->attrs.fields.gpt_att & CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >>
Bill Richardsonf1372d92010-06-11 09:15:55 -0700714 CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
715}
716
717
718#define TOSTRING(A) #A
719const char *GptError(int errnum) {
720 const char *error_string[] = {
721 TOSTRING(GPT_SUCCESS),
722 TOSTRING(GPT_ERROR_NO_VALID_KERNEL),
723 TOSTRING(GPT_ERROR_INVALID_HEADERS),
724 TOSTRING(GPT_ERROR_INVALID_ENTRIES),
725 TOSTRING(GPT_ERROR_INVALID_SECTOR_SIZE),
726 TOSTRING(GPT_ERROR_INVALID_SECTOR_NUMBER),
727 TOSTRING(GPT_ERROR_INVALID_UPDATE_TYPE)
728 };
729 if (errnum < 0 || errnum >= ARRAY_COUNT(error_string))
730 return "<illegal value>";
731 return error_string[errnum];
732}
733
734/* Update CRC value if necessary. */
735void UpdateCrc(GptData *gpt) {
736 GptHeader *primary_header, *secondary_header;
737
738 primary_header = (GptHeader*)gpt->primary_header;
739 secondary_header = (GptHeader*)gpt->secondary_header;
740
Stefan Reinauerb7b865c2012-08-23 15:06:25 -0700741 if (gpt->modified & GPT_MODIFIED_ENTRIES1 &&
742 memcmp(primary_header, GPT_HEADER_SIGNATURE2,
743 GPT_HEADER_SIGNATURE_SIZE)) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700744 primary_header->entries_crc32 =
745 Crc32(gpt->primary_entries, TOTAL_ENTRIES_SIZE);
746 }
747 if (gpt->modified & GPT_MODIFIED_ENTRIES2) {
748 secondary_header->entries_crc32 =
749 Crc32(gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
750 }
751 if (gpt->modified & GPT_MODIFIED_HEADER1) {
752 primary_header->header_crc32 = 0;
753 primary_header->header_crc32 = Crc32(
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800754 (const uint8_t *)primary_header, sizeof(GptHeader));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700755 }
756 if (gpt->modified & GPT_MODIFIED_HEADER2) {
757 secondary_header->header_crc32 = 0;
758 secondary_header->header_crc32 = Crc32(
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800759 (const uint8_t *)secondary_header, sizeof(GptHeader));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700760 }
761}
762/* Two headers are NOT bitwise identical. For example, my_lba pointers to header
763 * itself so that my_lba in primary and secondary is definitely different.
764 * Only the following fields should be identical.
765 *
766 * first_usable_lba
767 * last_usable_lba
768 * number_of_entries
769 * size_of_entry
770 * disk_uuid
771 *
772 * If any of above field are not matched, overwrite secondary with primary since
773 * we always trust primary.
774 * If any one of header is invalid, copy from another. */
775int IsSynonymous(const GptHeader* a, const GptHeader* b) {
776 if ((a->first_usable_lba == b->first_usable_lba) &&
777 (a->last_usable_lba == b->last_usable_lba) &&
778 (a->number_of_entries == b->number_of_entries) &&
779 (a->size_of_entry == b->size_of_entry) &&
780 (!memcmp(&a->disk_uuid, &b->disk_uuid, sizeof(Guid))))
781 return 1;
782 return 0;
783}
784
785/* Primary entries and secondary entries should be bitwise identical.
786 * If two entries tables are valid, compare them. If not the same,
787 * overwrites secondary with primary (primary always has higher priority),
788 * and marks secondary as modified.
789 * If only one is valid, overwrites invalid one.
790 * If all are invalid, does nothing.
791 * This function returns bit masks for GptData.modified field.
792 * Note that CRC is NOT re-computed in this function.
793 */
794uint8_t RepairEntries(GptData *gpt, const uint32_t valid_entries) {
Stefan Reinauerb7b865c2012-08-23 15:06:25 -0700795 /* If we have an alternate GPT header signature, don't overwrite
796 * the secondary GPT with the primary one as that might wipe the
797 * partition table. Also don't overwrite the primary one with the
798 * secondary one as that will stop Windows from booting. */
799 GptHeader* h = (GptHeader*)(gpt->primary_header);
800 if (!memcmp(h->signature, GPT_HEADER_SIGNATURE2, GPT_HEADER_SIGNATURE_SIZE))
801 return 0;
802
Bill Richardsonf1372d92010-06-11 09:15:55 -0700803 if (valid_entries == MASK_BOTH) {
804 if (memcmp(gpt->primary_entries, gpt->secondary_entries,
805 TOTAL_ENTRIES_SIZE)) {
806 memcpy(gpt->secondary_entries, gpt->primary_entries, TOTAL_ENTRIES_SIZE);
807 return GPT_MODIFIED_ENTRIES2;
808 }
809 } else if (valid_entries == MASK_PRIMARY) {
810 memcpy(gpt->secondary_entries, gpt->primary_entries, TOTAL_ENTRIES_SIZE);
811 return GPT_MODIFIED_ENTRIES2;
812 } else if (valid_entries == MASK_SECONDARY) {
813 memcpy(gpt->primary_entries, gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
814 return GPT_MODIFIED_ENTRIES1;
815 }
816
817 return 0;
818}
819
820/* The above five fields are shared between primary and secondary headers.
821 * We can recover one header from another through copying those fields. */
822void CopySynonymousParts(GptHeader* target, const GptHeader* source) {
823 target->first_usable_lba = source->first_usable_lba;
824 target->last_usable_lba = source->last_usable_lba;
825 target->number_of_entries = source->number_of_entries;
826 target->size_of_entry = source->size_of_entry;
827 memcpy(&target->disk_uuid, &source->disk_uuid, sizeof(Guid));
828}
829
830/* This function repairs primary and secondary headers if possible.
831 * If both headers are valid (CRC32 is correct) but
832 * a) indicate inconsistent usable LBA ranges,
833 * b) inconsistent partition entry size and number,
834 * c) inconsistent disk_uuid,
835 * we will use the primary header to overwrite secondary header.
836 * If primary is invalid (CRC32 is wrong), then we repair it from secondary.
837 * If secondary is invalid (CRC32 is wrong), then we repair it from primary.
838 * This function returns the bitmasks for modified header.
839 * Note that CRC value is NOT re-computed in this function. UpdateCrc() will
840 * do it later.
841 */
842uint8_t RepairHeader(GptData *gpt, const uint32_t valid_headers) {
843 GptHeader *primary_header, *secondary_header;
844
845 primary_header = (GptHeader*)gpt->primary_header;
846 secondary_header = (GptHeader*)gpt->secondary_header;
847
848 if (valid_headers == MASK_BOTH) {
849 if (!IsSynonymous(primary_header, secondary_header)) {
850 CopySynonymousParts(secondary_header, primary_header);
851 return GPT_MODIFIED_HEADER2;
852 }
853 } else if (valid_headers == MASK_PRIMARY) {
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800854 memcpy(secondary_header, primary_header, sizeof(GptHeader));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700855 secondary_header->my_lba = gpt->drive_sectors - 1; /* the last sector */
856 secondary_header->alternate_lba = primary_header->my_lba;
857 secondary_header->entries_lba = secondary_header->my_lba -
858 GPT_ENTRIES_SECTORS;
859 return GPT_MODIFIED_HEADER2;
860 } else if (valid_headers == MASK_SECONDARY) {
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800861 memcpy(primary_header, secondary_header, sizeof(GptHeader));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700862 primary_header->my_lba = GPT_PMBR_SECTOR; /* the second sector on drive */
863 primary_header->alternate_lba = secondary_header->my_lba;
864 primary_header->entries_lba = primary_header->my_lba + GPT_HEADER_SECTOR;
865 return GPT_MODIFIED_HEADER1;
866 }
867
868 return 0;
869}
870
Bill Richardson3430b322010-11-29 14:24:51 -0800871int GuidEqual(const Guid *guid1, const Guid *guid2) {
872 return (0 == memcmp(guid1, guid2, sizeof(Guid)));
873}
Bill Richardsonf1372d92010-06-11 09:15:55 -0700874
Bill Richardson3f806a22013-03-20 15:02:34 -0700875int GuidIsZero(const Guid *gp) {
Bill Richardson3430b322010-11-29 14:24:51 -0800876 return GuidEqual(gp, &guid_unused);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700877}
878
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700879void PMBRToStr(struct pmbr *pmbr, char *str, unsigned int buflen) {
880 char buf[GUID_STRLEN];
Bill Richardson3f806a22013-03-20 15:02:34 -0700881 if (GuidIsZero(&pmbr->boot_guid)) {
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700882 require(snprintf(str, buflen, "PMBR") < buflen);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700883 } else {
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700884 GuidToStr(&pmbr->boot_guid, buf, sizeof(buf));
885 require(snprintf(str, buflen, "PMBR (Boot GUID: %s)", buf) < buflen);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700886 }
887}