[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
link -L/-P (was: coreutils-7.7 beta release soon)
From: |
Eric Blake |
Subject: |
link -L/-P (was: coreutils-7.7 beta release soon) |
Date: |
Thu, 24 Sep 2009 18:02:10 +0000 (UTC) |
User-agent: |
Loom/3.14 (http://gmane.org/) |
Eric Blake <ebb9 <at> byu.net> writes:
> > I'd like to make a test release soon. Maybe as soon as Friday.
> > If you have something that you'd like included, please speak up.
>
> I'm nearly ready to post support for ln -P/-L, thanks to the pending addition
> of gnulib linkat(). That would be nice to have in the release.
How does this look?
From: Eric Blake <address@hidden>
Date: Thu, 24 Sep 2009 11:57:11 -0600
Subject: [PATCH] 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.
---
NEWS | 7 ++++
bootstrap.conf | 1 +
doc/coreutils.texi | 49 +++++++++++++++++++++++++++---
src/ln.c | 63 ++++++++++++++++++++-----------------
tests/Makefile.am | 1 +
tests/ln/hard-to-sym | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++
tests/ln/misc | 23 --------------
7 files changed, 169 insertions(+), 57 deletions(-)
create mode 100644 tests/ln/hard-to-sym
diff --git a/NEWS b/NEWS
index 1571c9c..5060502 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 d1dc128..f648e22 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 0bfbd56..e7cf5ea 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -8753,6 +8753,11 @@ link invocation
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
address@hidden 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}
+specify which behavior is desired.
+
@exitstatus
@@ -8808,8 +8813,10 @@ ln invocation
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 @@ ln invocation
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 @@ ln invocation
@cindex prompting, and @command{ln}
Prompt whether to remove existing destination files.
address@hidden -L
address@hidden --logical
address@hidden -L
address@hidden --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 @@ ln invocation
This option is weaker than the @option{--no-target-directory}
(@option{-T}) option, so it has no effect if both options are given.
address@hidden -P
address@hidden --physical
address@hidden -P
address@hidden --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 path 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 @@ ln invocation
@end table
address@hidden hard links to symbolic links
address@hidden 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 0c35338..0cc3d6f 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
-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 symblic links\n\
-s, --symbolic make symbolic links instead of hard links\n\
"), stdout);
fputs (_("\
@@ -391,6 +385,11 @@ The version control method may
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 63e60c8..2acad6b 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 100644
index 0000000..510b57a
--- /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 f13bd7b..d42d68a 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.
--
1.6.1.2
- coreutils-7.7 beta release soon, Jim Meyering, 2009/09/23
- Re: coreutils-7.7 beta release soon, Eric Blake, 2009/09/23
- Re: coreutils-7.7 beta release soon, Jim Meyering, 2009/09/23
- link -L/-P (was: coreutils-7.7 beta release soon),
Eric Blake <=
- Re: link -L/-P, Pádraig Brady, 2009/09/24
- Re: link -L/-P, Eric Blake, 2009/09/24
- Re: link -L/-P, Pádraig Brady, 2009/09/25
- Re: link -L/-P, Pádraig Brady, 2009/09/25
- stdbuf enhancement (was: link -L/-P), Eric Blake, 2009/09/25
- Re: stdbuf enhancement, Jim Meyering, 2009/09/25
- Re: link -L/-P, Jim Meyering, 2009/09/25