Skip to content

Commit ffbe1ad

Browse files
kaysieversLinus Torvalds
authored andcommitted
[PATCH] fix compare symlink against readlink not data
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>
1 parent 20d37ef commit ffbe1ad

File tree

3 files changed

+49
-9
lines changed

3 files changed

+49
-9
lines changed

diff-files.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ int main(int argc, char **argv)
111111
continue;
112112
}
113113

114-
if (stat(ce->name, &st) < 0) {
114+
if (lstat(ce->name, &st) < 0) {
115115
if (errno != ENOENT) {
116116
perror(ce->name);
117117
continue;

read-cache.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ int cache_match_stat(struct cache_entry *ce, struct stat *st)
1616
switch (ntohl(ce->ce_mode) & S_IFMT) {
1717
case S_IFREG:
1818
changed |= !S_ISREG(st->st_mode) ? TYPE_CHANGED : 0;
19+
/* We consider only the owner x bit to be relevant for "mode changes" */
20+
if (0100 & (ntohl(ce->ce_mode) ^ st->st_mode))
21+
changed |= MODE_CHANGED;
1922
break;
2023
case S_IFLNK:
2124
changed |= !S_ISLNK(st->st_mode) ? TYPE_CHANGED : 0;
@@ -43,9 +46,6 @@ int cache_match_stat(struct cache_entry *ce, struct stat *st)
4346
if (ce->ce_uid != htonl(st->st_uid) ||
4447
ce->ce_gid != htonl(st->st_gid))
4548
changed |= OWNER_CHANGED;
46-
/* We consider only the owner x bit to be relevant for "mode changes" */
47-
if (0100 & (ntohl(ce->ce_mode) ^ st->st_mode))
48-
changed |= MODE_CHANGED;
4949
if (ce->ce_dev != htonl(st->st_dev) ||
5050
ce->ce_ino != htonl(st->st_ino))
5151
changed |= INODE_CHANGED;

update-cache.c

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static int add_file_to_cache_1(char *path)
6464
struct stat st;
6565
int fd;
6666
unsigned int len;
67-
char target[1024];
67+
char *target;
6868

6969
if (lstat(path, &st) < 0) {
7070
if (errno == ENOENT || errno == ENOTDIR) {
@@ -90,11 +90,14 @@ static int add_file_to_cache_1(char *path)
9090
return -1;
9191
break;
9292
case S_IFLNK:
93-
len = readlink(path, target, sizeof(target));
94-
if (len == -1 || len+1 > sizeof(target))
93+
target = xmalloc(st.st_size+1);
94+
if (readlink(path, target, st.st_size+1) != st.st_size) {
95+
free(target);
9596
return -1;
96-
if (write_sha1_file(target, len, "blob", ce->sha1))
97+
}
98+
if (write_sha1_file(target, st.st_size, "blob", ce->sha1))
9799
return -1;
100+
free(target);
98101
break;
99102
default:
100103
return -1;
@@ -163,6 +166,33 @@ static int compare_data(struct cache_entry *ce, unsigned long expected_size)
163166
return match;
164167
}
165168

169+
static int compare_link(struct cache_entry *ce, unsigned long expected_size)
170+
{
171+
int match = -1;
172+
char *target;
173+
void *buffer;
174+
unsigned long size;
175+
char type[10];
176+
int len;
177+
178+
target = xmalloc(expected_size);
179+
len = readlink(ce->name, target, expected_size);
180+
if (len != expected_size) {
181+
free(target);
182+
return -1;
183+
}
184+
buffer = read_sha1_file(ce->sha1, type, &size);
185+
if (!buffer) {
186+
free(target);
187+
return -1;
188+
}
189+
if (size == expected_size)
190+
match = memcmp(buffer, target, size);
191+
free(buffer);
192+
free(target);
193+
return match;
194+
}
195+
166196
/*
167197
* "refresh" does not calculate a new sha1 file or bring the
168198
* 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)
194224
if (changed & (MODE_CHANGED | TYPE_CHANGED))
195225
return ERR_PTR(-EINVAL);
196226

197-
if (compare_data(ce, st.st_size))
227+
switch (st.st_mode & S_IFMT) {
228+
case S_IFREG:
229+
if (compare_data(ce, st.st_size))
230+
return ERR_PTR(-EINVAL);
231+
break;
232+
case S_IFLNK:
233+
if (compare_link(ce, st.st_size))
234+
return ERR_PTR(-EINVAL);
235+
break;
236+
default:
198237
return ERR_PTR(-EINVAL);
238+
}
199239

200240
cache_changed = 1;
201241
size = ce_size(ce);

0 commit comments

Comments
 (0)