From owner-svn-src-all@FreeBSD.ORG Sat Apr 17 22:39:54 2010 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 110861065670; Sat, 17 Apr 2010 22:39:54 +0000 (UTC) (envelope-from jilles@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id DB5008FC1A; Sat, 17 Apr 2010 22:39:53 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id o3HMdrNY092170; Sat, 17 Apr 2010 22:39:53 GMT (envelope-from jilles@svn.freebsd.org) Received: (from jilles@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id o3HMdrJY092168; Sat, 17 Apr 2010 22:39:53 GMT (envelope-from jilles@svn.freebsd.org) Message-Id: <201004172239.o3HMdrJY092168@svn.freebsd.org> From: Jilles Tjoelker Date: Sat, 17 Apr 2010 22:39:53 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r206773 - head/bin/ln X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 17 Apr 2010 22:39:54 -0000 Author: jilles Date: Sat Apr 17 22:39:53 2010 New Revision: 206773 URL: http://svn.freebsd.org/changeset/base/206773 Log: ln: Refuse deleting a directory entry by hardlinking it to itself. Two pathnames refer to the same directory entry iff the directories match and the final components' names match. Example: (assuming file1 is an existing file) ln -f file1 file1 This now fails while leaving file1 intact. It used to delete file1 and then complain it cannot be linked because it is gone. With -i, this error is detected before the question is asked. MFC after: 2 weeks Modified: head/bin/ln/ln.c Modified: head/bin/ln/ln.c ============================================================================== --- head/bin/ln/ln.c Sat Apr 17 22:38:16 2010 (r206772) +++ head/bin/ln/ln.c Sat Apr 17 22:39:53 2010 (r206773) @@ -172,6 +172,52 @@ main(int argc, char *argv[]) exit(exitval); } +/* + * Two pathnames refer to the same directory entry if the directories match + * and the final components' names match. + */ +static int +samedirent(const char *path1, const char *path2) +{ + const char *file1, *file2; + char pathbuf[PATH_MAX]; + struct stat sb1, sb2; + + if (strcmp(path1, path2) == 0) + return 1; + file1 = strrchr(path1, '/'); + if (file1 != NULL) + file1++; + else + file1 = path1; + file2 = strrchr(path2, '/'); + if (file2 != NULL) + file2++; + else + file2 = path2; + if (strcmp(file1, file2) != 0) + return 0; + if (file1 - path1 >= PATH_MAX || file2 - path2 >= PATH_MAX) + return 0; + if (file1 == path1) + memcpy(pathbuf, ".", 2); + else { + memcpy(pathbuf, path1, file1 - path1); + pathbuf[file1 - path1] = '\0'; + } + if (stat(pathbuf, &sb1) != 0) + return 0; + if (file2 == path2) + memcpy(pathbuf, ".", 2); + else { + memcpy(pathbuf, path2, file2 - path2); + pathbuf[file2 - path2] = '\0'; + } + if (stat(pathbuf, &sb2) != 0) + return 0; + return sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino; +} + int linkit(const char *source, const char *target, int isdir) { @@ -215,7 +261,6 @@ linkit(const char *source, const char *t target = path; } - exists = !lstat(target, &sb); /* * If the link source doesn't exist, and a symbolic link was * requested, and -w was specified, give a warning. @@ -242,8 +287,20 @@ linkit(const char *source, const char *t warn("warning: %s", source); } } + + /* + * If the file exists, first check it is not the same directory entry. + */ + exists = !lstat(target, &sb); + if (exists) { + if (!sflag && samedirent(source, target)) { + warnx("%s and %s are the same directory entry", + source, target); + return (1); + } + } /* - * If the file exists, then unlink it forcibly if -f was specified + * Then unlink it forcibly if -f was specified * and interactively if -i was specified. */ if (fflag && exists) {