untracked cache: load from UNTR index extension
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>
Sun, 8 Mar 2015 10:12:34 +0000 (17:12 +0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 12 Mar 2015 20:45:16 +0000 (13:45 -0700)
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
dir.c
dir.h
read-cache.c

diff --git a/dir.c b/dir.c
index f695e0c..b39a034 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -2283,3 +2283,222 @@ void write_untracked_extension(struct strbuf *out, struct untracked_cache *untra
        strbuf_release(&wd.sb_stat);
        strbuf_release(&wd.sb_sha1);
 }
+
+static void free_untracked(struct untracked_cache_dir *ucd)
+{
+       int i;
+       if (!ucd)
+               return;
+       for (i = 0; i < ucd->dirs_nr; i++)
+               free_untracked(ucd->dirs[i]);
+       for (i = 0; i < ucd->untracked_nr; i++)
+               free(ucd->untracked[i]);
+       free(ucd->untracked);
+       free(ucd->dirs);
+       free(ucd);
+}
+
+void free_untracked_cache(struct untracked_cache *uc)
+{
+       if (uc)
+               free_untracked(uc->root);
+       free(uc);
+}
+
+struct read_data {
+       int index;
+       struct untracked_cache_dir **ucd;
+       struct ewah_bitmap *check_only;
+       struct ewah_bitmap *valid;
+       struct ewah_bitmap *sha1_valid;
+       const unsigned char *data;
+       const unsigned char *end;
+};
+
+static void stat_data_from_disk(struct stat_data *to, const struct stat_data *from)
+{
+       to->sd_ctime.sec  = get_be32(&from->sd_ctime.sec);
+       to->sd_ctime.nsec = get_be32(&from->sd_ctime.nsec);
+       to->sd_mtime.sec  = get_be32(&from->sd_mtime.sec);
+       to->sd_mtime.nsec = get_be32(&from->sd_mtime.nsec);
+       to->sd_dev        = get_be32(&from->sd_dev);
+       to->sd_ino        = get_be32(&from->sd_ino);
+       to->sd_uid        = get_be32(&from->sd_uid);
+       to->sd_gid        = get_be32(&from->sd_gid);
+       to->sd_size       = get_be32(&from->sd_size);
+}
+
+static int read_one_dir(struct untracked_cache_dir **untracked_,
+                       struct read_data *rd)
+{
+       struct untracked_cache_dir ud, *untracked;
+       const unsigned char *next, *data = rd->data, *end = rd->end;
+       unsigned int value;
+       int i, len;
+
+       memset(&ud, 0, sizeof(ud));
+
+       next = data;
+       value = decode_varint(&next);
+       if (next > end)
+               return -1;
+       ud.recurse         = 1;
+       ud.untracked_alloc = value;
+       ud.untracked_nr    = value;
+       if (ud.untracked_nr)
+               ud.untracked = xmalloc(sizeof(*ud.untracked) * ud.untracked_nr);
+       data = next;
+
+       next = data;
+       ud.dirs_alloc = ud.dirs_nr = decode_varint(&next);
+       if (next > end)
+               return -1;
+       ud.dirs = xmalloc(sizeof(*ud.dirs) * ud.dirs_nr);
+       data = next;
+
+       len = strlen((const char *)data);
+       next = data + len + 1;
+       if (next > rd->end)
+               return -1;
+       *untracked_ = untracked = xmalloc(sizeof(*untracked) + len);
+       memcpy(untracked, &ud, sizeof(ud));
+       memcpy(untracked->name, data, len + 1);
+       data = next;
+
+       for (i = 0; i < untracked->untracked_nr; i++) {
+               len = strlen((const char *)data);
+               next = data + len + 1;
+               if (next > rd->end)
+                       return -1;
+               untracked->untracked[i] = xstrdup((const char*)data);
+               data = next;
+       }
+
+       rd->ucd[rd->index++] = untracked;
+       rd->data = data;
+
+       for (i = 0; i < untracked->dirs_nr; i++) {
+               len = read_one_dir(untracked->dirs + i, rd);
+               if (len < 0)
+                       return -1;
+       }
+       return 0;
+}
+
+static void set_check_only(size_t pos, void *cb)
+{
+       struct read_data *rd = cb;
+       struct untracked_cache_dir *ud = rd->ucd[pos];
+       ud->check_only = 1;
+}
+
+static void read_stat(size_t pos, void *cb)
+{
+       struct read_data *rd = cb;
+       struct untracked_cache_dir *ud = rd->ucd[pos];
+       if (rd->data + sizeof(struct stat_data) > rd->end) {
+               rd->data = rd->end + 1;
+               return;
+       }
+       stat_data_from_disk(&ud->stat_data, (struct stat_data *)rd->data);
+       rd->data += sizeof(struct stat_data);
+       ud->valid = 1;
+}
+
+static void read_sha1(size_t pos, void *cb)
+{
+       struct read_data *rd = cb;
+       struct untracked_cache_dir *ud = rd->ucd[pos];
+       if (rd->data + 20 > rd->end) {
+               rd->data = rd->end + 1;
+               return;
+       }
+       hashcpy(ud->exclude_sha1, rd->data);
+       rd->data += 20;
+}
+
+static void load_sha1_stat(struct sha1_stat *sha1_stat,
+                          const struct stat_data *stat,
+                          const unsigned char *sha1)
+{
+       stat_data_from_disk(&sha1_stat->stat, stat);
+       hashcpy(sha1_stat->sha1, sha1);
+       sha1_stat->valid = 1;
+}
+
+struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz)
+{
+       const struct ondisk_untracked_cache *ouc;
+       struct untracked_cache *uc;
+       struct read_data rd;
+       const unsigned char *next = data, *end = (const unsigned char *)data + sz;
+       int len;
+
+       if (sz <= 1 || end[-1] != '\0')
+               return NULL;
+       end--;
+
+       ouc = (const struct ondisk_untracked_cache *)next;
+       if (next + ouc_size(0) > end)
+               return NULL;
+
+       uc = xcalloc(1, sizeof(*uc));
+       load_sha1_stat(&uc->ss_info_exclude, &ouc->info_exclude_stat,
+                      ouc->info_exclude_sha1);
+       load_sha1_stat(&uc->ss_excludes_file, &ouc->excludes_file_stat,
+                      ouc->excludes_file_sha1);
+       uc->dir_flags = get_be32(&ouc->dir_flags);
+       uc->exclude_per_dir = xstrdup(ouc->exclude_per_dir);
+       /* NUL after exclude_per_dir is covered by sizeof(*ouc) */
+       next += ouc_size(strlen(ouc->exclude_per_dir));
+       if (next >= end)
+               goto done2;
+
+       len = decode_varint(&next);
+       if (next > end || len == 0)
+               goto done2;
+
+       rd.valid      = ewah_new();
+       rd.check_only = ewah_new();
+       rd.sha1_valid = ewah_new();
+       rd.data       = next;
+       rd.end        = end;
+       rd.index      = 0;
+       rd.ucd        = xmalloc(sizeof(*rd.ucd) * len);
+
+       if (read_one_dir(&uc->root, &rd) || rd.index != len)
+               goto done;
+
+       next = rd.data;
+       len = ewah_read_mmap(rd.valid, next, end - next);
+       if (len < 0)
+               goto done;
+
+       next += len;
+       len = ewah_read_mmap(rd.check_only, next, end - next);
+       if (len < 0)
+               goto done;
+
+       next += len;
+       len = ewah_read_mmap(rd.sha1_valid, next, end - next);
+       if (len < 0)
+               goto done;
+
+       ewah_each_bit(rd.check_only, set_check_only, &rd);
+       rd.data = next + len;
+       ewah_each_bit(rd.valid, read_stat, &rd);
+       ewah_each_bit(rd.sha1_valid, read_sha1, &rd);
+       next = rd.data;
+
+done:
+       free(rd.ucd);
+       ewah_free(rd.valid);
+       ewah_free(rd.check_only);
+       ewah_free(rd.sha1_valid);
+done2:
+       if (next != end) {
+               free_untracked_cache(uc);
+               uc = NULL;
+       }
+       return uc;
+}
diff --git a/dir.h b/dir.h
index dc3ee0b..40a679a 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -298,5 +298,7 @@ static inline int dir_path_match(const struct dir_entry *ent,
                              has_trailing_dir);
 }
 
+void free_untracked_cache(struct untracked_cache *);
+struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz);
 void write_untracked_extension(struct strbuf *out, struct untracked_cache *untracked);
 #endif
index 3a058d0..ee0ef04 100644 (file)
@@ -1371,6 +1371,9 @@ static int read_index_extension(struct index_state *istate,
                if (read_link_extension(istate, data, sz))
                        return -1;
                break;
+       case CACHE_EXT_UNTRACKED:
+               istate->untracked = read_untracked_extension(data, sz);
+               break;
        default:
                if (*ext < 'A' || 'Z' < *ext)
                        return error("index uses %.4s extension, which we do not understand",
@@ -1662,6 +1665,8 @@ int discard_index(struct index_state *istate)
        istate->cache = NULL;
        istate->cache_alloc = 0;
        discard_split_index(istate);
+       free_untracked_cache(istate->untracked);
+       istate->untracked = NULL;
        return 0;
 }