diff options
-rw-r--r-- | cache.h | 1 | ||||
-rw-r--r-- | sha1_file.c | 92 |
2 files changed, 74 insertions, 19 deletions
@@ -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) |