]> git.scripts.mit.edu Git - git.git/blobdiff - sha1_file.c
packed_object_info: make type lookup optional
[git.git] / sha1_file.c
index a016a699f92b0f9ba9b1b8e408ed4b9bc27b4eef..fa2809884bfc9100bf78bec61e9ee5809a1d2abe 100644 (file)
@@ -21,6 +21,7 @@
 #include "sha1-lookup.h"
 #include "bulk-checkin.h"
 #include "streaming.h"
+#include "dir.h"
 
 #ifndef O_NOATIME
 #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@ -123,8 +124,13 @@ int safe_create_leading_directories(char *path)
                        }
                }
                else if (mkdir(path, 0777)) {
-                       *pos = '/';
-                       return -1;
+                       if (errno == EEXIST &&
+                           !stat(path, &st) && S_ISDIR(st.st_mode)) {
+                               ; /* somebody created it since we checked */
+                       } else {
+                               *pos = '/';
+                               return -1;
+                       }
                }
                else if (adjust_shared_perm(path)) {
                        *pos = '/';
@@ -1000,6 +1006,63 @@ void install_packed_git(struct packed_git *pack)
        packed_git = pack;
 }
 
+void (*report_garbage)(const char *desc, const char *path);
+
+static void report_helper(const struct string_list *list,
+                         int seen_bits, int first, int last)
+{
+       const char *msg;
+       switch (seen_bits) {
+       case 0:
+               msg = "no corresponding .idx nor .pack";
+               break;
+       case 1:
+               msg = "no corresponding .idx";
+               break;
+       case 2:
+               msg = "no corresponding .pack";
+               break;
+       default:
+               return;
+       }
+       for (; first < last; first++)
+               report_garbage(msg, list->items[first].string);
+}
+
+static void report_pack_garbage(struct string_list *list)
+{
+       int i, baselen = -1, first = 0, seen_bits = 0;
+
+       if (!report_garbage)
+               return;
+
+       sort_string_list(list);
+
+       for (i = 0; i < list->nr; i++) {
+               const char *path = list->items[i].string;
+               if (baselen != -1 &&
+                   strncmp(path, list->items[first].string, baselen)) {
+                       report_helper(list, seen_bits, first, i);
+                       baselen = -1;
+                       seen_bits = 0;
+               }
+               if (baselen == -1) {
+                       const char *dot = strrchr(path, '.');
+                       if (!dot) {
+                               report_garbage("garbage found", path);
+                               continue;
+                       }
+                       baselen = dot - path + 1;
+                       first = i;
+               }
+               if (!strcmp(path + baselen, "pack"))
+                       seen_bits |= 1;
+               else if (!strcmp(path + baselen, "idx"))
+                       seen_bits |= 2;
+       }
+       report_helper(list, seen_bits, first, list->nr);
+}
+
 static void prepare_packed_git_one(char *objdir, int local)
 {
        /* Ensure that this buffer is large enough so that we can
@@ -1009,6 +1072,7 @@ static void prepare_packed_git_one(char *objdir, int local)
        int len;
        DIR *dir;
        struct dirent *de;
+       struct string_list garbage = STRING_LIST_INIT_DUP;
 
        sprintf(path, "%s/pack", objdir);
        len = strlen(path);
@@ -1024,29 +1088,49 @@ static void prepare_packed_git_one(char *objdir, int local)
                int namelen = strlen(de->d_name);
                struct packed_git *p;
 
-               if (!has_extension(de->d_name, ".idx"))
+               if (len + namelen + 1 > sizeof(path)) {
+                       if (report_garbage) {
+                               struct strbuf sb = STRBUF_INIT;
+                               strbuf_addf(&sb, "%.*s/%s", len - 1, path, de->d_name);
+                               report_garbage("path too long", sb.buf);
+                               strbuf_release(&sb);
+                       }
                        continue;
+               }
 
-               if (len + namelen + 1 > sizeof(path))
+               if (is_dot_or_dotdot(de->d_name))
                        continue;
 
-               /* Don't reopen a pack we already have. */
                strcpy(path + len, de->d_name);
-               for (p = packed_git; p; p = p->next) {
-                       if (!memcmp(path, p->pack_name, len + namelen - 4))
-                               break;
+
+               if (has_extension(de->d_name, ".idx")) {
+                       /* Don't reopen a pack we already have. */
+                       for (p = packed_git; p; p = p->next) {
+                               if (!memcmp(path, p->pack_name, len + namelen - 4))
+                                       break;
+                       }
+                       if (p == NULL &&
+                           /*
+                            * See if it really is a valid .idx file with
+                            * corresponding .pack file that we can map.
+                            */
+                           (p = add_packed_git(path, len + namelen, local)) != NULL)
+                               install_packed_git(p);
                }
-               if (p)
-                       continue;
-               /* See if it really is a valid .idx file with corresponding
-                * .pack file that we can map.
-                */
-               p = add_packed_git(path, len + namelen, local);
-               if (!p)
+
+               if (!report_garbage)
                        continue;
-               install_packed_git(p);
+
+               if (has_extension(de->d_name, ".idx") ||
+                   has_extension(de->d_name, ".pack") ||
+                   has_extension(de->d_name, ".keep"))
+                       string_list_append(&garbage, path);
+               else
+                       report_garbage("garbage found", path);
        }
        closedir(dir);
+       report_pack_garbage(&garbage);
+       string_list_clear(&garbage, 0);
 }
 
 static int sort_pack(const void *a_, const void *b_)
@@ -1187,6 +1271,10 @@ int check_sha1_signature(const unsigned char *sha1, void *map,
                char buf[1024 * 16];
                ssize_t readlen = read_istream(st, buf, sizeof(buf));
 
+               if (readlen < 0) {
+                       close_istream(st);
+                       return -1;
+               }
                if (!readlen)
                        break;
                git_SHA1_Update(&c, buf, readlen);
@@ -1215,6 +1303,26 @@ static int git_open_noatime(const char *name)
        }
 }
 
+static int stat_sha1_file(const unsigned char *sha1, struct stat *st)
+{
+       char *name = sha1_file_name(sha1);
+       struct alternate_object_database *alt;
+
+       if (!lstat(name, st))
+               return 0;
+
+       prepare_alt_odb();
+       errno = ENOENT;
+       for (alt = alt_odb_list; alt; alt = alt->next) {
+               name = alt->name;
+               fill_sha1_path(name, sha1);
+               if (!lstat(alt->base, st))
+                       return 0;
+       }
+
+       return -1;
+}
+
 static int open_sha1_file(const unsigned char *sha1)
 {
        int fd;
@@ -1602,46 +1710,21 @@ static int retry_bad_packed_offset(struct packed_git *p, off_t obj_offset)
        return type;
 }
 
-
 #define POI_STACK_PREALLOC 64
 
-static int packed_object_info(struct packed_git *p, off_t obj_offset,
-                             unsigned long *sizep, int *rtype)
+static enum object_type packed_to_object_type(struct packed_git *p,
+                                             off_t obj_offset,
+                                             enum object_type type,
+                                             struct pack_window **w_curs,
+                                             off_t curpos)
 {
-       struct pack_window *w_curs = NULL;
-       unsigned long size;
-       off_t curpos = obj_offset;
-       enum object_type type;
        off_t small_poi_stack[POI_STACK_PREALLOC];
        off_t *poi_stack = small_poi_stack;
        int poi_stack_nr = 0, poi_stack_alloc = POI_STACK_PREALLOC;
 
-       type = unpack_object_header(p, &w_curs, &curpos, &size);
-
-       if (rtype)
-               *rtype = type; /* representation type */
-
-       if (sizep) {
-               if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
-                       off_t tmp_pos = curpos;
-                       off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos,
-                                                          type, obj_offset);
-                       if (!base_offset) {
-                               type = OBJ_BAD;
-                               goto out;
-                       }
-                       *sizep = get_size_from_delta(p, &w_curs, tmp_pos);
-                       if (*sizep == 0) {
-                               type = OBJ_BAD;
-                               goto out;
-                       }
-               } else {
-                       *sizep = size;
-               }
-       }
-
        while (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
                off_t base_offset;
+               unsigned long size;
                /* Push the object we're going to leave behind */
                if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) {
                        poi_stack_alloc = alloc_nr(poi_stack_nr);
@@ -1652,11 +1735,11 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset,
                }
                poi_stack[poi_stack_nr++] = obj_offset;
                /* If parsing the base offset fails, just unwind */
-               base_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
+               base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
                if (!base_offset)
                        goto unwind;
                curpos = obj_offset = base_offset;
-               type = unpack_object_header(p, &w_curs, &curpos, &size);
+               type = unpack_object_header(p, w_curs, &curpos, &size);
                if (type <= OBJ_NONE) {
                        /* If getting the base itself fails, we first
                         * retry the base, otherwise unwind */
@@ -1683,7 +1766,6 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset,
 out:
        if (poi_stack != small_poi_stack)
                free(poi_stack);
-       unuse_pack(&w_curs);
        return type;
 
 unwind:
@@ -1697,6 +1779,58 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset,
        goto out;
 }
 
+static int packed_object_info(struct packed_git *p, off_t obj_offset,
+                             enum object_type *typep, unsigned long *sizep,
+                             unsigned long *disk_sizep)
+{
+       struct pack_window *w_curs = NULL;
+       unsigned long size;
+       off_t curpos = obj_offset;
+       enum object_type type;
+
+       /*
+        * We always get the representation type, but only convert it to
+        * a "real" type later if the caller is interested.
+        */
+       type = unpack_object_header(p, &w_curs, &curpos, &size);
+
+       if (sizep) {
+               if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
+                       off_t tmp_pos = curpos;
+                       off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos,
+                                                          type, obj_offset);
+                       if (!base_offset) {
+                               type = OBJ_BAD;
+                               goto out;
+                       }
+                       *sizep = get_size_from_delta(p, &w_curs, tmp_pos);
+                       if (*sizep == 0) {
+                               type = OBJ_BAD;
+                               goto out;
+                       }
+               } else {
+                       *sizep = size;
+               }
+       }
+
+       if (disk_sizep) {
+               struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
+               *disk_sizep = revidx[1].offset - obj_offset;
+       }
+
+       if (typep) {
+               *typep = packed_to_object_type(p, obj_offset, type, &w_curs, curpos);
+               if (*typep < 0) {
+                       type = OBJ_BAD;
+                       goto out;
+               }
+       }
+
+out:
+       unuse_pack(&w_curs);
+       return type;
+}
+
 static void *unpack_compressed_entry(struct packed_git *p,
                                    struct pack_window **w_curs,
                                    off_t curpos,
@@ -2250,7 +2384,10 @@ struct packed_git *find_sha1_pack(const unsigned char *sha1,
 
 }
 
-static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *sizep)
+static int sha1_loose_object_info(const unsigned char *sha1,
+                                 enum object_type *typep,
+                                 unsigned long *sizep,
+                                 unsigned long *disk_sizep)
 {
        int status;
        unsigned long mapsize, size;
@@ -2258,9 +2395,25 @@ static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *size
        git_zstream stream;
        char hdr[32];
 
+       /*
+        * If we don't care about type or size, then we don't
+        * need to look inside the object at all.
+        */
+       if (!typep && !sizep) {
+               if (disk_sizep) {
+                       struct stat st;
+                       if (stat_sha1_file(sha1, &st) < 0)
+                               return -1;
+                       *disk_sizep = st.st_size;
+               }
+               return 0;
+       }
+
        map = map_sha1_file(sha1, &mapsize);
        if (!map)
                return error("unable to find %s", sha1_to_hex(sha1));
+       if (disk_sizep)
+               *disk_sizep = mapsize;
        if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
                status = error("unable to unpack %s header",
                               sha1_to_hex(sha1));
@@ -2270,7 +2423,9 @@ static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *size
                *sizep = size;
        git_inflate_end(&stream);
        munmap(map, mapsize);
-       return status;
+       if (typep)
+               *typep = status;
+       return 0;
 }
 
 /* returns enum object_type or negative */
@@ -2278,34 +2433,37 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
 {
        struct cached_object *co;
        struct pack_entry e;
-       int status, rtype;
+       int type, rtype;
 
        co = find_cached_object(sha1);
        if (co) {
                if (oi->sizep)
                        *(oi->sizep) = co->size;
+               if (oi->disk_sizep)
+                       *(oi->disk_sizep) = 0;
                oi->whence = OI_CACHED;
                return co->type;
        }
 
        if (!find_pack_entry(sha1, &e)) {
                /* Most likely it's a loose object. */
-               status = sha1_loose_object_info(sha1, oi->sizep);
-               if (status >= 0) {
+               if (!sha1_loose_object_info(sha1, &type,
+                                           oi->sizep, oi->disk_sizep)) {
                        oi->whence = OI_LOOSE;
-                       return status;
+                       return type;
                }
 
                /* Not a loose object; someone else may have just packed it. */
                reprepare_packed_git();
                if (!find_pack_entry(sha1, &e))
-                       return status;
+                       return -1;
        }
 
-       status = packed_object_info(e.p, e.offset, oi->sizep, &rtype);
-       if (status < 0) {
+       rtype = packed_object_info(e.p, e.offset, &type, oi->sizep,
+                                  oi->disk_sizep);
+       if (rtype < 0) {
                mark_bad_packed_object(e.p, sha1);
-               status = sha1_object_info_extended(sha1, oi);
+               return sha1_object_info_extended(sha1, oi);
        } else if (in_delta_base_cache(e.p, e.offset)) {
                oi->whence = OI_DBCACHED;
        } else {
@@ -2316,12 +2474,12 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
                                         rtype == OBJ_OFS_DELTA);
        }
 
-       return status;
+       return type;
 }
 
 int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
 {
-       struct object_info oi;
+       struct object_info oi = {0};
 
        oi.sizep = sizep;
        return sha1_object_info_extended(sha1, &oi);