[PATCH] fix compare symlink against readlink not data
authorKay Sievers <kay.sievers@vrfy.org>
Fri, 6 May 2005 13:45:01 +0000 (15:45 +0200)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Fri, 6 May 2005 15:41:12 +0000 (08:41 -0700)
Fix update-cache to compare the blob of a symlink against the link-target
and not the file it points to. Also ignore all permissions applied to
links.

Thanks to Greg for recognizing this while he added our list of symlinks
back to the udev repository.

Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
diff-files.c
read-cache.c
update-cache.c

index 0cf2c244100e4266c00eddf430a28ed2d859fa4a..c51edc3315100d8ec75eec72ec2f8a7e3753ad98 100644 (file)
@@ -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;
index 5703f30b6a44c27a2ead3eb3a5ab17bcce670ad9..2a88d18b16b5bc6b9cbbef16a125aa4705b5cb90 100644 (file)
@@ -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;
index 893ba8679d5764ba84627558d567268ff2500558..210c786af6734f6ea772f2fc39b0303fde13e887 100644 (file)
@@ -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);