summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--diff-files.c2
-rw-r--r--read-cache.c6
-rw-r--r--update-cache.c50
3 files changed, 49 insertions, 9 deletions
diff --git a/diff-files.c b/diff-files.c
index 0cf2c24410..c51edc3315 100644
--- a/diff-files.c
+++ b/diff-files.c
@@ -111,7 +111,7 @@ int main(int argc, char **argv)
continue;
}
- if (stat(ce->name, &st) < 0) {
+ if (lstat(ce->name, &st) < 0) {
if (errno != ENOENT) {
perror(ce->name);
continue;
diff --git a/read-cache.c b/read-cache.c
index 5703f30b6a..2a88d18b16 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -16,6 +16,9 @@ int cache_match_stat(struct cache_entry *ce, struct stat *st)
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;
@@ -43,9 +46,6 @@ int cache_match_stat(struct cache_entry *ce, struct stat *st)
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;
diff --git a/update-cache.c b/update-cache.c
index 893ba8679d..210c786af6 100644
--- a/update-cache.c
+++ b/update-cache.c
@@ -64,7 +64,7 @@ static int add_file_to_cache_1(char *path)
struct stat st;
int fd;
unsigned int len;
- char target[1024];
+ char *target;
if (lstat(path, &st) < 0) {
if (errno == ENOENT || errno == ENOTDIR) {
@@ -90,11 +90,14 @@ static int add_file_to_cache_1(char *path)
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;
@@ -163,6 +166,33 @@ static int compare_data(struct cache_entry *ce, unsigned long expected_size)
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
@@ -194,8 +224,18 @@ static struct cache_entry *refresh_entry(struct cache_entry *ce)
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);