Before we proceed to "opportunistic update" we must verify that the
current index file is the same as the one that we read before. There
is a possible race if we don't do this:

  1. process A calls git-rebase

  2. process A applies 1st commit

  3. process B calls git-status

  4. process B reads index

  5. process A applies 2nd commit

  6. process B takes the lock, then overwrites process A's changes.

  7. process A applies 3rd commit

As an end result the 3rd commit will have a revert of the 2nd commit.

Signed-off-by: Yiannis Marangos <yiannis.maran...@gmail.com>
---
 cache.h      |  3 +++
 read-cache.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 69 insertions(+), 11 deletions(-)

diff --git a/cache.h b/cache.h
index 107ac61..76ce3d9 100644
--- a/cache.h
+++ b/cache.h
@@ -279,6 +279,7 @@ struct index_state {
                 initialized : 1;
        struct hashmap name_hash;
        struct hashmap dir_hash;
+       unsigned char sha1[20];
 };
 
 extern struct index_state the_index;
@@ -456,6 +457,8 @@ extern int daemonize(void);
        } while (0)
 
 /* Initialize and use the cache information */
+extern int verify_index_from(const struct index_state *, const char *path);
+extern int verify_index(const struct index_state *);
 extern int read_index(struct index_state *);
 extern int read_index_preload(struct index_state *, const struct pathspec 
*pathspec);
 extern int read_index_from(struct index_state *, const char *path);
diff --git a/read-cache.c b/read-cache.c
index ba13353..3604d8c 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -14,6 +14,7 @@
 #include "resolve-undo.h"
 #include "strbuf.h"
 #include "varint.h"
+#include "git-compat-util.h"
 
 static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
                                               unsigned int options);
@@ -1321,6 +1322,59 @@ static int verify_hdr(struct cache_header *hdr, unsigned 
long size)
        return 0;
 }
 
+/* This function verifies if index_state has the correct sha1 of an index file.
+ * Don't die if we have any other failure, just return 0. */
+int verify_index_from(const struct index_state *istate, const char *path)
+{
+       int fd;
+       struct stat st;
+       struct cache_header *hdr;
+       void *mmap_addr;
+       size_t mmap_size;
+
+       if (!istate->initialized)
+               return 0;
+
+       fd = open(path, O_RDONLY);
+       if (fd < 0)
+               return 0;
+
+       if (fstat(fd, &st))
+               return 0;
+
+       /* file is too big */
+       if (st.st_size > (size_t)st.st_size)
+               return 0;
+
+       mmap_size = (size_t)st.st_size;
+       if (mmap_size < sizeof(struct cache_header) + 20)
+               return 0;
+
+       mmap_addr = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, 
fd, 0);
+       close(fd);
+       if (mmap_addr == MAP_FAILED)
+               return 0;
+
+       hdr = mmap_addr;
+       if (verify_hdr(hdr, mmap_size) < 0)
+               goto unmap;
+
+       if (hashcmp(istate->sha1, (unsigned char *)hdr + mmap_size - 20))
+               goto unmap;
+
+       munmap(mmap_addr, mmap_size);
+       return 1;
+
+unmap:
+       munmap(mmap_addr, mmap_size);
+       return 0;
+}
+
+int verify_index(const struct index_state *istate)
+{
+       return verify_index_from(istate, get_index_file());
+}
+
 static int read_index_extension(struct index_state *istate,
                                const char *ext, void *data, unsigned long sz)
 {
@@ -1445,7 +1499,7 @@ int read_index_from(struct index_state *istate, const 
char *path)
        struct stat st;
        unsigned long src_offset;
        struct cache_header *hdr;
-       void *mmap;
+       void *mmap_addr;
        size_t mmap_size;
        struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
 
@@ -1468,15 +1522,16 @@ int read_index_from(struct index_state *istate, const 
char *path)
        if (mmap_size < sizeof(struct cache_header) + 20)
                die("index file smaller than expected");
 
-       mmap = xmmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 
0);
-       if (mmap == MAP_FAILED)
+       mmap_addr = xmmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, 
fd, 0);
+       if (mmap_addr == MAP_FAILED)
                die_errno("unable to map index file");
        close(fd);
 
-       hdr = mmap;
+       hdr = mmap_addr;
        if (verify_hdr(hdr, mmap_size) < 0)
                goto unmap;
 
+       hashcpy(istate->sha1, (unsigned char *)hdr + mmap_size - 20);
        istate->version = ntohl(hdr->hdr_version);
        istate->cache_nr = ntohl(hdr->hdr_entries);
        istate->cache_alloc = alloc_nr(istate->cache_nr);
@@ -1494,7 +1549,7 @@ int read_index_from(struct index_state *istate, const 
char *path)
                struct cache_entry *ce;
                unsigned long consumed;
 
-               disk_ce = (struct ondisk_cache_entry *)((char *)mmap + 
src_offset);
+               disk_ce = (struct ondisk_cache_entry *)((char *)mmap_addr + 
src_offset);
                ce = create_from_disk(disk_ce, &consumed, previous_name);
                set_index_entry(istate, i, ce);
 
@@ -1512,21 +1567,21 @@ int read_index_from(struct index_state *istate, const 
char *path)
                 * in 4-byte network byte order.
                 */
                uint32_t extsize;
-               memcpy(&extsize, (char *)mmap + src_offset + 4, 4);
+               memcpy(&extsize, (char *)mmap_addr + src_offset + 4, 4);
                extsize = ntohl(extsize);
                if (read_index_extension(istate,
-                                        (const char *) mmap + src_offset,
-                                        (char *) mmap + src_offset + 8,
+                                        (const char *) mmap_addr + src_offset,
+                                        (char *) mmap_addr + src_offset + 8,
                                         extsize) < 0)
                        goto unmap;
                src_offset += 8;
                src_offset += extsize;
        }
-       munmap(mmap, mmap_size);
+       munmap(mmap_addr, mmap_size);
        return istate->cache_nr;
 
 unmap:
-       munmap(mmap, mmap_size);
+       munmap(mmap_addr, mmap_size);
        die("index file corrupt");
 }
 
@@ -1779,7 +1834,7 @@ static int has_racy_timestamp(struct index_state *istate)
 void update_index_if_able(struct index_state *istate, struct lock_file 
*lockfile)
 {
        if ((istate->cache_changed || has_racy_timestamp(istate)) &&
-           !write_index(istate, lockfile->fd))
+           verify_index(istate) && !write_index(istate, lockfile->fd))
                commit_locked_index(lockfile);
        else
                rollback_lock_file(lockfile);
-- 
1.9.1

--
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