rename: fix regression for symlink with non-existing target
Since commit5454df9c31
("rename: check source file access early") rename fails early for symlinks with non-existing target (regression), because access() dereferences the link. From access(2): "access() checks whether the calling process can access the file pathname. If pathname is a symbolic link, it is dereferenced." Thus replace access() with faccessat() and lstat() as fallback, (as in do_symlink()), that is equivalent for symlink and files. From fsaccess(2) and stat(2): "The faccessat() system call operates in exactly the same way as access(), except for the differences described here. [...] If pathname is relative and dirfd is the special value AT_FDCWD, then pathname is interpreted relative to the current working directory of the calling process (like access()). [...] AT_SYMLINK_NOFOLLOW If pathname is a symbolic link, do not dereference it: instead return information about the link itself." "lstat() is identical to stat(), except that if pathname is a symbolic link, then it returns information about the link itself, not the file that it refers to." Testing ------- 1) symlink with existing target 2) symlink with non-existing target 3) non-existing symlink 4) existing file 5) non-existing file Before: $ touch file-found $ ln -s file-found symlink-1 $ ./rename sym symbolic- symlink-1 # XPASS. $ echo $? 0 $ ln -s file-not-found symlink-2 $ ./rename sym symbolic- symlink-2 # FAIL! REGRESSION. rename: symlink-2: not accessible: No such file or directory $ echo $? 1 $ ./rename sym symbolic- symlink-3 # XFAIL. rename: symlink-3: not accessible: No such file or directory $ echo $? 1 $ touch file-found $ ./rename found existing file-found # XPASS. $ echo $? 0 $ ./rename found existing file-not-found # XFAIL. rename: file-not-found: not accessible: No such file or directory $ echo $? 1 After: $ touch file-found $ ln -s file-found symlink-1 $ ./rename sym symbolic- symlink-1 # XPASS. $ echo $? 0 $ ln -s file-not-found symlink-2 $ ./rename sym symbolic- symlink-2 # PASS! REGRESSION FIXED. $ echo $? 0 $ ./rename sym symbolic- symlink-3 # XFAIL. rename: symlink-3: not accessible: No such file or directory $ echo $? 1 $ touch file-found $ ./rename found existing file-found # XPASS. $ echo $? 0 $ ./rename found existing file-not-found # XFAIL. rename: file-not-found: not accessible: No such file or directory $ echo $? 1 And to test/simulate faccessat()'s EINVAL for AT_SYMLINK_NOFOLLOW for Mac OS X, per commit826538bf64
("rename: skip faccessat() failure if AT_SYMLINK_NOFOLLOW is not a valid flag"), forced 'if' to evaluate to false so that lstat() is taken. It still fails early; the error messages are slightly different ('not accessible' vs. 'stat of ... failed') but still tell same 'No such file or directory'; exit code is the same as well. $ ./rename sym symbolic- symlink-3 # XFAIL. DIFF MSG/SAME RC. rename: stat of symlink-3 failed: No such file or directory $ echo $? 1 $ ./rename found existing file-not-found # XFAIL. DIFF MSG/SAME RC. rename: stat of file-not-found failed: No such file or directory $ echo $? 1 Tested on commit2b41c409e
("Merge branch 'blkd-err' of ...") Signed-off-by: Mauricio Faria de Oliveira <mfo@canonical.com>
This commit is contained in:
parent
2b41c409e7
commit
477239ce0d
|
@ -167,12 +167,21 @@ static int do_file(char *from, char *to, char *s, int verbose, int noact,
|
|||
{
|
||||
char *newname = NULL, *file=NULL;
|
||||
int ret = 1;
|
||||
struct stat sb;
|
||||
|
||||
if (access(s, F_OK) != 0) {
|
||||
if ( faccessat(AT_FDCWD, s, F_OK, AT_SYMLINK_NOFOLLOW) != 0 &&
|
||||
errno != EINVAL )
|
||||
/* Skip if AT_SYMLINK_NOFOLLOW is not supported; lstat() below will
|
||||
detect the access error */
|
||||
{
|
||||
warn(_("%s: not accessible"), s);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (lstat(s, &sb) == -1) {
|
||||
warn(_("stat of %s failed"), s);
|
||||
return 2;
|
||||
}
|
||||
if (strchr(from, '/') == NULL && strchr(to, '/') == NULL)
|
||||
file = strrchr(s, '/');
|
||||
if (file == NULL)
|
||||
|
|
Loading…
Reference in New Issue