| OLD | NEW |
| (Empty) |
| 1 /* 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 /* To compile on host without compatility to BSD, we include | |
| 9 * endian.h under chroot. */ | |
| 10 #define _BSD_SOURCE | |
| 11 #include "endian.h" | |
| 12 | |
| 13 #define __USE_LARGEFILE64 | |
| 14 #define __USE_FILE_OFFSET64 | |
| 15 #define _LARGEFILE64_SOURCE | |
| 16 #include "cgpt.h" | |
| 17 #include "cgpt_tofix.h" | |
| 18 #include <errno.h> | |
| 19 #include <fcntl.h> | |
| 20 #include <getopt.h> | |
| 21 #include <stdint.h> | |
| 22 #include <stdio.h> | |
| 23 #include <stdlib.h> | |
| 24 #include <string.h> | |
| 25 #include <sys/ioctl.h> | |
| 26 #include <sys/mount.h> | |
| 27 #include <sys/stat.h> | |
| 28 #include <sys/types.h> | |
| 29 #include <unistd.h> | |
| 30 #include "cgptlib_internal.h" | |
| 31 #include "utility.h" | |
| 32 | |
| 33 /* For usage print */ | |
| 34 const char* progname; | |
| 35 | |
| 36 /* Lists all command here. */ | |
| 37 struct { | |
| 38 const char *name; | |
| 39 int (*fp)(int argc, char *argv[]); | |
| 40 const char *comment; | |
| 41 } cmds[] = { | |
| 42 {"add", CgptAdm, "Add a partition to drive"}, | |
| 43 {"delete", CgptAdm, "Delete a partition on drive"}, | |
| 44 {"modify", CgptAdm, "Modify the partition on drive"}, | |
| 45 {"attribute", CgptAttribute, "Update GPT attribute bits " | |
| 46 "(for ChromeOS kernel entry only)"}, | |
| 47 {"dev", CgptDev, "Developper mode"}, | |
| 48 {"repair", CgptRepair, "Repair primary and secondary headers and tables"}, | |
| 49 {"show", CgptShow, "Show partition details"}, | |
| 50 }; | |
| 51 | |
| 52 /* Shows main menu. If 'message' is non-NULL, shows it as header. Then, this | |
| 53 * traverses cmds[] and shows supported commands and their comments. */ | |
| 54 void Usage(const char *message) { | |
| 55 int i; | |
| 56 | |
| 57 if (message) printf("%s\n", message); | |
| 58 printf("Usage: %s COMMAND [OPTIONS]\n\n" | |
| 59 "Supported COMMANDs:\n\n", | |
| 60 progname); | |
| 61 for (i = 0; i < sizeof(cmds)/sizeof(cmds[0]); ++i) { | |
| 62 printf(" %-10s %s\n", cmds[i].name, cmds[i].comment); | |
| 63 } | |
| 64 printf("\nFor more detailed usage, use %s COMMAND --help.\n\n", progname); | |
| 65 } | |
| 66 | |
| 67 /* GUID conversion functions. Accepted format: | |
| 68 * | |
| 69 * "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" | |
| 70 * | |
| 71 * Returns CGPT_OK if parsing is successful; otherwise CGPT_FAILED. | |
| 72 */ | |
| 73 int StrToGuid(const char *str, Guid *guid) { | |
| 74 uint32_t time_low, time_mid, time_high_and_version; | |
| 75 | |
| 76 if (11 > sscanf(str, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", | |
| 77 &time_low, | |
| 78 (unsigned int *)&time_mid, | |
| 79 (unsigned int *)&time_high_and_version, | |
| 80 (unsigned int *)&guid->u.Uuid.clock_seq_high_and_reserved, | |
| 81 (unsigned int *)&guid->u.Uuid.clock_seq_low, | |
| 82 (unsigned int *)&guid->u.Uuid.node[0], | |
| 83 (unsigned int *)&guid->u.Uuid.node[1], | |
| 84 (unsigned int *)&guid->u.Uuid.node[2], | |
| 85 (unsigned int *)&guid->u.Uuid.node[3], | |
| 86 (unsigned int *)&guid->u.Uuid.node[4], | |
| 87 (unsigned int *)&guid->u.Uuid.node[5])) return CGPT_FAILED; | |
| 88 | |
| 89 guid->u.Uuid.time_low = htole32(time_low); | |
| 90 guid->u.Uuid.time_mid = htole16(time_mid); | |
| 91 guid->u.Uuid.time_high_and_version = htole16(time_high_and_version); | |
| 92 | |
| 93 return CGPT_OK; | |
| 94 } | |
| 95 | |
| 96 void GuidToStr(const Guid *guid, char *str) { | |
| 97 sprintf(str, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", | |
| 98 le32toh(guid->u.Uuid.time_low), le16toh(guid->u.Uuid.time_mid), | |
| 99 le16toh(guid->u.Uuid.time_high_and_version), | |
| 100 guid->u.Uuid.clock_seq_high_and_reserved, guid->u.Uuid.clock_seq_low, | |
| 101 guid->u.Uuid.node[0], guid->u.Uuid.node[1], guid->u.Uuid.node[2], | |
| 102 guid->u.Uuid.node[3], guid->u.Uuid.node[4], guid->u.Uuid.node[5]); | |
| 103 } | |
| 104 | |
| 105 /* Convert UTF16 string to UTF8. Rewritten from gpt utility. | |
| 106 * Caller must prepare enough space for UTF8. The rough estimation is: | |
| 107 * | |
| 108 * utf8 length = bytecount(utf16) * 1.5 | |
| 109 */ | |
| 110 #define SIZEOF_GPTENTRY_NAME 36 /* sizeof(GptEntry.name[]) */ | |
| 111 void UTF16ToUTF8(const uint16_t *utf16, uint8_t *utf8) | |
| 112 { | |
| 113 size_t s8idx, s16idx, s16len; | |
| 114 uint32_t utfchar; | |
| 115 unsigned int next_utf16; | |
| 116 | |
| 117 for (s16len = 0; s16len < SIZEOF_GPTENTRY_NAME && utf16[s16len]; ++s16len); | |
| 118 | |
| 119 *utf8 = s8idx = s16idx = 0; | |
| 120 while (s16idx < s16len) { | |
| 121 utfchar = le16toh(utf16[s16idx++]); | |
| 122 if ((utfchar & 0xf800) == 0xd800) { | |
| 123 next_utf16 = le16toh(utf16[s16idx]); | |
| 124 if ((utfchar & 0x400) != 0 || (next_utf16 & 0xfc00) != 0xdc00) | |
| 125 utfchar = 0xfffd; | |
| 126 else | |
| 127 s16idx++; | |
| 128 } | |
| 129 if (utfchar < 0x80) { | |
| 130 utf8[s8idx++] = utfchar; | |
| 131 } else if (utfchar < 0x800) { | |
| 132 utf8[s8idx++] = 0xc0 | (utfchar >> 6); | |
| 133 utf8[s8idx++] = 0x80 | (utfchar & 0x3f); | |
| 134 } else if (utfchar < 0x10000) { | |
| 135 utf8[s8idx++] = 0xe0 | (utfchar >> 12); | |
| 136 utf8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f); | |
| 137 utf8[s8idx++] = 0x80 | (utfchar & 0x3f); | |
| 138 } else if (utfchar < 0x200000) { | |
| 139 utf8[s8idx++] = 0xf0 | (utfchar >> 18); | |
| 140 utf8[s8idx++] = 0x80 | ((utfchar >> 12) & 0x3f); | |
| 141 utf8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f); | |
| 142 utf8[s8idx++] = 0x80 | (utfchar & 0x3f); | |
| 143 } | |
| 144 } | |
| 145 utf8[s8idx++] = 0; | |
| 146 } | |
| 147 | |
| 148 /* Convert UTF8 string to UTF16. Rewritten from gpt utility. | |
| 149 * Caller must prepare enough space for UTF16. The conservative estimation is: | |
| 150 * | |
| 151 * utf16 bytecount = bytecount(utf8) / 3 * 4 | |
| 152 */ | |
| 153 void UTF8ToUTF16(const uint8_t *utf8, uint16_t *utf16) | |
| 154 { | |
| 155 size_t s16idx, s8idx, s8len; | |
| 156 uint32_t utfchar; | |
| 157 unsigned int c, utfbytes; | |
| 158 | |
| 159 for (s8len = 0; utf8[s8len]; ++s8len); | |
| 160 | |
| 161 s8idx = s16idx = 0; | |
| 162 utfbytes = 0; | |
| 163 do { | |
| 164 c = utf8[s8idx++]; | |
| 165 if ((c & 0xc0) != 0x80) { | |
| 166 /* Initial characters. */ | |
| 167 if (utfbytes != 0) { | |
| 168 /* Incomplete encoding. */ | |
| 169 utf16[s16idx++] = 0xfffd; | |
| 170 } | |
| 171 if ((c & 0xf8) == 0xf0) { | |
| 172 utfchar = c & 0x07; | |
| 173 utfbytes = 3; | |
| 174 } else if ((c & 0xf0) == 0xe0) { | |
| 175 utfchar = c & 0x0f; | |
| 176 utfbytes = 2; | |
| 177 } else if ((c & 0xe0) == 0xc0) { | |
| 178 utfchar = c & 0x1f; | |
| 179 utfbytes = 1; | |
| 180 } else { | |
| 181 utfchar = c & 0x7f; | |
| 182 utfbytes = 0; | |
| 183 } | |
| 184 } else { | |
| 185 /* Followup characters. */ | |
| 186 if (utfbytes > 0) { | |
| 187 utfchar = (utfchar << 6) + (c & 0x3f); | |
| 188 utfbytes--; | |
| 189 } else if (utfbytes == 0) | |
| 190 utfbytes = -1; | |
| 191 utfchar = 0xfffd; | |
| 192 } | |
| 193 if (utfbytes == 0) { | |
| 194 if (utfchar >= 0x10000) { | |
| 195 utf16[s16idx++] = htole16(0xd800 | ((utfchar>>10)-0x40)); | |
| 196 if (s16idx >= SIZEOF_GPTENTRY_NAME) break; | |
| 197 utf16[s16idx++] = htole16(0xdc00 | (utfchar & 0x3ff)); | |
| 198 } else { | |
| 199 utf16[s16idx++] = htole16(utfchar); | |
| 200 } | |
| 201 } | |
| 202 } while (c != 0 && s16idx < SIZEOF_GPTENTRY_NAME); | |
| 203 if (s16idx < SIZEOF_GPTENTRY_NAME) | |
| 204 utf16[s16idx++] = 0; | |
| 205 } | |
| 206 | |
| 207 struct { | |
| 208 Guid type; | |
| 209 char *name; | |
| 210 char *description; | |
| 211 } supported_types[] = { | |
| 212 {GPT_ENT_TYPE_UNUSED, "unused", "Unused partition"}, | |
| 213 {GPT_ENT_TYPE_EFI, "efi", "EFI System Partition"}, | |
| 214 {GPT_ENT_TYPE_CHROMEOS_KERNEL, "croskern", "ChromeOS kernel"}, | |
| 215 {GPT_ENT_TYPE_CHROMEOS_ROOTFS, "crosroot", "ChromeOS rootfs"}, | |
| 216 {GPT_ENT_TYPE_CHROMEOS_RESERVED, "crosresv", "ChromeOS reserved"}, | |
| 217 {GPT_ENT_TYPE_LINUX_DATA, "data", "Linux data"}, | |
| 218 }; | |
| 219 | |
| 220 /* Resolves human-readable GPT type. | |
| 221 * Returns CGPT_OK if found. | |
| 222 * Returns CGPT_FAILED if no known type found. */ | |
| 223 int ResolveType(const Guid *type, char *buf) { | |
| 224 int i; | |
| 225 for (i = 0; i < ARRAY_COUNT(supported_types); ++i) { | |
| 226 if (!Memcmp(type, &supported_types[i].type, sizeof(Guid))) { | |
| 227 strcpy(buf, supported_types[i].description); | |
| 228 return CGPT_OK; | |
| 229 } | |
| 230 } | |
| 231 return CGPT_FAILED; | |
| 232 } | |
| 233 | |
| 234 int SupportedType(const char *name, Guid *type) { | |
| 235 int i; | |
| 236 for (i = 0; i < ARRAY_COUNT(supported_types); ++i) { | |
| 237 if (!strcmp(name, supported_types[i].name)) { | |
| 238 Memcpy(type, &supported_types[i].type, sizeof(Guid)); | |
| 239 return CGPT_OK; | |
| 240 } | |
| 241 } | |
| 242 return CGPT_FAILED; | |
| 243 } | |
| 244 | |
| 245 void PrintTypes(void) { | |
| 246 int i; | |
| 247 printf("\n* For --type option, you can use the following alias, " | |
| 248 "instead of hex values:\n"); | |
| 249 for (i = 0; i < ARRAY_COUNT(supported_types); ++i) { | |
| 250 printf(" %-10s %s\n", supported_types[i].name, | |
| 251 supported_types[i].description); | |
| 252 } | |
| 253 printf("\n"); | |
| 254 } | |
| 255 | |
| 256 /* Loads sectors from 'fd'. | |
| 257 * *buf is pointed to an allocated memory when returned, and should be | |
| 258 * freed by cgpt_close(). | |
| 259 * | |
| 260 * fd -- file descriptot. | |
| 261 * buf -- pointer to buffer pointer | |
| 262 * sector -- offset of starting sector (in sectors) | |
| 263 * sector_bytes -- bytes per sector | |
| 264 * sector_count -- number of sectors to load | |
| 265 * | |
| 266 * Returns CGPT_OK for successful. Aborts if any error occurs. | |
| 267 */ | |
| 268 int Load(const int fd, uint8_t **buf, | |
| 269 const uint64_t sector, | |
| 270 const uint64_t sector_bytes, | |
| 271 const uint64_t sector_count) { | |
| 272 int count; /* byte count to read */ | |
| 273 int nread; | |
| 274 | |
| 275 assert(buf); | |
| 276 count = sector_bytes * sector_count; | |
| 277 *buf = Malloc(count); | |
| 278 assert(*buf); | |
| 279 | |
| 280 if (-1 == lseek64(fd, sector * sector_bytes, SEEK_SET)) | |
| 281 goto error_free; | |
| 282 | |
| 283 nread = read(fd, *buf, count); | |
| 284 if (nread < count) | |
| 285 goto error_free; | |
| 286 | |
| 287 return CGPT_OK; | |
| 288 | |
| 289 error_free: | |
| 290 Free(*buf); | |
| 291 *buf = 0; | |
| 292 abort(); | |
| 293 } | |
| 294 | |
| 295 /* Saves sectors to 'fd'. | |
| 296 * | |
| 297 * fd -- file descriptot. | |
| 298 * buf -- pointer to buffer | |
| 299 * sector -- starting sector offset | |
| 300 * sector_bytes -- bytes per sector | |
| 301 * sector_count -- number of sector to save | |
| 302 * | |
| 303 * Returns CGPT_OK for successful, CGPT_FAILED for failed. | |
| 304 */ | |
| 305 int Save(const int fd, const uint8_t *buf, | |
| 306 const uint64_t sector, | |
| 307 const uint64_t sector_bytes, | |
| 308 const uint64_t sector_count) { | |
| 309 int count; /* byte count to write */ | |
| 310 int nwrote; | |
| 311 | |
| 312 assert(buf); | |
| 313 count = sector_bytes * sector_count; | |
| 314 | |
| 315 if (-1 == lseek64(fd, sector * sector_bytes, SEEK_SET)) | |
| 316 return CGPT_FAILED; | |
| 317 | |
| 318 nwrote = write(fd, buf, count); | |
| 319 if (nwrote < count) | |
| 320 return CGPT_FAILED; | |
| 321 | |
| 322 return CGPT_OK; | |
| 323 } | |
| 324 | |
| 325 int CheckValid(const struct drive *drive) { | |
| 326 if ((drive->gpt.valid_headers != MASK_BOTH) || | |
| 327 (drive->gpt.valid_entries != MASK_BOTH)) { | |
| 328 printf("\n[ERROR] any of GPT header/entries is invalid, " | |
| 329 "please run '%s repair' first\n", progname); | |
| 330 return CGPT_FAILED; | |
| 331 } | |
| 332 return CGPT_OK; | |
| 333 } | |
| 334 | |
| 335 /* Opens a block device (a regular file works well too). | |
| 336 * | |
| 337 * Returns CGPT_FAILED if any error happens. | |
| 338 * Returns CGPT_OK if success and information are stored in 'drive'. */ | |
| 339 int DriveOpen(const char *drive_path, struct drive *drive) { | |
| 340 struct stat stat; | |
| 341 int gpt_retval; | |
| 342 | |
| 343 assert(drive_path); | |
| 344 assert(drive); | |
| 345 | |
| 346 Memset(drive, 0, sizeof(struct drive)); | |
| 347 drive->fd = open(drive_path, O_RDWR | O_LARGEFILE); | |
| 348 if (drive->fd == -1) { | |
| 349 printf("[ERROR] Cannot open drive file [%s]: %s\n", | |
| 350 drive_path, strerror(errno)); | |
| 351 return CGPT_FAILED; | |
| 352 } | |
| 353 | |
| 354 if (fstat(drive->fd, &stat) == -1) { | |
| 355 goto error_close; | |
| 356 } | |
| 357 if ((stat.st_mode & S_IFMT) != S_IFREG) { | |
| 358 if (ioctl(drive->fd, BLKGETSIZE64, &drive->size) < 0) { | |
| 359 printf("[ERROR] Cannot get sector size from drive file [%s]: %s\n", | |
| 360 drive_path, strerror(errno)); | |
| 361 goto error_close; | |
| 362 } | |
| 363 if (ioctl(drive->fd, BLKSSZGET, &drive->gpt.sector_bytes) < 0) { | |
| 364 printf("[ERROR] Cannot get drive size from drive file [%s]: %s\n", | |
| 365 drive_path, strerror(errno)); | |
| 366 goto error_close; | |
| 367 } | |
| 368 } else { | |
| 369 drive->gpt.sector_bytes = 512; /* bytes */ | |
| 370 drive->size = stat.st_size; | |
| 371 } | |
| 372 if (drive->size % drive->gpt.sector_bytes) { | |
| 373 printf("[ERROR] Media size (%llu) is not the multiple of sector size(%d)\n", | |
| 374 (long long unsigned int)drive->size, drive->gpt.sector_bytes); | |
| 375 goto error_close; | |
| 376 } | |
| 377 drive->gpt.drive_sectors = drive->size / drive->gpt.sector_bytes; | |
| 378 | |
| 379 Load(drive->fd, &drive->gpt.primary_header, GPT_PMBR_SECTOR, | |
| 380 drive->gpt.sector_bytes, GPT_HEADER_SECTOR); | |
| 381 Load(drive->fd, &drive->gpt.secondary_header, | |
| 382 drive->gpt.drive_sectors - GPT_PMBR_SECTOR, | |
| 383 drive->gpt.sector_bytes, GPT_HEADER_SECTOR); | |
| 384 Load(drive->fd, &drive->gpt.primary_entries, | |
| 385 GPT_PMBR_SECTOR + GPT_HEADER_SECTOR, | |
| 386 drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS); | |
| 387 Load(drive->fd, &drive->gpt.secondary_entries, | |
| 388 drive->gpt.drive_sectors - GPT_HEADER_SECTOR - GPT_ENTRIES_SECTORS, | |
| 389 drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS); | |
| 390 | |
| 391 if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive->gpt))) { | |
| 392 printf("[ERROR] GptSanityCheck(): %s\n", GptError(gpt_retval)); | |
| 393 goto error_close; | |
| 394 } | |
| 395 | |
| 396 drive->inited = 1; | |
| 397 | |
| 398 return CGPT_OK; | |
| 399 | |
| 400 error_close: | |
| 401 close(drive->fd); | |
| 402 return CGPT_FAILED; | |
| 403 } | |
| 404 | |
| 405 int DriveClose(struct drive *drive) { | |
| 406 if (drive->inited) { | |
| 407 if (drive->gpt.modified & GPT_MODIFIED_HEADER1) | |
| 408 assert(CGPT_OK == | |
| 409 Save(drive->fd, drive->gpt.primary_header, GPT_PMBR_SECTOR, | |
| 410 drive->gpt.sector_bytes, GPT_HEADER_SECTOR)); | |
| 411 if (drive->gpt.modified & GPT_MODIFIED_HEADER2) | |
| 412 assert(CGPT_OK == | |
| 413 Save(drive->fd, drive->gpt.secondary_header, | |
| 414 drive->gpt.drive_sectors - GPT_PMBR_SECTOR, | |
| 415 drive->gpt.sector_bytes, GPT_HEADER_SECTOR)); | |
| 416 if (drive->gpt.modified & GPT_MODIFIED_ENTRIES1) | |
| 417 assert(CGPT_OK == | |
| 418 Save(drive->fd, drive->gpt.primary_entries, | |
| 419 GPT_PMBR_SECTOR + GPT_HEADER_SECTOR, | |
| 420 drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)); | |
| 421 if (drive->gpt.modified & GPT_MODIFIED_ENTRIES2) | |
| 422 assert(CGPT_OK == | |
| 423 Save(drive->fd, drive->gpt.secondary_entries, | |
| 424 drive->gpt.drive_sectors - GPT_HEADER_SECTOR - | |
| 425 GPT_ENTRIES_SECTORS, | |
| 426 drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)); | |
| 427 | |
| 428 close(drive->fd); | |
| 429 } | |
| 430 | |
| 431 Free(drive->gpt.primary_header); | |
| 432 drive->gpt.primary_header = 0; | |
| 433 Free(drive->gpt.primary_entries); | |
| 434 drive->gpt.primary_entries = 0; | |
| 435 Free(drive->gpt.secondary_header); | |
| 436 drive->gpt.secondary_header = 0; | |
| 437 Free(drive->gpt.secondary_entries); | |
| 438 drive->gpt.secondary_entries = 0; | |
| 439 | |
| 440 drive->inited = 0; | |
| 441 return CGPT_OK; | |
| 442 } | |
| 443 | |
| 444 int main(int argc, char *argv[]) { | |
| 445 char *cmd; | |
| 446 int i; | |
| 447 | |
| 448 progname = argv[0]; | |
| 449 cmd = argv[optind++]; | |
| 450 for (i = 0; i < sizeof(cmds)/sizeof(cmds[0]); ++i) { | |
| 451 if (cmd && !strcmp(cmds[i].name, cmd)) | |
| 452 return cmds[i].fp(argc, argv); | |
| 453 } | |
| 454 | |
| 455 Usage(0); | |
| 456 return CGPT_FAILED; | |
| 457 } | |
| OLD | NEW |