diff options
author | Jilles Tjoelker <jilles@FreeBSD.org> | 2010-04-17 22:39:53 +0000 |
---|---|---|
committer | Jilles Tjoelker <jilles@FreeBSD.org> | 2010-04-17 22:39:53 +0000 |
commit | 5d66b54e27eb01b2bbea010f559c4c985c67886a (patch) | |
tree | 901049f193a870870489a09476f4b77200cfcc09 /bin/ln/ln.c | |
parent | 9eb448a7e634f3edde2ca41327e4a7a0dcac564e (diff) | |
download | src-5d66b54e27eb01b2bbea010f559c4c985c67886a.tar.gz src-5d66b54e27eb01b2bbea010f559c4c985c67886a.zip |
ln: Refuse deleting a directory entry by hardlinking it to itself.
Two pathnames refer to the same directory entry iff the directories match
and the final components' names match.
Example: (assuming file1 is an existing file)
ln -f file1 file1
This now fails while leaving file1 intact. It used to delete file1 and then
complain it cannot be linked because it is gone.
With -i, this error is detected before the question is asked.
MFC after: 2 weeks
Notes
Notes:
svn path=/head/; revision=206773
Diffstat (limited to 'bin/ln/ln.c')
-rw-r--r-- | bin/ln/ln.c | 61 |
1 files changed, 59 insertions, 2 deletions
diff --git a/bin/ln/ln.c b/bin/ln/ln.c index e946646775e6..d77939291f76 100644 --- a/bin/ln/ln.c +++ b/bin/ln/ln.c @@ -172,6 +172,52 @@ main(int argc, char *argv[]) exit(exitval); } +/* + * Two pathnames refer to the same directory entry if the directories match + * and the final components' names match. + */ +static int +samedirent(const char *path1, const char *path2) +{ + const char *file1, *file2; + char pathbuf[PATH_MAX]; + struct stat sb1, sb2; + + if (strcmp(path1, path2) == 0) + return 1; + file1 = strrchr(path1, '/'); + if (file1 != NULL) + file1++; + else + file1 = path1; + file2 = strrchr(path2, '/'); + if (file2 != NULL) + file2++; + else + file2 = path2; + if (strcmp(file1, file2) != 0) + return 0; + if (file1 - path1 >= PATH_MAX || file2 - path2 >= PATH_MAX) + return 0; + if (file1 == path1) + memcpy(pathbuf, ".", 2); + else { + memcpy(pathbuf, path1, file1 - path1); + pathbuf[file1 - path1] = '\0'; + } + if (stat(pathbuf, &sb1) != 0) + return 0; + if (file2 == path2) + memcpy(pathbuf, ".", 2); + else { + memcpy(pathbuf, path2, file2 - path2); + pathbuf[file2 - path2] = '\0'; + } + if (stat(pathbuf, &sb2) != 0) + return 0; + return sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino; +} + int linkit(const char *source, const char *target, int isdir) { @@ -215,7 +261,6 @@ linkit(const char *source, const char *target, int isdir) target = path; } - exists = !lstat(target, &sb); /* * If the link source doesn't exist, and a symbolic link was * requested, and -w was specified, give a warning. @@ -242,8 +287,20 @@ linkit(const char *source, const char *target, int isdir) warn("warning: %s", source); } } + + /* + * If the file exists, first check it is not the same directory entry. + */ + exists = !lstat(target, &sb); + if (exists) { + if (!sflag && samedirent(source, target)) { + warnx("%s and %s are the same directory entry", + source, target); + return (1); + } + } /* - * If the file exists, then unlink it forcibly if -f was specified + * Then unlink it forcibly if -f was specified * and interactively if -i was specified. */ if (fflag && exists) { |