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 static int
linkit(const char * source,const char * target,bool isdir)216 linkit(const char *source, const char *target, bool isdir)
217 {
218 char path[PATH_MAX];
219 char wbuf[PATH_MAX];
220 char bbuf[PATH_MAX];
221 struct stat sb;
222 const char *p;
223 int ch, first;
224 bool exists;
225
226 if (!sflag) {
227 /* If source doesn't exist, quit now. */
228 if ((Pflag ? lstat : stat)(source, &sb)) {
229 warn("%s", source);
230 return (1);
231 }
232 /* Only symbolic links to directories. */
233 if (S_ISDIR(sb.st_mode)) {
234 errno = EISDIR;
235 warn("%s", source);
236 return (1);
237 }
238 }
239
240 /*
241 * If the target is a directory (and not a symlink if hflag),
242 * append the source's name, unless Fflag is set.
243 */
244 if (!Fflag && (isdir ||
245 (lstat(target, &sb) == 0 && S_ISDIR(sb.st_mode)) ||
246 (!hflag && stat(target, &sb) == 0 && S_ISDIR(sb.st_mode)))) {
247 if (strlcpy(bbuf, source, sizeof(bbuf)) >= sizeof(bbuf) ||
248 (p = basename(bbuf)) == NULL ||
249 snprintf(path, sizeof(path), "%s/%s", target, p) >=
250 (ssize_t)sizeof(path)) {
251 errno = ENAMETOOLONG;
252 warn("%s", source);
253 return (1);
254 }
255 target = path;
256 }
257
258 /*
259 * If the link source doesn't exist, and a symbolic link was
260 * requested, and -w was specified, give a warning.
261 */
262 if (sflag && wflag) {
263 if (*source == '/') {
264 /* Absolute link source. */
265 if (stat(source, &sb) != 0)
266 warn("warning: %s inaccessible", source);
267 } else {
268 /*
269 * Relative symlink source. Try to construct the
270 * absolute path of the source, by appending `source'
271 * to the parent directory of the target.
272 */
273 strlcpy(bbuf, target, sizeof(bbuf));
274 p = dirname(bbuf);
275 if (p != NULL) {
276 (void)snprintf(wbuf, sizeof(wbuf), "%s/%s",
277 p, source);
278 if (stat(wbuf, &sb) != 0)
279 warn("warning: %s", source);
280 }
281 }
282 }
283
284 /*
285 * If the file exists, first check it is not the same directory entry.
286 */
287 exists = lstat(target, &sb) == 0;
288 if (exists) {
289 if (!sflag && samedirent(source, target)) {
290 warnx("%s and %s are the same directory entry",
291 source, target);
292 return (1);
293 }
294 }
295 /*
296 * Then unlink it forcibly if -f was specified
297 * and interactively if -i was specified.
298 */
299 if (fflag && exists) {
300 if (Fflag && S_ISDIR(sb.st_mode)) {
301 if (rmdir(target)) {
302 warn("%s", target);
303 return (1);
304 }
305 } else if (unlink(target)) {
306 warn("%s", target);
307 return (1);
308 }
309 } else if (iflag && exists) {
310 fflush(stdout);
311 fprintf(stderr, "replace %s? ", target);
312
313 first = ch = getchar();
314 while(ch != '\n' && ch != EOF)
315 ch = getchar();
316 if (first != 'y' && first != 'Y') {
317 fprintf(stderr, "not replaced\n");
318 return (1);
319 }
320
321 if (Fflag && S_ISDIR(sb.st_mode)) {
322 if (rmdir(target)) {
323 warn("%s", target);
324 return (1);
325 }
326 } else if (unlink(target)) {
327 warn("%s", target);
328 return (1);
329 }
330 }
331
332 /* Attempt the link. */
333 if (sflag ? symlink(source, target) :
334 linkat(AT_FDCWD, source, AT_FDCWD, target,
335 Pflag ? 0 : AT_SYMLINK_FOLLOW)) {
336 warn("%s", target);
337 return (1);
338 }
339 if (vflag)
340 (void)printf("%s %c> %s\n", target, linkch, source);
341 return (0);
342 }
343
344 static void
link_usage(void)345 link_usage(void)
346 {
347 (void)fprintf(stderr, "usage: link source_file target_file\n");
348 exit(1);
349 }
350
351 static void
usage(void)352 usage(void)
353 {
354 (void)fprintf(stderr, "%s\n%s\n",
355 "usage: ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file [target_file]",
356 " ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file ... target_dir");
357 exit(1);
358 }
359