1 /* $OpenBSD: ln.c,v 1.19 2013/03/12 06:00:05 guenther Exp $ */ 2 /* $NetBSD: ln.c,v 1.10 1995/03/21 09:06:10 cgd Exp $ */ 3 4 /* 5 * Copyright (c) 1987, 1993, 1994 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/stat.h> 35 36 #include <err.h> 37 #include <errno.h> 38 #include <fcntl.h> 39 #include <libgen.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 45 int dirflag; /* Undocumented directory flag. */ 46 int fflag; /* Unlink existing files. */ 47 int hflag; /* Check new name for symlink first. */ 48 int Pflag; /* Hard link to symlink. */ 49 int sflag; /* Symbolic, not hard, link. */ 50 51 int linkit(char *, char *, int); 52 void usage(void) __dead; 53 54 int 55 main(int argc, char *argv[]) 56 { 57 struct stat sb; 58 int ch, exitval; 59 char *sourcedir; 60 61 while ((ch = getopt(argc, argv, "FfhLnPs")) != -1) 62 switch (ch) { 63 case 'F': 64 dirflag = 1; /* XXX: deliberately undocumented. */ 65 break; 66 case 'f': 67 fflag = 1; 68 break; 69 case 'h': 70 case 'n': 71 hflag = 1; 72 break; 73 case 'L': 74 Pflag = 0; 75 break; 76 case 'P': 77 Pflag = 1; 78 break; 79 case 's': 80 sflag = 1; 81 break; 82 default: 83 usage(); 84 } 85 86 argv += optind; 87 argc -= optind; 88 89 switch(argc) { 90 case 0: 91 usage(); 92 case 1: /* ln target */ 93 exit(linkit(argv[0], ".", 1)); 94 case 2: /* ln target source */ 95 exit(linkit(argv[0], argv[1], 0)); 96 } 97 /* ln target1 target2 directory */ 98 sourcedir = argv[argc - 1]; 99 if (stat(sourcedir, &sb)) 100 err(1, "%s", sourcedir); 101 if (!S_ISDIR(sb.st_mode)) 102 usage(); 103 for (exitval = 0; *argv != sourcedir; ++argv) 104 exitval |= linkit(*argv, sourcedir, 1); 105 exit(exitval); 106 } 107 108 /* 109 * Nomenclature warning! 110 * 111 * In this source "target" and "source" are used the opposite way they 112 * are used in the ln(1) manual. Here "target" is the existing file and 113 * "source" specifies the to-be-created link to "target". 114 */ 115 int 116 linkit(char *target, char *source, int isdir) 117 { 118 struct stat sb; 119 char *p, path[MAXPATHLEN]; 120 int (*statf)(const char *, struct stat *); 121 int exists, n; 122 123 if (!sflag) { 124 /* If target doesn't exist, quit now. */ 125 if ((Pflag ? lstat : stat)(target, &sb)) { 126 warn("%s", target); 127 return (1); 128 } 129 /* Only symbolic links to directories, unless -F option used. */ 130 if (!dirflag && S_ISDIR(sb.st_mode)) { 131 errno = EISDIR; 132 warn("%s", target); 133 return (1); 134 } 135 } 136 137 statf = hflag ? lstat : stat; 138 139 /* If the source is a directory, append the target's name. */ 140 if (isdir || (!statf(source, &sb) && S_ISDIR(sb.st_mode))) { 141 if ((p = basename(target)) == NULL) { 142 warn("%s", target); 143 return (1); 144 } 145 n = snprintf(path, sizeof(path), "%s/%s", source, p); 146 if (n < 0 || n >= sizeof(path)) { 147 errno = ENAMETOOLONG; 148 warn("%s/%s", source, p); 149 return (1); 150 } 151 source = path; 152 } 153 154 exists = (lstat(source, &sb) == 0); 155 /* 156 * If doing hard links and the source (destination) exists and it 157 * actually is the same file like the target (existing file), we 158 * complain that the files are identical. If -f is specified, we 159 * accept the job as already done and return with success. 160 */ 161 if (exists && !sflag) { 162 struct stat tsb; 163 164 if ((Pflag ? lstat : stat)(target, &tsb)) { 165 warn("%s: disappeared", target); 166 return (1); 167 } 168 169 if (tsb.st_dev == sb.st_dev && tsb.st_ino == sb.st_ino) { 170 if (fflag) 171 return (0); 172 else { 173 warnx("%s and %s are identical (nothing done).", 174 target, source); 175 return (1); 176 } 177 } 178 } 179 /* 180 * If the file exists, and -f was specified, unlink it. 181 * Attempt the link. 182 */ 183 if ((fflag && unlink(source) < 0 && errno != ENOENT) || 184 sflag ? symlink(target, source) : 185 linkat(AT_FDCWD, target, AT_FDCWD, source, 186 Pflag ? 0 : AT_SYMLINK_FOLLOW)) { 187 warn("%s", source); 188 return (1); 189 } 190 191 return (0); 192 } 193 194 void 195 usage(void) 196 { 197 extern char *__progname; 198 199 (void)fprintf(stderr, 200 "usage: %s [-fhLnPs] source [target]\n" 201 " %s [-fLPs] source ... [directory]\n", 202 __progname, __progname); 203 exit(1); 204 } 205