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 #include <sys/param.h>
33 #include <sys/stat.h>
34
35 #include <err.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <libgen.h>
39 #include <limits.h>
40 #include <stdbool.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45
46 static bool fflag; /* Unlink existing files. */
47 static bool Fflag; /* Remove empty directories also. */
48 static bool hflag; /* Check new name for symlink first. */
49 static bool iflag; /* Interactive mode. */
50 static bool Pflag; /* Create hard links to symlinks. */
51 static bool sflag; /* Symbolic, not hard, link. */
52 static bool vflag; /* Verbose output. */
53 static bool wflag; /* Warn if symlink target does not
54 * exist, and -f is not enabled. */
55 static char linkch;
56
57 static int linkit(const char *, const char *, bool);
58 static void link_usage(void) __dead2;
59 static void usage(void) __dead2;
60
61 int
main(int argc,char * argv[])62 main(int argc, char *argv[])
63 {
64 struct stat sb;
65 char *targetdir;
66 int ch, exitval;
67
68 /*
69 * Test for the special case where the utility is called as
70 * "link", for which the functionality provided is greatly
71 * simplified.
72 */
73 if (strcmp(getprogname(), "link") == 0) {
74 while (getopt(argc, argv, "") != -1)
75 link_usage();
76 argc -= optind;
77 argv += optind;
78 if (argc != 2)
79 link_usage();
80 if (lstat(argv[1], &sb) == 0)
81 errc(1, EEXIST, "%s", argv[1]);
82 /*
83 * We could simply call link(2) here, but linkit()
84 * performs additional checks and gives better
85 * diagnostics.
86 */
87 exit(linkit(argv[0], argv[1], false));
88 }
89
90 while ((ch = getopt(argc, argv, "FLPfhinsvw")) != -1)
91 switch (ch) {
92 case 'F':
93 Fflag = true;
94 break;
95 case 'L':
96 Pflag = false;
97 break;
98 case 'P':
99 Pflag = true;
100 break;
101 case 'f':
102 fflag = true;
103 iflag = false;
104 wflag = false;
105 break;
106 case 'h':
107 case 'n':
108 hflag = true;
109 break;
110 case 'i':
111 iflag = true;
112 fflag = false;
113 break;
114 case 's':
115 sflag = true;
116 break;
117 case 'v':
118 vflag = true;
119 break;
120 case 'w':
121 wflag = true;
122 break;
123 case '?':
124 default:
125 usage();
126 }
127
128 argv += optind;
129 argc -= optind;
130
131 linkch = sflag ? '-' : '=';
132 if (!sflag)
133 Fflag = false;
134 if (Fflag && !iflag) {
135 fflag = true;
136 wflag = false; /* Implied when fflag is true */
137 }
138
139 switch (argc) {
140 case 0:
141 usage();
142 /* NOTREACHED */
143 case 1: /* ln source */
144 exit(linkit(argv[0], ".", true));
145 case 2: /* ln source target */
146 exit(linkit(argv[0], argv[1], false));
147 default:
148 ;
149 }
150 /* ln source1 source2 directory */
151 targetdir = argv[argc - 1];
152 if (hflag && lstat(targetdir, &sb) == 0 && S_ISLNK(sb.st_mode)) {
153 /*
154 * We were asked not to follow symlinks, but found one at
155 * the target--simulate "not a directory" error
156 */
157 errno = ENOTDIR;
158 err(1, "%s", targetdir);
159 }
160 if (stat(targetdir, &sb))
161 err(1, "%s", targetdir);
162 if (!S_ISDIR(sb.st_mode))
163 usage();
164 for (exitval = 0; *argv != targetdir; ++argv)
165 exitval |= linkit(*argv, targetdir, true);
166 exit(exitval);
167 }
168
169 /*
170 * Two pathnames refer to the same directory entry if the directories match
171 * and the final components' names match.
172 */
173 static int
samedirent(const char * path1,const char * path2)174 samedirent(const char *path1, const char *path2)
175 {
176 const char *file1, *file2;
177 char pathbuf[PATH_MAX];
178 struct stat sb1, sb2;
179
180 if (strcmp(path1, path2) == 0)
181 return 1;
182 file1 = strrchr(path1, '/');
183 if (file1 != NULL)
184 file1++;
185 else
186 file1 = path1;
187 file2 = strrchr(path2, '/');
188 if (file2 != NULL)
189 file2++;
190 else
191 file2 = path2;
192 if (strcmp(file1, file2) != 0)
193 return 0;
194 if (file1 - path1 >= PATH_MAX || file2 - path2 >= PATH_MAX)
195 return 0;
196 if (file1 == path1)
197 memcpy(pathbuf, ".", 2);
198 else {
199 memcpy(pathbuf, path1, file1 - path1);
200 pathbuf[file1 - path1] = '\0';
201 }
202 if (stat(pathbuf, &sb1) != 0)
203 return 0;
204 if (file2 == path2)
205 memcpy(pathbuf, ".", 2);
206 else {
207 memcpy(pathbuf, path2, file2 - path2);
208 pathbuf[file2 - path2] = '\0';
209 }
210 if (stat(pathbuf, &sb2) != 0)
211 return 0;
212 return sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino;
213 }
214
215 /*
216 * Create a link to source. If target is a directory (and some additional
217 * conditions apply, see comments within) the link will be created within
218 * target and have the basename of source. Otherwise, the link will be
219 * named target. If isdir is true, target has already been determined to
220 * be a directory; otherwise, we will check, if needed.
221 */
222 static int
linkit(const char * source,const char * target,bool isdir)223 linkit(const char *source, const char *target, bool isdir)
224 {
225 char path[PATH_MAX];
226 char wbuf[PATH_MAX];
227 char bbuf[PATH_MAX];
228 struct stat sb;
229 const char *p;
230 int ch, first;
231 bool append, exists;
232
233 if (!sflag) {
234 /* If source doesn't exist, quit now. */
235 if ((Pflag ? lstat : stat)(source, &sb)) {
236 warn("%s", source);
237 return (1);
238 }
239 /* Only symbolic links to directories. */
240 if (S_ISDIR(sb.st_mode)) {
241 errno = EISDIR;
242 warn("%s", source);
243 return (1);
244 }
245 }
246
247 /*
248 * Append a slash and the source's basename if:
249 * - the target is "." or ends in "/" or "/.", or
250 * - the target is a directory (and not a symlink if hflag) and
251 * Fflag is not set
252 */
253 if ((p = strrchr(target, '/')) == NULL)
254 p = target;
255 else
256 p++;
257 append = false;
258 if (p[0] == '\0' || (p[0] == '.' && p[1] == '\0')) {
259 append = true;
260 } else if (!Fflag) {
261 if (isdir || (lstat(target, &sb) == 0 && S_ISDIR(sb.st_mode)) ||
262 (!hflag && stat(target, &sb) == 0 && S_ISDIR(sb.st_mode))) {
263 append = true;
264 }
265 }
266 if (append) {
267 if (strlcpy(bbuf, source, sizeof(bbuf)) >= sizeof(bbuf) ||
268 (p = basename(bbuf)) == NULL /* can't happen */ ||
269 snprintf(path, sizeof(path), "%s/%s", target, p) >=
270 (ssize_t)sizeof(path)) {
271 errno = ENAMETOOLONG;
272 warn("%s", source);
273 return (1);
274 }
275 target = path;
276 }
277
278 /*
279 * If the link source doesn't exist, and a symbolic link was
280 * requested, and -w was specified, give a warning.
281 */
282 if (sflag && wflag) {
283 if (*source == '/') {
284 /* Absolute link source. */
285 if (stat(source, &sb) != 0)
286 warn("warning: %s inaccessible", source);
287 } else {
288 /*
289 * Relative symlink source. Try to construct the
290 * absolute path of the source, by appending `source'
291 * to the parent directory of the target.
292 */
293 strlcpy(bbuf, target, sizeof(bbuf));
294 p = dirname(bbuf);
295 if (p != NULL) {
296 (void)snprintf(wbuf, sizeof(wbuf), "%s/%s",
297 p, source);
298 if (stat(wbuf, &sb) != 0)
299 warn("warning: %s", source);
300 }
301 }
302 }
303
304 /*
305 * If the file exists, first check it is not the same directory entry.
306 */
307 exists = lstat(target, &sb) == 0;
308 if (exists) {
309 if (!sflag && samedirent(source, target)) {
310 warnx("%s and %s are the same directory entry",
311 source, target);
312 return (1);
313 }
314 }
315 /*
316 * Then unlink it forcibly if -f was specified
317 * and interactively if -i was specified.
318 */
319 if (fflag && exists) {
320 if (Fflag && S_ISDIR(sb.st_mode)) {
321 if (rmdir(target)) {
322 warn("%s", target);
323 return (1);
324 }
325 } else if (unlink(target)) {
326 warn("%s", target);
327 return (1);
328 }
329 } else if (iflag && exists) {
330 fflush(stdout);
331 fprintf(stderr, "replace %s? ", target);
332
333 first = ch = getchar();
334 while(ch != '\n' && ch != EOF)
335 ch = getchar();
336 if (first != 'y' && first != 'Y') {
337 fprintf(stderr, "not replaced\n");
338 return (1);
339 }
340
341 if (Fflag && S_ISDIR(sb.st_mode)) {
342 if (rmdir(target)) {
343 warn("%s", target);
344 return (1);
345 }
346 } else if (unlink(target)) {
347 warn("%s", target);
348 return (1);
349 }
350 }
351
352 /* Attempt the link. */
353 if (sflag ? symlink(source, target) :
354 linkat(AT_FDCWD, source, AT_FDCWD, target,
355 Pflag ? 0 : AT_SYMLINK_FOLLOW)) {
356 warn("%s", target);
357 return (1);
358 }
359 if (vflag)
360 (void)printf("%s %c> %s\n", target, linkch, source);
361 return (0);
362 }
363
364 static void
link_usage(void)365 link_usage(void)
366 {
367 (void)fprintf(stderr, "usage: link source_file target_file\n");
368 exit(1);
369 }
370
371 static void
usage(void)372 usage(void)
373 {
374 (void)fprintf(stderr, "%s\n%s\n",
375 "usage: ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file [target_file]",
376 " ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file ... target_dir");
377 exit(1);
378 }
379