summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Blake <ebb9@byu.net>2009-09-24 11:57:11 -0600
committerEric Blake <ebb9@byu.net>2009-09-25 07:03:03 -0600
commitefcee783e4d576898130ccbf1cb2d7d6bf1b8420 (patch)
tree401900376fd200b55ebede212a2b777b1d3fa0d4
parentfb59d72f0a48ef6bc45d795ab96131368157c59f (diff)
downloadcoreutils-efcee783e4d576898130ccbf1cb2d7d6bf1b8420.tar.xz
ln: add -L/-P options
* src/ln.c (STAT_LIKE_LINK): Delete. (logical): New flag. (long_options): Add -L, -P. (usage): Mention them. (main): Choose between them. (do_link): Perform correct action. * tests/ln/misc: Move hard-to-sym portion of test... * tests/ln/hard-to-sym: ...into new test, and add more. * tests/Makefile.am (TESTS): Run new test. * NEWS: Document this. * doc/coreutils.texi (link invocation, ln invocation): Likewise. * bootstrap.conf (gnulib_modules): Add linkat.
-rw-r--r--NEWS7
-rw-r--r--bootstrap.conf1
-rw-r--r--doc/coreutils.texi49
-rw-r--r--src/ln.c63
-rw-r--r--tests/Makefile.am1
-rwxr-xr-xtests/ln/hard-to-sym82
-rwxr-xr-xtests/ln/misc23
7 files changed, 169 insertions, 57 deletions
diff --git a/NEWS b/NEWS
index 1571c9c0d..506050203 100644
--- a/NEWS
+++ b/NEWS
@@ -32,6 +32,13 @@ GNU coreutils NEWS -*- outline -*-
last component (possibly via a dangling symlink) can be created,
since mkdir will succeed in that case.
+** New features
+
+ ln now accepts the options --logical (-L) and --physical (-P),
+ added by POSIX 2008. The default behavior is -P on systems like
+ GNU/Linux where link(2) creates hard links to symlinks, and -L on
+ BSD systems where link(2) follows symlinks.
+
** Improvements
rm: rewrite to use gnulib's fts
diff --git a/bootstrap.conf b/bootstrap.conf
index d1dc128e8..f648e226a 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -132,6 +132,7 @@ gnulib_modules="
linebuffer
link
link-follow
+ linkat
long-options
lstat
maintainer-makefile
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 0bfbd5689..b10cc100c 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -8753,6 +8753,11 @@ On a @acronym{GNU} system, this command acts like @samp{ln --directory
not specified by @acronym{POSIX}, and the @command{link} command is
more portable in practice.
+If @var{filename} is a symbolic link, it is unspecified whether
+@var{linkname} will be a hard link to the symbolic link or to the
+target of the symbolic link. Use @command{ln -P} or @command{ln -L}
+to specify which behavior is desired.
+
@exitstatus
@@ -8808,8 +8813,10 @@ A @dfn{hard link} is another name for an existing file; the link and the
original are indistinguishable. Technically speaking, they share the
same inode, and the inode contains all the information about a
file---indeed, it is not incorrect to say that the inode @emph{is} the
-file. On all existing implementations, you cannot make a hard link to
-a directory, and hard links cannot cross file system boundaries. (These
+file. Most systems prohibit making a hard link to
+a directory; on those where it is allowed, only the super-user can do
+so (and with caution, since creating a cycle will cause problems to many
+other utilities). Hard links cannot cross file system boundaries. (These
restrictions are not mandated by @acronym{POSIX}, however.)
@cindex dereferencing symbolic links
@@ -8821,9 +8828,13 @@ refers to a different file, by name. When most operations (opening,
reading, writing, and so on) are passed the symbolic link file, the
kernel automatically @dfn{dereferences} the link and operates on the
target of the link. But some operations (e.g., removing) work on the
-link file itself, rather than on its target. The owner, group, and
-mode of a symlink are not significant to file access performed through
-the link. @xref{Symbolic Links,,,
+link file itself, rather than on its target. The owner and group of a
+symlink are not significant to file access performed through
+the link, but do have implications on deleting a symbolic link from a
+directory with the restricted deletion bit set. On the GNU system,
+the mode of a symlink has no significance and cannot be changed, but
+on some BSD systems, the mode can be changed and will affect whether
+the symlink will be traversed in file name resolution. @xref{Symbolic Links,,,
libc, The GNU C Library Reference Manual}.
Symbolic links can contain arbitrary strings; a @dfn{dangling symlink}
@@ -8878,6 +8889,14 @@ Remove existing destination files.
@cindex prompting, and @command{ln}
Prompt whether to remove existing destination files.
+@item -L
+@itemx --logical
+@opindex -L
+@opindex --logical
+If @option{-s} is not in effect, and the source file is a symbolic
+link, create the hard link to the file referred to by the symbolic
+link, rather than the symbolic link itself.
+
@item -n
@itemx --no-dereference
@opindex -n
@@ -8899,6 +8918,17 @@ just like a directory.
This option is weaker than the @option{--no-target-directory}
(@option{-T}) option, so it has no effect if both options are given.
+@item -P
+@itemx --physical
+@opindex -P
+@opindex --physical
+If @option{-s} is not in effect, and the source file is a symbolic
+link, create the hard link to the symbolic link itself. On platforms
+where this is not supported by the kernel, this option creates a
+symbolic link with identical contents; since symbolic link contents
+cannot be edited, any file name resolution performed through either
+link will be the same as if a hard link had been created.
+
@item -s
@itemx --symbolic
@opindex -s
@@ -8920,6 +8950,15 @@ Print the name of each file after linking it successfully.
@end table
+@cindex hard links to symbolic links
+@cindex symbolic links and @command{ln}
+If @option{-L} and @option{-P} are both given, the last one takes
+precedence. If @option{-s} is also given, @option{-L} and @option{-P}
+are silently ignored. If neither option is given, then this
+implementation defaults to @option{-P} if the system @code{link} supports
+hard links to symbolic links (such as the GNU system), and @option{-L}
+if @code{link} follows symbolic links (such as on BSD).
+
@exitstatus
Examples:
diff --git a/src/ln.c b/src/ln.c
index 0c35338f2..4f75c1919 100644
--- a/src/ln.c
+++ b/src/ln.c
@@ -39,26 +39,15 @@
proper_name ("Mike Parker"), \
proper_name ("David MacKenzie")
-/* In being careful not even to try to make hard links to directories,
- we have to know whether link(2) follows symlinks. If it does, then
- we have to *stat* the `source' to see if the resulting link would be
- to a directory. Otherwise, we have to use *lstat* so that we allow
- users to make hard links to symlinks-that-point-to-directories. */
-
-#if LINK_FOLLOWS_SYMLINKS
-# define STAT_LIKE_LINK(File, Stat_buf) \
- stat (File, Stat_buf)
-#else
-# define STAT_LIKE_LINK(File, Stat_buf) \
- lstat (File, Stat_buf)
-#endif
-
/* FIXME: document */
static enum backup_type backup_type;
/* If true, make symbolic links; otherwise, make hard links. */
static bool symbolic_link;
+/* If true, hard links are logical rather than physical. */
+static bool logical = !!LINK_FOLLOWS_SYMLINKS;
+
/* If true, ask the user before removing existing files. */
static bool interactive;
@@ -71,7 +60,7 @@ static bool verbose;
/* If true, allow the superuser to *attempt* to make hard links
to directories. However, it appears that this option is not useful
in practice, since even the superuser is prohibited from hard-linking
- directories on most (all?) existing systems. */
+ directories on most existing systems (Solaris being an exception). */
static bool hard_dir_link;
/* If nonzero, and the specified destination is a symbolic link to a
@@ -99,6 +88,8 @@ static struct option const long_options[] =
{"interactive", no_argument, NULL, 'i'},
{"suffix", required_argument, NULL, 'S'},
{"target-directory", required_argument, NULL, 't'},
+ {"logical", no_argument, NULL, 'L'},
+ {"physical", no_argument, NULL, 'P'},
{"symbolic", no_argument, NULL, 's'},
{"verbose", no_argument, NULL, 'v'},
{GETOPT_HELP_OPTION_DECL},
@@ -143,18 +134,15 @@ do_link (const char *source, const char *dest)
bool source_is_dir = false;
bool ok;
- /* Use stat here instead of lstat.
- On SVR4, link does not follow symlinks, so this check disallows
- making hard links to symlinks that point to directories. Big deal.
- On other systems, link follows symlinks, so this check is right.
-
- FIXME - POSIX 2008 added the AT_SYMLINK_FOLLOW flag to linkat so
- that we can specify either behavior, via the new options -L
- (hard-link to symlinks) and -P (hard-link to the referent). Once
- gnulib has a decent implementation, we should use it here. */
if (!symbolic_link)
{
- if (STAT_LIKE_LINK (source, &source_stats) != 0)
+ /* Which stat to use depends on whether linkat will follow the
+ symlink. We can't use the shorter
+ (logical ? stat : lstat) (source, &source_stats)
+ since stat might be a function-like macro. */
+ if ((logical ? stat (source, &source_stats)
+ : lstat (source, &source_stats))
+ != 0)
{
error (0, errno, _("accessing %s"), quote (source));
return false;
@@ -258,7 +246,9 @@ do_link (const char *source, const char *dest)
}
}
- ok = ((symbolic_link ? symlink (source, dest) : link (source, dest))
+ ok = ((symbolic_link ? symlink (source, dest)
+ : linkat (AT_FDCWD, source, AT_FDCWD, dest,
+ logical ? AT_SYMLINK_FOLLOW : 0))
== 0);
/* If the attempt to create a link failed and we are removing or
@@ -289,7 +279,9 @@ do_link (const char *source, const char *dest)
return false;
}
- ok = ((symbolic_link ? symlink (source, dest) : link (source, dest))
+ ok = ((symbolic_link ? symlink (source, dest)
+ : linkat (AT_FDCWD, source, AT_FDCWD, dest,
+ logical ? AT_SYMLINK_FOLLOW : 0))
== 0);
}
@@ -370,9 +362,11 @@ Mandatory arguments to long options are mandatory for short options too.\n\
-f, --force remove existing destination files\n\
"), stdout);
fputs (_("\
+ -i, --interactive prompt whether to remove destinations\n\
+ -L, --logical make hard links to symbolic link references\n\
-n, --no-dereference treat destination that is a symlink to a\n\
directory as if it were a normal file\n\
- -i, --interactive prompt whether to remove destinations\n\
+ -P, --physical make hard links directly to symbolic links\n\
-s, --symbolic make symbolic links instead of hard links\n\
"), stdout);
fputs (_("\
@@ -391,6 +385,11 @@ The version control method may be selected via the --backup option or through\n\
the VERSION_CONTROL environment variable. Here are the values:\n\
\n\
"), stdout);
+ printf (_("\
+Using -s ignores -L and -P. Otherwise, the last option specified controls\n\
+behavior when the source is a symbolic link, defaulting to %s.\n\
+\n\
+"), LINK_FOLLOWS_SYMLINKS ? "-L" : "-P");
fputs (_("\
none, off never make backups (even if --backup is given)\n\
numbered, t make numbered backups\n\
@@ -430,7 +429,7 @@ main (int argc, char **argv)
symbolic_link = remove_existing_files = interactive = verbose
= hard_dir_link = false;
- while ((c = getopt_long (argc, argv, "bdfinst:vFS:T", long_options, NULL))
+ while ((c = getopt_long (argc, argv, "bdfinst:vFLPS:T", long_options, NULL))
!= -1)
{
switch (c)
@@ -452,9 +451,15 @@ main (int argc, char **argv)
remove_existing_files = false;
interactive = true;
break;
+ case 'L':
+ logical = true;
+ break;
case 'n':
dereference_dest_dir_symlinks = false;
break;
+ case 'P':
+ logical = false;
+ break;
case 's':
symbolic_link = true;
break;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 63e60c8c5..2acad6bd6 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -344,6 +344,7 @@ TESTS = \
install/trap \
ln/backup-1 \
ln/hard-backup \
+ ln/hard-to-sym \
ln/misc \
ln/sf-1 \
ln/slash-decorated-nonexistent-dest \
diff --git a/tests/ln/hard-to-sym b/tests/ln/hard-to-sym
new file mode 100755
index 000000000..510b57abf
--- /dev/null
+++ b/tests/ln/hard-to-sym
@@ -0,0 +1,82 @@
+#!/bin/sh
+# Tests for ln -L/-P.
+
+# Copyright (C) 2009 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+if test "$VERBOSE" = yes; then
+ set -x
+ ln --version
+fi
+
+. $srcdir/test-lib.sh
+
+fail=0
+
+# ===================================================
+# ensure -s silently overrides -L, -P
+touch a || framework_failure
+ln -L -s a symlink1 || fail=1
+ln -P -s symlink1 symlink2 || fail=1
+ln -s -L -P symlink2 symlink3 || fail=1
+
+# ===================================================
+# ensure that -L follows symlinks, and overrides -P
+ln -P -L symlink3 hard-to-a || fail=1
+ls=`ls -lG hard-to-a`x
+case "$ls" in
+ *'hard-to-ax') ;;
+ *'hard-to-a -> '*x) fail=1 ;;
+ *) framework_failure ;;
+esac
+
+# ===================================================
+# ensure that -P links (or at least duplicates) symlinks, and overrides -L
+ln -L -P symlink3 hard-to-3 || fail=1
+ls=`ls -lG hard-to-3`x
+case "$ls" in
+ *'hard-to-3 -> symlink2x') ;;
+ *'hard-to-3x') fail=1 ;;
+ *'hard-to-3 -> '*x) fail=1 ;;
+ *) framework_failure ;;
+esac
+
+# ===================================================
+# Create a hard link to a dangling symlink.
+ln -s /no-such-dir || framework_failure
+ln -L no-such-dir hard-to-dangle 2>err && fail=1
+case `cat err` in
+ *' accessing `no-such-dir'\':*) ;;
+ *) fail=1 ;;
+esac
+ln -P no-such-dir hard-to-dangle || fail=1
+
+# ===================================================
+# Create a hard link to a symlink to a directory.
+mkdir d || framework_failure
+ln -s d link-to-dir || framework_failure
+ln -L link-to-dir hard-to-dir-link 2>err && fail=1
+case `cat err` in
+ *': `link-to-dir'\'': hard link not allowed for directory'*) ;;
+ *) fail=1 ;;
+esac
+ln -P link-to-dir/ hard-to-dir-link 2>err && fail=1
+case `cat err` in
+ *': `link-to-dir/'\'': hard link not allowed for directory'*) ;;
+ *) fail=1 ;;
+esac
+ln -P link-to-dir hard-to-dir-link || fail=1
+
+Exit $fail
diff --git a/tests/ln/misc b/tests/ln/misc
index f13bd7bf3..d42d68a27 100755
--- a/tests/ln/misc
+++ b/tests/ln/misc
@@ -108,29 +108,6 @@ ln b b~ || framework_failure
ln -f --b=simple a b || fail=1
# ===================================================
-# determine if link(2) follows symlinks on this system
-touch a || framework_failure
-ln -s a symlink || framework_failure
-ln symlink hard-to-sym > /dev/null 2>&1 || framework_failure
-ls=`ls -lG hard-to-sym`x
-case "$ls" in
- *'hard-to-symx') link_follows_symlink=yes ;;
- *'hard-to-sym -> ax') link_follows_symlink=no ;;
- *) framework_failure ;;
-esac
-
-if test $link_follows_symlink = no; then
- # Create a hard link to a dangling symlink.
- # This is not portable. At least sunos4.1.4 and OpenBSD 2.3 fail this test.
- # They get this:
- # ln: cannot create hard link `hard-to-dangle' to `no-such-dir': \
- # No such file or directory
- #
- ln -s /no-such-dir || fail=1
- ln no-such-dir hard-to-dangle > /dev/null 2>&1 || fail=1
-fi
-rm -rf a symlink hard-to-sym hard-to-dangle
-# ===================================================
# Make sure ln can make simple backups.
# This was fixed in 4.0.34. Broken in 4.0r.