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