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.
This commit is contained in:
Eric Blake 2009-09-24 11:57:11 -06:00
parent fb59d72f0a
commit efcee783e4
7 changed files with 169 additions and 57 deletions

7
NEWS
View File

@ -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

View File

@ -132,6 +132,7 @@ gnulib_modules="
linebuffer
link
link-follow
linkat
long-options
lstat
maintainer-makefile

View File

@ -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:

View File

@ -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;

View File

@ -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 \

82
tests/ln/hard-to-sym Executable file
View File

@ -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

View File

@ -107,29 +107,6 @@ touch a b || framework_failure
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.