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