xref: /freebsd/bin/ln/ln.c (revision 97759ccc715c4b365432c16d763c50eecfcb1100)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1987, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #if 0
33 #ifndef lint
34 static char const copyright[] =
35 "@(#) Copyright (c) 1987, 1993, 1994\n\
36 	The Regents of the University of California.  All rights reserved.\n";
37 #endif /* not lint */
38 
39 #endif
40 #include <sys/cdefs.h>
41 #include <sys/param.h>
42 #include <sys/stat.h>
43 
44 #include <err.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <libgen.h>
48 #include <limits.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53 
54 static int	fflag;			/* Unlink existing files. */
55 static int	Fflag;			/* Remove empty directories also. */
56 static int	hflag;			/* Check new name for symlink first. */
57 static int	iflag;			/* Interactive mode. */
58 static int	Pflag;			/* Create hard links to symlinks. */
59 static int	sflag;			/* Symbolic, not hard, link. */
60 static int	vflag;			/* Verbose output. */
61 static int	wflag;			/* Warn if symlink target does not
62 					 * exist, and -f is not enabled. */
63 static char	linkch;
64 
65 static int	linkit(const char *, const char *, int);
66 static void	usage(void);
67 
68 int
69 main(int argc, char *argv[])
70 {
71 	struct stat sb;
72 	char *p, *targetdir;
73 	int ch, exitval;
74 
75 	/*
76 	 * Test for the special case where the utility is called as
77 	 * "link", for which the functionality provided is greatly
78 	 * simplified.
79 	 */
80 	if ((p = strrchr(argv[0], '/')) == NULL)
81 		p = argv[0];
82 	else
83 		++p;
84 	if (strcmp(p, "link") == 0) {
85 		while (getopt(argc, argv, "") != -1)
86 			usage();
87 		argc -= optind;
88 		argv += optind;
89 		if (argc != 2)
90 			usage();
91 		exit(linkit(argv[0], argv[1], 0));
92 	}
93 
94 	while ((ch = getopt(argc, argv, "FLPfhinsvw")) != -1)
95 		switch (ch) {
96 		case 'F':
97 			Fflag = 1;
98 			break;
99 		case 'L':
100 			Pflag = 0;
101 			break;
102 		case 'P':
103 			Pflag = 1;
104 			break;
105 		case 'f':
106 			fflag = 1;
107 			iflag = 0;
108 			wflag = 0;
109 			break;
110 		case 'h':
111 		case 'n':
112 			hflag = 1;
113 			break;
114 		case 'i':
115 			iflag = 1;
116 			fflag = 0;
117 			break;
118 		case 's':
119 			sflag = 1;
120 			break;
121 		case 'v':
122 			vflag = 1;
123 			break;
124 		case 'w':
125 			wflag = 1;
126 			break;
127 		case '?':
128 		default:
129 			usage();
130 		}
131 
132 	argv += optind;
133 	argc -= optind;
134 
135 	linkch = sflag ? '-' : '=';
136 	if (sflag == 0)
137 		Fflag = 0;
138 	if (Fflag == 1 && iflag == 0) {
139 		fflag = 1;
140 		wflag = 0;		/* Implied when fflag != 0 */
141 	}
142 
143 	switch(argc) {
144 	case 0:
145 		usage();
146 		/* NOTREACHED */
147 	case 1:				/* ln source */
148 		exit(linkit(argv[0], ".", 1));
149 	case 2:				/* ln source target */
150 		exit(linkit(argv[0], argv[1], 0));
151 	default:
152 		;
153 	}
154 					/* ln source1 source2 directory */
155 	targetdir = argv[argc - 1];
156 	if (hflag && lstat(targetdir, &sb) == 0 && S_ISLNK(sb.st_mode)) {
157 		/*
158 		 * We were asked not to follow symlinks, but found one at
159 		 * the target--simulate "not a directory" error
160 		 */
161 		errno = ENOTDIR;
162 		err(1, "%s", targetdir);
163 	}
164 	if (stat(targetdir, &sb))
165 		err(1, "%s", targetdir);
166 	if (!S_ISDIR(sb.st_mode))
167 		usage();
168 	for (exitval = 0; *argv != targetdir; ++argv)
169 		exitval |= linkit(*argv, targetdir, 1);
170 	exit(exitval);
171 }
172 
173 /*
174  * Two pathnames refer to the same directory entry if the directories match
175  * and the final components' names match.
176  */
177 static int
178 samedirent(const char *path1, const char *path2)
179 {
180 	const char *file1, *file2;
181 	char pathbuf[PATH_MAX];
182 	struct stat sb1, sb2;
183 
184 	if (strcmp(path1, path2) == 0)
185 		return 1;
186 	file1 = strrchr(path1, '/');
187 	if (file1 != NULL)
188 		file1++;
189 	else
190 		file1 = path1;
191 	file2 = strrchr(path2, '/');
192 	if (file2 != NULL)
193 		file2++;
194 	else
195 		file2 = path2;
196 	if (strcmp(file1, file2) != 0)
197 		return 0;
198 	if (file1 - path1 >= PATH_MAX || file2 - path2 >= PATH_MAX)
199 		return 0;
200 	if (file1 == path1)
201 		memcpy(pathbuf, ".", 2);
202 	else {
203 		memcpy(pathbuf, path1, file1 - path1);
204 		pathbuf[file1 - path1] = '\0';
205 	}
206 	if (stat(pathbuf, &sb1) != 0)
207 		return 0;
208 	if (file2 == path2)
209 		memcpy(pathbuf, ".", 2);
210 	else {
211 		memcpy(pathbuf, path2, file2 - path2);
212 		pathbuf[file2 - path2] = '\0';
213 	}
214 	if (stat(pathbuf, &sb2) != 0)
215 		return 0;
216 	return sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino;
217 }
218 
219 static int
220 linkit(const char *source, const char *target, int isdir)
221 {
222 	struct stat sb;
223 	const char *p;
224 	int ch, exists, first;
225 	char path[PATH_MAX];
226 	char wbuf[PATH_MAX];
227 	char bbuf[PATH_MAX];
228 
229 	if (!sflag) {
230 		/* If source doesn't exist, quit now. */
231 		if ((Pflag ? lstat : stat)(source, &sb)) {
232 			warn("%s", source);
233 			return (1);
234 		}
235 		/* Only symbolic links to directories. */
236 		if (S_ISDIR(sb.st_mode)) {
237 			errno = EISDIR;
238 			warn("%s", source);
239 			return (1);
240 		}
241 	}
242 
243 	/*
244 	 * If the target is a directory (and not a symlink if hflag),
245 	 * append the source's name, unless Fflag is set.
246 	 */
247 	if (!Fflag && (isdir ||
248 	    (lstat(target, &sb) == 0 && S_ISDIR(sb.st_mode)) ||
249 	    (!hflag && stat(target, &sb) == 0 && S_ISDIR(sb.st_mode)))) {
250 		if (strlcpy(bbuf, source, sizeof(bbuf)) >= sizeof(bbuf) ||
251 		    (p = basename(bbuf)) == NULL ||
252 		    snprintf(path, sizeof(path), "%s/%s", target, p) >=
253 		    (ssize_t)sizeof(path)) {
254 			errno = ENAMETOOLONG;
255 			warn("%s", source);
256 			return (1);
257 		}
258 		target = path;
259 	}
260 
261 	/*
262 	 * If the link source doesn't exist, and a symbolic link was
263 	 * requested, and -w was specified, give a warning.
264 	 */
265 	if (sflag && wflag) {
266 		if (*source == '/') {
267 			/* Absolute link source. */
268 			if (stat(source, &sb) != 0)
269 				 warn("warning: %s inaccessible", source);
270 		} else {
271 			/*
272 			 * Relative symlink source.  Try to construct the
273 			 * absolute path of the source, by appending `source'
274 			 * to the parent directory of the target.
275 			 */
276 			strlcpy(bbuf, target, sizeof(bbuf));
277 			p = dirname(bbuf);
278 			if (p != NULL) {
279 				(void)snprintf(wbuf, sizeof(wbuf), "%s/%s",
280 						p, source);
281 				if (stat(wbuf, &sb) != 0)
282 					warn("warning: %s", source);
283 			}
284 		}
285 	}
286 
287 	/*
288 	 * If the file exists, first check it is not the same directory entry.
289 	 */
290 	exists = !lstat(target, &sb);
291 	if (exists) {
292 		if (!sflag && samedirent(source, target)) {
293 			warnx("%s and %s are the same directory entry",
294 			    source, target);
295 			return (1);
296 		}
297 	}
298 	/*
299 	 * Then unlink it forcibly if -f was specified
300 	 * and interactively if -i was specified.
301 	 */
302 	if (fflag && exists) {
303 		if (Fflag && S_ISDIR(sb.st_mode)) {
304 			if (rmdir(target)) {
305 				warn("%s", target);
306 				return (1);
307 			}
308 		} else if (unlink(target)) {
309 			warn("%s", target);
310 			return (1);
311 		}
312 	} else if (iflag && exists) {
313 		fflush(stdout);
314 		fprintf(stderr, "replace %s? ", target);
315 
316 		first = ch = getchar();
317 		while(ch != '\n' && ch != EOF)
318 			ch = getchar();
319 		if (first != 'y' && first != 'Y') {
320 			fprintf(stderr, "not replaced\n");
321 			return (1);
322 		}
323 
324 		if (Fflag && S_ISDIR(sb.st_mode)) {
325 			if (rmdir(target)) {
326 				warn("%s", target);
327 				return (1);
328 			}
329 		} else if (unlink(target)) {
330 			warn("%s", target);
331 			return (1);
332 		}
333 	}
334 
335 	/* Attempt the link. */
336 	if (sflag ? symlink(source, target) :
337 	    linkat(AT_FDCWD, source, AT_FDCWD, target,
338 	    Pflag ? 0 : AT_SYMLINK_FOLLOW)) {
339 		warn("%s", target);
340 		return (1);
341 	}
342 	if (vflag)
343 		(void)printf("%s %c> %s\n", target, linkch, source);
344 	return (0);
345 }
346 
347 static void
348 usage(void)
349 {
350 	(void)fprintf(stderr, "%s\n%s\n%s\n",
351 	    "usage: ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file [target_file]",
352 	    "       ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file ... target_dir",
353 	    "       link source_file target_file");
354 	exit(1);
355 }
356