summaryrefslogtreecommitdiff
path: root/util/sbase/ln.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/sbase/ln.c')
-rw-r--r--util/sbase/ln.c103
1 files changed, 103 insertions, 0 deletions
diff --git a/util/sbase/ln.c b/util/sbase/ln.c
new file mode 100644
index 00000000..f62068a1
--- /dev/null
+++ b/util/sbase/ln.c
@@ -0,0 +1,103 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+static void
+usage(void)
+{
+ eprintf("usage: %s [-f] [-L | -P | -s] target [name]\n"
+ " %s [-f] [-L | -P | -s] target ... dir\n", argv0, argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ 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':
+ fflag = 1;
+ break;
+ case 'L':
+ flags |= AT_SYMLINK_FOLLOW;
+ break;
+ case 'P':
+ flags &= ~AT_SYMLINK_FOLLOW;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ default:
+ usage();
+ } ARGEND
+
+ if (!argc)
+ usage();
+
+ 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) {
+ hastarget = 1;
+ target = argv[argc - 1];
+ } else {
+ eprintf("%s: not a directory\n", argv[argc - 1]);
+ }
+ argv[argc - 1] = NULL;
+ argc--;
+ }
+
+ for (; *argv; argc--, argv++) {
+ if (!hastarget)
+ target = basename(*argv);
+
+ if (!sflag) {
+ 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) {
+ if (!fflag) {
+ 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 ((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;
+ }
+ }
+
+ return ret;
+}