diff options
-rw-r--r-- | bin/ln/ln.1 | 23 | ||||
-rw-r--r-- | bin/ln/ln.c | 28 | ||||
-rw-r--r-- | regress/bin/ln/Makefile | 18 | ||||
-rw-r--r-- | regress/bin/ln/test_ln.sh | 69 |
4 files changed, 122 insertions, 16 deletions
diff --git a/bin/ln/ln.1 b/bin/ln/ln.1 index 89a2be0f00a..8e72ac88dff 100644 --- a/bin/ln/ln.1 +++ b/bin/ln/ln.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ln.1,v 1.29 2011/03/02 07:47:21 jmc Exp $ +.\" $OpenBSD: ln.1,v 1.30 2013/03/12 06:00:05 guenther Exp $ .\" $NetBSD: ln.1,v 1.10 1995/07/25 19:37:04 jtc Exp $ .\" .\" Copyright (c) 1980, 1990, 1993 @@ -33,7 +33,7 @@ .\" .\" @(#)ln.1 8.2 (Berkeley) 12/30/93 .\" -.Dd $Mdocdate: March 2 2011 $ +.Dd $Mdocdate: March 12 2013 $ .Dt LN 1 .Os .Sh NAME @@ -41,11 +41,11 @@ .Nd make hard and symbolic links to files .Sh SYNOPSIS .Nm ln -.Op Fl fhns +.Op Fl fhLnPs .Ar source .Op Ar target .Nm ln -.Op Fl fs +.Op Fl fLPs .Ar source ...\& .Op Ar directory .Sh DESCRIPTION @@ -68,10 +68,23 @@ The options are as follows: Unlink any already existing file, permitting the link to occur. .It Fl h If the target is a symlink to a directory, do not descend into it. +.It Fl L +When creating a hard link and the source is a symbolic link, +link to the fully resolved target of the symbolic link. +This is the default. +This option cancels the +.Fl P +option. .It Fl n An alias for .Fl h for compatibility with other operating systems. +.It Fl P +When creating a hard link and the source is a symbolic link, +link to the symbolic link itself. +This option cancels the +.Fl L +option. .It Fl s Create a symbolic link. .El @@ -183,7 +196,7 @@ create a new symlink pointing to itself. This results from directory-walking. .Sh SEE ALSO -.Xr link 2 , +.Xr linkat 2 , .Xr lstat 2 , .Xr readlink 2 , .Xr stat 2 , diff --git a/bin/ln/ln.c b/bin/ln/ln.c index 0b0be08f7a4..c5c40e7c409 100644 --- a/bin/ln/ln.c +++ b/bin/ln/ln.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ln.c,v 1.18 2009/10/27 23:59:21 deraadt Exp $ */ +/* $OpenBSD: ln.c,v 1.19 2013/03/12 06:00:05 guenther Exp $ */ /* $NetBSD: ln.c,v 1.10 1995/03/21 09:06:10 cgd Exp $ */ /* @@ -35,6 +35,7 @@ #include <err.h> #include <errno.h> +#include <fcntl.h> #include <libgen.h> #include <stdio.h> #include <stdlib.h> @@ -44,9 +45,8 @@ int dirflag; /* Undocumented directory flag. */ int fflag; /* Unlink existing files. */ int hflag; /* Check new name for symlink first. */ +int Pflag; /* Hard link to symlink. */ int sflag; /* Symbolic, not hard, link. */ - /* System link call. */ -int (*linkf)(const char *, const char *); int linkit(char *, char *, int); void usage(void) __dead; @@ -58,7 +58,7 @@ main(int argc, char *argv[]) int ch, exitval; char *sourcedir; - while ((ch = getopt(argc, argv, "Ffhns")) != -1) + while ((ch = getopt(argc, argv, "FfhLnPs")) != -1) switch (ch) { case 'F': dirflag = 1; /* XXX: deliberately undocumented. */ @@ -70,6 +70,12 @@ main(int argc, char *argv[]) case 'n': hflag = 1; break; + case 'L': + Pflag = 0; + break; + case 'P': + Pflag = 1; + break; case 's': sflag = 1; break; @@ -80,8 +86,6 @@ main(int argc, char *argv[]) argv += optind; argc -= optind; - linkf = sflag ? symlink : link; - switch(argc) { case 0: usage(); @@ -118,7 +122,7 @@ linkit(char *target, char *source, int isdir) if (!sflag) { /* If target doesn't exist, quit now. */ - if (stat(target, &sb)) { + if ((Pflag ? lstat : stat)(target, &sb)) { warn("%s", target); return (1); } @@ -157,7 +161,7 @@ linkit(char *target, char *source, int isdir) if (exists && !sflag) { struct stat tsb; - if (stat(target, &tsb) != 0) { + if ((Pflag ? lstat : stat)(target, &tsb)) { warn("%s: disappeared", target); return (1); } @@ -177,7 +181,9 @@ linkit(char *target, char *source, int isdir) * Attempt the link. */ if ((fflag && unlink(source) < 0 && errno != ENOENT) || - (*linkf)(target, source)) { + sflag ? symlink(target, source) : + linkat(AT_FDCWD, target, AT_FDCWD, source, + Pflag ? 0 : AT_SYMLINK_FOLLOW)) { warn("%s", source); return (1); } @@ -191,8 +197,8 @@ usage(void) extern char *__progname; (void)fprintf(stderr, - "usage: %s [-fhns] source [target]\n" - " %s [-fs] source ... [directory]\n", + "usage: %s [-fhLnPs] source [target]\n" + " %s [-fLPs] source ... [directory]\n", __progname, __progname); exit(1); } diff --git a/regress/bin/ln/Makefile b/regress/bin/ln/Makefile new file mode 100644 index 00000000000..263b561ce37 --- /dev/null +++ b/regress/bin/ln/Makefile @@ -0,0 +1,18 @@ +# $OpenBSD: Makefile,v 1.1 2013/03/12 06:00:05 guenther Exp $ + +regress: recursive links/source + @sh ${.CURDIR}/test_ln.sh && touch $@ + +clean: + rm -rf links regress + +links: + @mkdir -p ${.OBJDIR}/links + +recursive: links +# @ln -s ${.OBJDIR}/links links/links + +links/source: links + @echo "Hello, world!" > ${.OBJDIR}/links/source + +.include <bsd.regress.mk> diff --git a/regress/bin/ln/test_ln.sh b/regress/bin/ln/test_ln.sh new file mode 100644 index 00000000000..68c1037a005 --- /dev/null +++ b/regress/bin/ln/test_ln.sh @@ -0,0 +1,69 @@ +#!/bin/sh +# +# $OpenBSD: test_ln.sh,v 1.1 2013/03/12 06:00:05 guenther Exp $ + +set -f + +get_dev_ino() +{ + stat -f %d:%i "$@" +} + +compare_dirents() +{ + opt= + if [ $# -eq 3 ] + then + opt=$1 + shift + fi + + echo Comparing $1 and $2 + if [ `get_dev_ino $opt $1` != `get_dev_ino $opt $2` ] + then + echo comparison failed: $1 different than $2 + exit 1 + fi +} + +test_ln() +{ + [ -e $2 ] || ln $1 $2 + compare_dirents $1 $2 +} + +test_ln_s() +{ + [ -h $2 ] || ln -s $1 $2 + compare_dirents -L $3 $2 +} + +test_ln_L() +{ + [ -e $2 ] || ln -L $1 $2 + + # Need 3rd argument because $2 follows symlink $1 + compare_dirents $2 $3 +} + +test_ln_P() +{ + [ -e $2 ] || ln -P $1 $2 + compare_dirents $1 $2 +} + +test_ln ./links/source ./links/hardlink1 +test_ln_s source ./links/symlink1 ./links/source +test_ln_L ./links/symlink1 ./links/hardlink2 ./links/source +test_ln_P ./links/symlink1 ./links/symlink2 +test_ln_s symlink1 ./links/symlink3 ./links/symlink1 +test_ln_L ./links/symlink3 ./links/hardlink3 ./links/source +err=`LC_ALL=C ln -P ./links/symlink1 ./links/symlink2 2>&1` +if [ $? -eq 0 ]; then + exit 1 +fi +case $err in + *"are identical"*"nothing done"*) ;; + *) exit 1;; +esac + |