Merge branch 'jc/fsck'
authorJunio C Hamano <junkio@cox.net>
Sun, 11 Mar 2007 07:10:26 +0000 (23:10 -0800)
committerJunio C Hamano <junkio@cox.net>
Sun, 11 Mar 2007 07:10:26 +0000 (23:10 -0800)
* jc/fsck:
fsck: exit with non-zero status upon errors
unpack_sha1_file(): detect corrupt loose object files.
fsck: fix broken loose object check.

1  2 
builtin-fsck.c
cache.h
sha1_file.c
diff --combined builtin-fsck.c
index 39cfc32818dc80d8bda04ff45fbf5996d1506922,4d03378c1bea72d3afccb4576a57e6e8a8864bce..b8e71b640b66ebd95a436a5c1c7f3fce0647be0c
@@@ -18,6 -18,9 +18,9 @@@ static int check_full
  static int check_strict;
  static int keep_cache_objects;
  static unsigned char head_sha1[20];
+ static int errors_found;
+ #define ERROR_OBJECT 01
+ #define ERROR_REACHABLE 02
  
  #ifdef NO_D_INO_IN_DIRENT
  #define SORT_DIRENT 0
@@@ -40,6 -43,7 +43,7 @@@ static int objerror(struct object *obj
  {
        va_list params;
        va_start(params, err);
+       errors_found |= ERROR_OBJECT;
        objreport(obj, "error", err, params);
        va_end(params);
        return -1;
@@@ -67,9 -71,10 +71,10 @@@ static void check_reachable_object(stru
         * do a full fsck
         */
        if (!obj->parsed) {
-               if (has_sha1_file(obj->sha1))
+               if (has_sha1_pack(obj->sha1, NULL))
                        return; /* it is in pack - forget about it */
                printf("missing %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1));
+               errors_found |= ERROR_REACHABLE;
                return;
        }
  
@@@ -88,6 -93,7 +93,7 @@@
                               typename(obj->type), sha1_to_hex(obj->sha1));
                        printf("              to %7s %s\n",
                               typename(ref->type), sha1_to_hex(ref->sha1));
+                       errors_found |= ERROR_REACHABLE;
                }
        }
  }
@@@ -346,8 -352,11 +352,11 @@@ static int fsck_tag(struct tag *tag
  static int fsck_sha1(unsigned char *sha1)
  {
        struct object *obj = parse_object(sha1);
-       if (!obj)
-               return error("%s: object corrupt or missing", sha1_to_hex(sha1));
+       if (!obj) {
+               errors_found |= ERROR_OBJECT;
+               return error("%s: object corrupt or missing",
+                            sha1_to_hex(sha1));
+       }
        if (obj->flags & SEEN)
                return 0;
        obj->flags |= SEEN;
                return fsck_commit((struct commit *) obj);
        if (obj->type == OBJ_TAG)
                return fsck_tag((struct tag *) obj);
        /* By now, parse_object() would've returned NULL instead. */
-       return objerror(obj, "unknown type '%d' (internal fsck error)", obj->type);
+       return objerror(obj, "unknown type '%d' (internal fsck error)",
+                       obj->type);
  }
  
  /*
@@@ -576,11 -587,16 +587,16 @@@ static int fsck_cache_tree(struct cache
        return err;
  }
  
+ static const char fsck_usage[] =
+ "git-fsck [--tags] [--root] [[--unreachable] [--cache] [--full] "
+ "[--strict] <head-sha1>*]";
  int cmd_fsck(int argc, char **argv, const char *prefix)
  {
        int i, heads;
  
        track_object_refs = 1;
+       errors_found = 0;
  
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
                        continue;
                }
                if (*arg == '-')
-                       usage("git-fsck [--tags] [--root] [[--unreachable] [--cache] [--full] [--strict] <head-sha1>*]");
+                       usage(fsck_usage);
        }
  
        fsck_head_link();
                        verify_pack(p, 0);
  
                for (p = packed_git; p; p = p->next) {
 -                      int num = num_packed_objects(p);
 +                      uint32_t i, num = num_packed_objects(p);
                        for (i = 0; i < num; i++) {
                                unsigned char sha1[20];
                                nth_packed_object_sha1(p, i, sha1);
        }
  
        check_connectivity();
-       return 0;
+       return errors_found;
  }
diff --combined cache.h
index ae25759c433a29caeb8662ef3ccfe4c0f3422bd5,4b5a7541a86c488f623793fdc498d0e149c40439..f172d02a653475fcf715eb5605012abf50c5621b
+++ b/cache.h
@@@ -281,7 -281,6 +281,6 @@@ char *enter_repo(char *path, int strict
  
  /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
  extern int sha1_object_info(const unsigned char *, unsigned long *);
- extern void * unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size);
  extern void * read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size);
  extern int hash_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *sha1);
  extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
@@@ -383,7 -382,7 +382,7 @@@ extern struct packed_git 
  } *packed_git;
  
  struct pack_entry {
 -      unsigned int offset;
 +      off_t offset;
        unsigned char sha1[20];
        struct packed_git *p;
  };
@@@ -422,15 -421,15 +421,15 @@@ extern struct packed_git *find_sha1_pac
                                         struct packed_git *packs);
  
  extern void pack_report(void);
 -extern unsigned char* use_pack(struct packed_git *, struct pack_window **, unsigned long, unsigned int *);
 +extern unsigned char* use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
  extern void unuse_pack(struct pack_window **);
  extern struct packed_git *add_packed_git(char *, int, int);
 -extern int num_packed_objects(const struct packed_git *p);
 -extern int nth_packed_object_sha1(const struct packed_git *, int, unsigned char*);
 -extern unsigned long find_pack_entry_one(const unsigned char *, struct packed_git *);
 -extern void *unpack_entry(struct packed_git *, unsigned long, enum object_type *, unsigned long *);
 +extern uint32_t num_packed_objects(const struct packed_git *p);
 +extern int nth_packed_object_sha1(const struct packed_git *, uint32_t, unsigned char*);
 +extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
 +extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
  extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
 -extern const char *packed_object_info_detail(struct packed_git *, unsigned long, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
 +extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
  
  /* Dumb servers support */
  extern int update_server_info(int);
@@@ -451,7 -450,7 +450,7 @@@ extern char git_default_email[MAX_GITNA
  extern char git_default_name[MAX_GITNAME];
  
  extern char *git_commit_encoding;
 -extern char *git_log_output_encoding;
 +extern const char *git_log_output_encoding;
  
  extern int copy_fd(int ifd, int ofd);
  extern int read_in_full(int fd, void *buf, size_t count);
diff --combined sha1_file.c
index 219a10f403e0c3aa2f8d60ecde5807cf51d1a659,ac6b5e00b6dec913a39cc54e84829dc3fc6c782f..7faa8bcd507f54a55cb9e2ac685654921475f995
@@@ -349,7 -349,6 +349,7 @@@ static void link_alt_odb_entries(const 
  static void read_info_alternates(const char * relative_base, int depth)
  {
        char *map;
 +      size_t mapsz;
        struct stat st;
        char path[PATH_MAX];
        int fd;
                close(fd);
                return;
        }
 -      map = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
 +      mapsz = xsize_t(st.st_size);
 +      map = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, fd, 0);
        close(fd);
  
 -      link_alt_odb_entries(map, map + st.st_size, '\n', relative_base, depth);
 +      link_alt_odb_entries(map, map + mapsz, '\n', relative_base, depth);
  
 -      munmap(map, st.st_size);
 +      munmap(map, mapsz);
  }
  
  void prepare_alt_odb(void)
@@@ -432,14 -430,13 +432,14 @@@ void pack_report(
                pack_mapped, peak_pack_mapped);
  }
  
 -static int check_packed_git_idx(const char *path, unsigned long *idx_size_,
 -                              void **idx_map_)
 +static int check_packed_git_idx(const char *path,
 +      unsigned long *idx_size_,
 +      void **idx_map_)
  {
        void *idx_map;
        uint32_t *index;
 -      unsigned long idx_size;
 -      int nr, i;
 +      size_t idx_size;
 +      uint32_t nr, i;
        int fd = open(path, O_RDONLY);
        struct stat st;
        if (fd < 0)
                close(fd);
                return -1;
        }
 -      idx_size = st.st_size;
 +      idx_size = xsize_t(st.st_size);
 +      if (idx_size < 4 * 256 + 20 + 20) {
 +              close(fd);
 +              return error("index file %s is too small", path);
 +      }
        idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
        close(fd);
  
        *idx_map_ = idx_map;
        *idx_size_ = idx_size;
  
 -      /* check index map */
 -      if (idx_size < 4*256 + 20 + 20)
 -              return error("index file %s is too small", path);
 -
        /* a future index format would start with this, as older git
         * binaries would fail the non-monotonic index check below.
         * give a nicer warning to the user if we can.
         */
 -      if (index[0] == htonl(PACK_IDX_SIGNATURE))
 +      if (index[0] == htonl(PACK_IDX_SIGNATURE)) {
 +              munmap(idx_map, idx_size);
                return error("index file %s is a newer version"
                        " and is not supported by this binary"
                        " (try upgrading GIT to a newer version)",
                        path);
 +      }
  
        nr = 0;
        for (i = 0; i < 256; i++) {
 -              unsigned int n = ntohl(index[i]);
 -              if (n < nr)
 +              uint32_t n = ntohl(index[i]);
 +              if (n < nr) {
 +                      munmap(idx_map, idx_size);
                        return error("non-monotonic index %s", path);
 +              }
                nr = n;
        }
  
         *  - 20-byte SHA1 of the packfile
         *  - 20-byte SHA1 file checksum
         */
 -      if (idx_size != 4*256 + nr * 24 + 20 + 20)
 +      if (idx_size != 4*256 + nr * 24 + 20 + 20) {
 +              munmap(idx_map, idx_size);
                return error("wrong index file size in %s", path);
 +      }
  
        return 0;
  }
@@@ -631,7 -622,7 +631,7 @@@ static int open_packed_git(struct packe
        return -1;
  }
  
 -static int in_window(struct pack_window *win, unsigned long offset)
 +static int in_window(struct pack_window *win, off_t offset)
  {
        /* We must promise at least 20 bytes (one hash) after the
         * offset is available from this window, otherwise the offset
  
  unsigned char* use_pack(struct packed_git *p,
                struct pack_window **w_cursor,
 -              unsigned long offset,
 +              off_t offset,
                unsigned int *left)
  {
        struct pack_window *win = *w_cursor;
                }
                if (!win) {
                        size_t window_align = packed_git_window_size / 2;
 +                      off_t len;
                        win = xcalloc(1, sizeof(*win));
                        win->offset = (offset / window_align) * window_align;
 -                      win->len = p->pack_size - win->offset;
 -                      if (win->len > packed_git_window_size)
 -                              win->len = packed_git_window_size;
 +                      len = p->pack_size - win->offset;
 +                      if (len > packed_git_window_size)
 +                              len = packed_git_window_size;
 +                      win->len = (size_t)len;
                        pack_mapped += win->len;
                        while (packed_git_limit < pack_mapped
                                && unuse_one_window(p))
        }
        offset -= win->offset;
        if (left)
 -              *left = win->len - offset;
 +              *left = win->len - xsize_t(offset);
        return win->base + offset;
  }
  
@@@ -882,9 -871,9 +882,9 @@@ void *map_sha1_file(const unsigned cha
                 */
                sha1_file_open_flag = 0;
        }
 -      map = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
 +      *size = xsize_t(st.st_size);
 +      map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0);
        close(fd);
 -      *size = st.st_size;
        return map;
  }
  
@@@ -967,11 -956,12 +967,12 @@@ static int unpack_sha1_header(z_stream 
        return 0;
  }
  
- static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size)
+ static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size, const unsigned char *sha1)
  {
        int bytes = strlen(buffer) + 1;
        unsigned char *buf = xmalloc(1+size);
        unsigned long n;
+       int status = Z_OK;
  
        n = stream->total_out - bytes;
        if (n > size)
        if (bytes < size) {
                stream->next_out = buf + bytes;
                stream->avail_out = size - bytes;
-               while (inflate(stream, Z_FINISH) == Z_OK)
-                       /* nothing */;
+               while (status == Z_OK)
+                       status = inflate(stream, Z_FINISH);
        }
        buf[size] = 0;
-       inflateEnd(stream);
-       return buf;
+       if ((status == Z_OK || status == Z_STREAM_END) && !stream->avail_in) {
+               inflateEnd(stream);
+               return buf;
+       }
+       if (status < 0)
+               error("corrupt loose object '%s'", sha1_to_hex(sha1));
+       else if (stream->avail_in)
+               error("garbage at end of loose object '%s'",
+                     sha1_to_hex(sha1));
+       free(buf);
+       return NULL;
  }
  
  /*
@@@ -1040,7 -1040,7 +1051,7 @@@ static int parse_sha1_header(const cha
        return *hdr ? -1 : type_from_string(type);
  }
  
void * unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size)
static void *unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size, const unsigned char *sha1)
  {
        int ret;
        z_stream stream;
        if (ret < Z_OK || (*type = parse_sha1_header(hdr, size)) < 0)
                return NULL;
  
-       return unpack_sha1_rest(&stream, hdr, *size);
+       return unpack_sha1_rest(&stream, hdr, *size, sha1);
  }
  
 -static unsigned long get_delta_base(struct packed_git *p,
 +static off_t get_delta_base(struct packed_git *p,
                                    struct pack_window **w_curs,
 -                                  unsigned long *curpos,
 +                                  off_t *curpos,
                                    enum object_type type,
 -                                  unsigned long delta_obj_offset)
 +                                  off_t delta_obj_offset)
  {
        unsigned char *base_info = use_pack(p, w_curs, *curpos, NULL);
 -      unsigned long base_offset;
 +      off_t base_offset;
  
        /* use_pack() assured us we have [base_info, base_info + 20)
         * as a range that we can look at without walking off the
  }
  
  /* forward declaration for a mutually recursive function */
 -static int packed_object_info(struct packed_git *p, unsigned long offset,
 +static int packed_object_info(struct packed_git *p, off_t offset,
                              unsigned long *sizep);
  
  static int packed_delta_info(struct packed_git *p,
                             struct pack_window **w_curs,
 -                           unsigned long curpos,
 +                           off_t curpos,
                             enum object_type type,
 -                           unsigned long obj_offset,
 +                           off_t obj_offset,
                             unsigned long *sizep)
  {
 -      unsigned long base_offset;
 +      off_t base_offset;
  
        base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
        type = packed_object_info(p, base_offset, NULL);
  
  static int unpack_object_header(struct packed_git *p,
                                struct pack_window **w_curs,
 -                              unsigned long *curpos,
 +                              off_t *curpos,
                                unsigned long *sizep)
  {
        unsigned char *base;
  }
  
  const char *packed_object_info_detail(struct packed_git *p,
 -                                    unsigned long obj_offset,
 +                                    off_t obj_offset,
                                      unsigned long *size,
                                      unsigned long *store_size,
                                      unsigned int *delta_chain_length,
                                      unsigned char *base_sha1)
  {
        struct pack_window *w_curs = NULL;
 -      unsigned long curpos, dummy;
 +      off_t curpos;
 +      unsigned long dummy;
        unsigned char *next_sha1;
        enum object_type type;
  
                        obj_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
                        if (*delta_chain_length == 0) {
                                /* TODO: find base_sha1 as pointed by curpos */
 +                              hashclr(base_sha1);
                        }
                        break;
                case OBJ_REF_DELTA:
        }
  }
  
 -static int packed_object_info(struct packed_git *p, unsigned long obj_offset,
 +static int packed_object_info(struct packed_git *p, off_t obj_offset,
                              unsigned long *sizep)
  {
        struct pack_window *w_curs = NULL;
 -      unsigned long size, curpos = obj_offset;
 +      unsigned long size;
 +      off_t curpos = obj_offset;
        enum object_type type;
  
        type = unpack_object_header(p, &w_curs, &curpos, &size);
  
  static void *unpack_compressed_entry(struct packed_git *p,
                                    struct pack_window **w_curs,
 -                                  unsigned long curpos,
 +                                  off_t curpos,
                                    unsigned long size)
  {
        int st;
  
  static void *unpack_delta_entry(struct packed_git *p,
                                struct pack_window **w_curs,
 -                              unsigned long curpos,
 +                              off_t curpos,
                                unsigned long delta_size,
 -                              unsigned long obj_offset,
 +                              off_t obj_offset,
                                enum object_type *type,
                                unsigned long *sizep)
  {
        void *delta_data, *result, *base;
 -      unsigned long base_size, base_offset;
 +      unsigned long base_size;
 +      off_t base_offset;
  
        base_offset = get_delta_base(p, w_curs, &curpos, *type, obj_offset);
        base = unpack_entry(p, base_offset, type, &base_size);
        if (!base)
 -              die("failed to read delta base object at %lu from %s",
 -                  base_offset, p->pack_name);
 +              die("failed to read delta base object"
 +                  " at %"PRIuMAX" from %s",
 +                  (uintmax_t)base_offset, p->pack_name);
  
        delta_data = unpack_compressed_entry(p, w_curs, curpos, delta_size);
        result = patch_delta(base, base_size,
        return result;
  }
  
 -void *unpack_entry(struct packed_git *p, unsigned long obj_offset,
 +void *unpack_entry(struct packed_git *p, off_t obj_offset,
                   enum object_type *type, unsigned long *sizep)
  {
        struct pack_window *w_curs = NULL;
 -      unsigned long curpos = obj_offset;
 +      off_t curpos = obj_offset;
        void *data;
  
        *type = unpack_object_header(p, &w_curs, &curpos, sizep);
        return data;
  }
  
 -int num_packed_objects(const struct packed_git *p)
 +uint32_t num_packed_objects(const struct packed_git *p)
  {
        /* See check_packed_git_idx() */
 -      return (p->index_size - 20 - 20 - 4*256) / 24;
 +      return (uint32_t)((p->index_size - 20 - 20 - 4*256) / 24);
  }
  
 -int nth_packed_object_sha1(const struct packed_git *p, int n,
 +int nth_packed_object_sha1(const struct packed_git *p, uint32_t n,
                           unsigned char* sha1)
  {
        void *index = p->index_base + 256;
 -      if (n < 0 || num_packed_objects(p) <= n)
 +      if (num_packed_objects(p) <= n)
                return -1;
        hashcpy(sha1, (unsigned char *) index + (24 * n) + 4);
        return 0;
  }
  
 -unsigned long find_pack_entry_one(const unsigned char *sha1,
 +off_t find_pack_entry_one(const unsigned char *sha1,
                                  struct packed_git *p)
  {
        uint32_t *level1_ofs = p->index_base;
@@@ -1405,7 -1400,7 +1416,7 @@@ static int matches_pack_name(struct pac
  static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed)
  {
        struct packed_git *p;
 -      unsigned long offset;
 +      off_t offset;
  
        prepare_packed_git();
  
@@@ -1571,7 -1566,7 +1582,7 @@@ void *read_sha1_file(const unsigned cha
                return buf;
        map = map_sha1_file(sha1, &mapsize);
        if (map) {
-               buf = unpack_sha1_file(map, mapsize, type, size);
+               buf = unpack_sha1_file(map, mapsize, type, size, sha1);
                munmap(map, mapsize);
                return buf;
        }
@@@ -2072,10 -2067,11 +2083,10 @@@ int index_pipe(unsigned char *sha1, in
  int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
             enum object_type type, const char *path)
  {
 -      unsigned long size = st->st_size;
 -      void *buf;
 +      size_t size = xsize_t(st->st_size);
 +      void *buf = NULL;
        int ret, re_allocated = 0;
  
 -      buf = "";
        if (size)
                buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
        close(fd);
@@@ -2115,7 -2111,6 +2126,7 @@@ int index_path(unsigned char *sha1, con
  {
        int fd;
        char *target;
 +      size_t len;
  
        switch (st->st_mode & S_IFMT) {
        case S_IFREG:
                                     path);
                break;
        case S_IFLNK:
 -              target = xmalloc(st->st_size+1);
 -              if (readlink(path, target, st->st_size+1) != st->st_size) {
 +              len = xsize_t(st->st_size);
 +              target = xmalloc(len + 1);
 +              if (readlink(path, target, len + 1) != st->st_size) {
                        char *errstr = strerror(errno);
                        free(target);
                        return error("readlink(\"%s\"): %s", path,
                                     errstr);
                }
                if (!write_object)
 -                      hash_sha1_file(target, st->st_size, blob_type, sha1);
 -              else if (write_sha1_file(target, st->st_size, blob_type, sha1))
 +                      hash_sha1_file(target, len, blob_type, sha1);
 +              else if (write_sha1_file(target, len, blob_type, sha1))
                        return error("%s: failed to insert into database",
                                     path);
                free(target);