---
 Makefile        |    1 +
 read-cache-v5.c | 1170 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 read-cache.c    |   27 ++
 3 files changed, 1198 insertions(+), 0 deletions(-)
 create mode 100644 read-cache-v5.c

diff --git a/Makefile b/Makefile
index b4a7c73..77be175 100644
--- a/Makefile
+++ b/Makefile
@@ -770,6 +770,7 @@ LIB_OBJS += quote.o
 LIB_OBJS += reachable.o
 LIB_OBJS += read-cache.o
 LIB_OBJS += read-cache-v2.o
+LIB_OBJS += read-cache-v5.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
 LIB_OBJS += remote.o
diff --git a/read-cache-v5.c b/read-cache-v5.c
new file mode 100644
index 0000000..a4a3746
--- /dev/null
+++ b/read-cache-v5.c
@@ -0,0 +1,1170 @@
+#include "cache.h"
+#include "read-cache.h"
+#include "resolve-undo.h"
+#include "cache-tree.h"
+
+struct cache_header_v5 {
+       unsigned int hdr_ndir;
+       unsigned int hdr_nfile;
+       unsigned int hdr_fblockoffset;
+       unsigned int hdr_nextension;
+};
+
+struct ondisk_cache_entry_v5 {
+       unsigned short flags;
+       unsigned short mode;
+       struct cache_time mtime;
+       int stat_crc;
+       unsigned char sha1[20];
+};
+
+struct ondisk_directory_entry {
+       unsigned int foffset;
+       unsigned int cr;
+       unsigned int ncr;
+       unsigned int nsubtrees;
+       unsigned int nfiles;
+       unsigned int nentries;
+       unsigned char sha1[20];
+       unsigned short flags;
+};
+
+static int match_stat_crc(struct stat *st, uint32_t expected_crc)
+{
+       uint32_t data, stat_crc = 0;
+       unsigned int ctimens = 0;
+
+       data = htonl(st->st_ctime);
+       stat_crc = crc32(0, (Bytef*)&data, 4);
+#ifdef USE_NSEC
+       ctimens = ST_MTIME_NSEC(*st);
+#endif
+       data = htonl(ctimens);
+       stat_crc = crc32(stat_crc, (Bytef*)&data, 4);
+       data = htonl(st->st_ino);
+       stat_crc = crc32(stat_crc, (Bytef*)&data, 4);
+       data = htonl(st->st_size);
+       stat_crc = crc32(stat_crc, (Bytef*)&data, 4);
+       data = htonl(st->st_dev);
+       stat_crc = crc32(stat_crc, (Bytef*)&data, 4);
+       data = htonl(st->st_uid);
+       stat_crc = crc32(stat_crc, (Bytef*)&data, 4);
+       data = htonl(st->st_gid);
+       stat_crc = crc32(stat_crc, (Bytef*)&data, 4);
+
+       return stat_crc == expected_crc;
+}
+
+static int match_stat_basic(struct cache_entry *ce,
+                           struct stat *st,
+                           int changed)
+{
+
+       if (ce->ce_mtime.sec != 0 && ce->ce_mtime.sec != (unsigned 
int)st->st_mtime)
+               changed |= MTIME_CHANGED;
+#ifdef USE_NSEC
+       if (ce->ce_mtime.nsec != 0 && ce->ce_mtime.nsec != ST_MTIME_NSEC(*st))
+               changed |= MTIME_CHANGED;
+#endif
+       if (!match_stat_crc(st, ce->ce_stat_crc)) {
+               changed |= OWNER_CHANGED;
+               changed |= INODE_CHANGED;
+       }
+       /* Racily smudged entry? */
+       if (!ce->ce_mtime.sec && !ce->ce_mtime.nsec) {
+               if (!changed && !is_empty_blob_sha1(ce->sha1) && 
ce_modified_check_fs(ce, st))
+                       changed |= DATA_CHANGED;
+       }
+       return changed;
+}
+
+static int check_crc32(int initialcrc,
+                       void *data,
+                       size_t len,
+                       unsigned int expected_crc)
+{
+       int crc;
+
+       crc = crc32(initialcrc, (Bytef*)data, len);
+       return crc == expected_crc;
+}
+
+static int verify_hdr(struct cache_version_header *hdr, unsigned long size)
+{
+       uint32_t *filecrc;
+       unsigned int header_size_v5;
+       struct cache_header_v5 *hdr_v5;
+       void *mmap = hdr;;
+
+       hdr = mmap;
+       hdr_v5 = mmap + sizeof(*hdr);
+       /* Size of the header + the size of the extensionoffsets */
+       header_size_v5 = sizeof(*hdr_v5) + hdr_v5->hdr_nextension * 4;
+       /* Initialize crc */
+       filecrc = mmap + sizeof(*hdr) + header_size_v5;
+       if (!check_crc32(0, hdr, sizeof(*hdr) + header_size_v5, 
ntohl(*filecrc)))
+               return error("bad index file header crc signature");
+       return 0;
+}
+
+static struct cache_entry *cache_entry_from_ondisk_v5(struct 
ondisk_cache_entry_v5 *ondisk,
+                                                  struct directory_entry *de,
+                                                  char *name,
+                                                  size_t len,
+                                                  size_t prefix_len)
+{
+       struct cache_entry *ce = xmalloc(cache_entry_size(len + 
de->de_pathlen));
+       int flags;
+
+       flags = ntoh_s(ondisk->flags);
+       ce->ce_ctime.sec  = 0;
+       ce->ce_mtime.sec  = ntoh_l(ondisk->mtime.sec);
+       ce->ce_ctime.nsec = 0;
+       ce->ce_mtime.nsec = ntoh_l(ondisk->mtime.nsec);
+       ce->ce_dev        = 0;
+       ce->ce_ino        = 0;
+       ce->ce_mode       = ntoh_s(ondisk->mode);
+       ce->ce_uid        = 0;
+       ce->ce_gid        = 0;
+       ce->ce_size       = 0;
+       ce->ce_flags      = flags & CE_STAGEMASK;
+       ce->ce_flags     |= flags & CE_VALID;
+       if (flags & CE_INTENT_TO_ADD_V5)
+               ce->ce_flags |= CE_INTENT_TO_ADD;
+       if (flags & CE_SKIP_WORKTREE_V5)
+               ce->ce_flags |= CE_SKIP_WORKTREE;
+       ce->ce_stat_crc   = ntoh_l(ondisk->stat_crc);
+       ce->ce_namelen    = len + de->de_pathlen;
+       hashcpy(ce->sha1, ondisk->sha1);
+       memcpy(ce->name, de->pathname, de->de_pathlen);
+       memcpy(ce->name + de->de_pathlen, name, len);
+       ce->name[len + de->de_pathlen] = '\0';
+       return ce;
+}
+
+static struct directory_entry *directory_entry_from_ondisk(struct 
ondisk_directory_entry *ondisk,
+                                                  const char *name,
+                                                  size_t len)
+{
+       struct directory_entry *de = xmalloc(directory_entry_size(len));
+
+
+       memcpy(de->pathname, name, len);
+       de->pathname[len] = '\0';
+       de->de_flags      = ntoh_s(ondisk->flags);
+       de->de_foffset    = ntoh_l(ondisk->foffset);
+       de->de_cr         = ntoh_l(ondisk->cr);
+       de->de_ncr        = ntoh_l(ondisk->ncr);
+       de->de_nsubtrees  = ntoh_l(ondisk->nsubtrees);
+       de->de_nfiles     = ntoh_l(ondisk->nfiles);
+       de->de_nentries   = ntoh_l(ondisk->nentries);
+       de->de_pathlen    = len;
+       hashcpy(de->sha1, ondisk->sha1);
+       return de;
+}
+
+static struct conflict_part *conflict_part_from_ondisk(struct 
ondisk_conflict_part *ondisk)
+{
+       struct conflict_part *cp = xmalloc(sizeof(struct conflict_part));
+
+       cp->flags      = ntoh_s(ondisk->flags);
+       cp->entry_mode = ntoh_s(ondisk->entry_mode);
+       hashcpy(cp->sha1, ondisk->sha1);
+       return cp;
+}
+
+static struct cache_entry *convert_conflict_part(struct conflict_part *cp,
+                                               char * name,
+                                               unsigned int len)
+{
+
+       struct cache_entry *ce = xmalloc(cache_entry_size(len));
+
+       ce->ce_ctime.sec  = 0;
+       ce->ce_mtime.sec  = 0;
+       ce->ce_ctime.nsec = 0;
+       ce->ce_mtime.nsec = 0;
+       ce->ce_dev        = 0;
+       ce->ce_ino        = 0;
+       ce->ce_mode       = cp->entry_mode;
+       ce->ce_uid        = 0;
+       ce->ce_gid        = 0;
+       ce->ce_size       = 0;
+       ce->ce_flags      = conflict_stage(cp) << CE_STAGESHIFT;
+       ce->ce_stat_crc   = 0;
+       ce->ce_namelen    = len;
+       hashcpy(ce->sha1, cp->sha1);
+       memcpy(ce->name, name, len);
+       ce->name[len] = '\0';
+       return ce;
+}
+
+static struct directory_entry *read_directories_v5(unsigned int *dir_offset,
+                               unsigned int *dir_table_offset,
+                               void *mmap,
+                               int mmap_size)
+{
+       int i, ondisk_directory_size;
+       uint32_t *filecrc, *beginning, *end;
+       struct directory_entry *current = NULL;
+       struct ondisk_directory_entry *disk_de;
+       struct directory_entry *de;
+       unsigned int data_len, len;
+       char *name;
+
+       ondisk_directory_size = sizeof(disk_de->flags)
+               + sizeof(disk_de->foffset)
+               + sizeof(disk_de->cr)
+               + sizeof(disk_de->ncr)
+               + sizeof(disk_de->nsubtrees)
+               + sizeof(disk_de->nfiles)
+               + sizeof(disk_de->nentries)
+               + sizeof(disk_de->sha1);
+       name = (char *)mmap + *dir_offset;
+       beginning = mmap + *dir_table_offset;
+       end = mmap + *dir_table_offset + 4;
+       len = ntoh_l(*end) - ntoh_l(*beginning) - ondisk_directory_size - 5;
+       disk_de = (struct ondisk_directory_entry *)
+                       ((char *)mmap + *dir_offset + len + 1);
+       de = directory_entry_from_ondisk(disk_de, name, len);
+       de->next = NULL;
+
+       /* Length of pathname + nul byte for termination + size of
+        * members of ondisk_directory_entry. (Just using the size
+        * of the stuct doesn't work, because there may be padding
+        * bytes for the struct)
+        */
+       data_len = len + 1 + ondisk_directory_size;
+
+       filecrc = mmap + *dir_offset + data_len;
+       if (!check_crc32(0, mmap + *dir_offset, data_len, ntoh_l(*filecrc)))
+               goto unmap;
+
+       *dir_table_offset += 4;
+       *dir_offset += data_len + 4; /* crc code */
+
+       current = de;
+       for (i = 0; i < de->de_nsubtrees; i++) {
+               current->next = read_directories_v5(dir_offset, 
dir_table_offset,
+                                               mmap, mmap_size);
+               while (current->next)
+                       current = current->next;
+       }
+
+       return de;
+unmap:
+       munmap(mmap, mmap_size);
+       die("directory crc doesn't match for '%s'", de->pathname);
+}
+
+static struct cache_entry *read_entry_v5(struct directory_entry *de,
+                       unsigned long *entry_offset,
+                       void **mmap,
+                       unsigned long mmap_size,
+                       unsigned int *foffsetblock,
+                       int fd)
+{
+       int len, crc_wrong, i = 0, offset_to_offset;
+       char *name;
+       uint32_t foffsetblockcrc;
+       uint32_t *filecrc, *beginning, *end;
+       struct cache_entry *ce;
+       struct ondisk_cache_entry_v5 *disk_ce;
+
+       do {
+               name = (char *)*mmap + *entry_offset;
+               beginning = *mmap + *foffsetblock;
+               end = *mmap + *foffsetblock + 4;
+               len = ntoh_l(*end) - ntoh_l(*beginning) - sizeof(struct 
ondisk_cache_entry_v5) - 5;
+               disk_ce = (struct ondisk_cache_entry_v5 *)
+                               ((char *)*mmap + *entry_offset + len + 1);
+               ce = cache_entry_from_ondisk_v5(disk_ce, de, name, len, 
de->de_pathlen);
+               filecrc = *mmap + *entry_offset + len + 1 + sizeof(*disk_ce);
+               offset_to_offset = htonl(*foffsetblock);
+               foffsetblockcrc = crc32(0, (Bytef*)&offset_to_offset, 4);
+               crc_wrong = !check_crc32(foffsetblockcrc,
+                       *mmap + *entry_offset, len + 1 + sizeof(*disk_ce),
+                       ntoh_l(*filecrc));
+               if (crc_wrong) {
+                       /* wait for 10 milliseconds */
+                       usleep(10*1000);
+                       munmap(*mmap, mmap_size);
+                       *mmap = xmmap(NULL, mmap_size, PROT_READ | PROT_WRITE, 
MAP_PRIVATE, fd, 0);
+               }
+               i++;
+               /*
+                * Retry for 500 ms maximum, before giving up and saying the
+                * checksum is wrong.
+                */
+       } while (crc_wrong && i < 50);
+       if (crc_wrong)
+               goto unmap;
+       *entry_offset += len + 1 + sizeof(*disk_ce) + 4;
+       return ce;
+unmap:
+       munmap(*mmap, mmap_size);
+       die("file crc doesn't match for '%s'", ce->name);
+}
+
+static void ce_queue_push(struct cache_entry **head,
+                            struct cache_entry **tail,
+                            struct cache_entry *ce)
+{
+       if (!*head) {
+               *head = *tail = ce;
+               (*tail)->next = NULL;
+               return;
+       }
+
+       (*tail)->next = ce;
+       ce->next = NULL;
+       *tail = (*tail)->next;
+}
+
+static void conflict_entry_push(struct conflict_entry **head,
+                               struct conflict_entry **tail,
+                               struct conflict_entry *conflict_entry)
+{
+       if (!*head) {
+               *head = *tail = conflict_entry;
+               (*tail)->next = NULL;
+               return;
+       }
+
+       (*tail)->next = conflict_entry;
+       conflict_entry->next = NULL;
+       *tail = (*tail)->next;
+}
+
+static struct cache_entry *ce_queue_pop(struct cache_entry **head)
+{
+       struct cache_entry *ce;
+
+       ce = *head;
+       *head = (*head)->next;
+       return ce;
+}
+
+static void conflict_part_head_remove(struct conflict_part **head)
+{
+       struct conflict_part *to_free;
+
+       to_free = *head;
+       *head = (*head)->next;
+       free(to_free);
+}
+
+static void conflict_entry_head_remove(struct conflict_entry **head)
+{
+       struct conflict_entry *to_free;
+
+       to_free = *head;
+       *head = (*head)->next;
+       free(to_free);
+}
+
+struct conflict_entry *create_new_conflict(char *name, int len, int pathlen)
+{
+       struct conflict_entry *conflict_entry;
+
+       if (pathlen)
+               pathlen++;
+       conflict_entry = xmalloc(conflict_entry_size(len));
+       conflict_entry->entries = NULL;
+       conflict_entry->nfileconflicts = 0;
+       conflict_entry->namelen = len;
+       memcpy(conflict_entry->name, name, len);
+       conflict_entry->name[len] = '\0';
+       conflict_entry->pathlen = pathlen;
+       conflict_entry->next = NULL;
+
+       return conflict_entry;
+}
+
+void add_part_to_conflict_entry(struct directory_entry *de,
+                                       struct conflict_entry *entry,
+                                       struct conflict_part *conflict_part)
+{
+
+       struct conflict_part *conflict_search;
+
+       entry->nfileconflicts++;
+       de->conflict_size += sizeof(struct ondisk_conflict_part);
+       if (!entry->entries)
+               entry->entries = conflict_part;
+       else {
+               conflict_search = entry->entries;
+               while (conflict_search->next)
+                       conflict_search = conflict_search->next;
+               conflict_search->next = conflict_part;
+       }
+}
+
+static struct conflict_entry *read_conflicts_v5(struct directory_entry *de,
+                                               void **mmap,
+                                               unsigned long mmap_size,
+                                               int fd)
+{
+       struct conflict_entry *head, *tail;
+       unsigned int croffset, i, j = 0;
+       char *full_name;
+
+       croffset = de->de_cr;
+       tail = NULL;
+       head = NULL;
+       for (i = 0; i < de->de_ncr; i++) {
+               struct conflict_entry *conflict_new;
+               unsigned int len, *nfileconflicts;
+               char *name;
+               void *crc_start;
+               int k, offset, crc_wrong;
+               uint32_t *filecrc;
+
+               do {
+                       offset = croffset;
+                       crc_start = *mmap + offset;
+                       name = (char *)*mmap + offset;
+                       len = strlen(name);
+                       offset += len + 1;
+                       nfileconflicts = *mmap + offset;
+                       offset += 4;
+
+                       full_name = xmalloc(sizeof(char) * (len + 
de->de_pathlen));
+                       memcpy(full_name, de->pathname, de->de_pathlen);
+                       memcpy(full_name + de->de_pathlen, name, len);
+                       conflict_new = create_new_conflict(full_name,
+                                       len + de->de_pathlen, de->de_pathlen);
+                       for (k = 0; k < ntoh_l(*nfileconflicts); k++) {
+                               struct ondisk_conflict_part *ondisk;
+                               struct conflict_part *cp;
+
+                               ondisk = *mmap + offset;
+                               cp = conflict_part_from_ondisk(ondisk);
+                               cp->next = NULL;
+                               add_part_to_conflict_entry(de, conflict_new, 
cp);
+                               offset += sizeof(struct ondisk_conflict_part);
+                       }
+                       filecrc = *mmap + offset;
+                       crc_wrong = !check_crc32(0, crc_start,
+                               len + 1 + 4 + conflict_new->nfileconflicts
+                               * sizeof(struct ondisk_conflict_part),
+                               ntoh_l(*filecrc));
+                       if (crc_wrong) {
+                               /* wait for 10 milliseconds */
+                               usleep(10*1000);
+                               munmap(*mmap, mmap_size);
+                               *mmap = xmmap(NULL, mmap_size, PROT_READ | 
PROT_WRITE, MAP_PRIVATE, fd, 0);
+                       }
+                       free(full_name);
+                       j++;
+               } while (crc_wrong && j < 50);
+               if (crc_wrong)
+                       goto unmap;
+               croffset = offset + 4;
+               conflict_entry_push(&head, &tail, conflict_new);
+       }
+       return head;
+unmap:
+       munmap(*mmap, mmap_size);
+       die("wrong crc for conflict: %s", full_name);
+}
+
+static struct directory_entry *read_entries_v5(struct index_state *istate,
+                                       struct directory_entry *de,
+                                       unsigned long *entry_offset,
+                                       void **mmap,
+                                       unsigned long mmap_size,
+                                       int *nr,
+                                       unsigned int *foffsetblock,
+                                       int fd)
+{
+       struct cache_entry *head = NULL, *tail = NULL;
+       struct conflict_entry *conflict_queue;
+       struct cache_entry *ce;
+       int i;
+
+       conflict_queue = read_conflicts_v5(de, mmap, mmap_size, fd);
+       resolve_undo_convert_v5(istate, conflict_queue);
+       for (i = 0; i < de->de_nfiles; i++) {
+               ce = read_entry_v5(de,
+                               entry_offset,
+                               mmap,
+                               mmap_size,
+                               foffsetblock,
+                               fd);
+               ce_queue_push(&head, &tail, ce);
+               *foffsetblock += 4;
+
+               /* Add the conflicted entries at the end of the index file
+                * to the in memory format
+                */
+               if (conflict_queue &&
+                   (conflict_queue->entries->flags & CONFLICT_CONFLICTED) != 0 
&&
+                   !cache_name_compare(conflict_queue->name, 
conflict_queue->namelen,
+                                       ce->name, ce_namelen(ce))) {
+                       struct conflict_part *cp;
+                       cp = conflict_queue->entries;
+                       cp = cp->next;
+                       while (cp) {
+                               ce = convert_conflict_part(cp,
+                                               conflict_queue->name,
+                                               conflict_queue->namelen);
+                               ce_queue_push(&head, &tail, ce);
+                               conflict_part_head_remove(&cp);
+                       }
+                       conflict_entry_head_remove(&conflict_queue);
+               }
+       }
+
+       de = de->next;
+
+       while (head) {
+               if (de != NULL
+                   && strcmp(head->name, de->pathname) > 0) {
+                       de = read_entries_v5(istate,
+                                       de,
+                                       entry_offset,
+                                       mmap,
+                                       mmap_size,
+                                       nr,
+                                       foffsetblock,
+                                       fd);
+               } else {
+                       ce = ce_queue_pop(&head);
+                       set_index_entry(istate, *nr, ce);
+                       (*nr)++;
+               }
+       }
+
+       return de;
+}
+
+static void read_index_v5(struct index_state *istate, void *mmap, int 
mmap_size, int fd)
+{
+       unsigned long entry_offset;
+       unsigned int dir_offset, dir_table_offset;
+       struct cache_version_header *hdr;
+       struct cache_header_v5 *hdr_v5;
+       struct directory_entry *root_directory, *de;
+       int nr;
+       unsigned int foffsetblock;
+
+       hdr = mmap;
+       hdr_v5 = mmap + sizeof(*hdr);
+       istate->version = ntohl(hdr->hdr_version);
+       istate->cache_nr = ntohl(hdr_v5->hdr_nfile);
+       istate->cache_alloc = alloc_nr(istate->cache_nr);
+       istate->cache = xcalloc(istate->cache_alloc, sizeof(struct cache_entry 
*));
+       istate->initialized = 1;
+
+       /* Skip size of the header + crc sum + size of offsets */
+       dir_offset = sizeof(*hdr) + sizeof(*hdr_v5) + 4 + 
(ntohl(hdr_v5->hdr_ndir) + 1) * 4;
+       dir_table_offset = sizeof(*hdr) + sizeof(*hdr_v5) + 4;
+       root_directory = read_directories_v5(&dir_offset, &dir_table_offset, 
mmap, mmap_size);
+
+       entry_offset = ntohl(hdr_v5->hdr_fblockoffset);
+
+       nr = 0;
+       foffsetblock = dir_offset;
+       de = root_directory;
+       while (de)
+               de = read_entries_v5(istate, de, &entry_offset,
+                               &mmap, mmap_size, &nr, &foffsetblock, fd);
+       istate->cache_tree = cache_tree_convert_v5(root_directory);
+}
+
+#define WRITE_BUFFER_SIZE 8192
+static unsigned char write_buffer[WRITE_BUFFER_SIZE];
+static unsigned long write_buffer_len;
+
+static int ce_flush_v5(int fd)
+{
+       unsigned int left = write_buffer_len;
+
+       if (left)
+               write_buffer_len = 0;
+
+       if (write_in_full(fd, write_buffer, left) != left)
+               return -1;
+
+       return 0;
+}
+
+static void ce_smudge_racily_clean_entry_v5(struct cache_entry *ce)
+{
+       /*
+        * This method shall only be called if the timestamp of ce
+        * is racy (check with is_racy_timestamp). If the timestamp
+        * is racy, the writer will just set the time to 0.
+        *
+        * The reader (ce_match_stat_basic_v5) will then take care
+        * of checking if the entry is really changed or not, by
+        * taking into account the stat_crc and if that hasn't changed
+        * checking the sha1.
+        */
+       ce->ce_mtime.sec = 0;
+       ce->ce_mtime.nsec = 0;
+}
+
+static int ce_write_flush_v5(int fd)
+{
+       unsigned int buffered = write_buffer_len;
+       if (buffered) {
+               if (write_in_full(fd, write_buffer, buffered) != buffered)
+                       return -1;
+               write_buffer_len = 0;
+       }
+       return 0;
+}
+
+static int ce_write_v5(uint32_t *crc, int fd, void *data, unsigned int len)
+{
+       if (crc)
+               *crc = crc32(*crc, (Bytef*)data, len);
+       while (len) {
+               unsigned int buffered = write_buffer_len;
+               unsigned int partial = WRITE_BUFFER_SIZE - buffered;
+               if (partial > len)
+                       partial = len;
+               memcpy(write_buffer + buffered, data, partial);
+               buffered += partial;
+               if (buffered == WRITE_BUFFER_SIZE) {
+                       write_buffer_len = buffered;
+                       if (ce_write_flush_v5(fd))
+                               return -1;
+                       buffered = 0;
+               }
+               write_buffer_len = buffered;
+               len -= partial;
+               data = (char *) data + partial;
+       }
+       return 0;
+}
+
+char *super_directory(const char *filename)
+{
+       char *slash;
+
+       slash = strrchr(filename, '/');
+       if (slash)
+               return xmemdupz(filename, slash-filename);
+       return NULL;
+}
+
+struct directory_entry *init_directory_entry(char *pathname, int len)
+{
+       struct directory_entry *de = xmalloc(directory_entry_size(len));
+
+       memcpy(de->pathname, pathname, len);
+       de->pathname[len] = '\0';
+       de->de_flags      = 0;
+       de->de_foffset    = 0;
+       de->de_cr         = 0;
+       de->de_ncr        = 0;
+       de->de_nsubtrees  = 0;
+       de->de_nfiles     = 0;
+       de->de_nentries   = 0;
+       memset(de->sha1, 0, 20);
+       de->de_pathlen    = len;
+       de->next          = NULL;
+       de->next_hash     = NULL;
+       de->ce            = NULL;
+       de->ce_last       = NULL;
+       de->conflict      = NULL;
+       de->conflict_last = NULL;
+       de->conflict_size = 0;
+       return de;
+}
+
+static void ondisk_from_directory_entry(struct directory_entry *de,
+                                       struct ondisk_directory_entry *ondisk)
+{
+       ondisk->foffset   = htonl(de->de_foffset);
+       ondisk->cr        = htonl(de->de_cr);
+       ondisk->ncr       = htonl(de->de_ncr);
+       ondisk->nsubtrees = htonl(de->de_nsubtrees);
+       ondisk->nfiles    = htonl(de->de_nfiles);
+       ondisk->nentries  = htonl(de->de_nentries);
+       hashcpy(ondisk->sha1, de->sha1);
+       ondisk->flags     = htons(de->de_flags);
+}
+
+static struct conflict_part *conflict_part_from_inmemory(struct cache_entry 
*ce)
+{
+       struct conflict_part *conflict;
+       short flags;
+
+       conflict = xmalloc(sizeof(struct conflict_part));
+       flags                = CONFLICT_CONFLICTED;
+       flags               |= ce_stage(ce) << CONFLICT_STAGESHIFT;
+       conflict->flags      = flags;
+       conflict->entry_mode = ce->ce_mode;
+       conflict->next       = NULL;
+       hashcpy(conflict->sha1, ce->sha1);
+       return conflict;
+}
+
+static void conflict_to_ondisk(struct conflict_part *cp,
+                               struct ondisk_conflict_part *ondisk)
+{
+       ondisk->flags      = htons(cp->flags);
+       ondisk->entry_mode = htons(cp->entry_mode);
+       hashcpy(ondisk->sha1, cp->sha1);
+}
+
+void add_conflict_to_directory_entry(struct directory_entry *de,
+                                       struct conflict_entry *conflict_entry)
+{
+       de->de_ncr++;
+       de->conflict_size += conflict_entry->namelen + 1 + 8 - 
conflict_entry->pathlen;
+       conflict_entry_push(&de->conflict, &de->conflict_last, conflict_entry);
+}
+
+void insert_directory_entry(struct directory_entry *de,
+                       struct hash_table *table,
+                       int *total_dir_len,
+                       unsigned int *ndir,
+                       uint32_t crc)
+{
+       struct directory_entry *insert;
+
+       insert = (struct directory_entry *)insert_hash(crc, de, table);
+       if (insert) {
+               de->next_hash = insert->next_hash;
+               insert->next_hash = de;
+       }
+       (*ndir)++;
+       if (de->de_pathlen == 0)
+               (*total_dir_len)++;
+       else
+               *total_dir_len += de->de_pathlen + 2;
+}
+
+static struct conflict_entry *create_conflict_entry_from_ce(struct cache_entry 
*ce,
+                                                               int pathlen)
+{
+       return create_new_conflict(ce->name, ce_namelen(ce), pathlen);
+}
+
+static struct directory_entry *compile_directory_data(struct index_state 
*istate,
+                                               int nfile,
+                                               unsigned int *ndir,
+                                               int *non_conflicted,
+                                               int *total_dir_len,
+                                               int *total_file_len)
+{
+       int i, dir_len = -1;
+       char *dir;
+       struct directory_entry *de, *current, *search, *found, *new, 
*previous_entry;
+       struct cache_entry **cache = istate->cache;
+       struct conflict_entry *conflict_entry;
+       struct hash_table table;
+       uint32_t crc;
+
+       init_hash(&table);
+       de = init_directory_entry("", 0);
+       current = de;
+       *ndir = 1;
+       *total_dir_len = 1;
+       crc = crc32(0, (Bytef*)de->pathname, de->de_pathlen);
+       insert_hash(crc, de, &table);
+       conflict_entry = NULL;
+       for (i = 0; i < nfile; i++) {
+               int new_entry;
+               if (cache[i]->ce_flags & CE_REMOVE)
+                       continue;
+
+               new_entry = !ce_stage(cache[i]) || !conflict_entry
+                   || cache_name_compare(conflict_entry->name, 
conflict_entry->namelen,
+                                       cache[i]->name, ce_namelen(cache[i]));
+               if (new_entry)
+                       (*non_conflicted)++;
+               if (dir_len < 0 || strncmp(cache[i]->name, dir, dir_len)
+                   || cache[i]->name[dir_len] != '/'
+                   || strchr(cache[i]->name + dir_len + 1, '/')) {
+                       dir = super_directory(cache[i]->name);
+                       if (!dir)
+                               dir_len = 0;
+                       else
+                               dir_len = strlen(dir);
+                       crc = crc32(0, (Bytef*)dir, dir_len);
+                       found = lookup_hash(crc, &table);
+                       search = found;
+                       while (search && dir_len != 0 && strcmp(dir, 
search->pathname) != 0)
+                               search = search->next_hash;
+               }
+               previous_entry = current;
+               if (!search || !found) {
+                       new = init_directory_entry(dir, dir_len);
+                       current->next = new;
+                       current = current->next;
+                       insert_directory_entry(new, &table, total_dir_len, 
ndir, crc);
+                       search = current;
+               }
+               if (new_entry) {
+                       search->de_nfiles++;
+                       *total_file_len += ce_namelen(cache[i]) + 1;
+                       if (search->de_pathlen)
+                               *total_file_len -= search->de_pathlen + 1;
+                       ce_queue_push(&(search->ce), &(search->ce_last), 
cache[i]);
+               }
+               if (ce_stage(cache[i]) > 0) {
+                       struct conflict_part *conflict_part;
+                       if (new_entry) {
+                               conflict_entry = 
create_conflict_entry_from_ce(cache[i], search->de_pathlen);
+                               add_conflict_to_directory_entry(search, 
conflict_entry);
+                       }
+                       conflict_part = conflict_part_from_inmemory(cache[i]);
+                       add_part_to_conflict_entry(search, conflict_entry, 
conflict_part);
+               }
+               if (dir && !found) {
+                       struct directory_entry *no_subtrees;
+
+                       no_subtrees = current;
+                       dir = super_directory(dir);
+                       if (dir)
+                               dir_len = strlen(dir);
+                       else
+                               dir_len = 0;
+                       crc = crc32(0, (Bytef*)dir, dir_len);
+                       found = lookup_hash(crc, &table);
+                       while (!found) {
+                               new = init_directory_entry(dir, dir_len);
+                               new->de_nsubtrees = 1;
+                               new->next = no_subtrees;
+                               no_subtrees = new;
+                               insert_directory_entry(new, &table, 
total_dir_len, ndir, crc);
+                               dir = super_directory(dir);
+                               if (!dir)
+                                       dir_len = 0;
+                               else
+                                       dir_len = strlen(dir);
+                               crc = crc32(0, (Bytef*)dir, dir_len);
+                               found = lookup_hash(crc, &table);
+                       }
+                       search = found;
+                       while (search->next_hash && strcmp(dir, 
search->pathname) != 0)
+                               search = search->next_hash;
+                       if (search)
+                               found = search;
+                       found->de_nsubtrees++;
+                       previous_entry->next = no_subtrees;
+               }
+       }
+       if (istate->cache_tree)
+               cache_tree_to_ondisk_v5(&table, istate->cache_tree);
+       resolve_undo_to_ondisk_v5(&table, istate->resolve_undo, ndir, 
total_dir_len, de);
+       return de;
+}
+
+static void ondisk_from_cache_entry(struct cache_entry *ce,
+                                   struct ondisk_cache_entry_v5 *ondisk)
+{
+       unsigned int flags;
+
+       flags  = ce->ce_flags & CE_STAGEMASK;
+       flags |= ce->ce_flags & CE_VALID;
+       if (ce->ce_flags & CE_INTENT_TO_ADD)
+               flags |= CE_INTENT_TO_ADD_V5;
+       if (ce->ce_flags & CE_SKIP_WORKTREE)
+               flags |= CE_SKIP_WORKTREE_V5;
+       ondisk->flags      = htons(flags);
+       ondisk->mode       = htons(ce->ce_mode);
+       ondisk->mtime.sec  = htonl(ce->ce_mtime.sec);
+#ifdef USE_NSEC
+       ondisk->mtime.nsec = htonl(ce->ce_mtime.nsec);
+#else
+       ondisk->mtime.nsec = 0;
+#endif
+       if (!ce->ce_stat_crc)
+               ce->ce_stat_crc = calculate_stat_crc(ce);
+       ondisk->stat_crc   = htonl(ce->ce_stat_crc);
+       hashcpy(ondisk->sha1, ce->sha1);
+}
+
+static int write_directories_v5(struct directory_entry *de, int fd, int 
conflict_offset)
+{
+       struct directory_entry *current;
+       struct ondisk_directory_entry ondisk;
+       int current_offset, offset_write, ondisk_size, foffset;
+       uint32_t crc;
+
+       /*
+        * This is needed because the compiler aligns structs to sizes multipe
+        * of 4
+        */
+       ondisk_size = sizeof(ondisk.flags)
+               + sizeof(ondisk.foffset)
+               + sizeof(ondisk.cr)
+               + sizeof(ondisk.ncr)
+               + sizeof(ondisk.nsubtrees)
+               + sizeof(ondisk.nfiles)
+               + sizeof(ondisk.nentries)
+               + sizeof(ondisk.sha1);
+       current = de;
+       current_offset = 0;
+       foffset = 0;
+       while (current) {
+               int pathlen;
+
+               offset_write = htonl(current_offset);
+               if (ce_write_v5(NULL, fd, &offset_write, 4) < 0)
+                       return -1;
+               if (current->de_pathlen == 0)
+                       pathlen = 0;
+               else
+                       pathlen = current->de_pathlen + 1;
+               current_offset += pathlen + 1 + ondisk_size + 4;
+               current = current->next;
+       }
+       /*
+        * Write one more offset, which points to the end of the entries,
+        * because we use it for calculating the dir length, instead of
+        * using strlen.
+        */
+       offset_write = htonl(current_offset);
+       if (ce_write_v5(NULL, fd, &offset_write, 4) < 0)
+               return -1;
+       current = de;
+       while (current) {
+               crc = 0;
+               if (current->de_pathlen == 0) {
+                       if (ce_write_v5(&crc, fd, current->pathname, 1) < 0)
+                               return -1;
+               } else {
+                       char *path;
+                       path = xmalloc(sizeof(char) * (current->de_pathlen + 
2));
+                       memcpy(path, current->pathname, current->de_pathlen);
+                       memcpy(path + current->de_pathlen, "/\0", 2);
+                       if (ce_write_v5(&crc, fd, path, current->de_pathlen + 
2) < 0)
+                               return -1;
+               }
+               current->de_foffset = foffset;
+               current->de_cr = conflict_offset;
+               ondisk_from_directory_entry(current, &ondisk);
+               if (ce_write_v5(&crc, fd, &ondisk, ondisk_size) < 0)
+                       return -1;
+               crc = htonl(crc);
+               if (ce_write_v5(NULL, fd, &crc, 4) < 0)
+                       return -1;
+               conflict_offset += current->conflict_size;
+               foffset += current->de_nfiles * 4;
+               current = current->next;
+       }
+       return 0;
+}
+
+static int write_entries_v5(struct index_state *istate,
+                           struct directory_entry *de,
+                           int entries,
+                           int fd,
+                           int offset_to_offset)
+{
+       int offset, offset_write, ondisk_size;
+       struct directory_entry *current;
+
+       offset = 0;
+       ondisk_size = sizeof(struct ondisk_cache_entry_v5);
+       current = de;
+       while (current) {
+               int pathlen;
+               struct cache_entry *ce = current->ce;
+
+               if (current->de_pathlen == 0)
+                       pathlen = 0;
+               else
+                       pathlen = current->de_pathlen + 1;
+               while (ce) {
+                       if (ce->ce_flags & CE_REMOVE)
+                               continue;
+                       if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
+                               ce_smudge_racily_clean_entry_v5(ce);
+
+                       offset_write = htonl(offset);
+                       if (ce_write_v5(NULL, fd, &offset_write, 4) < 0)
+                               return -1;
+                       offset += ce_namelen(ce) - pathlen + 1 + ondisk_size + 
4;
+                       ce = ce->next;
+               }
+               current = current->next;
+       }
+       /*
+        * Write one more offset, which points to the end of the entries,
+        * because we use it for calculating the file length, instead of
+        * using strlen.
+        */
+       offset_write = htonl(offset);
+       if (ce_write_v5(NULL, fd, &offset_write, 4) < 0)
+               return -1;
+
+       offset = offset_to_offset;
+       current = de;
+       while (current) {
+               int pathlen;
+               struct cache_entry *ce = current->ce;
+
+               if (current->de_pathlen == 0)
+                       pathlen = 0;
+               else
+                       pathlen = current->de_pathlen + 1;
+               while (ce) {
+                       struct ondisk_cache_entry_v5 ondisk;
+                       uint32_t crc, calc_crc;
+
+                       if (ce->ce_flags & CE_REMOVE)
+                               continue;
+                       calc_crc = htonl(offset);
+                       crc = crc32(0, (Bytef*)&calc_crc, 4);
+                       if (ce_write_v5(&crc, fd, ce->name + pathlen,
+                                       ce_namelen(ce) - pathlen + 1) < 0)
+                               return -1;
+                       ondisk_from_cache_entry(ce, &ondisk);
+                       if (ce_write_v5(&crc, fd, &ondisk, ondisk_size) < 0)
+                               return -1;
+                       crc = htonl(crc);
+                       if (ce_write_v5(NULL, fd, &crc, 4) < 0)
+                               return -1;
+                       offset += 4;
+                       ce = ce->next;
+               }
+               current = current->next;
+       }
+       return 0;
+}
+
+static int write_conflict_v5(struct conflict_entry *conflict, int fd)
+{
+       struct conflict_entry *current;
+       struct conflict_part *current_part;
+       uint32_t crc;
+
+       current = conflict;
+       while (current) {
+               unsigned int to_write;
+
+               crc = 0;
+               if (ce_write_v5(&crc, fd,
+                    (Bytef*)(current->name + current->pathlen),
+                    current->namelen - current->pathlen) < 0)
+                       return -1;
+               if (ce_write_v5(&crc, fd, (Bytef*)"\0", 1) < 0)
+                       return -1;
+               to_write = htonl(current->nfileconflicts);
+               if (ce_write_v5(&crc, fd, (Bytef*)&to_write, 4) < 0)
+                       return -1;
+               current_part = current->entries;
+               while (current_part) {
+                       struct ondisk_conflict_part ondisk;
+
+                       conflict_to_ondisk(current_part, &ondisk);
+                       if (ce_write_v5(&crc, fd, (Bytef*)&ondisk, 
sizeof(struct ondisk_conflict_part)) < 0)
+                               return 0;
+                       current_part = current_part->next;
+               }
+               to_write = htonl(crc);
+               if (ce_write_v5(NULL, fd, (Bytef*)&to_write, 4) < 0)
+                       return -1;
+               current = current->next;
+       }
+       return 0;
+}
+
+static int write_conflicts_v5(struct index_state *istate,
+                             struct directory_entry *de,
+                             int fd)
+{
+       struct directory_entry *current;
+
+       current = de;
+       while (current) {
+               if (current->de_ncr != 0) {
+                       if (write_conflict_v5(current->conflict, fd) < 0)
+                               return -1;
+               }
+               current = current->next;
+       }
+       return 0;
+}
+
+static int write_index_v5(struct index_state *istate, int newfd)
+{
+       struct cache_version_header hdr;
+       struct cache_header_v5 hdr_v5;
+       struct cache_entry **cache = istate->cache;
+       struct directory_entry *de;
+       struct ondisk_directory_entry *ondisk;
+       int entries = istate->cache_nr;
+       int i, removed, non_conflicted, total_dir_len, ondisk_directory_size;
+       int total_file_len, conflict_offset, offset_to_offset;
+       unsigned int ndir;
+       uint32_t crc;
+
+       for (i = removed = 0; i < entries; i++) {
+               if (cache[i]->ce_flags & CE_REMOVE)
+                       removed++;
+       }
+       hdr.hdr_signature = htonl(CACHE_SIGNATURE);
+       hdr.hdr_version = htonl(istate->version);
+       hdr_v5.hdr_nfile = htonl(entries - removed);
+       hdr_v5.hdr_nextension = htonl(0); /* Currently no extensions are 
supported */
+
+       non_conflicted = 0;
+       total_dir_len = 0;
+       total_file_len = 0;
+       de = compile_directory_data(istate, entries, &ndir, &non_conflicted,
+                       &total_dir_len, &total_file_len);
+       hdr_v5.hdr_ndir = htonl(ndir);
+
+       /*
+        * This is needed because the compiler aligns structs to sizes multipe
+        * of 4
+        */
+       ondisk_directory_size = sizeof(ondisk->flags)
+               + sizeof(ondisk->foffset)
+               + sizeof(ondisk->cr)
+               + sizeof(ondisk->ncr)
+               + sizeof(ondisk->nsubtrees)
+               + sizeof(ondisk->nfiles)
+               + sizeof(ondisk->nentries)
+               + sizeof(ondisk->sha1);
+       hdr_v5.hdr_fblockoffset = htonl(sizeof(hdr) + sizeof(hdr_v5) + 4
+               + (ndir + 1) * 4
+               + total_dir_len
+               + ndir * (ondisk_directory_size + 4)
+               + (non_conflicted + 1) * 4);
+
+       crc = 0;
+       if (ce_write_v5(&crc, newfd, &hdr, sizeof(hdr)) < 0)
+               return -1;
+       if (ce_write_v5(&crc, newfd, &hdr_v5, sizeof(hdr_v5)) < 0)
+               return -1;
+       crc = htonl(crc);
+       if (ce_write_v5(NULL, newfd, &crc, 4) < 0)
+               return -1;
+
+       conflict_offset = sizeof(hdr) + sizeof(hdr_v5) + 4
+               + (ndir + 1) * 4
+               + total_dir_len
+               + ndir * (ondisk_directory_size + 4)
+               + (non_conflicted + 1) * 4
+               + total_file_len
+               + non_conflicted * (sizeof(struct ondisk_cache_entry_v5) + 4);
+       if (write_directories_v5(de, newfd, conflict_offset) < 0)
+               return -1;
+       offset_to_offset = sizeof(hdr) + sizeof(hdr_v5) + 4
+               + (ndir + 1) * 4
+               + total_dir_len
+               + ndir * (ondisk_directory_size + 4);
+       if (write_entries_v5(istate, de, entries, newfd, offset_to_offset) < 0)
+               return -1;
+       if (write_conflicts_v5(istate, de, newfd) < 0)
+               return -1;
+       return ce_flush_v5(newfd);
+}
+
+struct index_ops v5_ops = {
+       match_stat_basic,
+       verify_hdr,
+       read_index_v5,
+       write_index_v5
+};
diff --git a/read-cache.c b/read-cache.c
index 215c91f..61e2ea8 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -55,6 +55,31 @@ void rename_index_entry_at(struct index_state *istate, int 
nr, const char *new_n
        add_index_entry(istate, new, 
ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 }
 
+uint32_t calculate_stat_crc(struct cache_entry *ce)
+{
+       unsigned int ctimens = 0;
+       uint32_t stat, stat_crc;
+
+       stat = htonl(ce->ce_ctime.sec);
+       stat_crc = crc32(0, (Bytef*)&stat, 4);
+#ifdef USE_NSEC
+       ctimens = ce->ce_ctime.nsec;
+#endif
+       stat = htonl(ctimens);
+       stat_crc = crc32(stat_crc, (Bytef*)&stat, 4);
+       stat = htonl(ce->ce_ino);
+       stat_crc = crc32(stat_crc, (Bytef*)&stat, 4);
+       stat = htonl(ce->ce_size);
+       stat_crc = crc32(stat_crc, (Bytef*)&stat, 4);
+       stat = htonl(ce->ce_dev);
+       stat_crc = crc32(stat_crc, (Bytef*)&stat, 4);
+       stat = htonl(ce->ce_uid);
+       stat_crc = crc32(stat_crc, (Bytef*)&stat, 4);
+       stat = htonl(ce->ce_gid);
+       stat_crc = crc32(stat_crc, (Bytef*)&stat, 4);
+       return stat_crc;
+}
+
 /*
  * This only updates the "non-critical" parts of the directory
  * cache, ie the parts that aren't tracked by GIT, and only used
@@ -77,6 +102,8 @@ void fill_stat_cache_info(struct cache_entry *ce, struct 
stat *st)
 
        if (S_ISREG(st->st_mode))
                ce_mark_uptodate(ce);
+
+       ce->ce_stat_crc = calculate_stat_crc(ce);
 }
 
 static int ce_compare_data(struct cache_entry *ce, struct stat *st)
-- 
1.7.8

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to