*
* Copyright (C) Linus Torvalds, 2005
*/
+#include <stdarg.h>
#include "cache.h"
const char *sha1_file_directory = NULL;
void usage(const char *err)
{
- fprintf(stderr, "read-tree: %s\n", err);
+ fprintf(stderr, "usage: %s\n", err);
exit(1);
}
+static void report(const char *prefix, const char *err, va_list params)
+{
+ fputs(prefix, stderr);
+ vfprintf(stderr, err, params);
+ fputs("\n", stderr);
+}
+
+void die(const char *err, ...)
+{
+ va_list params;
+
+ va_start(params, err);
+ report("fatal: ", err, params);
+ va_end(params);
+ exit(1);
+}
+
+int error(const char *err, ...)
+{
+ va_list params;
+
+ va_start(params, err);
+ report("error: ", err, params);
+ va_end(params);
+ return -1;
+}
+
+
static unsigned hexval(char c)
{
if (c >= '0' && c <= '9')
* careful about using it. Do a "strdup()" if you need to save the
* filename.
*/
-char *sha1_file_name(unsigned char *sha1)
+char *sha1_file_name(const unsigned char *sha1)
{
int i;
static char *name, *base;
return memcmp(sha1, real_sha1, 20) ? -1 : 0;
}
-void *map_sha1_file(unsigned char *sha1, unsigned long *size)
+void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
{
char *filename = sha1_file_name(sha1);
int fd = open(filename, O_RDONLY);
return NULL;
}
if (fstat(fd, &st) < 0) {
- close(fd);
+ close(fd);
return NULL;
}
map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
return buf;
}
-void * read_sha1_file(unsigned char *sha1, char *type, unsigned long *size)
+void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size)
{
unsigned long mapsize;
void *map, *buf;
return NULL;
}
-int write_sha1_file(char *buf, unsigned len)
+int write_sha1_file(char *buf, unsigned len, unsigned char *returnsha1)
{
int size;
char *compressed;
if (write_sha1_buffer(sha1, compressed, size) < 0)
return -1;
- printf("%s\n", sha1_to_hex(sha1));
+ if (returnsha1)
+ memcpy(returnsha1, sha1, 20);
return 0;
}
-int write_sha1_buffer(unsigned char *sha1, void *buf, unsigned int size)
+static inline int collision_check(char *filename, void *buf, unsigned int size)
{
- char *filename = sha1_file_name(sha1);
- int fd;
+#ifdef COLLISION_CHECK
+ void *map;
+ int fd = open(filename, O_RDONLY);
+ struct stat st;
+ int cmp;
- fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
+ /* Unreadable object, or object went away? Strange. */
if (fd < 0)
- return (errno == EEXIST) ? 0 : -1;
- write(fd, buf, size);
+ return -1;
+
+ if (fstat(fd, &st) < 0 || size != st.st_size)
+ return -1;
+
+ map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
+ if (map == MAP_FAILED)
+ return -1;
+ cmp = memcmp(buf, map, size);
+ munmap(map, size);
+ if (cmp)
+ return -1;
+#endif
return 0;
}
-static int error(const char * string)
+int write_sha1_buffer(const unsigned char *sha1, void *buf, unsigned int size)
{
- fprintf(stderr, "error: %s\n", string);
- return -1;
+ char *filename = sha1_file_name(sha1);
+ int fd;
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
+ if (fd < 0) {
+ if (errno != EEXIST)
+ return -1;
+ if (collision_check(filename, buf, size))
+ return error("SHA1 collision detected!"
+ " This is bad, bad, BAD!\a\n");
+ return 0;
+ }
+ write(fd, buf, size);
+ close(fd);
+ return 0;
}
int cache_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)
+ if (ce->ce_mtime.sec != htonl(st->st_mtime))
+ changed |= MTIME_CHANGED;
+ if (ce->ce_ctime.sec != htonl(st->st_ctime))
+ changed |= CTIME_CHANGED;
+
+#ifdef NSEC
+ /*
+ * nsec seems unreliable - not all filesystems support it, so
+ * as long as it is in the inode cache you get right nsec
+ * but after it gets flushed, you get zero nsec.
+ */
+ if (ce->ce_mtime.nsec != htonl(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)
+ if (ce->ce_ctime.nsec != htonl(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)
+#endif
+
+ if (ce->ce_uid != htonl(st->st_uid) ||
+ ce->ce_gid != htonl(st->st_gid))
changed |= OWNER_CHANGED;
- if (ce->st_mode != (unsigned int)st->st_mode)
+ /* We consider only the owner x bit to be relevant for "mode changes" */
+ if (0100 & (ntohl(ce->ce_mode) ^ st->st_mode))
changed |= MODE_CHANGED;
- if (ce->st_dev != (unsigned int)st->st_dev ||
- ce->st_ino != (unsigned int)st->st_ino)
+ if (ce->ce_dev != htonl(st->st_dev) ||
+ ce->ce_ino != htonl(st->st_ino))
changed |= INODE_CHANGED;
- if (ce->st_size != (unsigned int)st->st_size)
+ if (ce->ce_size != htonl(st->st_size))
changed |= DATA_CHANGED;
return changed;
}
-int cache_name_compare(const char *name1, int len1, const char *name2, int len2)
+int cache_name_compare(const char *name1, int flags1, const char *name2, int flags2)
{
+ int len1 = flags1 & CE_NAMEMASK;
+ int len2 = flags2 & CE_NAMEMASK;
int len = len1 < len2 ? len1 : len2;
int cmp;
return -1;
if (len1 > len2)
return 1;
+ if (flags1 < flags2)
+ return -1;
+ if (flags1 > flags2)
+ return 1;
return 0;
}
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);
+ int cmp = cache_name_compare(name, namelen, ce->name, htons(ce->ce_flags));
if (!cmp)
- return -next-1;
+ return next;
if (cmp < 0) {
last = next;
continue;
}
first = next+1;
}
- return first;
+ return -first-1;
+}
+
+/* Remove entry, return true if there are more entries to go.. */
+static int remove_entry_at(int pos)
+{
+ active_nr--;
+ if (pos >= active_nr)
+ return 0;
+ memmove(active_cache + pos, active_cache + pos + 1, (active_nr - pos) * sizeof(struct cache_entry *));
+ return 1;
}
int remove_file_from_cache(char *path)
{
int pos = cache_name_pos(path, strlen(path));
- if (pos < 0) {
+ 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 *));
- }
+ while (pos < active_nr && !strcmp(active_cache[pos]->name, path))
+ remove_entry_at(pos);
return 0;
}
-int add_cache_entry(struct cache_entry *ce)
+static int same_name(struct cache_entry *a, struct cache_entry *b)
+{
+ int len = ce_namelen(a);
+ return ce_namelen(b) == len && !memcmp(a->name, b->name, len);
+}
+
+int add_cache_entry(struct cache_entry *ce, int ok_to_add)
{
int pos;
- pos = cache_name_pos(ce->name, ce->namelen);
+ pos = cache_name_pos(ce->name, htons(ce->ce_flags));
/* existing match? Just replace it */
- if (pos < 0) {
- active_cache[-pos-1] = ce;
+ if (pos >= 0) {
+ active_cache[pos] = ce;
return 0;
}
+ pos = -pos-1;
+
+ /*
+ * Inserting a merged entry ("stage 0") into the index
+ * will always replace all non-merged entries..
+ */
+ if (pos < active_nr && ce_stage(ce) == 0) {
+ while (same_name(active_cache[pos], ce)) {
+ ok_to_add = 1;
+ active_nr--;
+ if (!remove_entry_at(pos))
+ break;
+ }
+ }
+
+ if (!ok_to_add)
+ return -1;
/* Make sure the array is big enough .. */
if (active_nr == active_alloc) {
SHA_CTX c;
unsigned char sha1[20];
- if (hdr->signature != CACHE_SIGNATURE)
+ if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
return error("bad signature");
- if (hdr->version != 1)
+ if (hdr->hdr_version != htonl(1))
return error("bad version");
SHA1_Init(&c);
SHA1_Update(&c, hdr, offsetof(struct cache_header, sha1));
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);
+ fd = open(".git/index", O_RDONLY);
if (fd < 0)
return (errno == ENOENT) ? 0 : error("open failed");
if (verify_hdr(hdr, size) < 0)
goto unmap;
- active_nr = hdr->entries;
+ active_nr = ntohl(hdr->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++) {
+ for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = map + offset;
offset = offset + ce_size(ce);
active_cache[i] = ce;
struct cache_header hdr;
int i;
- hdr.signature = CACHE_SIGNATURE;
- hdr.version = 1;
- hdr.entries = entries;
+ hdr.hdr_signature = htonl(CACHE_SIGNATURE);
+ hdr.hdr_version = htonl(1);
+ hdr.hdr_entries = htonl(entries);
SHA1_Init(&c);
SHA1_Update(&c, &hdr, offsetof(struct cache_header, sha1));