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 (4.9 kb)"
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:       "[-sfnbtv] [-S SUF] 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	Treat LINK as a file, not 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 #define LN_SYMLINK          (1 << 0)
45 #define LN_FORCE            (1 << 1)
46 #define LN_NODEREFERENCE    (1 << 2)
47 #define LN_BACKUP           (1 << 3)
48 #define LN_SUFFIX           (1 << 4)
49 #define LN_VERBOSE          (1 << 5)
50 #define LN_LINKFILE         (1 << 6)
51 
52 int ln_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
ln_main(int argc,char ** argv)53 int ln_main(int argc, char **argv)
54 {
55 	int status = EXIT_SUCCESS;
56 	int opts;
57 	char *last;
58 	char *src_name;
59 	char *src;
60 	char *suffix = (char*)"~";
61 	struct stat statbuf;
62 	int (*link_func)(const char *, const char *);
63 
64 	opts = getopt32(argv, "^" "sfnbS:vT" "\0" "-1", &suffix);
65 /*
66 	-s, --symbolic		make symbolic links instead of hard links
67 	-f, --force		remove existing destination files
68 	-n, --no-dereference	treat LINK_NAME as a normal file if it is a symbolic link to a directory
69 	-b			like --backup but does not accept an argument
70 	--backup[=CONTROL]	make a backup of each existing destination file
71 	-S, --suffix=SUFFIX	override the usual backup suffix
72 	-v, --verbose
73 	-T, --no-target-directory
74 	-d, -F, --directory	allow the superuser to attempt to hard link directories
75 	-i, --interactive	prompt whether to remove destinations
76 	-L, --logical		dereference TARGETs that are symbolic links
77 	-P, --physical		make hard links directly to symbolic links
78 	-r, --relative		create symbolic links relative to link location
79 	-t, --target-directory=DIRECTORY	specify the DIRECTORY in which to create the links
80  */
81 	last = argv[argc - 1];
82 	argv += optind;
83 	argc -= optind;
84 
85 	if ((opts & LN_LINKFILE) && argc > 2) {
86 		bb_simple_error_msg_and_die("-T accepts 2 args max");
87 	}
88 
89 	if (!argv[1]) {
90 		/* "ln PATH/TO/FILE" -> "ln PATH/TO/FILE FILE" */
91 		*--argv = last;
92 		/* xstrdup is needed: "ln -s PATH/TO/FILE/" is equivalent to
93 		 * "ln -s PATH/TO/FILE/ FILE", not "ln -s PATH/TO/FILE FILE"
94 		 */
95 		last = bb_get_last_path_component_strip(xstrdup(last));
96 	}
97 
98 	do {
99 		src_name = NULL;
100 		src = last;
101 
102 		if (is_directory(src,
103 				/*followlinks:*/ !(opts & (LN_NODEREFERENCE|LN_LINKFILE))
104 				/* Why LN_LINKFILE does not follow links:
105 				 * -T/--no-target-directory implies -n/--no-dereference
106 				 */
107 				)
108 		) {
109 			if (opts & LN_LINKFILE) {
110 				bb_error_msg_and_die("'%s' is a directory", src);
111 			}
112 			src_name = xstrdup(*argv);
113 			src = concat_path_file(src, bb_get_last_path_component_strip(src_name));
114 			free(src_name);
115 			src_name = src;
116 		}
117 		if (!(opts & LN_SYMLINK) && stat(*argv, &statbuf)) {
118 			// coreutils: "ln dangling_symlink new_hardlink" works
119 			if (lstat(*argv, &statbuf) || !S_ISLNK(statbuf.st_mode)) {
120 				bb_simple_perror_msg(*argv);
121 				status = EXIT_FAILURE;
122 				free(src_name);
123 				continue;
124 			}
125 		}
126 
127 		if (opts & LN_BACKUP) {
128 			char *backup;
129 			backup = xasprintf("%s%s", src, suffix);
130 			if (rename(src, backup) < 0 && errno != ENOENT) {
131 				bb_simple_perror_msg(src);
132 				status = EXIT_FAILURE;
133 				free(backup);
134 				continue;
135 			}
136 			free(backup);
137 			/*
138 			 * When the source and dest are both hard links to the same
139 			 * inode, a rename may succeed even though nothing happened.
140 			 * Therefore, always unlink().
141 			 */
142 			unlink(src);
143 		} else if (opts & LN_FORCE) {
144 			unlink(src);
145 		}
146 
147 		link_func = link;
148 		if (opts & LN_SYMLINK) {
149 			link_func = symlink;
150 		}
151 
152 		if (opts & LN_VERBOSE) {
153 			printf("'%s' -> '%s'\n", src, *argv);
154 		}
155 
156 		if (link_func(*argv, src) != 0) {
157 			bb_simple_perror_msg(src);
158 			status = EXIT_FAILURE;
159 		}
160 
161 		free(src_name);
162 	} while ((++argv)[1]);
163 
164 	return status;
165 }
166