blob: 193f2e1e11958f2921755b7897c0eb2a786ffccd [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 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
272 close(drive->fd);
273
274 if (drive->gpt.primary_header)
275 free(drive->gpt.primary_header);
276 drive->gpt.primary_header = 0;
277 if (drive->gpt.primary_entries)
278 free(drive->gpt.primary_entries);
279 drive->gpt.primary_entries = 0;
280 if (drive->gpt.secondary_header)
281 free(drive->gpt.secondary_header);
282 drive->gpt.secondary_header = 0;
283 if (drive->gpt.secondary_entries)
284 free(drive->gpt.secondary_entries);
285 drive->gpt.secondary_entries = 0;
286
287 return errors ? CGPT_FAILED : CGPT_OK;
288}
289
290
Bill Richardsonf1372d92010-06-11 09:15:55 -0700291/* GUID conversion functions. Accepted format:
292 *
293 * "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
294 *
295 * Returns CGPT_OK if parsing is successful; otherwise CGPT_FAILED.
296 */
297int StrToGuid(const char *str, Guid *guid) {
298 uint32_t time_low;
299 uint16_t time_mid;
300 uint16_t time_high_and_version;
301 unsigned int chunk[11];
302
303 if (11 != sscanf(str, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
304 chunk+0,
305 chunk+1,
306 chunk+2,
307 chunk+3,
308 chunk+4,
309 chunk+5,
310 chunk+6,
311 chunk+7,
312 chunk+8,
313 chunk+9,
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700314 chunk+10)) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700315 printf("FAILED\n");
316 return CGPT_FAILED;
317 }
318
319 time_low = chunk[0] & 0xffffffff;
320 time_mid = chunk[1] & 0xffff;
321 time_high_and_version = chunk[2] & 0xffff;
322
323 guid->u.Uuid.time_low = htole32(time_low);
324 guid->u.Uuid.time_mid = htole16(time_mid);
325 guid->u.Uuid.time_high_and_version = htole16(time_high_and_version);
326
327 guid->u.Uuid.clock_seq_high_and_reserved = chunk[3] & 0xff;
328 guid->u.Uuid.clock_seq_low = chunk[4] & 0xff;
329 guid->u.Uuid.node[0] = chunk[5] & 0xff;
330 guid->u.Uuid.node[1] = chunk[6] & 0xff;
331 guid->u.Uuid.node[2] = chunk[7] & 0xff;
332 guid->u.Uuid.node[3] = chunk[8] & 0xff;
333 guid->u.Uuid.node[4] = chunk[9] & 0xff;
334 guid->u.Uuid.node[5] = chunk[10] & 0xff;
335
336 return CGPT_OK;
337}
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700338void GuidToStr(const Guid *guid, char *str, unsigned int buflen) {
339 require(buflen >= GUID_STRLEN);
340 require(snprintf(str, buflen,
341 "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
342 le32toh(guid->u.Uuid.time_low),
343 le16toh(guid->u.Uuid.time_mid),
344 le16toh(guid->u.Uuid.time_high_and_version),
345 guid->u.Uuid.clock_seq_high_and_reserved,
346 guid->u.Uuid.clock_seq_low,
347 guid->u.Uuid.node[0], guid->u.Uuid.node[1],
348 guid->u.Uuid.node[2], guid->u.Uuid.node[3],
349 guid->u.Uuid.node[4], guid->u.Uuid.node[5]) == GUID_STRLEN-1);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700350}
351
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700352/* Convert possibly unterminated UTF16 string to UTF8.
353 * Caller must prepare enough space for UTF8, which could be up to
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800354 * twice the byte length of UTF16 string plus the terminating '\0'.
355 * See the following table for encoding lengths.
356 *
357 * Code point UTF16 UTF8
358 * 0x0000-0x007F 2 bytes 1 byte
359 * 0x0080-0x07FF 2 bytes 2 bytes
360 * 0x0800-0xFFFF 2 bytes 3 bytes
361 * 0x10000-0x10FFFF 4 bytes 4 bytes
362 *
363 * This function uses a simple state meachine to convert UTF-16 char(s) to
364 * a code point. Once a code point is parsed out, the state machine throws
365 * out sequencial UTF-8 chars in one time.
366 *
367 * Return: CGPT_OK --- all character are converted successfully.
368 * CGPT_FAILED --- convert error, i.e. output buffer is too short.
Bill Richardsonf1372d92010-06-11 09:15:55 -0700369 */
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800370int UTF16ToUTF8(const uint16_t *utf16, unsigned int maxinput,
371 uint8_t *utf8, unsigned int maxoutput)
Bill Richardsonf1372d92010-06-11 09:15:55 -0700372{
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700373 size_t s16idx, s8idx;
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800374 uint32_t code_point = 0;
375 int code_point_ready = 1; // code point is ready to output.
376 int retval = CGPT_OK;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700377
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700378 if (!utf16 || !maxinput || !utf8 || !maxoutput)
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800379 return CGPT_FAILED;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700380
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700381 maxoutput--; /* plan for termination now */
382
383 for (s16idx = s8idx = 0;
384 s16idx < maxinput && utf16[s16idx] && maxoutput;
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800385 s16idx++) {
386 uint16_t codeunit = le16toh(utf16[s16idx]);
387
388 if (code_point_ready) {
389 if (codeunit >= 0xD800 && codeunit <= 0xDBFF) {
390 /* high surrogate, need the low surrogate. */
391 code_point_ready = 0;
392 code_point = (codeunit & 0x03FF) + 0x0040;
393 } else {
394 /* BMP char, output it. */
395 code_point = codeunit;
396 }
397 } else {
398 /* expect the low surrogate */
399 if (codeunit >= 0xDC00 && codeunit <= 0xDFFF) {
400 code_point = (code_point << 10) | (codeunit & 0x03FF);
401 code_point_ready = 1;
402 } else {
403 /* the second code unit is NOT the low surrogate. Unexpected. */
404 code_point_ready = 0;
405 retval = CGPT_FAILED;
406 break;
407 }
408 }
409
410 /* If UTF code point is ready, output it. */
411 if (code_point_ready) {
412 require(code_point <= 0x10FFFF);
413 if (code_point <= 0x7F && maxoutput >= 1) {
414 maxoutput -= 1;
415 utf8[s8idx++] = code_point & 0x7F;
416 } else if (code_point <= 0x7FF && maxoutput >= 2) {
417 maxoutput -= 2;
418 utf8[s8idx++] = 0xC0 | (code_point >> 6);
419 utf8[s8idx++] = 0x80 | (code_point & 0x3F);
420 } else if (code_point <= 0xFFFF && maxoutput >= 3) {
421 maxoutput -= 3;
422 utf8[s8idx++] = 0xE0 | (code_point >> 12);
423 utf8[s8idx++] = 0x80 | ((code_point >> 6) & 0x3F);
424 utf8[s8idx++] = 0x80 | (code_point & 0x3F);
425 } else if (code_point <= 0x10FFFF && maxoutput >= 4) {
426 maxoutput -= 4;
427 utf8[s8idx++] = 0xF0 | (code_point >> 18);
428 utf8[s8idx++] = 0x80 | ((code_point >> 12) & 0x3F);
429 utf8[s8idx++] = 0x80 | ((code_point >> 6) & 0x3F);
430 utf8[s8idx++] = 0x80 | (code_point & 0x3F);
431 } else {
432 /* buffer underrun */
433 retval = CGPT_FAILED;
434 break;
435 }
436 }
Bill Richardsonf1372d92010-06-11 09:15:55 -0700437 }
438 utf8[s8idx++] = 0;
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800439 return retval;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700440}
441
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700442/* Convert UTF8 string to UTF16. The UTF8 string must be null-terminated.
443 * Caller must prepare enough space for UTF16, including a terminating 0x0000.
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800444 * See the following table for encoding lengths. In any case, the caller
445 * just needs to prepare the byte length of UTF8 plus the terminating 0x0000.
446 *
447 * Code point UTF16 UTF8
448 * 0x0000-0x007F 2 bytes 1 byte
449 * 0x0080-0x07FF 2 bytes 2 bytes
450 * 0x0800-0xFFFF 2 bytes 3 bytes
451 * 0x10000-0x10FFFF 4 bytes 4 bytes
452 *
453 * This function converts UTF8 chars to a code point first. Then, convrts it
454 * to UTF16 code unit(s).
455 *
456 * Return: CGPT_OK --- all character are converted successfully.
457 * CGPT_FAILED --- convert error, i.e. output buffer is too short.
Bill Richardsonf1372d92010-06-11 09:15:55 -0700458 */
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800459int UTF8ToUTF16(const uint8_t *utf8, uint16_t *utf16, unsigned int maxoutput)
Bill Richardsonf1372d92010-06-11 09:15:55 -0700460{
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700461 size_t s16idx, s8idx;
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800462 uint32_t code_point = 0;
463 unsigned int expected_units = 1;
464 unsigned int decoded_units = 1;
465 int retval = CGPT_OK;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700466
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700467 if (!utf8 || !utf16 || !maxoutput)
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800468 return CGPT_FAILED;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700469
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700470 maxoutput--; /* plan for termination */
471
472 for (s8idx = s16idx = 0;
473 utf8[s8idx] && maxoutput;
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800474 s8idx++) {
475 uint8_t code_unit;
476 code_unit = utf8[s8idx];
477
478 if (expected_units != decoded_units) {
479 /* Trailing bytes of multi-byte character */
480 if ((code_unit & 0xC0) == 0x80) {
481 code_point = (code_point << 6) | (code_unit & 0x3F);
482 ++decoded_units;
483 } else {
484 /* Unexpected code unit. */
485 retval = CGPT_FAILED;
486 break;
487 }
488 } else {
489 /* parsing a new code point. */
490 decoded_units = 1;
491 if (code_unit <= 0x7F) {
492 code_point = code_unit;
493 expected_units = 1;
494 } else if (code_unit <= 0xBF) {
495 /* 0x80-0xBF must NOT be the heading byte unit of a new code point. */
496 retval = CGPT_FAILED;
497 break;
498 } else if (code_unit >= 0xC2 && code_unit <= 0xDF) {
499 code_point = code_unit & 0x1F;
500 expected_units = 2;
501 } else if (code_unit >= 0xE0 && code_unit <= 0xEF) {
502 code_point = code_unit & 0x0F;
503 expected_units = 3;
504 } else if (code_unit >= 0xF0 && code_unit <= 0xF4) {
505 code_point = code_unit & 0x07;
506 expected_units = 4;
507 } else {
508 /* illegal code unit: 0xC0-0xC1, 0xF5-0xFF */
509 retval = CGPT_FAILED;
510 break;
511 }
512 }
513
514 /* If no more unit is needed, output the UTF16 unit(s). */
515 if ((retval == CGPT_OK) &&
516 (expected_units == decoded_units)) {
517 /* Check if the encoding is the shortest possible UTF-8 sequence. */
518 switch (expected_units) {
519 case 2:
520 if (code_point <= 0x7F) retval = CGPT_FAILED;
521 break;
522 case 3:
523 if (code_point <= 0x7FF) retval = CGPT_FAILED;
524 break;
525 case 4:
526 if (code_point <= 0xFFFF) retval = CGPT_FAILED;
527 break;
528 }
529 if (retval == CGPT_FAILED) break; /* leave immediately */
530
531 if ((code_point <= 0xD7FF) ||
532 (code_point >= 0xE000 && code_point <= 0xFFFF)) {
533 utf16[s16idx++] = code_point;
534 maxoutput -= 1;
535 } else if (code_point >= 0x10000 && code_point <= 0x10FFFF &&
536 maxoutput >= 2) {
537 utf16[s16idx++] = 0xD800 | ((code_point >> 10) - 0x0040);
538 utf16[s16idx++] = 0xDC00 | (code_point & 0x03FF);
539 maxoutput -= 2;
540 } else {
541 /* Three possibilities fall into here. Both are failure cases.
542 * a. surrogate pair (non-BMP characters; 0xD800~0xDFFF)
543 * b. invalid code point > 0x10FFFF
544 * c. buffer underrun
545 */
546 retval = CGPT_FAILED;
547 break;
548 }
549 }
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700550 }
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800551
552 /* A null-terminator shows up before the UTF8 sequence ends. */
553 if (expected_units != decoded_units) {
554 retval = CGPT_FAILED;
555 }
556
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700557 utf16[s16idx++] = 0;
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800558 return retval;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700559}
560
Bill Richardson3430b322010-11-29 14:24:51 -0800561/* global types to compare against */
Gabe Black93cf15e2011-07-07 16:00:00 -0700562const Guid guid_chromeos_firmware = GPT_ENT_TYPE_CHROMEOS_FIRMWARE;
Bill Richardson3430b322010-11-29 14:24:51 -0800563const Guid guid_chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL;
564const Guid guid_chromeos_rootfs = GPT_ENT_TYPE_CHROMEOS_ROOTFS;
565const Guid guid_linux_data = GPT_ENT_TYPE_LINUX_DATA;
566const Guid guid_chromeos_reserved = GPT_ENT_TYPE_CHROMEOS_RESERVED;
567const Guid guid_efi = GPT_ENT_TYPE_EFI;
568const Guid guid_unused = GPT_ENT_TYPE_UNUSED;
569
570static struct {
571 const Guid *type;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700572 char *name;
573 char *description;
574} supported_types[] = {
Gabe Black93cf15e2011-07-07 16:00:00 -0700575 {&guid_chromeos_firmware, "firmware", "ChromeOS firmware"},
Bill Richardson3430b322010-11-29 14:24:51 -0800576 {&guid_chromeos_kernel, "kernel", "ChromeOS kernel"},
577 {&guid_chromeos_rootfs, "rootfs", "ChromeOS rootfs"},
578 {&guid_linux_data, "data", "Linux data"},
579 {&guid_chromeos_reserved, "reserved", "ChromeOS reserved"},
580 {&guid_efi, "efi", "EFI System Partition"},
581 {&guid_unused, "unused", "Unused (nonexistent) partition"},
Bill Richardsonf1372d92010-06-11 09:15:55 -0700582};
583
584/* Resolves human-readable GPT type.
585 * Returns CGPT_OK if found.
586 * Returns CGPT_FAILED if no known type found. */
587int ResolveType(const Guid *type, char *buf) {
588 int i;
589 for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
Bill Richardson3430b322010-11-29 14:24:51 -0800590 if (!memcmp(type, supported_types[i].type, sizeof(Guid))) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700591 strcpy(buf, supported_types[i].description);
592 return CGPT_OK;
593 }
594 }
595 return CGPT_FAILED;
596}
597
598int SupportedType(const char *name, Guid *type) {
599 int i;
600 for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
601 if (!strcmp(name, supported_types[i].name)) {
Bill Richardson3430b322010-11-29 14:24:51 -0800602 memcpy(type, supported_types[i].type, sizeof(Guid));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700603 return CGPT_OK;
604 }
605 }
606 return CGPT_FAILED;
607}
608
609void PrintTypes(void) {
610 int i;
611 printf("The partition type may also be given as one of these aliases:\n\n");
612 for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
613 printf(" %-10s %s\n", supported_types[i].name,
614 supported_types[i].description);
615 }
616 printf("\n");
617}
618
619uint32_t GetNumberOfEntries(const GptData *gpt) {
620 GptHeader *header = 0;
621 if (gpt->valid_headers & MASK_PRIMARY)
622 header = (GptHeader*)gpt->primary_header;
623 else if (gpt->valid_headers & MASK_SECONDARY)
624 header = (GptHeader*)gpt->secondary_header;
625 else
626 return 0;
627 return header->number_of_entries;
628}
629
630static uint32_t GetSizeOfEntries(const GptData *gpt) {
631 GptHeader *header = 0;
632 if (gpt->valid_headers & MASK_PRIMARY)
633 header = (GptHeader*)gpt->primary_header;
634 else if (gpt->valid_headers & MASK_SECONDARY)
635 header = (GptHeader*)gpt->secondary_header;
636 else
637 return 0;
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700638 return header->size_of_entry;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700639}
640
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700641GptEntry *GetEntry(GptData *gpt, int secondary, uint32_t entry_index) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700642 uint8_t *entries;
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700643 uint32_t stride = GetSizeOfEntries(gpt);
644 require(stride);
645 require(entry_index < GetNumberOfEntries(gpt));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700646
647 if (secondary == PRIMARY) {
648 entries = gpt->primary_entries;
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800649 } else if (secondary == SECONDARY) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700650 entries = gpt->secondary_entries;
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800651 } else { /* ANY_VALID */
652 require(secondary == ANY_VALID);
653 if (gpt->valid_entries & MASK_PRIMARY) {
654 entries = gpt->primary_entries;
655 } else {
656 require(gpt->valid_entries & MASK_SECONDARY);
657 entries = gpt->secondary_entries;
658 }
Bill Richardsonf1372d92010-06-11 09:15:55 -0700659 }
660
661 return (GptEntry*)(&entries[stride * entry_index]);
662}
663
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700664void SetPriority(GptData *gpt, int secondary, uint32_t entry_index,
665 int priority) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700666 GptEntry *entry;
667 entry = GetEntry(gpt, secondary, entry_index);
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700668 require(priority >= 0 && priority <= CGPT_ATTRIBUTE_MAX_PRIORITY);
vbendebf7a45cc2010-06-21 08:44:16 -0700669 entry->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_PRIORITY_MASK;
670 entry->attrs.fields.gpt_att |= priority << CGPT_ATTRIBUTE_PRIORITY_OFFSET;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700671}
672
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700673int GetPriority(GptData *gpt, int secondary, uint32_t entry_index) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700674 GptEntry *entry;
675 entry = GetEntry(gpt, secondary, entry_index);
vbendebf7a45cc2010-06-21 08:44:16 -0700676 return (entry->attrs.fields.gpt_att & CGPT_ATTRIBUTE_PRIORITY_MASK) >>
677 CGPT_ATTRIBUTE_PRIORITY_OFFSET;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700678}
679
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700680void SetTries(GptData *gpt, int secondary, uint32_t entry_index, int tries) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700681 GptEntry *entry;
682 entry = GetEntry(gpt, secondary, entry_index);
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700683 require(tries >= 0 && tries <= CGPT_ATTRIBUTE_MAX_TRIES);
vbendebf7a45cc2010-06-21 08:44:16 -0700684 entry->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_TRIES_MASK;
685 entry->attrs.fields.gpt_att |= tries << CGPT_ATTRIBUTE_TRIES_OFFSET;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700686}
687
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700688int GetTries(GptData *gpt, int secondary, uint32_t entry_index) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700689 GptEntry *entry;
690 entry = GetEntry(gpt, secondary, entry_index);
vbendebf7a45cc2010-06-21 08:44:16 -0700691 return (entry->attrs.fields.gpt_att & CGPT_ATTRIBUTE_TRIES_MASK) >>
692 CGPT_ATTRIBUTE_TRIES_OFFSET;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700693}
694
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700695void SetSuccessful(GptData *gpt, int secondary, uint32_t entry_index,
696 int success) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700697 GptEntry *entry;
698 entry = GetEntry(gpt, secondary, entry_index);
699
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700700 require(success >= 0 && success <= CGPT_ATTRIBUTE_MAX_SUCCESSFUL);
vbendebf7a45cc2010-06-21 08:44:16 -0700701 entry->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_SUCCESSFUL_MASK;
702 entry->attrs.fields.gpt_att |= success << CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700703}
704
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700705int GetSuccessful(GptData *gpt, int secondary, uint32_t entry_index) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700706 GptEntry *entry;
707 entry = GetEntry(gpt, secondary, entry_index);
vbendebf7a45cc2010-06-21 08:44:16 -0700708 return (entry->attrs.fields.gpt_att & CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >>
Bill Richardsonf1372d92010-06-11 09:15:55 -0700709 CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
710}
711
712
713#define TOSTRING(A) #A
714const char *GptError(int errnum) {
715 const char *error_string[] = {
716 TOSTRING(GPT_SUCCESS),
717 TOSTRING(GPT_ERROR_NO_VALID_KERNEL),
718 TOSTRING(GPT_ERROR_INVALID_HEADERS),
719 TOSTRING(GPT_ERROR_INVALID_ENTRIES),
720 TOSTRING(GPT_ERROR_INVALID_SECTOR_SIZE),
721 TOSTRING(GPT_ERROR_INVALID_SECTOR_NUMBER),
722 TOSTRING(GPT_ERROR_INVALID_UPDATE_TYPE)
723 };
724 if (errnum < 0 || errnum >= ARRAY_COUNT(error_string))
725 return "<illegal value>";
726 return error_string[errnum];
727}
728
729/* Update CRC value if necessary. */
730void UpdateCrc(GptData *gpt) {
731 GptHeader *primary_header, *secondary_header;
732
733 primary_header = (GptHeader*)gpt->primary_header;
734 secondary_header = (GptHeader*)gpt->secondary_header;
735
Stefan Reinauerb7b865c2012-08-23 15:06:25 -0700736 if (gpt->modified & GPT_MODIFIED_ENTRIES1 &&
737 memcmp(primary_header, GPT_HEADER_SIGNATURE2,
738 GPT_HEADER_SIGNATURE_SIZE)) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700739 primary_header->entries_crc32 =
740 Crc32(gpt->primary_entries, TOTAL_ENTRIES_SIZE);
741 }
742 if (gpt->modified & GPT_MODIFIED_ENTRIES2) {
743 secondary_header->entries_crc32 =
744 Crc32(gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
745 }
746 if (gpt->modified & GPT_MODIFIED_HEADER1) {
747 primary_header->header_crc32 = 0;
748 primary_header->header_crc32 = Crc32(
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800749 (const uint8_t *)primary_header, sizeof(GptHeader));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700750 }
751 if (gpt->modified & GPT_MODIFIED_HEADER2) {
752 secondary_header->header_crc32 = 0;
753 secondary_header->header_crc32 = Crc32(
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800754 (const uint8_t *)secondary_header, sizeof(GptHeader));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700755 }
756}
757/* Two headers are NOT bitwise identical. For example, my_lba pointers to header
758 * itself so that my_lba in primary and secondary is definitely different.
759 * Only the following fields should be identical.
760 *
761 * first_usable_lba
762 * last_usable_lba
763 * number_of_entries
764 * size_of_entry
765 * disk_uuid
766 *
767 * If any of above field are not matched, overwrite secondary with primary since
768 * we always trust primary.
769 * If any one of header is invalid, copy from another. */
770int IsSynonymous(const GptHeader* a, const GptHeader* b) {
771 if ((a->first_usable_lba == b->first_usable_lba) &&
772 (a->last_usable_lba == b->last_usable_lba) &&
773 (a->number_of_entries == b->number_of_entries) &&
774 (a->size_of_entry == b->size_of_entry) &&
775 (!memcmp(&a->disk_uuid, &b->disk_uuid, sizeof(Guid))))
776 return 1;
777 return 0;
778}
779
780/* Primary entries and secondary entries should be bitwise identical.
781 * If two entries tables are valid, compare them. If not the same,
782 * overwrites secondary with primary (primary always has higher priority),
783 * and marks secondary as modified.
784 * If only one is valid, overwrites invalid one.
785 * If all are invalid, does nothing.
786 * This function returns bit masks for GptData.modified field.
787 * Note that CRC is NOT re-computed in this function.
788 */
789uint8_t RepairEntries(GptData *gpt, const uint32_t valid_entries) {
Stefan Reinauerb7b865c2012-08-23 15:06:25 -0700790 /* If we have an alternate GPT header signature, don't overwrite
791 * the secondary GPT with the primary one as that might wipe the
792 * partition table. Also don't overwrite the primary one with the
793 * secondary one as that will stop Windows from booting. */
794 GptHeader* h = (GptHeader*)(gpt->primary_header);
795 if (!memcmp(h->signature, GPT_HEADER_SIGNATURE2, GPT_HEADER_SIGNATURE_SIZE))
796 return 0;
797
Bill Richardsonf1372d92010-06-11 09:15:55 -0700798 if (valid_entries == MASK_BOTH) {
799 if (memcmp(gpt->primary_entries, gpt->secondary_entries,
800 TOTAL_ENTRIES_SIZE)) {
801 memcpy(gpt->secondary_entries, gpt->primary_entries, TOTAL_ENTRIES_SIZE);
802 return GPT_MODIFIED_ENTRIES2;
803 }
804 } else if (valid_entries == MASK_PRIMARY) {
805 memcpy(gpt->secondary_entries, gpt->primary_entries, TOTAL_ENTRIES_SIZE);
806 return GPT_MODIFIED_ENTRIES2;
807 } else if (valid_entries == MASK_SECONDARY) {
808 memcpy(gpt->primary_entries, gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
809 return GPT_MODIFIED_ENTRIES1;
810 }
811
812 return 0;
813}
814
815/* The above five fields are shared between primary and secondary headers.
816 * We can recover one header from another through copying those fields. */
817void CopySynonymousParts(GptHeader* target, const GptHeader* source) {
818 target->first_usable_lba = source->first_usable_lba;
819 target->last_usable_lba = source->last_usable_lba;
820 target->number_of_entries = source->number_of_entries;
821 target->size_of_entry = source->size_of_entry;
822 memcpy(&target->disk_uuid, &source->disk_uuid, sizeof(Guid));
823}
824
825/* This function repairs primary and secondary headers if possible.
826 * If both headers are valid (CRC32 is correct) but
827 * a) indicate inconsistent usable LBA ranges,
828 * b) inconsistent partition entry size and number,
829 * c) inconsistent disk_uuid,
830 * we will use the primary header to overwrite secondary header.
831 * If primary is invalid (CRC32 is wrong), then we repair it from secondary.
832 * If secondary is invalid (CRC32 is wrong), then we repair it from primary.
833 * This function returns the bitmasks for modified header.
834 * Note that CRC value is NOT re-computed in this function. UpdateCrc() will
835 * do it later.
836 */
837uint8_t RepairHeader(GptData *gpt, const uint32_t valid_headers) {
838 GptHeader *primary_header, *secondary_header;
839
840 primary_header = (GptHeader*)gpt->primary_header;
841 secondary_header = (GptHeader*)gpt->secondary_header;
842
843 if (valid_headers == MASK_BOTH) {
844 if (!IsSynonymous(primary_header, secondary_header)) {
845 CopySynonymousParts(secondary_header, primary_header);
846 return GPT_MODIFIED_HEADER2;
847 }
848 } else if (valid_headers == MASK_PRIMARY) {
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800849 memcpy(secondary_header, primary_header, sizeof(GptHeader));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700850 secondary_header->my_lba = gpt->drive_sectors - 1; /* the last sector */
851 secondary_header->alternate_lba = primary_header->my_lba;
852 secondary_header->entries_lba = secondary_header->my_lba -
853 GPT_ENTRIES_SECTORS;
854 return GPT_MODIFIED_HEADER2;
855 } else if (valid_headers == MASK_SECONDARY) {
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800856 memcpy(primary_header, secondary_header, sizeof(GptHeader));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700857 primary_header->my_lba = GPT_PMBR_SECTOR; /* the second sector on drive */
858 primary_header->alternate_lba = secondary_header->my_lba;
859 primary_header->entries_lba = primary_header->my_lba + GPT_HEADER_SECTOR;
860 return GPT_MODIFIED_HEADER1;
861 }
862
863 return 0;
864}
865
Bill Richardson3430b322010-11-29 14:24:51 -0800866int GuidEqual(const Guid *guid1, const Guid *guid2) {
867 return (0 == memcmp(guid1, guid2, sizeof(Guid)));
868}
Bill Richardsonf1372d92010-06-11 09:15:55 -0700869
870int IsZero(const Guid *gp) {
Bill Richardson3430b322010-11-29 14:24:51 -0800871 return GuidEqual(gp, &guid_unused);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700872}
873
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700874void PMBRToStr(struct pmbr *pmbr, char *str, unsigned int buflen) {
875 char buf[GUID_STRLEN];
Bill Richardsonf1372d92010-06-11 09:15:55 -0700876 if (IsZero(&pmbr->boot_guid)) {
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700877 require(snprintf(str, buflen, "PMBR") < buflen);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700878 } else {
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700879 GuidToStr(&pmbr->boot_guid, buf, sizeof(buf));
880 require(snprintf(str, buflen, "PMBR (Boot GUID: %s)", buf) < buflen);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700881 }
882}