| Index: cgpt/cgpt_common.c
|
| diff --git a/cgpt/cgpt_common.c b/cgpt/cgpt_common.c
|
| index 8e8196b4df2a97f3373b0d1860b855cfed9927d2..4b9a5a141dce0b847ab7a4b58549a241c26f9fed 100644
|
| --- a/cgpt/cgpt_common.c
|
| +++ b/cgpt/cgpt_common.c
|
| @@ -11,6 +11,7 @@
|
| #include <errno.h>
|
| #include <fcntl.h>
|
| #include <getopt.h>
|
| +#include <stdarg.h>
|
| #include <stdint.h>
|
| #include <stdio.h>
|
| #include <stdlib.h>
|
| @@ -20,13 +21,10 @@
|
| #include <sys/stat.h>
|
| #include <sys/types.h>
|
| #include <unistd.h>
|
| -#include <assert.h>
|
| -#include <stdarg.h>
|
|
|
| #include "cgptlib_internal.h"
|
| #include "crc32.h"
|
|
|
| -
|
| void Error(const char *format, ...) {
|
| va_list ap;
|
| va_start(ap, format);
|
| @@ -65,17 +63,32 @@ static int Load(const int fd, uint8_t **buf,
|
| int count; /* byte count to read */
|
| int nread;
|
|
|
| - assert(buf);
|
| + require(buf);
|
| + if (!sector_count || !sector_bytes) {
|
| + Error("%s() failed at line %d: sector_count=%d, sector_bytes=%d\n",
|
| + __FUNCTION__, __LINE__, sector_count, sector_bytes);
|
| + return CGPT_FAILED;
|
| + }
|
| + /* Make sure that sector_bytes * sector_count doesn't roll over. */
|
| + if (sector_bytes > (UINT64_MAX / sector_count)) {
|
| + Error("%s() failed at line %d: sector_count=%d, sector_bytes=%d\n",
|
| + __FUNCTION__, __LINE__, sector_count, sector_bytes);
|
| + return CGPT_FAILED;
|
| + }
|
| count = sector_bytes * sector_count;
|
| *buf = malloc(count);
|
| - assert(*buf);
|
| + require(*buf);
|
|
|
| - if (-1 == lseek(fd, sector * sector_bytes, SEEK_SET))
|
| + if (-1 == lseek(fd, sector * sector_bytes, SEEK_SET)) {
|
| + Error("Can't lseek: %s\n", strerror(errno));
|
| goto error_free;
|
| + }
|
|
|
| nread = read(fd, *buf, count);
|
| - if (nread < count)
|
| + if (nread < count) {
|
| + Error("Can't read enough: %d, not %d\n", nread, count);
|
| goto error_free;
|
| + }
|
|
|
| return CGPT_OK;
|
|
|
| @@ -125,7 +138,7 @@ static int Save(const int fd, const uint8_t *buf,
|
| int count; /* byte count to write */
|
| int nwrote;
|
|
|
| - assert(buf);
|
| + require(buf);
|
| count = sector_bytes * sector_count;
|
|
|
| if (-1 == lseek(fd, sector * sector_bytes, SEEK_SET))
|
| @@ -140,25 +153,26 @@ static int Save(const int fd, const uint8_t *buf,
|
|
|
|
|
| // Opens a block device or file, loads raw GPT data from it.
|
| -//
|
| +//
|
| // 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;
|
|
|
| - assert(drive_path);
|
| - assert(drive);
|
| + require(drive_path);
|
| + require(drive);
|
|
|
| // Clear struct for proper error handling.
|
| memset(drive, 0, sizeof(struct drive));
|
|
|
| - drive->fd = open(drive_path, O_RDWR | O_LARGEFILE);
|
| + drive->fd = open(drive_path, O_RDWR | O_LARGEFILE | O_NOFOLLOW);
|
| if (drive->fd == -1) {
|
| Error("Can't open %s: %s\n", drive_path, strerror(errno));
|
| return CGPT_FAILED;
|
| }
|
|
|
| if (fstat(drive->fd, &stat) == -1) {
|
| + Error("Can't fstat %s: %s\n", drive_path, strerror(errno));
|
| goto error_close;
|
| }
|
| if ((stat.st_mode & S_IFMT) != S_IFREG) {
|
| @@ -204,7 +218,7 @@ int DriveOpen(const char *drive_path, struct drive *drive) {
|
| drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
|
| goto error_close;
|
| }
|
| -
|
| +
|
| // We just load the data. Caller must validate it.
|
| return CGPT_OK;
|
|
|
| @@ -226,7 +240,7 @@ int DriveClose(struct drive *drive, int update_as_needed) {
|
| Error("Cannot write primary header: %s\n", strerror(errno));
|
| }
|
| }
|
| -
|
| +
|
| if (drive->gpt.modified & GPT_MODIFIED_HEADER2) {
|
| if(CGPT_OK != Save(drive->fd, drive->gpt.secondary_header,
|
| drive->gpt.drive_sectors - GPT_PMBR_SECTOR,
|
| @@ -273,7 +287,6 @@ int DriveClose(struct drive *drive, int update_as_needed) {
|
| }
|
|
|
|
|
| -
|
| /* GUID conversion functions. Accepted format:
|
| *
|
| * "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
|
| @@ -297,7 +310,7 @@ int StrToGuid(const char *str, Guid *guid) {
|
| chunk+7,
|
| chunk+8,
|
| chunk+9,
|
| - chunk+10)) {
|
| + chunk+10)) {
|
| printf("FAILED\n");
|
| return CGPT_FAILED;
|
| }
|
| @@ -321,115 +334,72 @@ int StrToGuid(const char *str, Guid *guid) {
|
|
|
| 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
|
| +void GuidToStr(const Guid *guid, char *str, unsigned int buflen) {
|
| + require(buflen >= GUID_STRLEN);
|
| + require(snprintf(str, buflen,
|
| + "%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]) == GUID_STRLEN-1);
|
| +}
|
| +
|
| +/* Convert possibly unterminated UTF16 string to UTF8.
|
| + * Caller must prepare enough space for UTF8, which could be up to
|
| + * twice the number of UTF16 chars plus the terminating '\0'.
|
| + * FIXME(wfrichar): The original implementation had security issues. As a
|
| + * temporary fix, I'm making this ONLY support ASCII codepoints. Bug 7542
|
| + * (http://code.google.com/p/chromium-os/issues/detail?id=7542) is filed to fix
|
| + * this.
|
| */
|
| -#define SIZEOF_GPTENTRY_NAME 36 /* sizeof(GptEntry.name[]) */
|
| -void UTF16ToUTF8(const uint16_t *utf16, uint8_t *utf8)
|
| +void UTF16ToUTF8(const uint16_t *utf16, unsigned int maxinput,
|
| + uint8_t *utf8, unsigned int maxoutput)
|
| {
|
| - size_t s8idx, s16idx, s16len;
|
| + size_t s16idx, s8idx;
|
| 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);
|
| - }
|
| +
|
| + if (!utf16 || !maxinput || !utf8 || !maxoutput)
|
| + return;
|
| +
|
| + maxoutput--; /* plan for termination now */
|
| +
|
| + for (s16idx = s8idx = 0;
|
| + s16idx < maxinput && utf16[s16idx] && maxoutput;
|
| + s16idx++, maxoutput--) {
|
| + utfchar = le16toh(utf16[s16idx]);
|
| + utf8[s8idx++] = utfchar & 0x7F;
|
| }
|
| utf8[s8idx++] = 0;
|
| }
|
|
|
| -/* 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
|
| +/* Convert UTF8 string to UTF16. The UTF8 string must be null-terminated.
|
| + * Caller must prepare enough space for UTF16, including a terminating 0x0000.
|
| + * FIXME(wfrichar): The original implementation had security issues. As a
|
| + * temporary fix, I'm making this ONLY support ASCII codepoints. Bug 7542
|
| + * (http://code.google.com/p/chromium-os/issues/detail?id=7542) is filed to fix
|
| + * this.
|
| */
|
| -void UTF8ToUTF16(const uint8_t *utf8, uint16_t *utf16)
|
| +void UTF8ToUTF16(const uint8_t *utf8, uint16_t *utf16, unsigned int maxoutput)
|
| {
|
| - size_t s16idx, s8idx, s8len;
|
| + size_t s16idx, s8idx;
|
| 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;
|
| +
|
| + if (!utf8 || !utf16 || !maxoutput)
|
| + return;
|
| +
|
| + maxoutput--; /* plan for termination */
|
| +
|
| + for (s8idx = s16idx = 0;
|
| + utf8[s8idx] && maxoutput;
|
| + s8idx++, maxoutput--) {
|
| + utfchar = utf8[s8idx];
|
| + utf16[s16idx++] = utfchar & 0x7F;
|
| + }
|
| + utf16[s16idx++] = 0;
|
| }
|
|
|
| struct {
|
| @@ -499,14 +469,14 @@ static uint32_t GetSizeOfEntries(const GptData *gpt) {
|
| header = (GptHeader*)gpt->secondary_header;
|
| else
|
| return 0;
|
| - return header->number_of_entries;
|
| + return header->size_of_entry;
|
| }
|
|
|
| -GptEntry *GetEntry(GptData *gpt, int secondary, int entry_index) {
|
| +GptEntry *GetEntry(GptData *gpt, int secondary, uint32_t entry_index) {
|
| uint8_t *entries;
|
| - int stride = GetSizeOfEntries(gpt);
|
| - if (!stride)
|
| - return 0;
|
| + uint32_t stride = GetSizeOfEntries(gpt);
|
| + require(stride);
|
| + require(entry_index < GetNumberOfEntries(gpt));
|
|
|
| if (secondary == PRIMARY) {
|
| entries = gpt->primary_entries;
|
| @@ -517,48 +487,48 @@ GptEntry *GetEntry(GptData *gpt, int secondary, int entry_index) {
|
| return (GptEntry*)(&entries[stride * entry_index]);
|
| }
|
|
|
| -void SetPriority(GptData *gpt, int secondary, int entry_index, int priority) {
|
| +void SetPriority(GptData *gpt, int secondary, uint32_t entry_index,
|
| + int priority) {
|
| GptEntry *entry;
|
| entry = GetEntry(gpt, secondary, entry_index);
|
| -
|
| - assert(priority >= 0 && priority <= CGPT_ATTRIBUTE_MAX_PRIORITY);
|
| + require(priority >= 0 && priority <= CGPT_ATTRIBUTE_MAX_PRIORITY);
|
| entry->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_PRIORITY_MASK;
|
| entry->attrs.fields.gpt_att |= priority << CGPT_ATTRIBUTE_PRIORITY_OFFSET;
|
| }
|
|
|
| -int GetPriority(GptData *gpt, int secondary, int entry_index) {
|
| +int GetPriority(GptData *gpt, int secondary, uint32_t entry_index) {
|
| GptEntry *entry;
|
| entry = GetEntry(gpt, secondary, entry_index);
|
| return (entry->attrs.fields.gpt_att & CGPT_ATTRIBUTE_PRIORITY_MASK) >>
|
| CGPT_ATTRIBUTE_PRIORITY_OFFSET;
|
| }
|
|
|
| -void SetTries(GptData *gpt, int secondary, int entry_index, int tries) {
|
| +void SetTries(GptData *gpt, int secondary, uint32_t entry_index, int tries) {
|
| GptEntry *entry;
|
| entry = GetEntry(gpt, secondary, entry_index);
|
| -
|
| - assert(tries >= 0 && tries <= CGPT_ATTRIBUTE_MAX_TRIES);
|
| + require(tries >= 0 && tries <= CGPT_ATTRIBUTE_MAX_TRIES);
|
| entry->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_TRIES_MASK;
|
| entry->attrs.fields.gpt_att |= tries << CGPT_ATTRIBUTE_TRIES_OFFSET;
|
| }
|
|
|
| -int GetTries(GptData *gpt, int secondary, int entry_index) {
|
| +int GetTries(GptData *gpt, int secondary, uint32_t entry_index) {
|
| GptEntry *entry;
|
| entry = GetEntry(gpt, secondary, entry_index);
|
| return (entry->attrs.fields.gpt_att & CGPT_ATTRIBUTE_TRIES_MASK) >>
|
| CGPT_ATTRIBUTE_TRIES_OFFSET;
|
| }
|
|
|
| -void SetSuccessful(GptData *gpt, int secondary, int entry_index, int success) {
|
| +void SetSuccessful(GptData *gpt, int secondary, uint32_t entry_index,
|
| + int success) {
|
| GptEntry *entry;
|
| entry = GetEntry(gpt, secondary, entry_index);
|
|
|
| - assert(success >= 0 && success <= CGPT_ATTRIBUTE_MAX_SUCCESSFUL);
|
| + require(success >= 0 && success <= CGPT_ATTRIBUTE_MAX_SUCCESSFUL);
|
| entry->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_SUCCESSFUL_MASK;
|
| entry->attrs.fields.gpt_att |= success << CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
|
| }
|
|
|
| -int GetSuccessful(GptData *gpt, int secondary, int entry_index) {
|
| +int GetSuccessful(GptData *gpt, int secondary, uint32_t entry_index) {
|
| GptEntry *entry;
|
| entry = GetEntry(gpt, secondary, entry_index);
|
| return (entry->attrs.fields.gpt_att & CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >>
|
| @@ -714,13 +684,13 @@ int IsZero(const Guid *gp) {
|
| return (0 == memcmp(gp, &guid_unused, sizeof(Guid)));
|
| }
|
|
|
| -void PMBRToStr(struct pmbr *pmbr, char *str) {
|
| - char buf[256];
|
| +void PMBRToStr(struct pmbr *pmbr, char *str, unsigned int buflen) {
|
| + char buf[GUID_STRLEN];
|
| if (IsZero(&pmbr->boot_guid)) {
|
| - sprintf(str, "PMBR");
|
| + require(snprintf(str, buflen, "PMBR") < buflen);
|
| } else {
|
| - GuidToStr(&pmbr->boot_guid, buf);
|
| - sprintf(str, "PMBR (Boot GUID: %s)", buf);
|
| + GuidToStr(&pmbr->boot_guid, buf, sizeof(buf));
|
| + require(snprintf(str, buflen, "PMBR (Boot GUID: %s)", buf) < buflen);
|
| }
|
| }
|
|
|
|
|