diff options
author | Adam Spragg <adam@spra.gg> | 2022-05-17 17:31:48 +0100 |
---|---|---|
committer | Adam Spragg <adam@spra.gg> | 2022-05-18 17:19:47 +0100 |
commit | 260fc47f01ef64368721ec93de4cbd988357b32b (patch) | |
tree | 551213a3f0ecdff66f525cf4b0a59c349ee898dd | |
parent | b3c08670f56d7c5ee85dffa1a2faa5e03d2df5a1 (diff) |
Add ability to read Format 1 metadata files
-rw-r--r-- | src/metaentry.c | 128 | ||||
-rw-r--r-- | src/utils.c | 125 | ||||
-rw-r--r-- | src/utils.h | 15 |
3 files changed, 264 insertions, 4 deletions
diff --git a/src/metaentry.c b/src/metaentry.c index 6e27f11..ed0e23d 100644 --- a/src/metaentry.c +++ b/src/metaentry.c @@ -555,6 +555,123 @@ mentries_fromfile_v0(struct metahash **mhash, const char *ptr, const char *max) return 0; } +/* Creates a metaentry list from a Format 1 file buffer */ +static int +mentries_fromfile_v1(struct metahash **mhash, const char *ptr, const char *max) +{ + struct metaentry *mentry; + int i; + char *mtime = NULL, *nsec, *tz; + struct tm tm; + + if (read_char(&ptr, max) != '\n') + return -1; + + while (ptr < max) { + mentry = mentry_alloc(); + + if ((mentry->path = read_string_url(&ptr, max)) == NULL) + goto err; + mentry->pathlen = strlen(mentry->path); + + if (read_char(&ptr, max) != '\t') + goto err; + mentry->owner = read_string_url(&ptr, max); + + if (read_char(&ptr, max) != '\t') + goto err; + mentry->group = read_string_url(&ptr, max); + + if (read_char(&ptr, max) != '\t') + goto err; + mentry->mode = read_int_string(&ptr, max, 8); + + /* Read the whole mtime string into `mtime`. */ + if (read_char(&ptr, max) != '\t') + goto err; + if ((mtime = read_string_url(&ptr, max)) == NULL) + goto err; + + /* Get the time_t part of `mtime` into mentry->mtime */ + if ((nsec = strptime(mtime, "%Y-%m-%dT%H:%M:%S", &tm)) == NULL) + goto err; + tm.tm_isdst = 0; + mentry->mtime = timegm(&tm); + + /* Check if there's a nanosecond portion of `mtime` */ + if (*nsec == '.') { + /* There is a decimal point in mtime. Get nanoseconds. */ + ++nsec; + mentry->mtimensec = strtol(nsec, &tz, 10); + i = tz - nsec; + while (i < 9) { + /* Too few digits for nanosecond precision. + * e.g. "...T10:15:23.5", which we've parsed as + * 5 nanoseconds, but is actually half a second + * or 500000000 nanoseconds. + * Scale to correct value. + */ + mentry->mtimensec *= 10; + ++i; + } + while (i > 9) { + /* Too many digits for nanosecond precision. + * Scale to correct value + */ + mentry->mtimensec /= 10; + --i; + } + } + else { + /* No decimal point. Set nanoseconds to zero */ + mentry->mtimensec = 0; + tz = nsec; + } + + /* Check for a 'Z' (Zulu/UTC) timesone specifier in `mtime`. */ + if (*tz == 'Z') + ++tz; + + /* Check that we've reached the end of `mtime` */ + if (*tz != '\0') + goto err; + free(mtime); + mtime = NULL; + + mentry->xattrs = 0; + mentry->xattr_names = NULL; + mentry->xattr_lvalues = NULL; + mentry->xattr_values = NULL; + while ((i = read_char(&ptr, max)) == '\t') { + mentry->xattr_names = xrealloc(mentry->xattr_names, + (mentry->xattrs + 1) * sizeof(char *)); + mentry->xattr_lvalues = xrealloc(mentry->xattr_lvalues, + (mentry->xattrs + 1) * sizeof(ssize_t)); + mentry->xattr_values = xrealloc(mentry->xattr_values, + (mentry->xattrs + 1) * sizeof(char *)); + + mentry->xattr_names[mentry->xattrs] = read_string_url(&ptr, max); + read_char(&ptr, max); + mentry->xattr_values[mentry->xattrs] = read_binary_url(&ptr, max, + &mentry->xattr_lvalues[mentry->xattrs]); + + ++mentry->xattrs; + } + if (i != '\n') + goto err; + + mentry_insert(mentry, *mhash); + } + + return 0; + +err: + if (mtime) + free(mtime); + mentry_free(mentry); + return -1; +} + /* Creates a metaentry list from a file */ void mentries_fromfile(struct metahash **mhash, const char *path) @@ -603,13 +720,16 @@ mentries_fromfile(struct metahash **mhash, const char *path) } ptr += SIGNATURELEN; - if (strncmp(ptr, VERSION_0, VERSIONLEN)) { + if (strncmp(ptr, VERSION_0, VERSIONLEN) == 0) { + i = mentries_fromfile_v0(mhash, ptr + VERSIONLEN, max); + } + else if (strncmp(ptr, VERSION_1, VERSIONLEN) == 0) { + i = mentries_fromfile_v1(mhash, ptr + VERSIONLEN, max); + } + else { msg(MSG_CRITICAL, "Invalid version of file %s\n", path); goto out; } - ptr += VERSIONLEN; - - i = mentries_fromfile_v0(mhash, ptr, max); if (i != 0) { msg(MSG_CRITICAL, "Invalid characters in file %s\n", path); goto out; diff --git a/src/utils.c b/src/utils.c index 5cdef6b..2247151 100644 --- a/src/utils.c +++ b/src/utils.c @@ -80,6 +80,18 @@ xmalloc(size_t size) return result; } +/* Ditto for realloc */ +void * +xrealloc(void *ptr, size_t size) +{ + void *result = realloc(ptr, size); + if (!result) { + msg(MSG_CRITICAL, "Failed to realloc %zu bytes\n", size); + exit(EXIT_FAILURE); + } + return result; +} + /* Ditto for strdup */ char * xstrdup(const char *s) @@ -143,6 +155,19 @@ write_string(const char *string, FILE *to) xfwrite(string, strlen(string) + 1, to); } +/* Get the value of a hex digit */ +static int +xdigitval(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'F') + return 10 + c - 'A'; + if (c >= 'a' && c <= 'f') + return 10 + c - 'a'; + return -1; +} + /* Check if a character being put into a URL must be encoded */ static int char_url_mustencode(int c) @@ -192,6 +217,15 @@ write_string_url(const char *string, FILE *to) write_char_url(c, to); } +/* Reads a single char from a file */ +int +read_char(const char **from, const char *max) +{ + if (*from >= max) + return EOF; + return *((*from)++); +} + /* Reads an int from a file, using len bytes, in little-endian order */ uint64_t read_int(const char **from, size_t len, const char *max) @@ -211,6 +245,27 @@ read_int(const char **from, size_t len, const char *max) return result; } +/* Reads an int from a file, stored as an ASCII string, to the first non-digit */ +long +read_int_string(const char **from, const char *max, int base) +{ + long result = 0; + int sign = 1, i; + + if (*from < max && **from == '-') { + sign = -1; + *from += 1; + } + + while (*from < max && (i = xdigitval(**from)) >= 0 && i < base) { + result *= base; + result += i; + *from += 1; + } + + return result * sign; +} + /* Reads a binary string from a file */ char * read_binary_string(const char **from, size_t len, const char *max) @@ -229,6 +284,69 @@ read_binary_string(const char **from, size_t len, const char *max) return result; } +/* Reads binary data from a file, which was stored URL-encoded */ +char * +read_binary_url(const char **pfrom, const char *max, ssize_t *plen) +{ + const char *from; + size_t bufsize, len; + char * buf; + int hi, lo; + + from = *pfrom; + bufsize = 32; + buf = xmalloc(bufsize); + len = 0; + + while (from < max) { + if (len == bufsize) { + bufsize *= 2; + buf = xrealloc(buf, bufsize); + } + + if (*from == '%' && (max - from) >= 3) { + /* URL-encoded char - decode */ + from++; + hi = xdigitval(*from++); + lo = xdigitval(*from++); + if (hi < 0 || lo < 0) { + /* Invalid encoding */ + free(buf); + *pfrom = from; + return NULL; + } + if (hi == 0 && lo == 0 && !plen) { + /* NUL in C string? Stop here */ + break; + } + buf[len++] = (hi << 4) + lo; + } + else if (char_url_mustencode(*from)) { + /* Un-encoded char. Stop here */ + break; + } + else { + buf[len++] = *from++; + } + } + + if (plen) { + /* plen is non-NULL means read binary data - return length */ + *plen = len; + } + else { + /* plen is NULL actually means read C string - NUL terminate */ + if (len == bufsize) { + bufsize *= 2; + buf = xrealloc(buf, bufsize); + } + buf[len++] = '\0'; + } + + *pfrom = from; + return buf; +} + /* Reads a normal C string from a file */ char * read_string(const char **from, const char *max) @@ -236,6 +354,13 @@ read_string(const char **from, const char *max) return read_binary_string(from, strlen(*from) + 1, max); } +/* Reads a normal C string from a file, which was stored URL-encoded */ +char * +read_string_url(const char **from, const char *max) +{ + return read_binary_url(from, max, NULL); +} + /* For group caching */ static struct group *gtable = NULL; diff --git a/src/utils.h b/src/utils.h index ef21b22..f920455 100644 --- a/src/utils.h +++ b/src/utils.h @@ -50,6 +50,9 @@ int msg(int level, const char *fmt, ...); /* Malloc which either succeeds or exits */ void *xmalloc(size_t size); +/* Ditto for realloc */ +void *xrealloc(void *ptr, size_t size); + /* Ditto for strdup */ char *xstrdup(const char *s); @@ -77,15 +80,27 @@ void write_binary_url(const char *string, size_t len, FILE *to); /* Writes a normal C string to a file, URL-encoding any chars if necessary */ void write_string_url(const char *string, FILE *to); +/* Reads a single char from a file */ +int read_char(const char **from, const char *max); + /* Reads an int from a file, using len bytes, in little-endian order */ uint64_t read_int(const char **from, size_t len, const char *max); +/* Reads an int from a file, stored as an ASCII string, to the first non-digit */ +long read_int_string(const char **from, const char *max, int base); + /* Reads a binary string from a file */ char *read_binary_string(const char **from, size_t len, const char *max); +/* Reads binary data from a file, which was stored URL-encoded */ +char *read_binary_url(const char **from, const char *max, ssize_t *len); + /* Reads a normal C string from a file */ char *read_string(const char **from, const char *max); +/* Reads a normal C string from a file, which was stored URL-encoded */ +char *read_string_url(const char **from, const char *max); + /* Caching version of getgrnam */ struct group *xgetgrnam(const char *name); |