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
This commit is contained in:
parent
9eb448a7e6
commit
5d66b54e27
61
bin/ln/ln.c
61
bin/ln/ln.c
@ -172,6 +172,52 @@ main(int argc, char *argv[])
|
|||||||
exit(exitval);
|
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
|
int
|
||||||
linkit(const char *source, const char *target, int isdir)
|
linkit(const char *source, const char *target, int isdir)
|
||||||
{
|
{
|
||||||
@ -215,7 +261,6 @@ linkit(const char *source, const char *target, int isdir)
|
|||||||
target = path;
|
target = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
exists = !lstat(target, &sb);
|
|
||||||
/*
|
/*
|
||||||
* If the link source doesn't exist, and a symbolic link was
|
* If the link source doesn't exist, and a symbolic link was
|
||||||
* requested, and -w was specified, give a warning.
|
* 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);
|
warn("warning: %s", source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the file exists, then unlink it forcibly if -f was specified
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Then unlink it forcibly if -f was specified
|
||||||
* and interactively if -i was specified.
|
* and interactively if -i was specified.
|
||||||
*/
|
*/
|
||||||
if (fflag && exists) {
|
if (fflag && exists) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user