sbase

suckless unix tools
git clone git://git.2f30.org/sbase.git
Log | Files | Refs | README | LICENSE

commit 78b285deb6c5e6770621223b3364eca1174d8e28
parent d23cc72490f42351a20cd12202d94d6e2d65c1f9
Author: FRIGN <dev@frign.de>
Date:   Wed May 27 18:15:24 +0200

Fix ln(1) symbolic link handling

This was broken in multiple ways. For instance, the overlay-
check of identical files (name and target) was omitted for
symbolic links for some reason.
While at it, I fixed the stat-handling, improved the error-
messages so the right paths were shown and removed the
illegimite bail-out when the target-fstatat failed (we want
only a warning here as well).

Diffstat:
ln.c | 61+++++++++++++++++++++++++++++++++++--------------------------
1 file changed, 35 insertions(+), 26 deletions(-)
diff --git a/ln.c b/ln.c @@ -4,6 +4,7 @@ #include <errno.h> #include <fcntl.h> #include <libgen.h> +#include <string.h> #include <unistd.h> #include "util.h" @@ -18,10 +19,10 @@ usage(void) int main(int argc, char *argv[]) { - char *fname, *to; - int sflag = 0, fflag = 0, hasto = 0, dirfd = AT_FDCWD, ret = 0, - flags = AT_SYMLINK_FOLLOW; - struct stat st, tost; + char *targetdir = ".", *target = NULL; + int ret = 0, sflag = 0, fflag = 0, dirfd = AT_FDCWD, + hastarget = 0, flags = AT_SYMLINK_FOLLOW; + struct stat st, tst; ARGBEGIN { case 'f': @@ -43,15 +44,16 @@ main(int argc, char *argv[]) if (!argc) usage(); - fname = sflag ? "symlink" : "link"; - - if (argc >= 2) { + if (argc > 1) { if (!stat(argv[argc - 1], &st) && S_ISDIR(st.st_mode)) { if ((dirfd = open(argv[argc - 1], O_RDONLY)) < 0) eprintf("open %s:", argv[argc - 1]); + targetdir = argv[argc - 1]; + if (targetdir[strlen(targetdir) - 1] == '/') + targetdir[strlen(targetdir) - 1] = '\0'; } else if (argc == 2) { - to = argv[argc - 1]; - hasto = 1; + hastarget = 1; + target = argv[argc - 1]; } else { eprintf("%s: not a directory\n", argv[argc - 1]); } @@ -60,27 +62,34 @@ main(int argc, char *argv[]) } for (; *argv; argc--, argv++) { - if (!hasto) - to = basename(*argv); - if (!sflag) { - if (stat(*argv, &st) < 0) { - weprintf("stat %s:", *argv); - ret = 1; - continue; - } else if (fstatat(dirfd, to, &tost, AT_SYMLINK_NOFOLLOW) < 0) { - if (errno != ENOENT) - eprintf("stat %s:", to); - } else if (st.st_dev == tost.st_dev && st.st_ino == tost.st_ino) { - weprintf("%s and %s are the same file\n", *argv, *argv); + if (!hastarget) + target = basename(*argv); + + if (stat(*argv, &st) < 0) { + weprintf("stat %s:", *argv); + ret = 1; + continue; + } else if (fstatat(dirfd, target, &tst, AT_SYMLINK_NOFOLLOW) < 0) { + if (errno != ENOENT) { + weprintf("fstatat %s %s:", targetdir, target); ret = 1; continue; } + } else if (st.st_dev == tst.st_dev && st.st_ino == tst.st_ino) { + weprintf("%s and %s/%s are the same file\n", *argv, targetdir, target); + ret = 1; + continue; + } + + if (fflag && unlinkat(dirfd, target, 0) < 0 && errno != ENOENT) { + weprintf("unlinkat %s %s:", targetdir, target); + ret = 1; + continue; } - if (fflag) - unlinkat(dirfd, to, 0); - if ((!sflag ? linkat(AT_FDCWD, *argv, dirfd, to, flags) - : symlinkat(*argv, dirfd, to)) < 0) { - weprintf("%s %s <- %s:", fname, *argv, to); + if ((sflag ? symlinkat(*argv, dirfd, target) : + linkat(AT_FDCWD, *argv, dirfd, target, flags)) < 0) { + weprintf("%s %s <- %s/%s:", sflag ? "symlinkat" : "linkat", + *argv, targetdir, target); ret = 1; } }