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 | *
|
---|
7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree.
|
---|
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 |
|
---|
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 |
|
---|
31 | #include "libbb.h"
|
---|
32 |
|
---|
33 | /* This is a NOEXEC applet. Be very careful! */
|
---|
34 |
|
---|
35 |
|
---|
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)
|
---|
43 |
|
---|
44 | int ln_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
---|
45 | int ln_main(int argc, char **argv)
|
---|
46 | {
|
---|
47 | int status = EXIT_SUCCESS;
|
---|
48 | int opts;
|
---|
49 | char *last;
|
---|
50 | char *src_name;
|
---|
51 | char *src;
|
---|
52 | char *suffix = (char*)"~";
|
---|
53 | struct stat statbuf;
|
---|
54 | int (*link_func)(const char *, const char *);
|
---|
55 |
|
---|
56 | opt_complementary = "-1"; /* min one arg */
|
---|
57 | opts = getopt32(argv, "sfnbS:vT", &suffix);
|
---|
58 |
|
---|
59 | last = argv[argc - 1];
|
---|
60 | argv += optind;
|
---|
61 | argc -= optind;
|
---|
62 |
|
---|
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" */
|
---|
69 | *--argv = last;
|
---|
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 | */
|
---|
73 | last = bb_get_last_path_component_strip(xstrdup(last));
|
---|
74 | }
|
---|
75 |
|
---|
76 | do {
|
---|
77 | src_name = NULL;
|
---|
78 | src = last;
|
---|
79 |
|
---|
80 | if (is_directory(src,
|
---|
81 | (opts & LN_NODEREFERENCE) ^ LN_NODEREFERENCE
|
---|
82 | )
|
---|
83 | ) {
|
---|
84 | if (opts & LN_LINKFILE) {
|
---|
85 | bb_error_msg_and_die("'%s' is a directory", src);
|
---|
86 | }
|
---|
87 | src_name = xstrdup(*argv);
|
---|
88 | src = concat_path_file(src, bb_get_last_path_component_strip(src_name));
|
---|
89 | free(src_name);
|
---|
90 | src_name = src;
|
---|
91 | }
|
---|
92 | if (!(opts & LN_SYMLINK) && stat(*argv, &statbuf)) {
|
---|
93 | // coreutils: "ln dangling_symlink new_hardlink" works
|
---|
94 | if (lstat(*argv, &statbuf) || !S_ISLNK(statbuf.st_mode)) {
|
---|
95 | bb_simple_perror_msg(*argv);
|
---|
96 | status = EXIT_FAILURE;
|
---|
97 | free(src_name);
|
---|
98 | continue;
|
---|
99 | }
|
---|
100 | }
|
---|
101 |
|
---|
102 | if (opts & LN_BACKUP) {
|
---|
103 | char *backup;
|
---|
104 | backup = xasprintf("%s%s", src, suffix);
|
---|
105 | if (rename(src, backup) < 0 && errno != ENOENT) {
|
---|
106 | bb_simple_perror_msg(src);
|
---|
107 | status = EXIT_FAILURE;
|
---|
108 | free(backup);
|
---|
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);
|
---|
118 | } else if (opts & LN_FORCE) {
|
---|
119 | unlink(src);
|
---|
120 | }
|
---|
121 |
|
---|
122 | link_func = link;
|
---|
123 | if (opts & LN_SYMLINK) {
|
---|
124 | link_func = symlink;
|
---|
125 | }
|
---|
126 |
|
---|
127 | if (opts & LN_VERBOSE) {
|
---|
128 | printf("'%s' -> '%s'\n", src, *argv);
|
---|
129 | }
|
---|
130 |
|
---|
131 | if (link_func(*argv, src) != 0) {
|
---|
132 | bb_simple_perror_msg(src);
|
---|
133 | status = EXIT_FAILURE;
|
---|
134 | }
|
---|
135 |
|
---|
136 | free(src_name);
|
---|
137 |
|
---|
138 | } while ((++argv)[1]);
|
---|
139 |
|
---|
140 | return status;
|
---|
141 | }
|
---|