blob: 7cfc4c12fb1e4d4c8f0a5b24b4c457587738e2e0 [file] [log] [blame]
/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Utility for ChromeOS-specific GPT partitions, Please see corresponding .c
* files for more details.
*/
/* To compile on host without compatility to BSD, we include
* endian.h under chroot. */
#define _BSD_SOURCE
#include "endian.h"
#define __USE_LARGEFILE64
#define __USE_FILE_OFFSET64
#define _LARGEFILE64_SOURCE
#include "cgpt.h"
#include "cgpt_tofix.h"
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "cgptlib_internal.h"
#include "utility.h"
/* For usage print */
const char* progname;
/* Lists all command here. */
struct {
const char *name;
int (*fp)(int argc, char *argv[]);
const char *comment;
} cmds[] = {
{"add", CgptAdm, "Add a partition to drive"},
{"delete", CgptAdm, "Delete a partition on drive"},
{"modify", CgptAdm, "Modify the partition on drive"},
{"attribute", CgptAttribute, "Update GPT attribute bits "
"(for ChromeOS kernel entry only)"},
{"dev", CgptDev, "Developper mode"},
{"repair", CgptRepair, "Repair primary and secondary headers and tables"},
{"show", CgptShow, "Show partition details"},
};
/* Shows main menu. If 'message' is non-NULL, shows it as header. Then, this
* traverses cmds[] and shows supported commands and their comments. */
void Usage(const char *message) {
int i;
if (message) printf("%s\n", message);
printf("Usage: %s COMMAND [OPTIONS]\n\n"
"Supported COMMANDs:\n\n",
progname);
for (i = 0; i < sizeof(cmds)/sizeof(cmds[0]); ++i) {
printf(" %-10s %s\n", cmds[i].name, cmds[i].comment);
}
printf("\nFor more detailed usage, use %s COMMAND --help.\n\n", progname);
}
/* GUID conversion functions. Accepted format:
*
* "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
*
* Returns CGPT_OK if parsing is successful; otherwise CGPT_FAILED.
*/
int StrToGuid(const char *str, Guid *guid) {
uint32_t time_low, time_mid, time_high_and_version;
if (11 > sscanf(str, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
&time_low,
(unsigned int *)&time_mid,
(unsigned int *)&time_high_and_version,
(unsigned int *)&guid->u.Uuid.clock_seq_high_and_reserved,
(unsigned int *)&guid->u.Uuid.clock_seq_low,
(unsigned int *)&guid->u.Uuid.node[0],
(unsigned int *)&guid->u.Uuid.node[1],
(unsigned int *)&guid->u.Uuid.node[2],
(unsigned int *)&guid->u.Uuid.node[3],
(unsigned int *)&guid->u.Uuid.node[4],
(unsigned int *)&guid->u.Uuid.node[5])) return CGPT_FAILED;
guid->u.Uuid.time_low = htole32(time_low);
guid->u.Uuid.time_mid = htole16(time_mid);
guid->u.Uuid.time_high_and_version = htole16(time_high_and_version);
return CGPT_OK;
}
void GuidToStr(const Guid *guid, char *str) {
sprintf(str, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
le32toh(guid->u.Uuid.time_low), le16toh(guid->u.Uuid.time_mid),
le16toh(guid->u.Uuid.time_high_and_version),
guid->u.Uuid.clock_seq_high_and_reserved, guid->u.Uuid.clock_seq_low,
guid->u.Uuid.node[0], guid->u.Uuid.node[1], guid->u.Uuid.node[2],
guid->u.Uuid.node[3], guid->u.Uuid.node[4], guid->u.Uuid.node[5]);
}
/* Convert UTF16 string to UTF8. Rewritten from gpt utility.
* Caller must prepare enough space for UTF8. The rough estimation is:
*
* utf8 length = bytecount(utf16) * 1.5
*/
#define SIZEOF_GPTENTRY_NAME 36 /* sizeof(GptEntry.name[]) */
void UTF16ToUTF8(const uint16_t *utf16, uint8_t *utf8)
{
size_t s8idx, s16idx, s16len;
uint32_t utfchar;
unsigned int next_utf16;
for (s16len = 0; s16len < SIZEOF_GPTENTRY_NAME && utf16[s16len]; ++s16len);
*utf8 = s8idx = s16idx = 0;
while (s16idx < s16len) {
utfchar = le16toh(utf16[s16idx++]);
if ((utfchar & 0xf800) == 0xd800) {
next_utf16 = le16toh(utf16[s16idx]);
if ((utfchar & 0x400) != 0 || (next_utf16 & 0xfc00) != 0xdc00)
utfchar = 0xfffd;
else
s16idx++;
}
if (utfchar < 0x80) {
utf8[s8idx++] = utfchar;
} else if (utfchar < 0x800) {
utf8[s8idx++] = 0xc0 | (utfchar >> 6);
utf8[s8idx++] = 0x80 | (utfchar & 0x3f);
} else if (utfchar < 0x10000) {
utf8[s8idx++] = 0xe0 | (utfchar >> 12);
utf8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f);
utf8[s8idx++] = 0x80 | (utfchar & 0x3f);
} else if (utfchar < 0x200000) {
utf8[s8idx++] = 0xf0 | (utfchar >> 18);
utf8[s8idx++] = 0x80 | ((utfchar >> 12) & 0x3f);
utf8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f);
utf8[s8idx++] = 0x80 | (utfchar & 0x3f);
}
}
}
/* Convert UTF8 string to UTF16. Rewritten from gpt utility.
* Caller must prepare enough space for UTF16. The conservative estimation is:
*
* utf16 bytecount = bytecount(utf8) / 3 * 4
*/
void UTF8ToUTF16(const uint8_t *utf8, uint16_t *utf16)
{
size_t s16idx, s8idx, s8len;
uint32_t utfchar;
unsigned int c, utfbytes;
for (s8len = 0; utf8[s8len]; ++s8len);
s8idx = s16idx = 0;
utfbytes = 0;
do {
c = utf8[s8idx++];
if ((c & 0xc0) != 0x80) {
/* Initial characters. */
if (utfbytes != 0) {
/* Incomplete encoding. */
utf16[s16idx++] = 0xfffd;
}
if ((c & 0xf8) == 0xf0) {
utfchar = c & 0x07;
utfbytes = 3;
} else if ((c & 0xf0) == 0xe0) {
utfchar = c & 0x0f;
utfbytes = 2;
} else if ((c & 0xe0) == 0xc0) {
utfchar = c & 0x1f;
utfbytes = 1;
} else {
utfchar = c & 0x7f;
utfbytes = 0;
}
} else {
/* Followup characters. */
if (utfbytes > 0) {
utfchar = (utfchar << 6) + (c & 0x3f);
utfbytes--;
} else if (utfbytes == 0)
utfbytes = -1;
utfchar = 0xfffd;
}
if (utfbytes == 0) {
if (utfchar >= 0x10000) {
utf16[s16idx++] = htole16(0xd800 | ((utfchar>>10)-0x40));
if (s16idx >= SIZEOF_GPTENTRY_NAME) break;
utf16[s16idx++] = htole16(0xdc00 | (utfchar & 0x3ff));
} else {
utf16[s16idx++] = htole16(utfchar);
}
}
} while (c != 0 && s16idx < SIZEOF_GPTENTRY_NAME);
if (s16idx < SIZEOF_GPTENTRY_NAME)
utf16[s16idx++] = 0;
}
struct {
Guid type;
char *name;
char *description;
} supported_types[] = {
{GPT_ENT_TYPE_UNUSED, "unused", "Unused partition"},
{GPT_ENT_TYPE_EFI, "efi", "EFI partition"},
{GPT_ENT_TYPE_CHROMEOS_KERNEL, "croskern", "ChromeOS kernel"},
{GPT_ENT_TYPE_CHROMEOS_ROOTFS, "crosroot", "ChromeOS rootfs"},
{GPT_ENT_TYPE_CHROMEOS_RESERVED, "crosresv", "ChromeOS reserved"},
};
/* Resolves human-readable GPT type.
* Returns CGPT_OK if found.
* Returns CGPT_FAILED if no known type found. */
int ResolveType(const Guid *type, char *buf) {
int i;
for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
if (!Memcmp(type, &supported_types[i].type, sizeof(Guid))) {
strcpy(buf, supported_types[i].description);
return CGPT_OK;
}
}
return CGPT_FAILED;
}
int SupportedType(const char *name, Guid *type) {
int i;
for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
if (!strcmp(name, supported_types[i].name)) {
Memcpy(type, &supported_types[i].type, sizeof(Guid));
return CGPT_OK;
}
}
return CGPT_FAILED;
}
void PrintTypes(void) {
int i;
printf("\n* For --type option, you can use the following alias, "
"instead of hex values:\n");
for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
printf(" %-10s %s\n", supported_types[i].name,
supported_types[i].description);
}
printf("\n");
}
/* Loads sectors from 'fd'.
* *buf is pointed to an allocated memory when returned, and should be
* freed by cgpt_close().
*
* fd -- file descriptot.
* buf -- pointer to buffer pointer
* sector -- offset of starting sector (in sectors)
* sector_bytes -- bytes per sector
* sector_count -- number of sectors to load
*
* Returns CGPT_OK for successful. Aborts if any error occurs.
*/
int Load(const int fd, uint8_t **buf,
const uint64_t sector,
const uint64_t sector_bytes,
const uint64_t sector_count) {
int count; /* byte count to read */
int nread;
assert(buf);
count = sector_bytes * sector_count;
*buf = Malloc(count);
assert(*buf);
if (-1 == lseek64(fd, sector * sector_bytes, SEEK_SET))
goto error_free;
nread = read(fd, *buf, count);
if (nread < count)
goto error_free;
return CGPT_OK;
error_free:
Free(*buf);
*buf = 0;
abort();
}
/* Saves sectors to 'fd'.
*
* fd -- file descriptot.
* buf -- pointer to buffer
* sector -- starting sector offset
* sector_bytes -- bytes per sector
* sector_count -- number of sector to save
*
* Returns CGPT_OK for successful, CGPT_FAILED for failed.
*/
int Save(const int fd, const uint8_t *buf,
const uint64_t sector,
const uint64_t sector_bytes,
const uint64_t sector_count) {
int count; /* byte count to write */
int nwrote;
assert(buf);
count = sector_bytes * sector_count;
if (-1 == lseek64(fd, sector * sector_bytes, SEEK_SET))
return CGPT_FAILED;
nwrote = write(fd, buf, count);
if (nwrote < count)
return CGPT_FAILED;
return CGPT_OK;
}
int CheckValid(const struct drive *drive) {
if ((drive->gpt.valid_headers != MASK_BOTH) ||
(drive->gpt.valid_entries != MASK_BOTH)) {
printf("\n[ERROR] any of GPT header/entries is invalid, "
"please run '%s repair' first\n", progname);
return CGPT_FAILED;
}
return CGPT_OK;
}
/* Opens a block device (a regular file works well too).
*
* Returns CGPT_FAILED if any error happens.
* Returns CGPT_OK if success and information are stored in 'drive'. */
int DriveOpen(const char *drive_path, struct drive *drive) {
struct stat stat;
int gpt_retval;
assert(drive_path);
assert(drive);
Memset(drive, 0, sizeof(struct drive));
drive->fd = open(drive_path, O_RDWR | O_LARGEFILE);
if (drive->fd == -1) {
printf("[ERROR] Cannot open drive file [%s]: %s\n",
drive_path, strerror(errno));
return CGPT_FAILED;
}
if (fstat(drive->fd, &stat) == -1) {
goto error_close;
}
if ((stat.st_mode & S_IFMT) != S_IFREG) {
if (ioctl(drive->fd, BLKGETSIZE64, &drive->size) < 0) {
printf("[ERROR] Cannot get sector size from drive file [%s]: %s\n",
drive_path, strerror(errno));
goto error_close;
}
if (ioctl(drive->fd, BLKSSZGET, &drive->gpt.sector_bytes) < 0) {
printf("[ERROR] Cannot get drive size from drive file [%s]: %s\n",
drive_path, strerror(errno));
goto error_close;
}
} else {
drive->gpt.sector_bytes = 512; /* bytes */
drive->size = stat.st_size;
}
if (drive->size % drive->gpt.sector_bytes) {
printf("[ERROR] Media size (%llu) is not the multiple of sector size(%d)\n",
(long long unsigned int)drive->size, drive->gpt.sector_bytes);
goto error_close;
}
drive->gpt.drive_sectors = drive->size / drive->gpt.sector_bytes;
Load(drive->fd, &drive->gpt.primary_header, GPT_PMBR_SECTOR,
drive->gpt.sector_bytes, GPT_HEADER_SECTOR);
Load(drive->fd, &drive->gpt.secondary_header,
drive->gpt.drive_sectors - GPT_PMBR_SECTOR,
drive->gpt.sector_bytes, GPT_HEADER_SECTOR);
Load(drive->fd, &drive->gpt.primary_entries,
GPT_PMBR_SECTOR + GPT_HEADER_SECTOR,
drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS);
Load(drive->fd, &drive->gpt.secondary_entries,
drive->gpt.drive_sectors - GPT_HEADER_SECTOR - GPT_ENTRIES_SECTORS,
drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS);
if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive->gpt))) {
printf("[ERROR] GptSanityCheck(): %s\n", GptError(gpt_retval));
goto error_close;
}
drive->inited = 1;
return CGPT_OK;
error_close:
close(drive->fd);
return CGPT_FAILED;
}
int DriveClose(struct drive *drive) {
if (drive->inited) {
if (drive->gpt.modified & GPT_MODIFIED_HEADER1)
assert(CGPT_OK ==
Save(drive->fd, drive->gpt.primary_header, GPT_PMBR_SECTOR,
drive->gpt.sector_bytes, GPT_HEADER_SECTOR));
if (drive->gpt.modified & GPT_MODIFIED_HEADER2)
assert(CGPT_OK ==
Save(drive->fd, drive->gpt.secondary_header,
drive->gpt.drive_sectors - GPT_PMBR_SECTOR,
drive->gpt.sector_bytes, GPT_HEADER_SECTOR));
if (drive->gpt.modified & GPT_MODIFIED_ENTRIES1)
assert(CGPT_OK ==
Save(drive->fd, drive->gpt.primary_entries,
GPT_PMBR_SECTOR + GPT_HEADER_SECTOR,
drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS));
if (drive->gpt.modified & GPT_MODIFIED_ENTRIES2)
assert(CGPT_OK ==
Save(drive->fd, drive->gpt.secondary_entries,
drive->gpt.drive_sectors - GPT_HEADER_SECTOR -
GPT_ENTRIES_SECTORS,
drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS));
close(drive->fd);
}
Free(drive->gpt.primary_header);
drive->gpt.primary_header = 0;
Free(drive->gpt.primary_entries);
drive->gpt.primary_entries = 0;
Free(drive->gpt.secondary_header);
drive->gpt.secondary_header = 0;
Free(drive->gpt.secondary_entries);
drive->gpt.secondary_entries = 0;
drive->inited = 0;
return CGPT_OK;
}
int main(int argc, char *argv[]) {
char *cmd;
int i;
progname = argv[0];
printf("Copyright (c) 2010 The Chromium OS Authors. All rights reserved.\n");
cmd = argv[optind++];
for (i = 0; i < sizeof(cmds)/sizeof(cmds[0]); ++i) {
if (cmd && !strcmp(cmds[i].name, cmd))
return cmds[i].fp(argc, argv);
}
Usage(0);
return CGPT_FAILED;
}