summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/metaentry.c128
-rw-r--r--src/utils.c125
-rw-r--r--src/utils.h15
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);