1139969Simp/*- 21556Srgrimes * Copyright (c) 1987, 1993, 1994 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * Redistribution and use in source and binary forms, with or without 61556Srgrimes * modification, are permitted provided that the following conditions 71556Srgrimes * are met: 81556Srgrimes * 1. Redistributions of source code must retain the above copyright 91556Srgrimes * notice, this list of conditions and the following disclaimer. 101556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111556Srgrimes * notice, this list of conditions and the following disclaimer in the 121556Srgrimes * documentation and/or other materials provided with the distribution. 131556Srgrimes * 4. Neither the name of the University nor the names of its contributors 141556Srgrimes * may be used to endorse or promote products derived from this software 151556Srgrimes * without specific prior written permission. 161556Srgrimes * 171556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271556Srgrimes * SUCH DAMAGE. 281556Srgrimes */ 291556Srgrimes 30114433Sobrien#if 0 311556Srgrimes#ifndef lint 3220420Sstevestatic char const copyright[] = 331556Srgrimes"@(#) Copyright (c) 1987, 1993, 1994\n\ 341556Srgrimes The Regents of the University of California. All rights reserved.\n"; 351556Srgrimes#endif /* not lint */ 361556Srgrimes 371556Srgrimes#ifndef lint 3836046Scharnierstatic char sccsid[] = "@(#)ln.c 8.2 (Berkeley) 3/31/94"; 39114433Sobrien#endif /* not lint */ 4036046Scharnier#endif 4199109Sobrien#include <sys/cdefs.h> 4299109Sobrien__FBSDID("$FreeBSD$"); 431556Srgrimes 441556Srgrimes#include <sys/param.h> 451556Srgrimes#include <sys/stat.h> 461556Srgrimes 471556Srgrimes#include <err.h> 481556Srgrimes#include <errno.h> 49195768Sjilles#include <fcntl.h> 50207021Sjilles#include <libgen.h> 5177404Simp#include <limits.h> 521556Srgrimes#include <stdio.h> 531556Srgrimes#include <stdlib.h> 541556Srgrimes#include <string.h> 551556Srgrimes#include <unistd.h> 561556Srgrimes 57226961Sedstatic int fflag; /* Unlink existing files. */ 58226961Sedstatic int Fflag; /* Remove empty directories also. */ 59226961Sedstatic int hflag; /* Check new name for symlink first. */ 60226961Sedstatic int iflag; /* Interactive mode. */ 61226961Sedstatic int Pflag; /* Create hard links to symlinks. */ 62226961Sedstatic int sflag; /* Symbolic, not hard, link. */ 63226961Sedstatic int vflag; /* Verbose output. */ 64226961Sedstatic int wflag; /* Warn if symlink target does not 65179603Skeramida * exist, and -f is not enabled. */ 66226961Sedstatic char linkch; 671556Srgrimes 68251261Seadlerstatic int linkit(const char *, const char *, int); 69251261Seadlerstatic void usage(void); 701556Srgrimes 711556Srgrimesint 7290110Simpmain(int argc, char *argv[]) 731556Srgrimes{ 741556Srgrimes struct stat sb; 75173701Sru char *p, *targetdir; 761556Srgrimes int ch, exitval; 771556Srgrimes 7854895Ssheldonh /* 7954895Ssheldonh * Test for the special case where the utility is called as 8054895Ssheldonh * "link", for which the functionality provided is greatly 8154895Ssheldonh * simplified. 8254895Ssheldonh */ 83219680Sjilles if ((p = strrchr(argv[0], '/')) == NULL) 8454895Ssheldonh p = argv[0]; 8554895Ssheldonh else 8654895Ssheldonh ++p; 8754895Ssheldonh if (strcmp(p, "link") == 0) { 8897531Stjr while (getopt(argc, argv, "") != -1) 8954895Ssheldonh usage(); 9097531Stjr argc -= optind; 9197531Stjr argv += optind; 9297531Stjr if (argc != 2) 9397531Stjr usage(); 9497531Stjr exit(linkit(argv[0], argv[1], 0)); 9554895Ssheldonh } 9654895Ssheldonh 97195768Sjilles while ((ch = getopt(argc, argv, "FLPfhinsvw")) != -1) 981556Srgrimes switch (ch) { 99155667Sglebius case 'F': 100155667Sglebius Fflag = 1; 101155667Sglebius break; 102195768Sjilles case 'L': 103195768Sjilles Pflag = 0; 104195768Sjilles break; 105195768Sjilles case 'P': 106195768Sjilles Pflag = 1; 107195768Sjilles break; 1081556Srgrimes case 'f': 1091556Srgrimes fflag = 1; 11064787Ssheldonh iflag = 0; 111179603Skeramida wflag = 0; 1121556Srgrimes break; 11376039Ssobomax case 'h': 11476039Ssobomax case 'n': 11576039Ssobomax hflag = 1; 11676039Ssobomax break; 11764638Ssheldonh case 'i': 11864638Ssheldonh iflag = 1; 11964787Ssheldonh fflag = 0; 12064638Ssheldonh break; 1211556Srgrimes case 's': 1221556Srgrimes sflag = 1; 1231556Srgrimes break; 12451148Sobrien case 'v': 12551148Sobrien vflag = 1; 12651148Sobrien break; 127179603Skeramida case 'w': 128179603Skeramida wflag = 1; 129179603Skeramida break; 1301556Srgrimes case '?': 1311556Srgrimes default: 1321556Srgrimes usage(); 1331556Srgrimes } 1341556Srgrimes 1351556Srgrimes argv += optind; 1361556Srgrimes argc -= optind; 1371556Srgrimes 13851148Sobrien linkch = sflag ? '-' : '='; 139155667Sglebius if (sflag == 0) 140155667Sglebius Fflag = 0; 141179603Skeramida if (Fflag == 1 && iflag == 0) { 142155667Sglebius fflag = 1; 143179603Skeramida wflag = 0; /* Implied when fflag != 0 */ 144179603Skeramida } 1451556Srgrimes 1461556Srgrimes switch(argc) { 1471556Srgrimes case 0: 1481556Srgrimes usage(); 14976039Ssobomax /* NOTREACHED */ 150173701Sru case 1: /* ln source */ 1511556Srgrimes exit(linkit(argv[0], ".", 1)); 152173701Sru case 2: /* ln source target */ 1531556Srgrimes exit(linkit(argv[0], argv[1], 0)); 15491084Smarkm default: 15596373Sjedgar ; 1561556Srgrimes } 157173701Sru /* ln source1 source2 directory */ 158173701Sru targetdir = argv[argc - 1]; 159173701Sru if (hflag && lstat(targetdir, &sb) == 0 && S_ISLNK(sb.st_mode)) { 16076039Ssobomax /* 16176039Ssobomax * We were asked not to follow symlinks, but found one at 16276039Ssobomax * the target--simulate "not a directory" error 16376039Ssobomax */ 16476039Ssobomax errno = ENOTDIR; 165173701Sru err(1, "%s", targetdir); 16676039Ssobomax } 167173701Sru if (stat(targetdir, &sb)) 168173701Sru err(1, "%s", targetdir); 1691556Srgrimes if (!S_ISDIR(sb.st_mode)) 1701556Srgrimes usage(); 171173701Sru for (exitval = 0; *argv != targetdir; ++argv) 172173701Sru exitval |= linkit(*argv, targetdir, 1); 1731556Srgrimes exit(exitval); 1741556Srgrimes} 1751556Srgrimes 176206773Sjilles/* 177206773Sjilles * Two pathnames refer to the same directory entry if the directories match 178206773Sjilles * and the final components' names match. 179206773Sjilles */ 180206773Sjillesstatic int 181206773Sjillessamedirent(const char *path1, const char *path2) 182206773Sjilles{ 183206773Sjilles const char *file1, *file2; 184206773Sjilles char pathbuf[PATH_MAX]; 185206773Sjilles struct stat sb1, sb2; 186206773Sjilles 187206773Sjilles if (strcmp(path1, path2) == 0) 188206773Sjilles return 1; 189206773Sjilles file1 = strrchr(path1, '/'); 190206773Sjilles if (file1 != NULL) 191206773Sjilles file1++; 192206773Sjilles else 193206773Sjilles file1 = path1; 194206773Sjilles file2 = strrchr(path2, '/'); 195206773Sjilles if (file2 != NULL) 196206773Sjilles file2++; 197206773Sjilles else 198206773Sjilles file2 = path2; 199206773Sjilles if (strcmp(file1, file2) != 0) 200206773Sjilles return 0; 201206773Sjilles if (file1 - path1 >= PATH_MAX || file2 - path2 >= PATH_MAX) 202206773Sjilles return 0; 203206773Sjilles if (file1 == path1) 204206773Sjilles memcpy(pathbuf, ".", 2); 205206773Sjilles else { 206206773Sjilles memcpy(pathbuf, path1, file1 - path1); 207206773Sjilles pathbuf[file1 - path1] = '\0'; 208206773Sjilles } 209206773Sjilles if (stat(pathbuf, &sb1) != 0) 210206773Sjilles return 0; 211206773Sjilles if (file2 == path2) 212206773Sjilles memcpy(pathbuf, ".", 2); 213206773Sjilles else { 214206773Sjilles memcpy(pathbuf, path2, file2 - path2); 215206773Sjilles pathbuf[file2 - path2] = '\0'; 216206773Sjilles } 217206773Sjilles if (stat(pathbuf, &sb2) != 0) 218206773Sjilles return 0; 219206773Sjilles return sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino; 220206773Sjilles} 221206773Sjilles 222251261Seadlerstatic int 223173701Srulinkit(const char *source, const char *target, int isdir) 2241556Srgrimes{ 2251556Srgrimes struct stat sb; 22676877Skris const char *p; 22764787Ssheldonh int ch, exists, first; 22877404Simp char path[PATH_MAX]; 229179603Skeramida char wbuf[PATH_MAX]; 230207021Sjilles char bbuf[PATH_MAX]; 2311556Srgrimes 2321556Srgrimes if (!sflag) { 233173701Sru /* If source doesn't exist, quit now. */ 234195768Sjilles if ((Pflag ? lstat : stat)(source, &sb)) { 235173701Sru warn("%s", source); 2361556Srgrimes return (1); 2371556Srgrimes } 23815900Speter /* Only symbolic links to directories. */ 23915900Speter if (S_ISDIR(sb.st_mode)) { 24015900Speter errno = EISDIR; 241173701Sru warn("%s", source); 2421556Srgrimes return (1); 2431556Srgrimes } 2441556Srgrimes } 2451556Srgrimes 24676039Ssobomax /* 247173701Sru * If the target is a directory (and not a symlink if hflag), 248173701Sru * append the source's name. 24976039Ssobomax */ 25076039Ssobomax if (isdir || 251173701Sru (lstat(target, &sb) == 0 && S_ISDIR(sb.st_mode)) || 252173701Sru (!hflag && stat(target, &sb) == 0 && S_ISDIR(sb.st_mode))) { 253207021Sjilles if (strlcpy(bbuf, source, sizeof(bbuf)) >= sizeof(bbuf) || 254207021Sjilles (p = basename(bbuf)) == NULL || 255207021Sjilles snprintf(path, sizeof(path), "%s/%s", target, p) >= 256101094Smarkm (ssize_t)sizeof(path)) { 25799019Stjr errno = ENAMETOOLONG; 258173701Sru warn("%s", source); 25999019Stjr return (1); 26099019Stjr } 261173701Sru target = path; 26276039Ssobomax } 2631556Srgrimes 2641556Srgrimes /* 265179603Skeramida * If the link source doesn't exist, and a symbolic link was 266179603Skeramida * requested, and -w was specified, give a warning. 267179603Skeramida */ 268179603Skeramida if (sflag && wflag) { 269179603Skeramida if (*source == '/') { 270179603Skeramida /* Absolute link source. */ 271179603Skeramida if (stat(source, &sb) != 0) 272179603Skeramida warn("warning: %s inaccessible", source); 273179603Skeramida } else { 274179603Skeramida /* 275179603Skeramida * Relative symlink source. Try to construct the 276179603Skeramida * absolute path of the source, by appending `source' 277179603Skeramida * to the parent directory of the target. 278179603Skeramida */ 279207021Sjilles strlcpy(bbuf, target, sizeof(bbuf)); 280207021Sjilles p = dirname(bbuf); 281207021Sjilles if (p != NULL) { 282207021Sjilles (void)snprintf(wbuf, sizeof(wbuf), "%s/%s", 283207021Sjilles p, source); 284207021Sjilles if (stat(wbuf, &sb) != 0) 285207021Sjilles warn("warning: %s", source); 286207021Sjilles } 287179603Skeramida } 288179603Skeramida } 289206773Sjilles 290179603Skeramida /* 291206773Sjilles * If the file exists, first check it is not the same directory entry. 292206773Sjilles */ 293206773Sjilles exists = !lstat(target, &sb); 294206773Sjilles if (exists) { 295206773Sjilles if (!sflag && samedirent(source, target)) { 296206773Sjilles warnx("%s and %s are the same directory entry", 297206773Sjilles source, target); 298206773Sjilles return (1); 299206773Sjilles } 300206773Sjilles } 301206773Sjilles /* 302206773Sjilles * Then unlink it forcibly if -f was specified 30364787Ssheldonh * and interactively if -i was specified. 3041556Srgrimes */ 30564787Ssheldonh if (fflag && exists) { 306155667Sglebius if (Fflag && S_ISDIR(sb.st_mode)) { 307173701Sru if (rmdir(target)) { 308173701Sru warn("%s", target); 309155667Sglebius return (1); 310155667Sglebius } 311173701Sru } else if (unlink(target)) { 312173701Sru warn("%s", target); 31364787Ssheldonh return (1); 31464787Ssheldonh } 31564638Ssheldonh } else if (iflag && exists) { 31664787Ssheldonh fflush(stdout); 317173701Sru fprintf(stderr, "replace %s? ", target); 31864638Ssheldonh 31964638Ssheldonh first = ch = getchar(); 32064638Ssheldonh while(ch != '\n' && ch != EOF) 32164638Ssheldonh ch = getchar(); 32264787Ssheldonh if (first != 'y' && first != 'Y') { 32364787Ssheldonh fprintf(stderr, "not replaced\n"); 32464787Ssheldonh return (1); 32564787Ssheldonh } 32664638Ssheldonh 327155667Sglebius if (Fflag && S_ISDIR(sb.st_mode)) { 328173701Sru if (rmdir(target)) { 329173701Sru warn("%s", target); 330155667Sglebius return (1); 331155667Sglebius } 332173701Sru } else if (unlink(target)) { 333173701Sru warn("%s", target); 33464638Ssheldonh return (1); 33564638Ssheldonh } 3361556Srgrimes } 33764787Ssheldonh 33864787Ssheldonh /* Attempt the link. */ 339195768Sjilles if (sflag ? symlink(source, target) : 340195768Sjilles linkat(AT_FDCWD, source, AT_FDCWD, target, 341195768Sjilles Pflag ? 0 : AT_SYMLINK_FOLLOW)) { 342173701Sru warn("%s", target); 34364638Ssheldonh return (1); 34464638Ssheldonh } 34551148Sobrien if (vflag) 346173701Sru (void)printf("%s %c> %s\n", target, linkch, source); 3471556Srgrimes return (0); 3481556Srgrimes} 3491556Srgrimes 350251261Seadlerstatic void 35190110Simpusage(void) 3521556Srgrimes{ 35354895Ssheldonh (void)fprintf(stderr, "%s\n%s\n%s\n", 354195768Sjilles "usage: ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file [target_file]", 355195768Sjilles " ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file ... target_dir", 356141578Sru " link source_file target_file"); 3571556Srgrimes exit(1); 3581556Srgrimes} 359