Add strbuf_read_file().
[gitweb.git] / sha1_file.c
index a3637d7e5b7763b97b5325923e586722f5e431bf..385c5d891af9b7c79dd93e0e6777b0975ff5dbef 100644 (file)
@@ -33,7 +33,7 @@ const unsigned char null_sha1[20];
 
 static unsigned int sha1_file_open_flag = O_NOATIME;
 
-signed char hexval_table[256] = {
+const signed char hexval_table[256] = {
         -1, -1, -1, -1, -1, -1, -1, -1,                /* 00-07 */
         -1, -1, -1, -1, -1, -1, -1, -1,                /* 08-0f */
         -1, -1, -1, -1, -1, -1, -1, -1,                /* 10-17 */
@@ -193,7 +193,7 @@ char *sha1_pack_name(const unsigned char *sha1)
                *buf++ = hex[val >> 4];
                *buf++ = hex[val & 0xf];
        }
-       
+
        return base;
 }
 
@@ -218,7 +218,7 @@ char *sha1_pack_index_name(const unsigned char *sha1)
                *buf++ = hex[val >> 4];
                *buf++ = hex[val & 0xf];
        }
-       
+
        return base;
 }
 
@@ -352,10 +352,14 @@ static void read_info_alternates(const char * relative_base, int depth)
        char *map;
        size_t mapsz;
        struct stat st;
-       char path[PATH_MAX];
+       const char alt_file_name[] = "info/alternates";
+       /* Given that relative_base is no longer than PATH_MAX,
+          ensure that "path" has enough space to append "/", the
+          file name, "info/alternates", and a trailing NUL.  */
+       char path[PATH_MAX + 1 + sizeof alt_file_name];
        int fd;
 
-       sprintf(path, "%s/info/alternates", relative_base);
+       sprintf(path, "%s/%s", relative_base, alt_file_name);
        fd = open(path, O_RDONLY);
        if (fd < 0)
                return;
@@ -413,7 +417,7 @@ static size_t peak_pack_mapped;
 static size_t pack_mapped;
 struct packed_git *packed_git;
 
-void pack_report()
+void pack_report(void)
 {
        fprintf(stderr,
                "pack_report: getpagesize()            = %10" SZ_FMT "\n"
@@ -493,7 +497,7 @@ static int check_packed_git_idx(const char *path,  struct packed_git *p)
                 */
                if (idx_size != 4*256 + nr * 24 + 20 + 20) {
                        munmap(idx_map, idx_size);
-                       return error("wrong index file size in %s", path);
+                       return error("wrong index v1 file size in %s", path);
                }
        } else if (version == 2) {
                /*
@@ -510,9 +514,12 @@ static int check_packed_git_idx(const char *path,  struct packed_git *p)
                 * for offsets larger than 2^31.
                 */
                unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20;
-               if (idx_size < min_size || idx_size > min_size + (nr - 1)*8) {
+               unsigned long max_size = min_size;
+               if (nr)
+                       max_size += (nr - 1)*8;
+               if (idx_size < min_size || idx_size > max_size) {
                        munmap(idx_map, idx_size);
-                       return error("wrong index file size in %s", path);
+                       return error("wrong index v2 file size in %s", path);
                }
                if (idx_size != min_size) {
                        /* make sure we can deal with large pack offsets */
@@ -531,7 +538,7 @@ static int check_packed_git_idx(const char *path,  struct packed_git *p)
        return 0;
 }
 
-int open_pack_index (struct packed_git *p)
+int open_pack_index(struct packed_git *p)
 {
        char *idx_name;
        int ret;
@@ -833,7 +840,10 @@ void install_packed_git(struct packed_git *pack)
 
 static void prepare_packed_git_one(char *objdir, int local)
 {
-       char path[PATH_MAX];
+       /* Ensure that this buffer is large enough so that we can
+          append "/pack/" without clobbering the stack even if
+          strlen(objdir) were PATH_MAX.  */
+       char path[PATH_MAX + 1 + 4 + 1 + 1];
        int len;
        DIR *dir;
        struct dirent *de;
@@ -855,6 +865,9 @@ static void prepare_packed_git_one(char *objdir, int local)
                if (!has_extension(de->d_name, ".idx"))
                        continue;
 
+               if (len + namelen + 1 > sizeof(path))
+                       continue;
+
                /* Don't reopen a pack we already have. */
                strcpy(path + len, de->d_name);
                for (p = packed_git; p; p = p->next) {
@@ -959,7 +972,7 @@ int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long siz
        return hashcmp(sha1, real_sha1) ? -1 : 0;
 }
 
-void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
+static void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
 {
        struct stat st;
        void *map;
@@ -1139,7 +1152,7 @@ static int parse_sha1_header(const char *hdr, unsigned long *sizep)
        unsigned long size;
 
        /*
-        * The type can be at most ten bytes (including the 
+        * The type can be at most ten bytes (including the
         * terminating '\0' that we add), and is followed by
         * a space.
         */
@@ -1478,11 +1491,8 @@ static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
                ent->lru.next->prev = ent->lru.prev;
                ent->lru.prev->next = ent->lru.next;
                delta_base_cached -= ent->size;
-       }
-       else {
-               ret = xmalloc(ent->size + 1);
-               memcpy(ret, ent->data, ent->size);
-               ((char *)ret)[ent->size] = 0;
+       } else {
+               ret = xmemdupz(ent->data, ent->size);
        }
        *type = ent->type;
        *base_size = ent->size;
@@ -1557,6 +1567,10 @@ static void *unpack_delta_entry(struct packed_git *p,
                    (uintmax_t)base_offset, p->pack_name);
 
        delta_data = unpack_compressed_entry(p, w_curs, curpos, delta_size);
+       if (!delta_data)
+               die("failed to unpack compressed delta"
+                   " at %"PRIuMAX" from %s",
+                   (uintmax_t)curpos, p->pack_name);
        result = patch_delta(base, base_size,
                             delta_data, delta_size,
                             sizep);
@@ -1687,20 +1701,25 @@ static int matches_pack_name(struct packed_git *p, const char *ig)
 
 static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed)
 {
+       static struct packed_git *last_found = (void *)1;
        struct packed_git *p;
        off_t offset;
 
        prepare_packed_git();
+       if (!packed_git)
+               return 0;
+       p = (last_found == (void *)1) ? packed_git : last_found;
 
-       for (p = packed_git; p; p = p->next) {
+       do {
                if (ignore_packed) {
                        const char **ig;
                        for (ig = ignore_packed; *ig; ig++)
                                if (!matches_pack_name(p, *ig))
                                        break;
                        if (*ig)
-                               continue;
+                               goto next;
                }
+
                offset = find_pack_entry_one(sha1, p);
                if (offset) {
                        /*
@@ -1713,18 +1732,27 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, cons
                         */
                        if (p->pack_fd == -1 && open_packed_git(p)) {
                                error("packfile %s cannot be accessed", p->pack_name);
-                               continue;
+                               goto next;
                        }
                        e->offset = offset;
                        e->p = p;
                        hashcpy(e->sha1, sha1);
+                       last_found = p;
                        return 1;
                }
-       }
+
+               next:
+               if (p == last_found)
+                       p = packed_git;
+               else
+                       p = p->next;
+               if (p == last_found)
+                       p = p->next;
+       } while (p);
        return 0;
 }
 
-struct packed_git *find_sha1_pack(const unsigned char *sha1, 
+struct packed_git *find_sha1_pack(const unsigned char *sha1,
                                  struct packed_git *packs)
 {
        struct packed_git *p;
@@ -1841,12 +1869,9 @@ void *read_sha1_file(const unsigned char *sha1, enum object_type *type,
 
        co = find_cached_object(sha1);
        if (co) {
-               buf = xmalloc(co->size + 1);
-               memcpy(buf, co->buf, co->size);
-               ((char*)buf)[co->size] = 0;
                *type = co->type;
                *size = co->size;
-               return buf;
+               return xmemdupz(co->buf, co->size);
        }
 
        buf = read_packed_sha1(sha1, type, size);
@@ -2271,59 +2296,25 @@ int has_sha1_file(const unsigned char *sha1)
        return find_sha1_file(sha1, &st) ? 1 : 0;
 }
 
-/*
- * reads from fd as long as possible into a supplied buffer of size bytes.
- * If necessary the buffer's size is increased using realloc()
- *
- * returns 0 if anything went fine and -1 otherwise
- *
- * NOTE: both buf and size may change, but even when -1 is returned
- * you still have to free() it yourself.
- */
-int read_pipe(int fd, char** return_buf, unsigned long* return_size)
-{
-       char* buf = *return_buf;
-       unsigned long size = *return_size;
-       ssize_t iret;
-       unsigned long off = 0;
-
-       do {
-               iret = xread(fd, buf + off, size - off);
-               if (iret > 0) {
-                       off += iret;
-                       if (off == size) {
-                               size *= 2;
-                               buf = xrealloc(buf, size);
-                       }
-               }
-       } while (iret > 0);
-
-       *return_buf = buf;
-       *return_size = off;
-
-       if (iret < 0)
-               return -1;
-       return 0;
-}
-
 int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
 {
-       unsigned long size = 4096;
-       char *buf = xmalloc(size);
+       struct strbuf buf;
        int ret;
 
-       if (read_pipe(fd, &buf, &size)) {
-               free(buf);
+       strbuf_init(&buf, 0);
+       if (strbuf_read(&buf, fd, 4096) < 0) {
+               strbuf_release(&buf);
                return -1;
        }
 
        if (!type)
                type = blob_type;
        if (write_object)
-               ret = write_sha1_file(buf, size, type, sha1);
+               ret = write_sha1_file(buf.buf, buf.len, type, sha1);
        else
-               ret = hash_sha1_file(buf, size, type, sha1);
-       free(buf);
+               ret = hash_sha1_file(buf.buf, buf.len, type, sha1);
+       strbuf_release(&buf);
+
        return ret;
 }
 
@@ -2345,12 +2336,12 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
         * Convert blobs to git internal format
         */
        if ((type == OBJ_BLOB) && S_ISREG(st->st_mode)) {
-               unsigned long nsize = size;
-               char *nbuf = convert_to_git(path, buf, &nsize);
-               if (nbuf) {
+               struct strbuf nbuf;
+               strbuf_init(&nbuf, 0);
+               if (convert_to_git(path, buf, size, &nbuf)) {
                        munmap(buf, size);
-                       size = nsize;
-                       buf = nbuf;
+                       size = nbuf.len;
+                       buf = nbuf.buf;
                        re_allocated = 1;
                }
        }