mirror of
https://github.com/coreutils/coreutils.git
synced 2024-11-24 10:23:31 +08:00
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:
parent
fb59d72f0a
commit
efcee783e4
7
NEWS
7
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
|
||||
|
@ -132,6 +132,7 @@ gnulib_modules="
|
||||
linebuffer
|
||||
link
|
||||
link-follow
|
||||
linkat
|
||||
long-options
|
||||
lstat
|
||||
maintainer-makefile
|
||||
|
@ -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:
|
||||
|
63
src/ln.c
63
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;
|
||||
|
@ -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
82
tests/ln/hard-to-sym
Executable 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
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user