switch (ntohl(ce->ce_mode) & S_IFMT) {
case S_IFREG:
changed |= !S_ISREG(st->st_mode) ? TYPE_CHANGED : 0;
+ /* 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;
break;
case S_IFLNK:
changed |= !S_ISLNK(st->st_mode) ? TYPE_CHANGED : 0;
if (ce->ce_uid != htonl(st->st_uid) ||
ce->ce_gid != htonl(st->st_gid))
changed |= OWNER_CHANGED;
- /* 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->ce_dev != htonl(st->st_dev) ||
ce->ce_ino != htonl(st->st_ino))
changed |= INODE_CHANGED;
struct stat st;
int fd;
unsigned int len;
- char target[1024];
+ char *target;
if (lstat(path, &st) < 0) {
if (errno == ENOENT || errno == ENOTDIR) {
return -1;
break;
case S_IFLNK:
- len = readlink(path, target, sizeof(target));
- if (len == -1 || len+1 > sizeof(target))
+ target = xmalloc(st.st_size+1);
+ if (readlink(path, target, st.st_size+1) != st.st_size) {
+ free(target);
return -1;
- if (write_sha1_file(target, len, "blob", ce->sha1))
+ }
+ if (write_sha1_file(target, st.st_size, "blob", ce->sha1))
return -1;
+ free(target);
break;
default:
return -1;
return match;
}
+static int compare_link(struct cache_entry *ce, unsigned long expected_size)
+{
+ int match = -1;
+ char *target;
+ void *buffer;
+ unsigned long size;
+ char type[10];
+ int len;
+
+ target = xmalloc(expected_size);
+ len = readlink(ce->name, target, expected_size);
+ if (len != expected_size) {
+ free(target);
+ return -1;
+ }
+ buffer = read_sha1_file(ce->sha1, type, &size);
+ if (!buffer) {
+ free(target);
+ return -1;
+ }
+ if (size == expected_size)
+ match = memcmp(buffer, target, size);
+ free(buffer);
+ free(target);
+ return match;
+}
+
/*
* "refresh" does not calculate a new sha1 file or bring the
* cache up-to-date for mode/content changes. But what it
if (changed & (MODE_CHANGED | TYPE_CHANGED))
return ERR_PTR(-EINVAL);
- if (compare_data(ce, st.st_size))
+ switch (st.st_mode & S_IFMT) {
+ case S_IFREG:
+ if (compare_data(ce, st.st_size))
+ return ERR_PTR(-EINVAL);
+ break;
+ case S_IFLNK:
+ if (compare_link(ce, st.st_size))
+ return ERR_PTR(-EINVAL);
+ break;
+ default:
return ERR_PTR(-EINVAL);
+ }
cache_changed = 1;
size = ce_size(ce);