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 |