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