aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cache.h1
-rw-r--r--sha1_file.c92
2 files changed, 74 insertions, 19 deletions
diff --git a/cache.h b/cache.h
index 69b63ba1d..aa74bcc01 100644
--- a/cache.h
+++ b/cache.h
@@ -152,6 +152,7 @@ extern char *sha1_file_name(const unsigned char *sha1);
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
extern void * map_sha1_file(const unsigned char *sha1, unsigned long *size);
extern int unpack_sha1_header(z_stream *stream, void *map, unsigned long mapsize, void *buffer, unsigned long size);
+extern int parse_sha1_header(char *hdr, char *type, unsigned long *sizep);
extern void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned long *size);
extern void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size);
extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
diff --git a/sha1_file.c b/sha1_file.c
index bc3d70fdd..af39e8860 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -320,31 +320,85 @@ int unpack_sha1_header(z_stream *stream, void *map, unsigned long mapsize, void
return inflate(stream, 0);
}
+void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size)
+{
+ int bytes = strlen(buffer) + 1;
+ char *buf = xmalloc(1+size);
+
+ memcpy(buf, buffer + bytes, stream->total_out - bytes);
+ bytes = stream->total_out - bytes;
+ if (bytes < size) {
+ stream->next_out = buf + bytes;
+ stream->avail_out = size - bytes;
+ while (inflate(stream, Z_FINISH) == Z_OK)
+ /* nothing */;
+ }
+ buf[size] = 0;
+ inflateEnd(stream);
+ return buf;
+}
+
+/*
+ * We used to just use "sscanf()", but that's actually way
+ * too permissive for what we want to check. So do an anal
+ * object header parse by hand.
+ */
+int parse_sha1_header(char *hdr, char *type, unsigned long *sizep)
+{
+ int i;
+ unsigned long size;
+
+ /*
+ * The type can be at most ten bytes (including the
+ * terminating '\0' that we add), and is followed by
+ * a space.
+ */
+ i = 10;
+ for (;;) {
+ char c = *hdr++;
+ if (c == ' ')
+ break;
+ if (!--i)
+ return -1;
+ *type++ = c;
+ }
+ *type = 0;
+
+ /*
+ * The length must follow immediately, and be in canonical
+ * decimal format (ie "010" is not valid).
+ */
+ size = *hdr++ - '0';
+ if (size > 9)
+ return -1;
+ if (size) {
+ for (;;) {
+ unsigned long c = *hdr - '0';
+ if (c > 9)
+ break;
+ hdr++;
+ size = size * 10 + c;
+ }
+ }
+ *sizep = size;
+
+ /*
+ * The length must be followed by a zero byte
+ */
+ return *hdr ? -1 : 0;
+}
+
void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned long *size)
{
- int ret, bytes;
+ int ret;
z_stream stream;
- char buffer[8192];
- unsigned char *buf;
+ char hdr[8192];
- ret = unpack_sha1_header(&stream, map, mapsize, buffer, sizeof(buffer));
- if (ret < Z_OK || sscanf(buffer, "%10s %lu", type, size) != 2)
+ ret = unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr));
+ if (ret < Z_OK || parse_sha1_header(hdr, type, size) < 0)
return NULL;
- bytes = strlen(buffer) + 1;
- buf = xmalloc(1+*size);
-
- memcpy(buf, buffer + bytes, stream.total_out - bytes);
- bytes = stream.total_out - bytes;
- if (bytes < *size && ret == Z_OK) {
- stream.next_out = buf + bytes;
- stream.avail_out = *size - bytes;
- while (inflate(&stream, Z_FINISH) == Z_OK)
- /* nothing */;
- }
- buf[*size] = 0;
- inflateEnd(&stream);
- return buf;
+ return unpack_sha1_rest(&stream, hdr, *size);
}
void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size)