summaryrefslogtreecommitdiffstats
path: root/bin/ln
diff options
context:
space:
mode:
authorjilles <jilles@FreeBSD.org>2010-04-17 22:39:53 +0000
committerjilles <jilles@FreeBSD.org>2010-04-17 22:39:53 +0000
commitfd633ce008119f57b9dcf1b829bb499d83bd0976 (patch)
tree901049f193a870870489a09476f4b77200cfcc09 /bin/ln
parent3b409255b40aabbe07732cbb576bb30585baa1a3 (diff)
downloadFreeBSD-src-fd633ce008119f57b9dcf1b829bb499d83bd0976.zip
FreeBSD-src-fd633ce008119f57b9dcf1b829bb499d83bd0976.tar.gz
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
Diffstat (limited to 'bin/ln')
-rw-r--r--bin/ln/ln.c61
1 files changed, 59 insertions, 2 deletions
diff --git a/bin/ln/ln.c b/bin/ln/ln.c
index e946646..d779392 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) {
OpenPOWER on IntegriCloud