[821] | 1 | /* vi: set sw=4 ts=4: */
|
---|
| 2 | /*
|
---|
| 3 | * Mini ln implementation for busybox
|
---|
| 4 | *
|
---|
| 5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
|
---|
| 6 | *
|
---|
[2725] | 7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree.
|
---|
[821] | 8 | */
|
---|
| 9 |
|
---|
| 10 | /* BB_AUDIT SUSv3 compliant */
|
---|
| 11 | /* BB_AUDIT GNU options missing: -d, -F, -i, and -v. */
|
---|
| 12 | /* http://www.opengroup.org/onlinepubs/007904975/utilities/ln.html */
|
---|
| 13 |
|
---|
[3232] | 14 | //usage:#define ln_trivial_usage
|
---|
| 15 | //usage: "[OPTIONS] TARGET... LINK|DIR"
|
---|
| 16 | //usage:#define ln_full_usage "\n\n"
|
---|
| 17 | //usage: "Create a link LINK or DIR/TARGET to the specified TARGET(s)\n"
|
---|
| 18 | //usage: "\n -s Make symlinks instead of hardlinks"
|
---|
| 19 | //usage: "\n -f Remove existing destinations"
|
---|
| 20 | //usage: "\n -n Don't dereference symlinks - treat like normal file"
|
---|
| 21 | //usage: "\n -b Make a backup of the target (if exists) before link operation"
|
---|
| 22 | //usage: "\n -S suf Use suffix instead of ~ when making backup files"
|
---|
| 23 | //usage: "\n -T 2nd arg must be a DIR"
|
---|
| 24 | //usage: "\n -v Verbose"
|
---|
| 25 | //usage:
|
---|
| 26 | //usage:#define ln_example_usage
|
---|
| 27 | //usage: "$ ln -s BusyBox /tmp/ls\n"
|
---|
| 28 | //usage: "$ ls -l /tmp/ls\n"
|
---|
| 29 | //usage: "lrwxrwxrwx 1 root root 7 Apr 12 18:39 ls -> BusyBox*\n"
|
---|
| 30 |
|
---|
[1765] | 31 | #include "libbb.h"
|
---|
[821] | 32 |
|
---|
[1765] | 33 | /* This is a NOEXEC applet. Be very careful! */
|
---|
| 34 |
|
---|
| 35 |
|
---|
[3232] | 36 | #define LN_SYMLINK (1 << 0)
|
---|
| 37 | #define LN_FORCE (1 << 1)
|
---|
| 38 | #define LN_NODEREFERENCE (1 << 2)
|
---|
| 39 | #define LN_BACKUP (1 << 3)
|
---|
| 40 | #define LN_SUFFIX (1 << 4)
|
---|
| 41 | #define LN_VERBOSE (1 << 5)
|
---|
| 42 | #define LN_LINKFILE (1 << 6)
|
---|
[821] | 43 |
|
---|
[2725] | 44 | int ln_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
---|
[821] | 45 | int ln_main(int argc, char **argv)
|
---|
| 46 | {
|
---|
| 47 | int status = EXIT_SUCCESS;
|
---|
[2725] | 48 | int opts;
|
---|
[821] | 49 | char *last;
|
---|
| 50 | char *src_name;
|
---|
| 51 | char *src;
|
---|
[1765] | 52 | char *suffix = (char*)"~";
|
---|
[821] | 53 | struct stat statbuf;
|
---|
| 54 | int (*link_func)(const char *, const char *);
|
---|
| 55 |
|
---|
[2725] | 56 | opt_complementary = "-1"; /* min one arg */
|
---|
[3232] | 57 | opts = getopt32(argv, "sfnbS:vT", &suffix);
|
---|
[821] | 58 |
|
---|
| 59 | last = argv[argc - 1];
|
---|
| 60 | argv += optind;
|
---|
[3232] | 61 | argc -= optind;
|
---|
[821] | 62 |
|
---|
[3232] | 63 | if ((opts & LN_LINKFILE) && argc > 2) {
|
---|
| 64 | bb_error_msg_and_die("-T accepts 2 args max");
|
---|
| 65 | }
|
---|
| 66 |
|
---|
| 67 | if (!argv[1]) {
|
---|
| 68 | /* "ln PATH/TO/FILE" -> "ln PATH/TO/FILE FILE" */
|
---|
[821] | 69 | *--argv = last;
|
---|
[3232] | 70 | /* xstrdup is needed: "ln -s PATH/TO/FILE/" is equivalent to
|
---|
| 71 | * "ln -s PATH/TO/FILE/ FILE", not "ln -s PATH/TO/FILE FILE"
|
---|
| 72 | */
|
---|
[2725] | 73 | last = bb_get_last_path_component_strip(xstrdup(last));
|
---|
[821] | 74 | }
|
---|
| 75 |
|
---|
| 76 | do {
|
---|
| 77 | src_name = NULL;
|
---|
| 78 | src = last;
|
---|
| 79 |
|
---|
| 80 | if (is_directory(src,
|
---|
[3232] | 81 | (opts & LN_NODEREFERENCE) ^ LN_NODEREFERENCE
|
---|
| 82 | )
|
---|
[1765] | 83 | ) {
|
---|
[3232] | 84 | if (opts & LN_LINKFILE) {
|
---|
| 85 | bb_error_msg_and_die("'%s' is a directory", src);
|
---|
| 86 | }
|
---|
[1765] | 87 | src_name = xstrdup(*argv);
|
---|
[2725] | 88 | src = concat_path_file(src, bb_get_last_path_component_strip(src_name));
|
---|
[821] | 89 | free(src_name);
|
---|
| 90 | src_name = src;
|
---|
| 91 | }
|
---|
[2725] | 92 | if (!(opts & LN_SYMLINK) && stat(*argv, &statbuf)) {
|
---|
[1765] | 93 | // coreutils: "ln dangling_symlink new_hardlink" works
|
---|
| 94 | if (lstat(*argv, &statbuf) || !S_ISLNK(statbuf.st_mode)) {
|
---|
[2725] | 95 | bb_simple_perror_msg(*argv);
|
---|
[1765] | 96 | status = EXIT_FAILURE;
|
---|
| 97 | free(src_name);
|
---|
| 98 | continue;
|
---|
| 99 | }
|
---|
[821] | 100 | }
|
---|
| 101 |
|
---|
[2725] | 102 | if (opts & LN_BACKUP) {
|
---|
[1765] | 103 | char *backup;
|
---|
| 104 | backup = xasprintf("%s%s", src, suffix);
|
---|
| 105 | if (rename(src, backup) < 0 && errno != ENOENT) {
|
---|
[2725] | 106 | bb_simple_perror_msg(src);
|
---|
[1765] | 107 | status = EXIT_FAILURE;
|
---|
[821] | 108 | free(backup);
|
---|
[1765] | 109 | continue;
|
---|
| 110 | }
|
---|
| 111 | free(backup);
|
---|
| 112 | /*
|
---|
| 113 | * When the source and dest are both hard links to the same
|
---|
| 114 | * inode, a rename may succeed even though nothing happened.
|
---|
| 115 | * Therefore, always unlink().
|
---|
| 116 | */
|
---|
| 117 | unlink(src);
|
---|
[2725] | 118 | } else if (opts & LN_FORCE) {
|
---|
[821] | 119 | unlink(src);
|
---|
| 120 | }
|
---|
| 121 |
|
---|
| 122 | link_func = link;
|
---|
[2725] | 123 | if (opts & LN_SYMLINK) {
|
---|
[821] | 124 | link_func = symlink;
|
---|
| 125 | }
|
---|
| 126 |
|
---|
[3232] | 127 | if (opts & LN_VERBOSE) {
|
---|
| 128 | printf("'%s' -> '%s'\n", src, *argv);
|
---|
| 129 | }
|
---|
| 130 |
|
---|
[821] | 131 | if (link_func(*argv, src) != 0) {
|
---|
[2725] | 132 | bb_simple_perror_msg(src);
|
---|
[821] | 133 | status = EXIT_FAILURE;
|
---|
| 134 | }
|
---|
| 135 |
|
---|
| 136 | free(src_name);
|
---|
| 137 |
|
---|
| 138 | } while ((++argv)[1]);
|
---|
| 139 |
|
---|
| 140 | return status;
|
---|
| 141 | }
|
---|