Merge branch 'bp/read-index-from-skip-verification'
authorJunio C Hamano <gitster@pobox.com>
Wed, 15 Nov 2017 03:14:37 +0000 (12:14 +0900)
committerJunio C Hamano <gitster@pobox.com>
Wed, 15 Nov 2017 03:14:37 +0000 (12:14 +0900)
Drop (perhaps overly cautious) sanity check before using the index
read from the filesystem at runtime.

* bp/read-index-from-skip-verification:
read_index_from(): speed index loading by skipping verification of the entry order

1  2 
builtin/fsck.c
cache.h
read-cache.c
diff --combined builtin/fsck.c
index 5f91116d7303ec126593ce0b764f8ea4ad96155d,81cc5ad78d76701d46e4f3cc1340fe63c2acc2cb..04846d46f91d6cde14d379da8e0e3dd458c08dc5
@@@ -15,7 -15,6 +15,7 @@@
  #include "progress.h"
  #include "streaming.h"
  #include "decorate.h"
 +#include "packfile.h"
  
  #define REACHABLE 0x0001
  #define SEEN      0x0002
@@@ -180,9 -179,14 +180,9 @@@ static int traverse_reachable(void
        unsigned int nr = 0;
        int result = 0;
        if (show_progress)
 -              progress = start_progress_delay(_("Checking connectivity"), 0, 0, 2);
 +              progress = start_delayed_progress(_("Checking connectivity"), 0);
        while (pending.nr) {
 -              struct object_array_entry *entry;
 -              struct object *obj;
 -
 -              entry = pending.objects + --pending.nr;
 -              obj = entry->item;
 -              result |= traverse_one_object(obj);
 +              result |= traverse_one_object(object_array_pop(&pending));
                display_progress(progress, ++nr);
        }
        stop_progress(&progress);
@@@ -555,7 -559,7 +555,7 @@@ static int fsck_head_link(void
        if (verbose)
                fprintf(stderr, "Checking HEAD link\n");
  
 -      head_points_at = resolve_ref_unsafe("HEAD", 0, head_oid.hash, NULL);
 +      head_points_at = resolve_ref_unsafe("HEAD", 0, &head_oid, NULL);
        if (!head_points_at) {
                errors_found |= ERROR_REFS;
                return error("Invalid HEAD");
@@@ -726,12 -730,12 +726,12 @@@ int cmd_fsck(int argc, const char **arg
  
        for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
 -              unsigned char sha1[20];
 -              if (!get_sha1(arg, sha1)) {
 -                      struct object *obj = lookup_object(sha1);
 +              struct object_id oid;
 +              if (!get_oid(arg, &oid)) {
 +                      struct object *obj = lookup_object(oid.hash);
  
                        if (!obj || !(obj->flags & HAS_OBJ)) {
 -                              error("%s: object missing", sha1_to_hex(sha1));
 +                              error("%s: object missing", oid_to_hex(&oid));
                                errors_found |= ERROR_OBJECT;
                                continue;
                        }
  
        if (keep_cache_objects) {
                verify_index_checksum = 1;
+               verify_ce_order = 1;
                read_cache();
                for (i = 0; i < active_nr; i++) {
                        unsigned int mode;
diff --combined cache.h
index cb7fb7c004beb0b228269469a2c389c248bb72d1,177f5ebcf2552437fa3521bf373bd04f6dfc5c03..89f5d2457994587cc86fe5e1dbd80aa8caf708f9
+++ b/cache.h
@@@ -4,7 -4,6 +4,7 @@@
  #include "git-compat-util.h"
  #include "strbuf.h"
  #include "hashmap.h"
 +#include "mru.h"
  #include "advice.h"
  #include "gettext.h"
  #include "convert.h"
@@@ -418,6 -417,7 +418,6 @@@ static inline enum object_type object_t
  #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
  #define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
  #define GIT_SUPER_PREFIX_ENVIRONMENT "GIT_INTERNAL_SUPER_PREFIX"
 -#define GIT_TOPLEVEL_PREFIX_ENVIRONMENT "GIT_INTERNAL_TOPLEVEL_PREFIX"
  #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
  #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
  #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
  #define GITATTRIBUTES_FILE ".gitattributes"
  #define INFOATTRIBUTES_FILE "info/attributes"
  #define ATTRIBUTE_MACRO_PREFIX "[attr]"
 +#define GITMODULES_FILE ".gitmodules"
  #define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
  #define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
  #define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
  #define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS"
  #define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
  #define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH"
 +#define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
  
  /*
   * This environment variable is expected to contain a boolean indicating
@@@ -602,28 -600,9 +602,28 @@@ extern int do_read_index(struct index_s
  extern int read_index_from(struct index_state *, const char *path);
  extern int is_index_unborn(struct index_state *);
  extern int read_index_unmerged(struct index_state *);
 +
 +/* For use with `write_locked_index()`. */
  #define COMMIT_LOCK           (1 << 0)
 -#define CLOSE_LOCK            (1 << 1)
 +
 +/*
 + * Write the index while holding an already-taken lock. Close the lock,
 + * and if `COMMIT_LOCK` is given, commit it.
 + *
 + * Unless a split index is in use, write the index into the lockfile.
 + *
 + * With a split index, write the shared index to a temporary file,
 + * adjust its permissions and rename it into place, then write the
 + * split index to the lockfile. If the temporary file for the shared
 + * index cannot be created, fall back to the behavior described in
 + * the previous paragraph.
 + *
 + * With `COMMIT_LOCK`, the lock is always committed or rolled back.
 + * Without it, the lock is closed, but neither committed nor rolled
 + * back.
 + */
  extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
 +
  extern int discard_index(struct index_state *);
  extern void move_index_extensions(struct index_state *dst, struct index_state *src);
  extern int unmerged_index(const struct index_state *);
@@@ -705,8 -684,8 +705,8 @@@ extern int ie_modified(const struct ind
  
  #define HASH_WRITE_OBJECT 1
  #define HASH_FORMAT_CHECK 2
 -extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
 -extern int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned flags);
 +extern int index_fd(struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
 +extern int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags);
  
  /*
   * Record to sd the data from st that we use to check whether a file
@@@ -735,16 -714,13 +735,17 @@@ extern void fill_stat_cache_info(struc
  extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
  extern struct cache_entry *refresh_cache_entry(struct cache_entry *, unsigned int);
  
 +/*
 + * Opportunistically update the index but do not complain if we can't.
 + * The lockfile is always committed or rolled back.
 + */
  extern void update_index_if_able(struct index_state *, struct lock_file *);
  
  extern int hold_locked_index(struct lock_file *, int);
  extern void set_alternate_index_output(const char *);
  
  extern int verify_index_checksum;
+ extern int verify_ce_order;
  
  /* Environment bits from configuration mechanism */
  extern int trust_executable_bit;
@@@ -807,11 -783,6 +808,11 @@@ extern int protect_ntfs
   */
  extern int ref_paranoia;
  
 +/*
 + * Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value).
 + */
 +int use_optional_locks(void);
 +
  /*
   * The character that begins a commented line in user-editable file
   * that is subject to stripspace.
@@@ -932,6 -903,20 +933,6 @@@ extern void check_repository_format(voi
   */
  extern const char *sha1_file_name(const unsigned char *sha1);
  
 -/*
 - * Return the name of the (local) packfile with the specified sha1 in
 - * its name.  The return value is a pointer to memory that is
 - * overwritten each time this function is called.
 - */
 -extern char *sha1_pack_name(const unsigned char *sha1);
 -
 -/*
 - * Return the name of the (local) pack index file with the specified
 - * sha1 in its name.  The return value is a pointer to memory that is
 - * overwritten each time this function is called.
 - */
 -extern char *sha1_pack_index_name(const unsigned char *sha1);
 -
  /*
   * Return an abbreviated sha1 unique within this repository's object database.
   * The result will be at least `len` characters long, and will be NUL
@@@ -1208,7 -1193,7 +1209,7 @@@ static inline const unsigned char *look
  extern int sha1_object_info(const unsigned char *, unsigned long *);
  extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
  extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
 -extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, unsigned char *sha1, unsigned flags);
 +extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, struct object_id *oid, unsigned flags);
  extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
  extern int force_object_loose(const unsigned char *sha1, time_t mtime);
  extern int git_open_cloexec(const char *name, int flags);
@@@ -1217,10 -1202,15 +1218,10 @@@ extern void *map_sha1_file(const unsign
  extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
  extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
  
 -/* global flag to enable extra checks when accessing packed objects */
 -extern int do_check_packed_object_crc;
 -
  extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
  
  extern int finalize_object_file(const char *tmpfile, const char *filename);
  
 -extern int has_sha1_pack(const unsigned char *sha1);
 -
  /*
   * Open the loose object at path, check its sha1, and return the contents,
   * type, and size. If the object is a blob, then "contents" may return NULL,
@@@ -1256,6 -1246,8 +1257,6 @@@ extern int has_object_file_with_flags(c
   */
  extern int has_loose_object_nonlocal(const unsigned char *sha1);
  
 -extern int has_pack_index(const unsigned char *sha1);
 -
  extern void assert_sha1_type(const unsigned char *sha1, enum object_type expect);
  
  /* Helper to check and "touch" a file */
@@@ -1293,37 -1285,38 +1294,37 @@@ struct object_context 
         */
        struct strbuf symlink_path;
        /*
 -       * If GET_SHA1_RECORD_PATH is set, this will record path (if any)
 +       * If GET_OID_RECORD_PATH is set, this will record path (if any)
         * found when resolving the name. The caller is responsible for
         * releasing the memory.
         */
        char *path;
  };
  
 -#define GET_SHA1_QUIETLY           01
 -#define GET_SHA1_COMMIT            02
 -#define GET_SHA1_COMMITTISH        04
 -#define GET_SHA1_TREE             010
 -#define GET_SHA1_TREEISH          020
 -#define GET_SHA1_BLOB             040
 -#define GET_SHA1_FOLLOW_SYMLINKS 0100
 -#define GET_SHA1_RECORD_PATH     0200
 -#define GET_SHA1_ONLY_TO_DIE    04000
 -
 -#define GET_SHA1_DISAMBIGUATORS \
 -      (GET_SHA1_COMMIT | GET_SHA1_COMMITTISH | \
 -      GET_SHA1_TREE | GET_SHA1_TREEISH | \
 -      GET_SHA1_BLOB)
 -
 -extern int get_sha1(const char *str, unsigned char *sha1);
 -extern int get_sha1_commit(const char *str, unsigned char *sha1);
 -extern int get_sha1_committish(const char *str, unsigned char *sha1);
 -extern int get_sha1_tree(const char *str, unsigned char *sha1);
 -extern int get_sha1_treeish(const char *str, unsigned char *sha1);
 -extern int get_sha1_blob(const char *str, unsigned char *sha1);
 -extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
 -extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *oc);
 +#define GET_OID_QUIETLY           01
 +#define GET_OID_COMMIT            02
 +#define GET_OID_COMMITTISH        04
 +#define GET_OID_TREE             010
 +#define GET_OID_TREEISH          020
 +#define GET_OID_BLOB             040
 +#define GET_OID_FOLLOW_SYMLINKS 0100
 +#define GET_OID_RECORD_PATH     0200
 +#define GET_OID_ONLY_TO_DIE    04000
 +
 +#define GET_OID_DISAMBIGUATORS \
 +      (GET_OID_COMMIT | GET_OID_COMMITTISH | \
 +      GET_OID_TREE | GET_OID_TREEISH | \
 +      GET_OID_BLOB)
  
  extern int get_oid(const char *str, struct object_id *oid);
 +extern int get_oid_commit(const char *str, struct object_id *oid);
 +extern int get_oid_committish(const char *str, struct object_id *oid);
 +extern int get_oid_tree(const char *str, struct object_id *oid);
 +extern int get_oid_treeish(const char *str, struct object_id *oid);
 +extern int get_oid_blob(const char *str, struct object_id *oid);
 +extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
 +extern int get_oid_with_context(const char *str, unsigned flags, struct object_id *oid, struct object_context *oc);
 +
  
  typedef int each_abbrev_fn(const struct object_id *oid, void *);
  extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
@@@ -1340,13 -1333,6 +1341,13 @@@ extern int set_disambiguate_hint_config
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
  extern int get_oid_hex(const char *hex, struct object_id *sha1);
  
 +/*
 + * Read `len` pairs of hexadecimal digits from `hex` and write the
 + * values to `binary` as `len` bytes. Return 0 on success, or -1 if
 + * the input does not consist of hex digits).
 + */
 +extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
 +
  /*
   * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
   * and writes the NUL-terminated output to the buffer `out`, which must be at
@@@ -1626,7 -1612,8 +1627,7 @@@ extern struct packed_git 
   * A most-recently-used ordered version of the packed_git list, which can
   * be iterated instead of packed_git (and marked via mru_mark).
   */
 -struct mru;
 -extern struct mru *packed_git_mru;
 +extern struct mru packed_git_mru;
  
  struct pack_entry {
        off_t offset;
        struct packed_git *p;
  };
  
 -extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
 -
 -/* A hook to report invalid files in pack directory */
 -#define PACKDIR_FILE_PACK 1
 -#define PACKDIR_FILE_IDX 2
 -#define PACKDIR_FILE_GARBAGE 4
 -extern void (*report_garbage)(unsigned seen_bits, const char *path);
 -
 -extern void prepare_packed_git(void);
 -extern void reprepare_packed_git(void);
 -extern void install_packed_git(struct packed_git *pack);
 -
 -/*
 - * Give a rough count of objects in the repository. This sacrifices accuracy
 - * for speed.
 - */
 -unsigned long approximate_object_count(void);
 -
 -extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
 -                                       struct packed_git *packs);
 -
 -extern void pack_report(void);
 -
  /*
   * Create a temporary file rooted in the object database directory, or
   * die on failure. The filename is taken from "pattern", which should have the
   */
  extern int odb_mkstemp(struct strbuf *template, const char *pattern);
  
 -/*
 - * Generate the filename to be used for a pack file with checksum "sha1" and
 - * extension "ext". The result is written into the strbuf "buf", overwriting
 - * any existing contents. A pointer to buf->buf is returned as a convenience.
 - *
 - * Example: odb_pack_name(out, sha1, "idx") => ".git/objects/pack/pack-1234..idx"
 - */
 -extern char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, const char *ext);
 -
  /*
   * Create a pack .keep file named "name" (which should generally be the output
   * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on
   */
  extern int odb_pack_keep(const char *name);
  
 -/*
 - * mmap the index file for the specified packfile (if it is not
 - * already mmapped).  Return 0 on success.
 - */
 -extern int open_pack_index(struct packed_git *);
 -
 -/*
 - * munmap the index file for the specified packfile (if it is
 - * currently mmapped).
 - */
 -extern void close_pack_index(struct packed_git *);
 -
 -extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
 -extern void close_pack_windows(struct packed_git *);
 -extern void close_all_packs(void);
 -extern void unuse_pack(struct pack_window **);
 -extern void clear_delta_base_cache(void);
 -extern struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
 -
 -/*
 - * Make sure that a pointer access into an mmap'd index file is within bounds,
 - * and can provide at least 8 bytes of data.
 - *
 - * Note that this is only necessary for variable-length segments of the file
 - * (like the 64-bit extended offset table), as we compare the size to the
 - * fixed-length parts when we open the file.
 - */
 -extern void check_pack_index_ptr(const struct packed_git *p, const void *ptr);
 -
 -/*
 - * Return the SHA-1 of the nth object within the specified packfile.
 - * Open the index if it is not already open.  The return value points
 - * at the SHA-1 within the mmapped index.  Return NULL if there is an
 - * error.
 - */
 -extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t n);
 -/*
 - * Like nth_packed_object_sha1, but write the data into the object specified by
 - * the the first argument.  Returns the first argument on success, and NULL on
 - * error.
 - */
 -extern const struct object_id *nth_packed_object_oid(struct object_id *, struct packed_git *, uint32_t n);
 -
 -/*
 - * Return the offset of the nth object within the specified packfile.
 - * The index must already be opened.
 - */
 -extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t n);
 -
 -/*
 - * If the object named sha1 is present in the specified packfile,
 - * return its offset within the packfile; otherwise, return 0.
 - */
 -extern off_t find_pack_entry_one(const unsigned char *sha1, struct packed_git *);
 -
 -extern int is_pack_valid(struct packed_git *);
 -extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
 -extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
 -extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
 -extern int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *);
 -
  /*
   * Iterate over the files in the loose-object parts of the object
   * directory "path", triggering the following callbacks:
@@@ -1698,12 -1778,17 +1699,12 @@@ int for_each_loose_file_in_objdir_buf(s
                                      void *data);
  
  /*
 - * Iterate over loose and packed objects in both the local
 + * Iterate over loose objects in both the local
   * repository and any alternates repositories (unless the
   * LOCAL_ONLY flag is set).
   */
  #define FOR_EACH_OBJECT_LOCAL_ONLY 0x1
 -typedef int each_packed_object_fn(const struct object_id *oid,
 -                                struct packed_git *pack,
 -                                uint32_t pos,
 -                                void *data);
  extern int for_each_loose_object(each_loose_object_fn, void *, unsigned flags);
 -extern int for_each_packed_object(each_packed_object_fn, void *, unsigned flags);
  
  struct object_info {
        /* Request */
  /* Do not retry packed storage after checking packed and loose storage */
  #define OBJECT_INFO_QUICK 8
  extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
 -extern int packed_object_info(struct packed_git *pack, off_t offset, struct object_info *);
  
  /* Dumb servers support */
  extern int update_server_info(int);
@@@ -1871,8 -1957,6 +1872,8 @@@ void shift_tree_by(const struct object_
  #define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
  #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
  #define WS_TAB_WIDTH_MASK        077
 +/* All WS_* -- when extended, adapt diff.c emit_symbol */
 +#define WS_RULE_MASK           07777
  extern unsigned whitespace_rule_cfg;
  extern unsigned whitespace_rule(const char *);
  extern unsigned parse_whitespace_rule(const char *);
diff --combined read-cache.c
index f3d125c114b4f41a4d92b87a0c63d42f5fd7b03f,945a377447e69427c5b67210d507ff4993be565f..b13a1cb8f2d079d8475cffca154dc6b0822bc0d0
@@@ -160,9 -160,9 +160,9 @@@ static int ce_compare_data(const struc
        int fd = git_open_cloexec(ce->name, O_RDONLY);
  
        if (fd >= 0) {
 -              unsigned char sha1[20];
 -              if (!index_fd(sha1, fd, st, OBJ_BLOB, ce->name, 0))
 -                      match = hashcmp(sha1, ce->oid.hash);
 +              struct object_id oid;
 +              if (!index_fd(&oid, fd, st, OBJ_BLOB, ce->name, 0))
 +                      match = oidcmp(&oid, &ce->oid);
                /* index_fd() closed the file descriptor already */
        }
        return match;
@@@ -191,7 -191,7 +191,7 @@@ static int ce_compare_link(const struc
  
  static int ce_compare_gitlink(const struct cache_entry *ce)
  {
 -      unsigned char sha1[20];
 +      struct object_id oid;
  
        /*
         * We don't actually require that the .git directory
         *
         * If so, we consider it always to match.
         */
 -      if (resolve_gitlink_ref(ce->name, "HEAD", sha1) < 0)
 +      if (resolve_gitlink_ref(ce->name, "HEAD", &oid) < 0)
                return 0;
 -      return hashcmp(sha1, ce->oid.hash);
 +      return oidcmp(&oid, &ce->oid);
  }
  
  static int ce_modified_check_fs(const struct cache_entry *ce, struct stat *st)
        case S_IFDIR:
                if (S_ISGITLINK(ce->ce_mode))
                        return ce_compare_gitlink(ce) ? DATA_CHANGED : 0;
 +              /* else fallthrough */
        default:
                return TYPE_CHANGED;
        }
@@@ -690,7 -689,7 +690,7 @@@ int add_to_index(struct index_state *is
                return 0;
        }
        if (!intent_only) {
 -              if (index_path(ce->oid.hash, path, st, HASH_WRITE_OBJECT)) {
 +              if (index_path(&ce->oid, path, st, HASH_WRITE_OBJECT)) {
                        free(ce);
                        return error("unable to index file %s", path);
                }
@@@ -1500,7 -1499,6 +1500,7 @@@ struct ondisk_cache_entry_extended 
  };
  
  /* These are only used for v3 or lower */
 +#define align_padding_size(size, len) ((size + (len) + 8) & ~7) - (size + len)
  #define align_flex_name(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
  #define ondisk_cache_entry_size(len) align_flex_name(ondisk_cache_entry,len)
  #define ondisk_cache_entry_extended_size(len) align_flex_name(ondisk_cache_entry_extended,len)
  /* Allow fsck to force verification of the index checksum. */
  int verify_index_checksum;
  
+ /* Allow fsck to force verification of the cache entry order. */
+ int verify_ce_order;
  static int verify_hdr(struct cache_header *hdr, unsigned long size)
  {
        git_SHA_CTX c;
@@@ -1668,6 -1669,9 +1671,9 @@@ static void check_ce_order(struct index
  {
        unsigned int i;
  
+       if (!verify_ce_order)
+               return;
        for (i = 1; i < istate->cache_nr; i++) {
                struct cache_entry *ce = istate->cache[i - 1];
                struct cache_entry *next_ce = istate->cache[i];
@@@ -2034,7 -2038,7 +2040,7 @@@ static void ce_smudge_racily_clean_entr
  }
  
  /* Copy miscellaneous fields but not the name */
 -static char *copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
 +static void copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
                                       struct cache_entry *ce)
  {
        short flags;
                struct ondisk_cache_entry_extended *ondisk2;
                ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
                ondisk2->flags2 = htons((ce->ce_flags & CE_EXTENDED_FLAGS) >> 16);
 -              return ondisk2->name;
 -      }
 -      else {
 -              return ondisk->name;
        }
  }
  
  static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
 -                        struct strbuf *previous_name)
 +                        struct strbuf *previous_name, struct ondisk_cache_entry *ondisk)
  {
        int size;
 -      struct ondisk_cache_entry *ondisk;
        int saved_namelen = saved_namelen; /* compiler workaround */
 -      char *name;
        int result;
 +      static unsigned char padding[8] = { 0x00 };
  
        if (ce->ce_flags & CE_STRIP_NAME) {
                saved_namelen = ce_namelen(ce);
                ce->ce_namelen = 0;
        }
  
 +      if (ce->ce_flags & CE_EXTENDED)
 +              size = offsetof(struct ondisk_cache_entry_extended, name);
 +      else
 +              size = offsetof(struct ondisk_cache_entry, name);
 +
        if (!previous_name) {
 -              size = ondisk_ce_size(ce);
 -              ondisk = xcalloc(1, size);
 -              name = copy_cache_entry_to_ondisk(ondisk, ce);
 -              memcpy(name, ce->name, ce_namelen(ce));
 +              int len = ce_namelen(ce);
 +              copy_cache_entry_to_ondisk(ondisk, ce);
 +              result = ce_write(c, fd, ondisk, size);
 +              if (!result)
 +                      result = ce_write(c, fd, ce->name, len);
 +              if (!result)
 +                      result = ce_write(c, fd, padding, align_padding_size(size, len));
        } else {
                int common, to_remove, prefix_size;
                unsigned char to_remove_vi[16];
                to_remove = previous_name->len - common;
                prefix_size = encode_varint(to_remove, to_remove_vi);
  
 -              if (ce->ce_flags & CE_EXTENDED)
 -                      size = offsetof(struct ondisk_cache_entry_extended, name);
 -              else
 -                      size = offsetof(struct ondisk_cache_entry, name);
 -              size += prefix_size + (ce_namelen(ce) - common + 1);
 -
 -              ondisk = xcalloc(1, size);
 -              name = copy_cache_entry_to_ondisk(ondisk, ce);
 -              memcpy(name, to_remove_vi, prefix_size);
 -              memcpy(name + prefix_size, ce->name + common, ce_namelen(ce) - common);
 +              copy_cache_entry_to_ondisk(ondisk, ce);
 +              result = ce_write(c, fd, ondisk, size);
 +              if (!result)
 +                      result = ce_write(c, fd, to_remove_vi, prefix_size);
 +              if (!result)
 +                      result = ce_write(c, fd, ce->name + common, ce_namelen(ce) - common);
 +              if (!result)
 +                      result = ce_write(c, fd, padding, 1);
  
                strbuf_splice(previous_name, common, to_remove,
                              ce->name + common, ce_namelen(ce) - common);
                ce->ce_flags &= ~CE_STRIP_NAME;
        }
  
 -      result = ce_write(c, fd, ondisk, size);
 -      free(ondisk);
        return result;
  }
  
@@@ -2176,33 -2181,27 +2182,33 @@@ static int has_racy_timestamp(struct in
        return 0;
  }
  
 -/*
 - * Opportunistically update the index but do not complain if we can't
 - */
  void update_index_if_able(struct index_state *istate, struct lock_file *lockfile)
  {
        if ((istate->cache_changed || has_racy_timestamp(istate)) &&
 -          verify_index(istate) &&
 -          write_locked_index(istate, lockfile, COMMIT_LOCK))
 +          verify_index(istate))
 +              write_locked_index(istate, lockfile, COMMIT_LOCK);
 +      else
                rollback_lock_file(lockfile);
  }
  
 +/*
 + * On success, `tempfile` is closed. If it is the temporary file
 + * of a `struct lock_file`, we will therefore effectively perform
 + * a 'close_lock_file_gently()`. Since that is an implementation
 + * detail of lockfiles, callers of `do_write_index()` should not
 + * rely on it.
 + */
  static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                          int strip_extensions)
  {
        int newfd = tempfile->fd;
        git_SHA_CTX c;
        struct cache_header hdr;
 -      int i, err, removed, extended, hdr_version;
 +      int i, err = 0, removed, extended, hdr_version;
        struct cache_entry **cache = istate->cache;
        int entries = istate->cache_nr;
        struct stat st;
 +      struct ondisk_cache_entry_extended ondisk;
        struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
        int drop_cache_tree = 0;
  
                return -1;
  
        previous_name = (hdr_version == 4) ? &previous_name_buf : NULL;
 +
        for (i = 0; i < entries; i++) {
                struct cache_entry *ce = cache[i];
                if (ce->ce_flags & CE_REMOVE)
                        if (allow)
                                warning(msg, ce->name);
                        else
 -                              return error(msg, ce->name);
 +                              err = error(msg, ce->name);
  
                        drop_cache_tree = 1;
                }
 -              if (ce_write_entry(&c, newfd, ce, previous_name) < 0)
 -                      return -1;
 +              if (ce_write_entry(&c, newfd, ce, previous_name, (struct ondisk_cache_entry *)&ondisk) < 0)
 +                      err = -1;
 +
 +              if (err)
 +                      break;
        }
        strbuf_release(&previous_name_buf);
  
 +      if (err)
 +              return err;
 +
        /* Write extension data here */
        if (!strip_extensions && istate->split_index) {
                struct strbuf sb = STRBUF_INIT;
  
        if (ce_flush(&c, newfd, istate->sha1))
                return -1;
 -      if (close_tempfile(tempfile))
 -              return error(_("could not close '%s'"), tempfile->filename.buf);
 +      if (close_tempfile_gently(tempfile)) {
 +              error(_("could not close '%s'"), tempfile->filename.buf);
 +              return -1;
 +      }
        if (stat(tempfile->filename.buf, &st))
                return -1;
        istate->timestamp.sec = (unsigned int)st.st_mtime;
@@@ -2344,12 -2334,17 +2350,12 @@@ static int commit_locked_index(struct l
  static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
                                 unsigned flags)
  {
 -      int ret = do_write_index(istate, &lock->tempfile, 0);
 +      int ret = do_write_index(istate, lock->tempfile, 0);
        if (ret)
                return ret;
 -      assert((flags & (COMMIT_LOCK | CLOSE_LOCK)) !=
 -             (COMMIT_LOCK | CLOSE_LOCK));
        if (flags & COMMIT_LOCK)
                return commit_locked_index(lock);
 -      else if (flags & CLOSE_LOCK)
 -              return close_lock_file(lock);
 -      else
 -              return ret;
 +      return close_lock_file_gently(lock);
  }
  
  static int write_split_index(struct index_state *istate,
@@@ -2422,33 -2417,34 +2428,33 @@@ static int clean_shared_index_files(con
        return 0;
  }
  
 -static struct tempfile temporary_sharedindex;
 -
  static int write_shared_index(struct index_state *istate,
                              struct lock_file *lock, unsigned flags)
  {
 +      struct tempfile *temp;
        struct split_index *si = istate->split_index;
 -      int fd, ret;
 +      int ret;
  
 -      fd = mks_tempfile(&temporary_sharedindex, git_path("sharedindex_XXXXXX"));
 -      if (fd < 0) {
 +      temp = mks_tempfile(git_path("sharedindex_XXXXXX"));
 +      if (!temp) {
                hashclr(si->base_sha1);
                return do_write_locked_index(istate, lock, flags);
        }
        move_cache_to_base_index(istate);
 -      ret = do_write_index(si->base, &temporary_sharedindex, 1);
 +      ret = do_write_index(si->base, temp, 1);
        if (ret) {
 -              delete_tempfile(&temporary_sharedindex);
 +              delete_tempfile(&temp);
                return ret;
        }
 -      ret = adjust_shared_perm(get_tempfile_path(&temporary_sharedindex));
 +      ret = adjust_shared_perm(get_tempfile_path(temp));
        if (ret) {
                int save_errno = errno;
 -              error("cannot fix permission bits on %s", get_tempfile_path(&temporary_sharedindex));
 -              delete_tempfile(&temporary_sharedindex);
 +              error("cannot fix permission bits on %s", get_tempfile_path(temp));
 +              delete_tempfile(&temp);
                errno = save_errno;
                return ret;
        }
 -      ret = rename_tempfile(&temporary_sharedindex,
 +      ret = rename_tempfile(&temp,
                              git_path("sharedindex.%s", sha1_to_hex(si->base->sha1)));
        if (!ret) {
                hashcpy(si->base_sha1, si->base->sha1);
@@@ -2498,8 -2494,7 +2504,8 @@@ int write_locked_index(struct index_sta
            (istate->cache_changed & ~EXTMASK)) {
                if (si)
                        hashclr(si->base_sha1);
 -              return do_write_locked_index(istate, lock, flags);
 +              ret = do_write_locked_index(istate, lock, flags);
 +              goto out;
        }
  
        if (getenv("GIT_TEST_SPLIT_INDEX")) {
        if (new_shared_index) {
                ret = write_shared_index(istate, lock, flags);
                if (ret)
 -                      return ret;
 +                      goto out;
        }
  
        ret = write_split_index(istate, lock, flags);
        if (!ret && !new_shared_index)
                freshen_shared_index(sha1_to_hex(si->base_sha1), 1);
  
 +out:
 +      if (flags & COMMIT_LOCK)
 +              rollback_lock_file(lock);
        return ret;
  }