| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| #include "cache.h" | ||
|
|
||
| #include <pwd.h> | ||
| #include <time.h> | ||
|
|
||
| #define BLOCKING (1ul << 14) | ||
| #define ORIG_OFFSET (40) | ||
|
|
||
| /* | ||
| * Leave space at the beginning to insert the tag | ||
| * once we know how big things are. | ||
| * | ||
| * FIXME! Share the code with "write-tree.c" | ||
| */ | ||
| static void init_buffer(char **bufp, unsigned int *sizep) | ||
| { | ||
| char *buf = malloc(BLOCKING); | ||
| memset(buf, 0, ORIG_OFFSET); | ||
| *sizep = ORIG_OFFSET; | ||
| *bufp = buf; | ||
| } | ||
|
|
||
| static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...) | ||
| { | ||
| char one_line[2048]; | ||
| va_list args; | ||
| int len; | ||
| unsigned long alloc, size, newsize; | ||
| char *buf; | ||
|
|
||
| va_start(args, fmt); | ||
| len = vsnprintf(one_line, sizeof(one_line), fmt, args); | ||
| va_end(args); | ||
| size = *sizep; | ||
| newsize = size + len; | ||
| alloc = (size + 32767) & ~32767; | ||
| buf = *bufp; | ||
| if (newsize > alloc) { | ||
| alloc = (newsize + 32767) & ~32767; | ||
| buf = realloc(buf, alloc); | ||
| *bufp = buf; | ||
| } | ||
| *sizep = newsize; | ||
| memcpy(buf + size, one_line, len); | ||
| } | ||
|
|
||
| static int prepend_integer(char *buffer, unsigned val, int i) | ||
| { | ||
| buffer[--i] = '\0'; | ||
| do { | ||
| buffer[--i] = '0' + (val % 10); | ||
| val /= 10; | ||
| } while (val); | ||
| return i; | ||
| } | ||
|
|
||
| static void finish_buffer(char *tag, char **bufp, unsigned int *sizep) | ||
| { | ||
| int taglen; | ||
| int offset; | ||
| char *buf = *bufp; | ||
| unsigned int size = *sizep; | ||
|
|
||
| offset = prepend_integer(buf, size - ORIG_OFFSET, ORIG_OFFSET); | ||
| taglen = strlen(tag); | ||
| offset -= taglen; | ||
| buf += offset; | ||
| size -= offset; | ||
| memcpy(buf, tag, taglen); | ||
|
|
||
| *bufp = buf; | ||
| *sizep = size; | ||
| } | ||
|
|
||
| static void remove_special(char *p) | ||
| { | ||
| char c; | ||
| char *dst = p; | ||
|
|
||
| for (;;) { | ||
| c = *p; | ||
| p++; | ||
| switch(c) { | ||
| case '\n': case '<': case '>': | ||
| continue; | ||
| } | ||
| *dst++ = c; | ||
| if (!c) | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| /* | ||
| * Having more than two parents may be strange, but hey, there's | ||
| * no conceptual reason why the file format couldn't accept multi-way | ||
| * merges. It might be the "union" of several packages, for example. | ||
| * | ||
| * I don't really expect that to happen, but this is here to make | ||
| * it clear that _conceptually_ it's ok.. | ||
| */ | ||
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
Sorry, something went wrong. |
||
| #define MAXPARENT (16) | ||
|
|
||
| int main(int argc, char **argv) | ||
| { | ||
| int i, len; | ||
| int parents = 0; | ||
| unsigned char tree_sha1[20]; | ||
| unsigned char parent_sha1[MAXPARENT][20]; | ||
| char *gecos, *realgecos; | ||
| char *email, realemail[1000]; | ||
| char *date, *realdate; | ||
| char comment[1000]; | ||
| struct passwd *pw; | ||
| time_t now; | ||
| char *buffer; | ||
| unsigned int size; | ||
|
|
||
| if (argc < 2 || get_sha1_hex(argv[1], tree_sha1) < 0) | ||
| usage("commit-tree <sha1> [-p <sha1>]* < changelog"); | ||
|
|
||
| for (i = 2; i < argc; i += 2) { | ||
| char *a, *b; | ||
| a = argv[i]; b = argv[i+1]; | ||
| if (!b || strcmp(a, "-p") || get_sha1_hex(b, parent_sha1[parents])) | ||
| usage("commit-tree <sha1> [-p <sha1>]* < changelog"); | ||
| parents++; | ||
| } | ||
| if (!parents) | ||
| fprintf(stderr, "Committing initial tree %s\n", argv[1]); | ||
| pw = getpwuid(getuid()); | ||
| if (!pw) | ||
| usage("You don't exist. Go away!"); | ||
| realgecos = pw->pw_gecos; | ||
| len = strlen(pw->pw_name); | ||
| memcpy(realemail, pw->pw_name, len); | ||
| realemail[len] = '@'; | ||
| gethostname(realemail+len+1, sizeof(realemail)-len-1); | ||
| time(&now); | ||
| realdate = ctime(&now); | ||
|
|
||
| gecos = getenv("COMMITTER_NAME") ? : realgecos; | ||
| email = getenv("COMMITTER_EMAIL") ? : realemail; | ||
| date = getenv("COMMITTER_DATE") ? : realdate; | ||
|
|
||
| remove_special(gecos); remove_special(realgecos); | ||
| remove_special(email); remove_special(realemail); | ||
| remove_special(date); remove_special(realdate); | ||
|
|
||
| init_buffer(&buffer, &size); | ||
| add_buffer(&buffer, &size, "tree %s\n", sha1_to_hex(tree_sha1)); | ||
|
|
||
| /* | ||
| * NOTE! This ordering means that the same exact tree merged with a | ||
| * different order of parents will be a _different_ changeset even | ||
| * if everything else stays the same. | ||
| */ | ||
| for (i = 0; i < parents; i++) | ||
| add_buffer(&buffer, &size, "parent %s\n", sha1_to_hex(parent_sha1[i])); | ||
|
|
||
| /* Person/date information */ | ||
| add_buffer(&buffer, &size, "author %s <%s> %s\n", gecos, email, date); | ||
| add_buffer(&buffer, &size, "committer %s <%s> %s\n\n", realgecos, realemail, realdate); | ||
|
|
||
| /* And add the comment */ | ||
| while (fgets(comment, sizeof(comment), stdin) != NULL) | ||
| add_buffer(&buffer, &size, "%s", comment); | ||
|
|
||
| finish_buffer("commit ", &buffer, &size); | ||
|
|
||
| write_sha1_file(buffer, size); | ||
| return 0; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| #include "cache.h" | ||
|
|
||
| int main(int argc, char **argv) | ||
| { | ||
| char *sha1_dir = getenv(DB_ENVIRONMENT), *path; | ||
| int len, i, fd; | ||
|
|
||
| if (mkdir(".dircache", 0700) < 0) { | ||
| perror("unable to create .dircache"); | ||
| exit(1); | ||
| } | ||
|
|
||
| /* | ||
| * If you want to, you can share the DB area with any number of branches. | ||
| * That has advantages: you can save space by sharing all the SHA1 objects. | ||
| * On the other hand, it might just make lookup slower and messier. You | ||
| * be the judge. | ||
| */ | ||
| sha1_dir = getenv(DB_ENVIRONMENT); | ||
| if (sha1_dir) { | ||
| struct stat st; | ||
| if (!stat(sha1_dir, &st) < 0 && S_ISDIR(st.st_mode)) | ||
| return; | ||
| fprintf(stderr, "DB_ENVIRONMENT set to bad directory %s: ", sha1_dir); | ||
| } | ||
|
|
||
| /* | ||
| * The default case is to have a DB per managed directory. | ||
| */ | ||
| sha1_dir = DEFAULT_DB_ENVIRONMENT; | ||
| fprintf(stderr, "defaulting to private storage area\n"); | ||
| len = strlen(sha1_dir); | ||
| if (mkdir(sha1_dir, 0700) < 0) { | ||
| if (errno != EEXIST) { | ||
| perror(sha1_dir); | ||
| exit(1); | ||
| } | ||
| } | ||
| path = malloc(len + 40); | ||
| memcpy(path, sha1_dir, len); | ||
| for (i = 0; i < 256; i++) { | ||
| sprintf(path+len, "/%02x", i); | ||
| if (mkdir(path, 0700) < 0) { | ||
| if (errno != EEXIST) { | ||
| perror(path); | ||
| exit(1); | ||
| } | ||
| } | ||
| } | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,259 @@ | ||
| #include "cache.h" | ||
|
|
||
| const char *sha1_file_directory = NULL; | ||
| struct cache_entry **active_cache = NULL; | ||
| unsigned int active_nr = 0, active_alloc = 0; | ||
|
|
||
| void usage(const char *err) | ||
| { | ||
| fprintf(stderr, "read-tree: %s\n", err); | ||
| exit(1); | ||
| } | ||
|
|
||
| static unsigned hexval(char c) | ||
| { | ||
| if (c >= '0' && c <= '9') | ||
| return c - '0'; | ||
| if (c >= 'a' && c <= 'f') | ||
| return c - 'a' + 10; | ||
| if (c >= 'A' && c <= 'F') | ||
| return c - 'A' + 10; | ||
| return ~0; | ||
| } | ||
|
|
||
| int get_sha1_hex(char *hex, unsigned char *sha1) | ||
| { | ||
| int i; | ||
| for (i = 0; i < 20; i++) { | ||
| unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]); | ||
| if (val & ~0xff) | ||
| return -1; | ||
| *sha1++ = val; | ||
| hex += 2; | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| char * sha1_to_hex(unsigned char *sha1) | ||
| { | ||
| static char buffer[50]; | ||
| static const char hex[] = "0123456789abcdef"; | ||
| char *buf = buffer; | ||
| int i; | ||
|
|
||
| for (i = 0; i < 20; i++) { | ||
| unsigned int val = *sha1++; | ||
| *buf++ = hex[val >> 4]; | ||
| *buf++ = hex[val & 0xf]; | ||
| } | ||
| return buffer; | ||
| } | ||
|
|
||
| /* | ||
| * NOTE! This returns a statically allocated buffer, so you have to be | ||
| * careful about using it. Do a "strdup()" if you need to save the | ||
| * filename. | ||
| */ | ||
| char *sha1_file_name(unsigned char *sha1) | ||
| { | ||
| int i; | ||
| static char *name, *base; | ||
|
|
||
| if (!base) { | ||
| char *sha1_file_directory = getenv(DB_ENVIRONMENT) ? : DEFAULT_DB_ENVIRONMENT; | ||
| int len = strlen(sha1_file_directory); | ||
| base = malloc(len + 60); | ||
| memcpy(base, sha1_file_directory, len); | ||
| memset(base+len, 0, 60); | ||
| base[len] = '/'; | ||
| base[len+3] = '/'; | ||
| name = base + len + 1; | ||
| } | ||
| for (i = 0; i < 20; i++) { | ||
| static char hex[] = "0123456789abcdef"; | ||
| unsigned int val = sha1[i]; | ||
| char *pos = name + i*2 + (i > 0); | ||
| *pos++ = hex[val >> 4]; | ||
| *pos = hex[val & 0xf]; | ||
| } | ||
| return base; | ||
| } | ||
|
|
||
| void * read_sha1_file(unsigned char *sha1, char *type, unsigned long *size) | ||
| { | ||
| z_stream stream; | ||
| char buffer[8192]; | ||
| struct stat st; | ||
| int i, fd, ret, bytes; | ||
| void *map, *buf; | ||
| char *filename = sha1_file_name(sha1); | ||
|
|
||
| fd = open(filename, O_RDONLY); | ||
| if (fd < 0) { | ||
| perror(filename); | ||
| return NULL; | ||
| } | ||
| if (fstat(fd, &st) < 0) { | ||
| close(fd); | ||
| return NULL; | ||
| } | ||
| map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); | ||
| close(fd); | ||
| if (-1 == (int)(long)map) | ||
| return NULL; | ||
|
|
||
| /* Get the data stream */ | ||
| memset(&stream, 0, sizeof(stream)); | ||
| stream.next_in = map; | ||
| stream.avail_in = st.st_size; | ||
| stream.next_out = buffer; | ||
| stream.avail_out = sizeof(buffer); | ||
|
|
||
| inflateInit(&stream); | ||
| ret = inflate(&stream, 0); | ||
| if (sscanf(buffer, "%10s %lu", type, size) != 2) | ||
| return NULL; | ||
| bytes = strlen(buffer) + 1; | ||
| buf = malloc(*size); | ||
| if (!buf) | ||
| return NULL; | ||
|
|
||
| memcpy(buf, buffer + bytes, stream.total_out - bytes); | ||
| bytes = stream.total_out - bytes; | ||
| if (bytes < *size && ret == Z_OK) { | ||
| stream.next_out = buf + bytes; | ||
| stream.avail_out = *size - bytes; | ||
| while (inflate(&stream, Z_FINISH) == Z_OK) | ||
| /* nothing */; | ||
| } | ||
| inflateEnd(&stream); | ||
| return buf; | ||
| } | ||
|
|
||
| int write_sha1_file(char *buf, unsigned len) | ||
| { | ||
| int size; | ||
| char *compressed; | ||
| z_stream stream; | ||
| unsigned char sha1[20]; | ||
| SHA_CTX c; | ||
|
|
||
| /* Set it up */ | ||
| memset(&stream, 0, sizeof(stream)); | ||
| deflateInit(&stream, Z_BEST_COMPRESSION); | ||
| size = deflateBound(&stream, len); | ||
| compressed = malloc(size); | ||
|
|
||
| /* Compress it */ | ||
| stream.next_in = buf; | ||
| stream.avail_in = len; | ||
| stream.next_out = compressed; | ||
| stream.avail_out = size; | ||
| while (deflate(&stream, Z_FINISH) == Z_OK) | ||
| /* nothing */; | ||
| deflateEnd(&stream); | ||
| size = stream.total_out; | ||
|
|
||
| /* Sha1.. */ | ||
| SHA1_Init(&c); | ||
| SHA1_Update(&c, compressed, size); | ||
| SHA1_Final(sha1, &c); | ||
|
|
||
| if (write_sha1_buffer(sha1, compressed, size) < 0) | ||
| return -1; | ||
| printf("%s\n", sha1_to_hex(sha1)); | ||
| return 0; | ||
| } | ||
|
|
||
| int write_sha1_buffer(unsigned char *sha1, void *buf, unsigned int size) | ||
| { | ||
| char *filename = sha1_file_name(sha1); | ||
| int i, fd; | ||
|
|
||
| fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0666); | ||
| if (fd < 0) | ||
| return (errno == EEXIST) ? 0 : -1; | ||
| write(fd, buf, size); | ||
| close(fd); | ||
| return 0; | ||
| } | ||
|
|
||
| static int error(const char * string) | ||
| { | ||
| fprintf(stderr, "error: %s\n", string); | ||
| return -1; | ||
| } | ||
|
|
||
| static int verify_hdr(struct cache_header *hdr, unsigned long size) | ||
| { | ||
| SHA_CTX c; | ||
| unsigned char sha1[20]; | ||
|
|
||
| if (hdr->signature != CACHE_SIGNATURE) | ||
| return error("bad signature"); | ||
| if (hdr->version != 1) | ||
| return error("bad version"); | ||
| SHA1_Init(&c); | ||
| SHA1_Update(&c, hdr, offsetof(struct cache_header, sha1)); | ||
| SHA1_Update(&c, hdr+1, size - sizeof(*hdr)); | ||
| SHA1_Final(sha1, &c); | ||
| if (memcmp(sha1, hdr->sha1, 20)) | ||
| return error("bad header sha1"); | ||
| return 0; | ||
| } | ||
|
|
||
| int read_cache(void) | ||
| { | ||
| int fd, i; | ||
| struct stat st; | ||
| unsigned long size, offset; | ||
| void *map; | ||
| struct cache_header *hdr; | ||
|
|
||
| errno = EBUSY; | ||
| if (active_cache) | ||
| return error("more than one cachefile"); | ||
| errno = ENOENT; | ||
| sha1_file_directory = getenv(DB_ENVIRONMENT); | ||
| if (!sha1_file_directory) | ||
| sha1_file_directory = DEFAULT_DB_ENVIRONMENT; | ||
| if (access(sha1_file_directory, X_OK) < 0) | ||
| return error("no access to SHA1 file directory"); | ||
| fd = open(".dircache/index", O_RDONLY); | ||
| if (fd < 0) | ||
| return (errno == ENOENT) ? 0 : error("open failed"); | ||
|
|
||
| map = (void *)-1; | ||
| if (!fstat(fd, &st)) { | ||
| map = NULL; | ||
| size = st.st_size; | ||
| errno = EINVAL; | ||
| if (size > sizeof(struct cache_header)) | ||
| map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); | ||
| } | ||
| close(fd); | ||
| if (-1 == (int)(long)map) | ||
| return error("mmap failed"); | ||
|
|
||
| hdr = map; | ||
| if (verify_hdr(hdr, size) < 0) | ||
| goto unmap; | ||
|
|
||
| active_nr = hdr->entries; | ||
| active_alloc = alloc_nr(active_nr); | ||
| active_cache = calloc(active_alloc, sizeof(struct cache_entry *)); | ||
|
|
||
| offset = sizeof(*hdr); | ||
| for (i = 0; i < hdr->entries; i++) { | ||
| struct cache_entry *ce = map + offset; | ||
| offset = offset + ce_size(ce); | ||
| active_cache[i] = ce; | ||
| } | ||
| return active_nr; | ||
|
|
||
| unmap: | ||
| munmap(map, size); | ||
| errno = EINVAL; | ||
| return error("verify header failed"); | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| #include "cache.h" | ||
|
|
||
| static int unpack(unsigned char *sha1) | ||
| { | ||
| void *buffer; | ||
| unsigned long size; | ||
| char type[20]; | ||
|
|
||
| buffer = read_sha1_file(sha1, type, &size); | ||
| if (!buffer) | ||
| usage("unable to read sha1 file"); | ||
| if (strcmp(type, "tree")) | ||
| usage("expected a 'tree' node"); | ||
| while (size) { | ||
| int len = strlen(buffer)+1; | ||
| unsigned char *sha1 = buffer + len; | ||
| char *path = strchr(buffer, ' ')+1; | ||
| unsigned int mode; | ||
| if (size < len + 20 || sscanf(buffer, "%o", &mode) != 1) | ||
| usage("corrupt 'tree' file"); | ||
| buffer = sha1 + 20; | ||
| size -= len + 20; | ||
| printf("%o %s (%s)\n", mode, path, sha1_to_hex(sha1)); | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| int main(int argc, char **argv) | ||
| { | ||
| int fd; | ||
| unsigned char sha1[20]; | ||
|
|
||
| if (argc != 2) | ||
| usage("read-tree <key>"); | ||
| if (get_sha1_hex(argv[1], sha1) < 0) | ||
| usage("read-tree <key>"); | ||
| sha1_file_directory = getenv(DB_ENVIRONMENT); | ||
| if (!sha1_file_directory) | ||
| sha1_file_directory = DEFAULT_DB_ENVIRONMENT; | ||
| if (unpack(sha1) < 0) | ||
| usage("unpack failed"); | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| #include "cache.h" | ||
|
|
||
| #define MTIME_CHANGED 0x0001 | ||
| #define CTIME_CHANGED 0x0002 | ||
| #define OWNER_CHANGED 0x0004 | ||
| #define MODE_CHANGED 0x0008 | ||
| #define INODE_CHANGED 0x0010 | ||
| #define DATA_CHANGED 0x0020 | ||
|
|
||
| static int match_stat(struct cache_entry *ce, struct stat *st) | ||
| { | ||
| unsigned int changed = 0; | ||
|
|
||
| if (ce->mtime.sec != (unsigned int)st->st_mtim.tv_sec || | ||
| ce->mtime.nsec != (unsigned int)st->st_mtim.tv_nsec) | ||
| changed |= MTIME_CHANGED; | ||
| if (ce->ctime.sec != (unsigned int)st->st_ctim.tv_sec || | ||
| ce->ctime.nsec != (unsigned int)st->st_ctim.tv_nsec) | ||
| changed |= CTIME_CHANGED; | ||
| if (ce->st_uid != (unsigned int)st->st_uid || | ||
| ce->st_gid != (unsigned int)st->st_gid) | ||
| changed |= OWNER_CHANGED; | ||
| if (ce->st_mode != (unsigned int)st->st_mode) | ||
| changed |= MODE_CHANGED; | ||
| if (ce->st_dev != (unsigned int)st->st_dev || | ||
| ce->st_ino != (unsigned int)st->st_ino) | ||
| changed |= INODE_CHANGED; | ||
| if (ce->st_size != (unsigned int)st->st_size) | ||
| changed |= DATA_CHANGED; | ||
| return changed; | ||
| } | ||
|
|
||
| static void show_differences(struct cache_entry *ce, struct stat *cur, | ||
| void *old_contents, unsigned long long old_size) | ||
| { | ||
| static char cmd[1000]; | ||
| FILE *f; | ||
|
|
||
| snprintf(cmd, sizeof(cmd), "diff -u - %s", ce->name); | ||
| f = popen(cmd, "w"); | ||
| fwrite(old_contents, old_size, 1, f); | ||
| pclose(f); | ||
| } | ||
|
|
||
| int main(int argc, char **argv) | ||
| { | ||
| int entries = read_cache(); | ||
| int i; | ||
|
|
||
| if (entries < 0) { | ||
| perror("read_cache"); | ||
| exit(1); | ||
| } | ||
| for (i = 0; i < entries; i++) { | ||
| struct stat st; | ||
| struct cache_entry *ce = active_cache[i]; | ||
| int n, changed; | ||
| unsigned int mode; | ||
| unsigned long size; | ||
| char type[20]; | ||
| void *new; | ||
|
|
||
| if (stat(ce->name, &st) < 0) { | ||
| printf("%s: %s\n", ce->name, strerror(errno)); | ||
| continue; | ||
| } | ||
| changed = match_stat(ce, &st); | ||
| if (!changed) { | ||
| printf("%s: ok\n", ce->name); | ||
| continue; | ||
| } | ||
| printf("%.*s: ", ce->namelen, ce->name); | ||
| for (n = 0; n < 20; n++) | ||
| printf("%02x", ce->sha1[n]); | ||
| printf("\n"); | ||
| new = read_sha1_file(ce->sha1, type, &size); | ||
| show_differences(ce, &st, new, size); | ||
| free(new); | ||
| } | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,248 @@ | ||
| #include "cache.h" | ||
|
|
||
| static int cache_name_compare(const char *name1, int len1, const char *name2, int len2) | ||
| { | ||
| int len = len1 < len2 ? len1 : len2; | ||
| int cmp; | ||
|
|
||
| cmp = memcmp(name1, name2, len); | ||
| if (cmp) | ||
| return cmp; | ||
| if (len1 < len2) | ||
| return -1; | ||
| if (len1 > len2) | ||
| return 1; | ||
| return 0; | ||
| } | ||
|
|
||
| static int cache_name_pos(const char *name, int namelen) | ||
| { | ||
| int first, last; | ||
|
|
||
| first = 0; | ||
| last = active_nr; | ||
| while (last > first) { | ||
| int next = (last + first) >> 1; | ||
| struct cache_entry *ce = active_cache[next]; | ||
| int cmp = cache_name_compare(name, namelen, ce->name, ce->namelen); | ||
| if (!cmp) | ||
| return -next-1; | ||
| if (cmp < 0) { | ||
| last = next; | ||
| continue; | ||
| } | ||
| first = next+1; | ||
| } | ||
| return first; | ||
| } | ||
|
|
||
| static int remove_file_from_cache(char *path) | ||
| { | ||
| int pos = cache_name_pos(path, strlen(path)); | ||
| if (pos < 0) { | ||
| pos = -pos-1; | ||
| active_nr--; | ||
| if (pos < active_nr) | ||
| memmove(active_cache + pos, active_cache + pos + 1, (active_nr - pos - 1) * sizeof(struct cache_entry *)); | ||
| } | ||
| } | ||
|
|
||
| static int add_cache_entry(struct cache_entry *ce) | ||
| { | ||
| int pos; | ||
|
|
||
| pos = cache_name_pos(ce->name, ce->namelen); | ||
|
|
||
| /* existing match? Just replace it */ | ||
| if (pos < 0) { | ||
| active_cache[-pos-1] = ce; | ||
| return 0; | ||
| } | ||
|
|
||
| /* Make sure the array is big enough .. */ | ||
| if (active_nr == active_alloc) { | ||
| active_alloc = alloc_nr(active_alloc); | ||
| active_cache = realloc(active_cache, active_alloc * sizeof(struct cache_entry *)); | ||
| } | ||
|
|
||
| /* Add it in.. */ | ||
| active_nr++; | ||
| if (active_nr > pos) | ||
| memmove(active_cache + pos + 1, active_cache + pos, (active_nr - pos - 1) * sizeof(ce)); | ||
| active_cache[pos] = ce; | ||
| return 0; | ||
| } | ||
|
|
||
| static int index_fd(const char *path, int namelen, struct cache_entry *ce, int fd, struct stat *st) | ||
| { | ||
| z_stream stream; | ||
| int max_out_bytes = namelen + st->st_size + 200; | ||
| void *out = malloc(max_out_bytes); | ||
| void *metadata = malloc(namelen + 200); | ||
| void *in = mmap(NULL, st->st_size, PROT_READ, MAP_PRIVATE, fd, 0); | ||
| SHA_CTX c; | ||
|
|
||
| close(fd); | ||
| if (!out || (int)(long)in == -1) | ||
| return -1; | ||
|
|
||
| memset(&stream, 0, sizeof(stream)); | ||
| deflateInit(&stream, Z_BEST_COMPRESSION); | ||
|
|
||
| /* | ||
| * ASCII size + nul byte | ||
| */ | ||
| stream.next_in = metadata; | ||
| stream.avail_in = 1+sprintf(metadata, "blob %lu", (unsigned long) st->st_size); | ||
| stream.next_out = out; | ||
| stream.avail_out = max_out_bytes; | ||
| while (deflate(&stream, 0) == Z_OK) | ||
| /* nothing */; | ||
|
|
||
| /* | ||
| * File content | ||
| */ | ||
| stream.next_in = in; | ||
| stream.avail_in = st->st_size; | ||
| while (deflate(&stream, Z_FINISH) == Z_OK) | ||
| /*nothing */; | ||
|
|
||
| deflateEnd(&stream); | ||
|
|
||
| SHA1_Init(&c); | ||
| SHA1_Update(&c, out, stream.total_out); | ||
| SHA1_Final(ce->sha1, &c); | ||
|
|
||
| return write_sha1_buffer(ce->sha1, out, stream.total_out); | ||
| } | ||
|
|
||
| static int add_file_to_cache(char *path) | ||
| { | ||
| int size, namelen; | ||
| struct cache_entry *ce; | ||
| struct stat st; | ||
| int fd; | ||
|
|
||
| fd = open(path, O_RDONLY); | ||
| if (fd < 0) { | ||
| if (errno == ENOENT) | ||
| return remove_file_from_cache(path); | ||
| return -1; | ||
| } | ||
| if (fstat(fd, &st) < 0) { | ||
| close(fd); | ||
| return -1; | ||
| } | ||
| namelen = strlen(path); | ||
| size = cache_entry_size(namelen); | ||
| ce = malloc(size); | ||
| memset(ce, 0, size); | ||
| memcpy(ce->name, path, namelen); | ||
| ce->ctime.sec = st.st_ctime; | ||
| ce->ctime.nsec = st.st_ctim.tv_nsec; | ||
| ce->mtime.sec = st.st_mtime; | ||
| ce->mtime.nsec = st.st_mtim.tv_nsec; | ||
| ce->st_dev = st.st_dev; | ||
| ce->st_ino = st.st_ino; | ||
| ce->st_mode = st.st_mode; | ||
| ce->st_uid = st.st_uid; | ||
| ce->st_gid = st.st_gid; | ||
| ce->st_size = st.st_size; | ||
| ce->namelen = namelen; | ||
|
|
||
| if (index_fd(path, namelen, ce, fd, &st) < 0) | ||
| return -1; | ||
|
|
||
| return add_cache_entry(ce); | ||
| } | ||
|
|
||
| static int write_cache(int newfd, struct cache_entry **cache, int entries) | ||
| { | ||
| SHA_CTX c; | ||
| struct cache_header hdr; | ||
| int i; | ||
|
|
||
| hdr.signature = CACHE_SIGNATURE; | ||
| hdr.version = 1; | ||
| hdr.entries = entries; | ||
|
|
||
| SHA1_Init(&c); | ||
| SHA1_Update(&c, &hdr, offsetof(struct cache_header, sha1)); | ||
| for (i = 0; i < entries; i++) { | ||
| struct cache_entry *ce = cache[i]; | ||
| int size = ce_size(ce); | ||
| SHA1_Update(&c, ce, size); | ||
| } | ||
| SHA1_Final(hdr.sha1, &c); | ||
|
|
||
| if (write(newfd, &hdr, sizeof(hdr)) != sizeof(hdr)) | ||
| return -1; | ||
|
|
||
| for (i = 0; i < entries; i++) { | ||
| struct cache_entry *ce = cache[i]; | ||
| int size = ce_size(ce); | ||
| if (write(newfd, ce, size) != size) | ||
| return -1; | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| /* | ||
| * We fundamentally don't like some paths: we don't want | ||
| * dot or dot-dot anywhere, and in fact, we don't even want | ||
| * any other dot-files (.dircache or anything else). They | ||
| * are hidden, for chist sake. | ||
| * | ||
| * Also, we don't want double slashes or slashes at the | ||
| * end that can make pathnames ambiguous. | ||
| */ | ||
| static int verify_path(char *path) | ||
| { | ||
| char c; | ||
|
|
||
| goto inside; | ||
| for (;;) { | ||
| if (!c) | ||
| return 1; | ||
| if (c == '/') { | ||
| inside: | ||
| c = *path++; | ||
| if (c != '/' && c != '.' && c != '\0') | ||
| continue; | ||
| return 0; | ||
| } | ||
| c = *path++; | ||
| } | ||
| } | ||
|
|
||
| int main(int argc, char **argv) | ||
| { | ||
| int i, newfd, entries; | ||
|
|
||
| entries = read_cache(); | ||
| if (entries < 0) { | ||
| perror("cache corrupted"); | ||
| return -1; | ||
| } | ||
|
|
||
| newfd = open(".dircache/index.lock", O_RDWR | O_CREAT | O_EXCL, 0600); | ||
| if (newfd < 0) { | ||
| perror("unable to create new cachefile"); | ||
| return -1; | ||
| } | ||
| for (i = 1 ; i < argc; i++) { | ||
| char *path = argv[i]; | ||
| if (!verify_path(path)) { | ||
| fprintf(stderr, "Ignoring path %s\n", argv[i]); | ||
| continue; | ||
| } | ||
| if (add_file_to_cache(path)) { | ||
| fprintf(stderr, "Unable to add %s to database\n", path); | ||
| goto out; | ||
| } | ||
| } | ||
| if (!write_cache(newfd, active_cache, active_nr) && !rename(".dircache/index.lock", ".dircache/index")) | ||
| return 0; | ||
| out: | ||
| unlink(".dircache/index.lock"); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| #include "cache.h" | ||
|
|
||
| static int check_valid_sha1(unsigned char *sha1) | ||
| { | ||
| char *filename = sha1_file_name(sha1); | ||
| int ret; | ||
|
|
||
| /* If we were anal, we'd check that the sha1 of the contents actually matches */ | ||
| ret = access(filename, R_OK); | ||
| if (ret) | ||
| perror(filename); | ||
| return ret; | ||
| } | ||
|
|
||
| static int prepend_integer(char *buffer, unsigned val, int i) | ||
| { | ||
| buffer[--i] = '\0'; | ||
| do { | ||
| buffer[--i] = '0' + (val % 10); | ||
| val /= 10; | ||
| } while (val); | ||
| return i; | ||
| } | ||
|
|
||
| #define ORIG_OFFSET (40) /* Enough space to add the header of "tree <size>\0" */ | ||
|
|
||
| int main(int argc, char **argv) | ||
| { | ||
| unsigned long size, offset, val; | ||
| int i, entries = read_cache(); | ||
| char *buffer; | ||
|
|
||
| if (entries <= 0) { | ||
| fprintf(stderr, "No file-cache to create a tree of\n"); | ||
| exit(1); | ||
| } | ||
|
|
||
| /* Guess at an initial size */ | ||
| size = entries * 40 + 400; | ||
| buffer = malloc(size); | ||
| offset = ORIG_OFFSET; | ||
|
|
||
| for (i = 0; i < entries; i++) { | ||
| struct cache_entry *ce = active_cache[i]; | ||
| if (check_valid_sha1(ce->sha1) < 0) | ||
| exit(1); | ||
| if (offset + ce->namelen + 60 > size) { | ||
| size = alloc_nr(offset + ce->namelen + 60); | ||
| buffer = realloc(buffer, size); | ||
| } | ||
| offset += sprintf(buffer + offset, "%o %s", ce->st_mode, ce->name); | ||
| buffer[offset++] = 0; | ||
| memcpy(buffer + offset, ce->sha1, 20); | ||
| offset += 20; | ||
| } | ||
|
|
||
| i = prepend_integer(buffer, offset - ORIG_OFFSET, ORIG_OFFSET); | ||
| i -= 5; | ||
| memcpy(buffer+i, "tree ", 5); | ||
|
|
||
| buffer += i; | ||
| offset -= i; | ||
|
|
||
| write_sha1_file(buffer, offset); | ||
| return 0; | ||
| } |
What does this mean?