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