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