413e09d48fa0aad26ef4863a47e1c75c08844d81
   1/*
   2 * GIT - The information manager from hell
   3 *
   4 * Copyright (C) Linus Torvalds, 2005
   5 */
   6#include "cache.h"
   7
   8static int cache_name_compare(const char *name1, int len1, const char *name2, int len2)
   9{
  10        int len = len1 < len2 ? len1 : len2;
  11        int cmp;
  12
  13        cmp = memcmp(name1, name2, len);
  14        if (cmp)
  15                return cmp;
  16        if (len1 < len2)
  17                return -1;
  18        if (len1 > len2)
  19                return 1;
  20        return 0;
  21}
  22
  23static int cache_name_pos(const char *name, int namelen)
  24{
  25        int first, last;
  26
  27        first = 0;
  28        last = active_nr;
  29        while (last > first) {
  30                int next = (last + first) >> 1;
  31                struct cache_entry *ce = active_cache[next];
  32                int cmp = cache_name_compare(name, namelen, ce->name, ce->namelen);
  33                if (!cmp)
  34                        return -next-1;
  35                if (cmp < 0) {
  36                        last = next;
  37                        continue;
  38                }
  39                first = next+1;
  40        }
  41        return first;
  42}
  43
  44static int remove_file_from_cache(char *path)
  45{
  46        int pos = cache_name_pos(path, strlen(path));
  47        if (pos < 0) {
  48                pos = -pos-1;
  49                active_nr--;
  50                if (pos < active_nr)
  51                        memmove(active_cache + pos, active_cache + pos + 1, (active_nr - pos - 1) * sizeof(struct cache_entry *));
  52        }
  53        return 0;
  54}
  55
  56static int add_cache_entry(struct cache_entry *ce)
  57{
  58        int pos;
  59
  60        pos = cache_name_pos(ce->name, ce->namelen);
  61
  62        /* existing match? Just replace it */
  63        if (pos < 0) {
  64                active_cache[-pos-1] = ce;
  65                return 0;
  66        }
  67
  68        /* Make sure the array is big enough .. */
  69        if (active_nr == active_alloc) {
  70                active_alloc = alloc_nr(active_alloc);
  71                active_cache = realloc(active_cache, active_alloc * sizeof(struct cache_entry *));
  72        }
  73
  74        /* Add it in.. */
  75        active_nr++;
  76        if (active_nr > pos)
  77                memmove(active_cache + pos + 1, active_cache + pos, (active_nr - pos - 1) * sizeof(ce));
  78        active_cache[pos] = ce;
  79        return 0;
  80}
  81
  82static int index_fd(const char *path, int namelen, struct cache_entry *ce, int fd, struct stat *st)
  83{
  84        z_stream stream;
  85        int max_out_bytes = namelen + st->st_size + 200;
  86        void *out = malloc(max_out_bytes);
  87        void *metadata = malloc(namelen + 200);
  88        void *in = mmap(NULL, st->st_size, PROT_READ, MAP_PRIVATE, fd, 0);
  89        SHA_CTX c;
  90
  91        close(fd);
  92        if (!out || (int)(long)in == -1)
  93                return -1;
  94
  95        memset(&stream, 0, sizeof(stream));
  96        deflateInit(&stream, Z_BEST_COMPRESSION);
  97
  98        /*
  99         * ASCII size + nul byte
 100         */     
 101        stream.next_in = metadata;
 102        stream.avail_in = 1+sprintf(metadata, "blob %lu", (unsigned long) st->st_size);
 103        stream.next_out = out;
 104        stream.avail_out = max_out_bytes;
 105        while (deflate(&stream, 0) == Z_OK)
 106                /* nothing */;
 107
 108        /*
 109         * File content
 110         */
 111        stream.next_in = in;
 112        stream.avail_in = st->st_size;
 113        while (deflate(&stream, Z_FINISH) == Z_OK)
 114                /*nothing */;
 115
 116        deflateEnd(&stream);
 117        
 118        SHA1_Init(&c);
 119        SHA1_Update(&c, out, stream.total_out);
 120        SHA1_Final(ce->sha1, &c);
 121
 122        return write_sha1_buffer(ce->sha1, out, stream.total_out);
 123}
 124
 125static int add_file_to_cache(char *path)
 126{
 127        int size, namelen;
 128        struct cache_entry *ce;
 129        struct stat st;
 130        int fd;
 131
 132        fd = open(path, O_RDONLY);
 133        if (fd < 0) {
 134                if (errno == ENOENT)
 135                        return remove_file_from_cache(path);
 136                return -1;
 137        }
 138        if (fstat(fd, &st) < 0) {
 139                close(fd);
 140                return -1;
 141        }
 142        namelen = strlen(path);
 143        size = cache_entry_size(namelen);
 144        ce = malloc(size);
 145        memset(ce, 0, size);
 146        memcpy(ce->name, path, namelen);
 147        ce->ctime.sec = st.st_ctime;
 148        ce->ctime.nsec = st.st_ctim.tv_nsec;
 149        ce->mtime.sec = st.st_mtime;
 150        ce->mtime.nsec = st.st_mtim.tv_nsec;
 151        ce->st_dev = st.st_dev;
 152        ce->st_ino = st.st_ino;
 153        ce->st_mode = st.st_mode;
 154        ce->st_uid = st.st_uid;
 155        ce->st_gid = st.st_gid;
 156        ce->st_size = st.st_size;
 157        ce->namelen = namelen;
 158
 159        if (index_fd(path, namelen, ce, fd, &st) < 0)
 160                return -1;
 161
 162        return add_cache_entry(ce);
 163}
 164
 165static int write_cache(int newfd, struct cache_entry **cache, int entries)
 166{
 167        SHA_CTX c;
 168        struct cache_header hdr;
 169        int i;
 170
 171        hdr.signature = CACHE_SIGNATURE;
 172        hdr.version = 1;
 173        hdr.entries = entries;
 174
 175        SHA1_Init(&c);
 176        SHA1_Update(&c, &hdr, offsetof(struct cache_header, sha1));
 177        for (i = 0; i < entries; i++) {
 178                struct cache_entry *ce = cache[i];
 179                int size = ce_size(ce);
 180                SHA1_Update(&c, ce, size);
 181        }
 182        SHA1_Final(hdr.sha1, &c);
 183
 184        if (write(newfd, &hdr, sizeof(hdr)) != sizeof(hdr))
 185                return -1;
 186
 187        for (i = 0; i < entries; i++) {
 188                struct cache_entry *ce = cache[i];
 189                int size = ce_size(ce);
 190                if (write(newfd, ce, size) != size)
 191                        return -1;
 192        }
 193        return 0;
 194}               
 195
 196/*
 197 * We fundamentally don't like some paths: we don't want
 198 * dot or dot-dot anywhere, and in fact, we don't even want
 199 * any other dot-files (.dircache or anything else). They
 200 * are hidden, for chist sake.
 201 *
 202 * Also, we don't want double slashes or slashes at the
 203 * end that can make pathnames ambiguous. 
 204 */
 205static int verify_path(char *path)
 206{
 207        char c;
 208
 209        goto inside;
 210        for (;;) {
 211                if (!c)
 212                        return 1;
 213                if (c == '/') {
 214inside:
 215                        c = *path++;
 216                        if (c != '/' && c != '.' && c != '\0')
 217                                continue;
 218                        return 0;
 219                }
 220                c = *path++;
 221        }
 222}
 223
 224int main(int argc, char **argv)
 225{
 226        int i, newfd, entries;
 227
 228        entries = read_cache();
 229        if (entries < 0) {
 230                perror("cache corrupted");
 231                return -1;
 232        }
 233
 234        newfd = open(".dircache/index.lock", O_RDWR | O_CREAT | O_EXCL, 0600);
 235        if (newfd < 0) {
 236                perror("unable to create new cachefile");
 237                return -1;
 238        }
 239        for (i = 1 ; i < argc; i++) {
 240                char *path = argv[i];
 241                if (!verify_path(path)) {
 242                        fprintf(stderr, "Ignoring path %s\n", argv[i]);
 243                        continue;
 244                }
 245                if (add_file_to_cache(path)) {
 246                        fprintf(stderr, "Unable to add %s to database\n", path);
 247                        goto out;
 248                }
 249        }
 250        if (!write_cache(newfd, active_cache, active_nr) && !rename(".dircache/index.lock", ".dircache/index"))
 251                return 0;
 252out:
 253        unlink(".dircache/index.lock");
 254        return 0;
 255}