1 /* 2 * Copyright (c) 1987, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * @(#) Copyright (c) 1987, 1993, 1994 The Regents of the University of California. All rights reserved. 30 * @(#)ln.c 8.2 (Berkeley) 3/31/94 31 * $FreeBSD: src/bin/ln/ln.c,v 1.15.2.4 2002/07/12 07:34:38 tjr Exp $ 32 * $DragonFly: src/bin/ln/ln.c,v 1.11 2006/09/25 09:27:21 corecode Exp $ 33 */ 34 35 #include <sys/param.h> 36 #include <sys/stat.h> 37 38 #include <err.h> 39 #include <errno.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 hflag; /* Check new name for symlink first. */ 47 static int iflag; /* Interactive mode. */ 48 static int sflag; /* Symbolic, not hard, link. */ 49 static int vflag; /* Verbose output. */ 50 /* System link call. */ 51 static int (*linkf)(const char *, const char *); 52 static char linkch; 53 54 static int linkit(const char *, const char *, int); 55 static void usage(void); 56 57 int 58 main(int argc, char *argv[]) 59 { 60 struct stat sb; 61 char *p, *sourcedir; 62 int ch, exitval; 63 64 /* 65 * Test for the special case where the utility is called as 66 * "link", for which the functionality provided is greatly 67 * simplified. 68 */ 69 if ((p = strrchr(argv[0], '/')) == NULL) 70 p = argv[0]; 71 else 72 ++p; 73 if (strcmp(p, "link") == 0) { 74 while (getopt(argc, argv, "") != -1) 75 usage(); 76 argc -= optind; 77 argv += optind; 78 if (argc != 2) 79 usage(); 80 linkf = link; 81 exit(linkit(argv[0], argv[1], 0)); 82 } 83 84 while ((ch = getopt(argc, argv, "fhinsv")) != -1) { 85 switch (ch) { 86 case 'f': 87 fflag = 1; 88 iflag = 0; 89 break; 90 case 'h': 91 case 'n': 92 hflag = 1; 93 break; 94 case 'i': 95 iflag = 1; 96 fflag = 0; 97 break; 98 case 's': 99 sflag = 1; 100 break; 101 case 'v': 102 vflag = 1; 103 break; 104 case '?': 105 default: 106 usage(); 107 } 108 } 109 110 argv += optind; 111 argc -= optind; 112 113 linkf = sflag ? symlink : link; 114 linkch = sflag ? '-' : '='; 115 116 switch (argc) { 117 case 0: 118 usage(); 119 /* NOTREACHED */ 120 case 1: /* ln target */ 121 exit(linkit(argv[0], ".", 1)); 122 case 2: /* ln target source */ 123 exit(linkit(argv[0], argv[1], 0)); 124 default: 125 ; 126 } 127 /* ln target1 target2 directory */ 128 sourcedir = argv[argc - 1]; 129 if (hflag && lstat(sourcedir, &sb) == 0 && S_ISLNK(sb.st_mode)) { 130 /* 131 * We were asked not to follow symlinks, but found one at 132 * the target--simulate "not a directory" error 133 */ 134 errc(1, ENOTDIR, "%s", sourcedir); 135 } 136 if (stat(sourcedir, &sb) != 0) 137 err(1, "%s", sourcedir); 138 if (!S_ISDIR(sb.st_mode)) 139 usage(); 140 for (exitval = 0; *argv != sourcedir; ++argv) 141 exitval |= linkit(*argv, sourcedir, 1); 142 exit(exitval); 143 } 144 145 /* 146 * Nomenclature warning! 147 * 148 * In this source "target" and "source" are used the opposite way they 149 * are used in the ln(1) manual. Here "target" is the existing file and 150 * "source" specifies the to-be-created link to "target". 151 */ 152 static int 153 linkit(const char *target, const char *source, int isdir) 154 { 155 struct stat sb; 156 const char *p; 157 int ch, exists, first; 158 char path[PATH_MAX]; 159 160 if (!sflag) { 161 /* If target doesn't exist, quit now. */ 162 if (stat(target, &sb) != 0) { 163 warn("%s", target); 164 return (1); 165 } 166 /* Only symbolic links to directories. */ 167 if (S_ISDIR(sb.st_mode)) { 168 errno = EISDIR; 169 warn("%s", target); 170 return (1); 171 } 172 } 173 174 /* 175 * If the source is a directory (and not a symlink if hflag), 176 * append the target's name. 177 */ 178 if (isdir || 179 (lstat(source, &sb) == 0 && S_ISDIR(sb.st_mode)) || 180 (!hflag && stat(source, &sb) == 0 && S_ISDIR(sb.st_mode))) { 181 if ((p = strrchr(target, '/')) == NULL) 182 p = target; 183 else 184 ++p; 185 if (snprintf(path, sizeof(path), "%s/%s", source, p) >= 186 (int)sizeof(path)) { 187 errno = ENAMETOOLONG; 188 warn("%s", target); 189 return (1); 190 } 191 source = path; 192 } 193 194 exists = (lstat(source, &sb) == 0); 195 /* 196 * If doing hard links and the source (destination) exists and it 197 * actually is the same file like the target (existing file), we 198 * complain that the files are identical. If -f is specified, we 199 * accept the job as already done and return with success. 200 */ 201 if (exists && !sflag) { 202 struct stat tsb; 203 204 if (stat(target, &tsb) != 0) { 205 warn("%s: disappeared", target); 206 return (1); 207 } 208 209 if (tsb.st_dev == sb.st_dev && tsb.st_ino == sb.st_ino) { 210 warnx("%s and %s are identical (not linked).", target, source); 211 if (fflag) 212 return (0); 213 else 214 return (1); 215 } 216 } 217 /* 218 * If the file exists, then unlink it forcibly if -f was specified 219 * and interactively if -i was specified. 220 */ 221 if (fflag && exists) { 222 if (unlink(source) != 0) { 223 warn("%s", source); 224 return (1); 225 } 226 } else if (iflag && exists) { 227 fflush(stdout); 228 fprintf(stderr, "replace %s? ", source); 229 230 first = ch = getchar(); 231 while(ch != '\n' && ch != EOF) 232 ch = getchar(); 233 if (first != 'y' && first != 'Y') { 234 fprintf(stderr, "not replaced\n"); 235 return (1); 236 } 237 238 if (unlink(source) != 0) { 239 warn("%s", source); 240 return (1); 241 } 242 } 243 244 /* Attempt the link. */ 245 if ((*linkf)(target, source) != 0) { 246 warn("%s", source); 247 return (1); 248 } 249 if (vflag) 250 printf("%s %c> %s\n", source, linkch, target); 251 return (0); 252 } 253 254 static void 255 usage(void) 256 { 257 fprintf(stderr, "%s\n%s\n%s\n", 258 "usage: ln [-fhinsv] source_file [target_file]", 259 " ln [-fhinsv] source_file ... target_dir", 260 " link source_file target_file"); 261 exit(1); 262 } 263