Code

branch -r -v: do not spit out garbage
[git.git] / sha1_file.c
index adcf37c3f68d16200b01adae441fa3d8f68e3c02..2187e6caedc7b1824291c3330ed92851fed69704 100644 (file)
@@ -35,8 +35,6 @@ static size_t sz_fmt(size_t s) { return s; }
 
 const unsigned char null_sha1[20];
 
-static unsigned int sha1_file_open_flag = O_NOATIME;
-
 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 */
@@ -85,14 +83,18 @@ int get_sha1_hex(const char *hex, unsigned char *sha1)
        return 0;
 }
 
+static inline int offset_1st_component(const char *path)
+{
+       if (has_dos_drive_prefix(path))
+               return 2 + (path[2] == '/');
+       return *path == '/';
+}
+
 int safe_create_leading_directories(char *path)
 {
-       char *pos = path;
+       char *pos = path + offset_1st_component(path);
        struct stat st;
 
-       if (is_absolute_path(path))
-               pos++;
-
        while (pos) {
                pos = strchr(pos, '/');
                if (!pos)
@@ -118,7 +120,16 @@ int safe_create_leading_directories(char *path)
        return 0;
 }
 
-char * sha1_to_hex(const unsigned char *sha1)
+int safe_create_leading_directories_const(const char *path)
+{
+       /* path points to cache entries, so xstrdup before messing with it */
+       char *buf = xstrdup(path);
+       int result = safe_create_leading_directories(buf);
+       free(buf);
+       return result;
+}
+
+char *sha1_to_hex(const unsigned char *sha1)
 {
        static int bufno;
        static char hexbuffer[4][50];
@@ -394,26 +405,26 @@ void prepare_alt_odb(void)
        if (!alt) alt = "";
 
        alt_odb_tail = &alt_odb_list;
-       link_alt_odb_entries(alt, alt + strlen(alt), ':', NULL, 0);
+       link_alt_odb_entries(alt, alt + strlen(alt), PATH_SEP, NULL, 0);
 
        read_info_alternates(get_object_directory(), 0);
 }
 
-static char *find_sha1_file(const unsigned char *sha1, struct stat *st)
+static int has_loose_object(const unsigned char *sha1)
 {
        char *name = sha1_file_name(sha1);
        struct alternate_object_database *alt;
 
-       if (!stat(name, st))
-               return name;
+       if (!access(name, F_OK))
+               return 1;
        prepare_alt_odb();
        for (alt = alt_odb_list; alt; alt = alt->next) {
                name = alt->name;
                fill_sha1_path(name, sha1);
-               if (!stat(alt->base, st))
-                       return alt->base;
+               if (!access(alt->base, F_OK))
+                       return 1;
        }
-       return NULL;
+       return 0;
 }
 
 static unsigned int pack_used_ctr;
@@ -794,18 +805,28 @@ unsigned char* use_pack(struct packed_git *p,
        return win->base + offset;
 }
 
+static struct packed_git *alloc_packed_git(int extra)
+{
+       struct packed_git *p = xmalloc(sizeof(*p) + extra);
+       memset(p, 0, sizeof(*p));
+       p->pack_fd = -1;
+       return p;
+}
+
 struct packed_git *add_packed_git(const char *path, int path_len, int local)
 {
        struct stat st;
-       struct packed_git *p = xmalloc(sizeof(*p) + path_len + 2);
+       struct packed_git *p = alloc_packed_git(path_len + 2);
 
        /*
         * Make sure a corresponding .pack file exists and that
         * the index looks sane.
         */
        path_len -= strlen(".idx");
-       if (path_len < 1)
+       if (path_len < 1) {
+               free(p);
                return NULL;
+       }
        memcpy(p->pack_name, path, path_len);
        strcpy(p->pack_name + path_len, ".pack");
        if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode)) {
@@ -816,14 +837,7 @@ struct packed_git *add_packed_git(const char *path, int path_len, int local)
        /* ok, it looks sane as far as we can check without
         * actually mapping the pack file.
         */
-       p->index_version = 0;
-       p->index_data = NULL;
-       p->index_size = 0;
-       p->num_objects = 0;
        p->pack_size = st.st_size;
-       p->next = NULL;
-       p->windows = NULL;
-       p->pack_fd = -1;
        p->pack_local = local;
        p->mtime = st.st_mtime;
        if (path_len < 40 || get_sha1_hex(path + path_len - 40, p->sha1))
@@ -833,27 +847,17 @@ struct packed_git *add_packed_git(const char *path, int path_len, int local)
 
 struct packed_git *parse_pack_index(unsigned char *sha1)
 {
-       char *path = sha1_pack_index_name(sha1);
-       return parse_pack_index_file(sha1, path);
-}
-
-struct packed_git *parse_pack_index_file(const unsigned char *sha1,
-                                        const char *idx_path)
-{
+       const char *idx_path = sha1_pack_index_name(sha1);
        const char *path = sha1_pack_name(sha1);
-       struct packed_git *p = xmalloc(sizeof(*p) + strlen(path) + 2);
+       struct packed_git *p = alloc_packed_git(strlen(path) + 1);
 
+       strcpy(p->pack_name, path);
+       hashcpy(p->sha1, sha1);
        if (check_packed_git_idx(idx_path, p)) {
                free(p);
                return NULL;
        }
 
-       strcpy(p->pack_name, path);
-       p->pack_size = 0;
-       p->next = NULL;
-       p->windows = NULL;
-       p->pack_fd = -1;
-       hashcpy(p->sha1, sha1);
        return p;
 }
 
@@ -990,6 +994,18 @@ void reprepare_packed_git(void)
        prepare_packed_git();
 }
 
+static void mark_bad_packed_object(struct packed_git *p,
+                                  const unsigned char *sha1)
+{
+       unsigned i;
+       for (i = 0; i < p->num_bad_objects; i++)
+               if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
+                       return;
+       p->bad_object_sha1 = xrealloc(p->bad_object_sha1, 20 * (p->num_bad_objects + 1));
+       hashcpy(p->bad_object_sha1 + 20 * p->num_bad_objects, sha1);
+       p->num_bad_objects++;
+}
+
 int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type)
 {
        unsigned char real_sha1[20];
@@ -997,38 +1013,58 @@ int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long siz
        return hashcmp(sha1, real_sha1) ? -1 : 0;
 }
 
+static int git_open_noatime(const char *name)
+{
+       static int sha1_file_open_flag = O_NOATIME;
+       int fd = open(name, O_RDONLY | sha1_file_open_flag);
+
+       /* Might the failure be due to O_NOATIME? */
+       if (fd < 0 && errno != ENOENT && sha1_file_open_flag) {
+               fd = open(name, O_RDONLY);
+               if (fd >= 0)
+                       sha1_file_open_flag = 0;
+       }
+       return fd;
+}
+
+static int open_sha1_file(const unsigned char *sha1)
+{
+       int fd;
+       char *name = sha1_file_name(sha1);
+       struct alternate_object_database *alt;
+
+       fd = git_open_noatime(name);
+       if (fd >= 0)
+               return fd;
+
+       prepare_alt_odb();
+       errno = ENOENT;
+       for (alt = alt_odb_list; alt; alt = alt->next) {
+               name = alt->name;
+               fill_sha1_path(name, sha1);
+               fd = git_open_noatime(alt->base);
+               if (fd >= 0)
+                       return fd;
+       }
+       return -1;
+}
+
 static void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
 {
-       struct stat st;
        void *map;
        int fd;
-       char *filename = find_sha1_file(sha1, &st);
 
-       if (!filename) {
-               return NULL;
-       }
+       fd = open_sha1_file(sha1);
+       map = NULL;
+       if (fd >= 0) {
+               struct stat st;
 
-       fd = open(filename, O_RDONLY | sha1_file_open_flag);
-       if (fd < 0) {
-               /* See if it works without O_NOATIME */
-               switch (sha1_file_open_flag) {
-               default:
-                       fd = open(filename, O_RDONLY);
-                       if (fd >= 0)
-                               break;
-               /* Fallthrough */
-               case 0:
-                       return NULL;
+               if (!fstat(fd, &st)) {
+                       *size = xsize_t(st.st_size);
+                       map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0);
                }
-
-               /* If it failed once, it will probably fail again.
-                * Stop using O_NOATIME
-                */
-               sha1_file_open_flag = 0;
+               close(fd);
        }
-       *size = xsize_t(st.st_size);
-       map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0);
-       close(fd);
        return map;
 }
 
@@ -1288,20 +1324,17 @@ static off_t get_delta_base(struct packed_git *p,
                while (c & 128) {
                        base_offset += 1;
                        if (!base_offset || MSB(base_offset, 7))
-                               die("offset value overflow for delta base object");
+                               return 0;  /* overflow */
                        c = base_info[used++];
                        base_offset = (base_offset << 7) + (c & 127);
                }
                base_offset = delta_obj_offset - base_offset;
                if (base_offset >= delta_obj_offset)
-                       die("delta base offset out of bound");
+                       return 0;  /* out of bound */
                *curpos += used;
        } else if (type == OBJ_REF_DELTA) {
                /* The base entry _must_ be in the same pack */
                base_offset = find_pack_entry_one(base_info, p);
-               if (!base_offset)
-                       die("failed to find delta-pack base object %s",
-                               sha1_to_hex(base_info));
                *curpos += 20;
        } else
                die("I am totally screwed");
@@ -1394,6 +1427,9 @@ const char *packed_object_info_detail(struct packed_git *p,
                        return typename(type);
                case OBJ_OFS_DELTA:
                        obj_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
+                       if (!obj_offset)
+                               die("pack %s contains bad delta base reference of type %s",
+                                   p->pack_name, typename(type));
                        if (*delta_chain_length == 0) {
                                revidx = find_pack_revindex(p, obj_offset);
                                hashcpy(base_sha1, nth_packed_object_sha1(p, revidx->nr));
@@ -1588,17 +1624,41 @@ static void *unpack_delta_entry(struct packed_git *p,
        off_t base_offset;
 
        base_offset = get_delta_base(p, w_curs, &curpos, *type, obj_offset);
+       if (!base_offset) {
+               error("failed to validate delta base reference "
+                     "at offset %"PRIuMAX" from %s",
+                     (uintmax_t)curpos, p->pack_name);
+               return NULL;
+       }
        base = cache_or_unpack_entry(p, base_offset, &base_size, type, 0);
-       if (!base)
-               die("failed to read delta base object"
-                   " at %"PRIuMAX" from %s",
-                   (uintmax_t)base_offset, p->pack_name);
+       if (!base) {
+               /*
+                * We're probably in deep shit, but let's try to fetch
+                * the required base anyway from another pack or loose.
+                * This is costly but should happen only in the presence
+                * of a corrupted pack, and is better than failing outright.
+                */
+               struct revindex_entry *revidx = find_pack_revindex(p, base_offset);
+               const unsigned char *base_sha1 =
+                                       nth_packed_object_sha1(p, revidx->nr);
+               error("failed to read delta base object %s"
+                     " at offset %"PRIuMAX" from %s",
+                     sha1_to_hex(base_sha1), (uintmax_t)base_offset,
+                     p->pack_name);
+               mark_bad_packed_object(p, base_sha1);
+               base = read_sha1_file(base_sha1, type, &base_size);
+               if (!base)
+                       return NULL;
+       }
 
        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);
+       if (!delta_data) {
+               error("failed to unpack compressed delta "
+                     "at offset %"PRIuMAX" from %s",
+                     (uintmax_t)curpos, p->pack_name);
+               free(base);
+               return NULL;
+       }
        result = patch_delta(base, base_size,
                             delta_data, delta_size,
                             sizep);
@@ -1630,7 +1690,9 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
                data = unpack_compressed_entry(p, &w_curs, curpos, *sizep);
                break;
        default:
-               die("unknown object type %i in %s", *type, p->pack_name);
+               data = NULL;
+               error("unknown object type %i at offset %"PRIuMAX" in %s",
+                     *type, (uintmax_t)obj_offset, p->pack_name);
        }
        unuse_pack(&w_curs);
        return data;
@@ -1656,7 +1718,7 @@ const unsigned char *nth_packed_object_sha1(struct packed_git *p,
        }
 }
 
-static off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n)
+off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n)
 {
        const unsigned char *index = p->index_data;
        index += 4 * 256;
@@ -1776,6 +1838,13 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, cons
                                goto next;
                }
 
+               if (p->num_bad_objects) {
+                       unsigned i;
+                       for (i = 0; i < p->num_bad_objects; i++)
+                               if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
+                                       goto next;
+               }
+
                offset = find_pack_entry_one(sha1, p);
                if (offset) {
                        /*
@@ -1860,11 +1929,24 @@ static void *read_packed_sha1(const unsigned char *sha1,
                              enum object_type *type, unsigned long *size)
 {
        struct pack_entry e;
+       void *data;
 
        if (!find_pack_entry(sha1, &e, NULL))
                return NULL;
-       else
-               return cache_or_unpack_entry(e.p, e.offset, size, type, 1);
+       data = cache_or_unpack_entry(e.p, e.offset, size, type, 1);
+       if (!data) {
+               /*
+                * We're probably in deep shit, but let's try to fetch
+                * the required object anyway from another pack or loose.
+                * This should happen only in the presence of a corrupted
+                * pack, and is better than failing outright.
+                */
+               error("failed to read object %s at offset %"PRIuMAX" from %s",
+                     sha1_to_hex(sha1), (uintmax_t)e.offset, e.p->pack_name);
+               mark_bad_packed_object(e.p, sha1);
+               data = read_sha1_file(sha1, type, size);
+       }
+       return data;
 }
 
 /*
@@ -2018,49 +2100,12 @@ static void write_sha1_file_prepare(const void *buf, unsigned long len,
        SHA1_Final(sha1, &c);
 }
 
-/*
- * Link the tempfile to the final place, possibly creating the
- * last directory level as you do so.
- *
- * Returns the errno on failure, 0 on success.
- */
-static int link_temp_to_file(const char *tmpfile, const char *filename)
-{
-       int ret;
-       char *dir;
-
-       if (!link(tmpfile, filename))
-               return 0;
-
-       /*
-        * Try to mkdir the last path component if that failed.
-        *
-        * Re-try the "link()" regardless of whether the mkdir
-        * succeeds, since a race might mean that somebody
-        * else succeeded.
-        */
-       ret = errno;
-       dir = strrchr(filename, '/');
-       if (dir) {
-               *dir = 0;
-               if (!mkdir(filename, 0777) && adjust_shared_perm(filename)) {
-                       *dir = '/';
-                       return -2;
-               }
-               *dir = '/';
-               if (!link(tmpfile, filename))
-                       return 0;
-               ret = errno;
-       }
-       return ret;
-}
-
 /*
  * Move the just written object into its final resting place
  */
 int move_temp_to_file(const char *tmpfile, const char *filename)
 {
-       int ret = link_temp_to_file(tmpfile, filename);
+       int ret = link(tmpfile, filename);
 
        /*
         * Coda hack - coda doesn't like cross-directory links,
@@ -2105,6 +2150,57 @@ int hash_sha1_file(const void *buf, unsigned long len, const char *type,
        return 0;
 }
 
+/* Finalize a file on disk, and close it. */
+static void close_sha1_file(int fd)
+{
+       if (fsync_object_files)
+               fsync_or_die(fd, "sha1 file");
+       fchmod(fd, 0444);
+       if (close(fd) != 0)
+               die("unable to write sha1 file");
+}
+
+/* Size of directory component, including the ending '/' */
+static inline int directory_size(const char *filename)
+{
+       const char *s = strrchr(filename, '/');
+       if (!s)
+               return 0;
+       return s - filename + 1;
+}
+
+/*
+ * This creates a temporary file in the same directory as the final
+ * 'filename'
+ *
+ * We want to avoid cross-directory filename renames, because those
+ * can have problems on various filesystems (FAT, NFS, Coda).
+ */
+static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename)
+{
+       int fd, dirlen = directory_size(filename);
+
+       if (dirlen + 20 > bufsiz) {
+               errno = ENAMETOOLONG;
+               return -1;
+       }
+       memcpy(buffer, filename, dirlen);
+       strcpy(buffer + dirlen, "tmp_obj_XXXXXX");
+       fd = mkstemp(buffer);
+       if (fd < 0 && dirlen) {
+               /* Make sure the directory exists */
+               memcpy(buffer, filename, dirlen);
+               buffer[dirlen-1] = 0;
+               if (mkdir(buffer, 0777) || adjust_shared_perm(buffer))
+                       return -1;
+
+               /* Try again */
+               strcpy(buffer + dirlen - 1, "/tmp_obj_XXXXXX");
+               fd = mkstemp(buffer);
+       }
+       return fd;
+}
+
 static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
                              void *buf, unsigned long len, time_t mtime)
 {
@@ -2115,23 +2211,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
        static char tmpfile[PATH_MAX];
 
        filename = sha1_file_name(sha1);
-       fd = open(filename, O_RDONLY);
-       if (fd >= 0) {
-               /*
-                * FIXME!!! We might do collision checking here, but we'd
-                * need to uncompress the old file and check it. Later.
-                */
-               close(fd);
-               return 0;
-       }
-
-       if (errno != ENOENT) {
-               return error("sha1 file %s: %s\n", filename, strerror(errno));
-       }
-
-       snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory());
-
-       fd = mkstemp(tmpfile);
+       fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
        if (fd < 0) {
                if (errno == EPERM)
                        return error("insufficient permission for adding an object to repository database %s\n", get_object_directory());
@@ -2170,9 +2250,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
 
        if (write_buffer(fd, compressed, size) < 0)
                die("unable to write sha1 file");
-       fchmod(fd, 0444);
-       if (close(fd))
-               die("unable to write sha1 file");
+       close_sha1_file(fd);
        free(compressed);
 
        if (mtime) {
@@ -2206,14 +2284,13 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
 
 int force_object_loose(const unsigned char *sha1, time_t mtime)
 {
-       struct stat st;
        void *buf;
        unsigned long len;
        enum object_type type;
        char hdr[32];
        int hdrlen;
 
-       if (find_sha1_file(sha1, &st))
+       if (has_loose_object(sha1))
                return 0;
        buf = read_packed_sha1(sha1, &type, &len);
        if (!buf)
@@ -2222,150 +2299,6 @@ int force_object_loose(const unsigned char *sha1, time_t mtime)
        return write_loose_object(sha1, hdr, hdrlen, buf, len, mtime);
 }
 
-/*
- * We need to unpack and recompress the object for writing
- * it out to a different file.
- */
-static void *repack_object(const unsigned char *sha1, unsigned long *objsize)
-{
-       size_t size;
-       z_stream stream;
-       unsigned char *unpacked;
-       unsigned long len;
-       enum object_type type;
-       char hdr[32];
-       int hdrlen;
-       void *buf;
-
-       /* need to unpack and recompress it by itself */
-       unpacked = read_packed_sha1(sha1, &type, &len);
-       if (!unpacked)
-               error("cannot read sha1_file for %s", sha1_to_hex(sha1));
-
-       hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1;
-
-       /* Set it up */
-       memset(&stream, 0, sizeof(stream));
-       deflateInit(&stream, zlib_compression_level);
-       size = deflateBound(&stream, len + hdrlen);
-       buf = xmalloc(size);
-
-       /* Compress it */
-       stream.next_out = buf;
-       stream.avail_out = size;
-
-       /* First header.. */
-       stream.next_in = (void *)hdr;
-       stream.avail_in = hdrlen;
-       while (deflate(&stream, 0) == Z_OK)
-               /* nothing */;
-
-       /* Then the data itself.. */
-       stream.next_in = unpacked;
-       stream.avail_in = len;
-       while (deflate(&stream, Z_FINISH) == Z_OK)
-               /* nothing */;
-       deflateEnd(&stream);
-       free(unpacked);
-
-       *objsize = stream.total_out;
-       return buf;
-}
-
-int write_sha1_to_fd(int fd, const unsigned char *sha1)
-{
-       int retval;
-       unsigned long objsize;
-       void *buf = map_sha1_file(sha1, &objsize);
-
-       if (buf) {
-               retval = write_buffer(fd, buf, objsize);
-               munmap(buf, objsize);
-               return retval;
-       }
-
-       buf = repack_object(sha1, &objsize);
-       retval = write_buffer(fd, buf, objsize);
-       free(buf);
-       return retval;
-}
-
-int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
-                      size_t bufsize, size_t *bufposn)
-{
-       char tmpfile[PATH_MAX];
-       int local;
-       z_stream stream;
-       unsigned char real_sha1[20];
-       unsigned char discard[4096];
-       int ret;
-       SHA_CTX c;
-
-       snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory());
-
-       local = mkstemp(tmpfile);
-       if (local < 0) {
-               if (errno == EPERM)
-                       return error("insufficient permission for adding an object to repository database %s\n", get_object_directory());
-               else
-                       return error("unable to create temporary sha1 filename %s: %s\n", tmpfile, strerror(errno));
-       }
-
-       memset(&stream, 0, sizeof(stream));
-
-       inflateInit(&stream);
-
-       SHA1_Init(&c);
-
-       do {
-               ssize_t size;
-               if (*bufposn) {
-                       stream.avail_in = *bufposn;
-                       stream.next_in = (unsigned char *) buffer;
-                       do {
-                               stream.next_out = discard;
-                               stream.avail_out = sizeof(discard);
-                               ret = inflate(&stream, Z_SYNC_FLUSH);
-                               SHA1_Update(&c, discard, sizeof(discard) -
-                                           stream.avail_out);
-                       } while (stream.avail_in && ret == Z_OK);
-                       if (write_buffer(local, buffer, *bufposn - stream.avail_in) < 0)
-                               die("unable to write sha1 file");
-                       memmove(buffer, buffer + *bufposn - stream.avail_in,
-                               stream.avail_in);
-                       *bufposn = stream.avail_in;
-                       if (ret != Z_OK)
-                               break;
-               }
-               size = xread(fd, buffer + *bufposn, bufsize - *bufposn);
-               if (size <= 0) {
-                       close(local);
-                       unlink(tmpfile);
-                       if (!size)
-                               return error("Connection closed?");
-                       perror("Reading from connection");
-                       return -1;
-               }
-               *bufposn += size;
-       } while (1);
-       inflateEnd(&stream);
-
-       fchmod(local, 0444);
-       if (close(local) != 0)
-               die("unable to write sha1 file");
-       SHA1_Final(real_sha1, &c);
-       if (ret != Z_STREAM_END) {
-               unlink(tmpfile);
-               return error("File %s corrupted", sha1_to_hex(sha1));
-       }
-       if (hashcmp(sha1, real_sha1)) {
-               unlink(tmpfile);
-               return error("File %s has bad hash", sha1_to_hex(sha1));
-       }
-
-       return move_temp_to_file(tmpfile, sha1_file_name(sha1));
-}
-
 int has_pack_index(const unsigned char *sha1)
 {
        struct stat st;
@@ -2390,12 +2323,11 @@ int has_sha1_pack(const unsigned char *sha1, const char **ignore_packed)
 
 int has_sha1_file(const unsigned char *sha1)
 {
-       struct stat st;
        struct pack_entry e;
 
        if (find_pack_entry(sha1, &e, NULL))
                return 1;
-       return find_sha1_file(sha1, &st) ? 1 : 0;
+       return has_loose_object(sha1);
 }
 
 int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)