summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bin/ln/ln.123
-rw-r--r--bin/ln/ln.c28
-rw-r--r--regress/bin/ln/Makefile18
-rw-r--r--regress/bin/ln/test_ln.sh69
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
+