1 /* $OpenBSD: ln.c,v 1.25 2019/06/28 13:34:59 deraadt 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/types.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 #include <limits.h> 45 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 if (pledge("stdio rpath cpath", NULL) == -1) 62 err(1, "pledge"); 63 64 while ((ch = getopt(argc, argv, "fhLnPs")) != -1) 65 switch (ch) { 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[PATH_MAX]; 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. */ 130 if (S_ISDIR(sb.st_mode)) { 131 warnc(EISDIR, "%s", target); 132 return (1); 133 } 134 } 135 136 statf = hflag ? lstat : stat; 137 138 /* If the source is a directory, append the target's name. */ 139 if (isdir || (!statf(source, &sb) && S_ISDIR(sb.st_mode))) { 140 if ((p = basename(target)) == NULL) { 141 warn("%s", target); 142 return (1); 143 } 144 n = snprintf(path, sizeof(path), "%s/%s", source, p); 145 if (n < 0 || n >= sizeof(path)) { 146 warnc(ENAMETOOLONG, "%s/%s", source, p); 147 return (1); 148 } 149 source = path; 150 } 151 152 exists = (lstat(source, &sb) == 0); 153 /* 154 * If doing hard links and the source (destination) exists and it 155 * actually is the same file like the target (existing file), we 156 * complain that the files are identical. If -f is specified, we 157 * accept the job as already done and return with success. 158 */ 159 if (exists && !sflag) { 160 struct stat tsb; 161 162 if ((Pflag ? lstat : stat)(target, &tsb)) { 163 warn("%s: disappeared", target); 164 return (1); 165 } 166 167 if (tsb.st_dev == sb.st_dev && tsb.st_ino == sb.st_ino) { 168 if (fflag) 169 return (0); 170 else { 171 warnx("%s and %s are identical (nothing done).", 172 target, source); 173 return (1); 174 } 175 } 176 } 177 /* 178 * If the file exists, and -f was specified, unlink it. 179 * Attempt the link. 180 */ 181 if ((fflag && unlink(source) == -1 && errno != ENOENT) || 182 (sflag ? symlink(target, source) : 183 linkat(AT_FDCWD, target, AT_FDCWD, source, 184 Pflag ? 0 : AT_SYMLINK_FOLLOW))) { 185 warn("%s", source); 186 return (1); 187 } 188 189 return (0); 190 } 191 192 void 193 usage(void) 194 { 195 extern char *__progname; 196 197 (void)fprintf(stderr, 198 "usage: %s [-fhLnPs] source [target]\n" 199 " %s [-fLPs] source ... [directory]\n", 200 __progname, __progname); 201 exit(1); 202 } 203