-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 According to Eric Blake on 9/4/2009 2:06 PM: > According to Jim Meyering on 9/4/2009 12:12 PM: >>> copy.c needs to use #if LINK_FOLLOWS_SYMLINKS instead of #ifdef. >> Sounds good. > > How about the following two patches?
Aargh. Did it again, and attached the wrong file. - -- Don't work too hard, make some time for fun as well! Eric Blake [email protected] -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (Cygwin) Comment: Public key at home.comcast.net/~ericblake/eblake.gpg Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iEYEARECAAYFAkqhdDUACgkQ84KuGfSFAYA6wwCgq5STSFqPB/hNXqJGmkhNhELw tq8AoLVYolZz6Z9cs41zPQEkMsZcgLkF =byq4 -----END PGP SIGNATURE-----
>From ed13602fa561e66a2695ddd7eeeb2f218ce9020f Mon Sep 17 00:00:00 2001 From: Eric Blake <[email protected]> Date: Fri, 4 Sep 2009 12:40:39 -0600 Subject: [PATCH 1/2] mv, cp: tweak LINK_FOLLOWS_SYMLINKS logic * gnulib: Update to latest gnulib. * src/copy.c (copy_internal): Adjust comment in light of POSIX 2008, and deal with macro now being tri-state. --- gnulib | 2 +- src/copy.c | 30 ++++++++++++++++-------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/gnulib b/gnulib index 9b740ea..ee67d8e 160000 --- a/gnulib +++ b/gnulib @@ -1 +1 @@ -Subproject commit 9b740ea4bcb104eeceb0c847a376d2b7dbb77d1b +Subproject commit ee67d8ea6bd183f5ce32d90a6764705d766c3d6b diff --git a/src/copy.c b/src/copy.c index e604ec5..178a640 100644 --- a/src/copy.c +++ b/src/copy.c @@ -1974,21 +1974,23 @@ copy_internal (char const *src_name, char const *dst_name, } } + /* POSIX 2008 states that it is implementation-defined whether + link() on a symlink creates a hard-link to the symlink, or only + to the referent (effectively dereferencing the symlink) (POSIX + 2001 required the latter behavior, although many systems provided + the former). Yet cp, invoked with `--link --no-dereference', + should not follow the link. We can approximate the desired + behavior by skipping this hard-link creating block and instead + copying the symlink, via the `S_ISLNK'- copying code below. + LINK_FOLLOWS_SYMLINKS is tri-state; if it is -1, we don't know + how link() behaves, so we use the fallback case for safety. + + FIXME - use a gnulib linkat emulation for more fine-tuned + emulation, particularly when LINK_FOLLOWS_SYMLINKS is -1. */ else if (x->hard_link -#ifdef LINK_FOLLOWS_SYMLINKS - /* A POSIX-conforming link syscall dereferences a symlink, yet cp, - invoked with `--link --no-dereference', should not. Thus, with - a POSIX-conforming link system call, we can't use link() here, - since that would create a hard link to the referent (effectively - dereferencing the symlink), rather than to the symlink itself. - We can approximate the desired behavior by skipping this hard-link - creating block and instead copying the symlink, via the `S_ISLNK'- - copying code below. - When link operates on the symlinks themselves, we use this block - and just call link(). */ - && !(S_ISLNK (src_mode) && x->dereference == DEREF_NEVER) -#endif - ) + && (!LINK_FOLLOWS_SYMLINKS + || !S_ISLNK (src_mode) + || x->dereference != DEREF_NEVER)) { if (link (src_name, dst_name)) { -- 1.6.3.3.334.g916e1 >From 410eddc47c10082591b2487b234a3b5a6155daaf Mon Sep 17 00:00:00 2001 From: Eric Blake <[email protected]> Date: Fri, 4 Sep 2009 13:56:16 -0600 Subject: [PATCH 2/2] ln: add comments related to POSIX 2008 * src/ln.c (ENABLE_HARD_LINK_TO_SYMLINK_WARNING): Delete. (do_link): Update comments per POSIX; add FIXME for -L, -P. --- src/ln.c | 35 ++++++++++++++--------------------- 1 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/ln.c b/src/ln.c index d9b9285..4c6ec8a 100644 --- a/src/ln.c +++ b/src/ln.c @@ -39,10 +39,6 @@ proper_name ("Mike Parker"), \ proper_name ("David MacKenzie") -#ifndef ENABLE_HARD_LINK_TO_SYMLINK_WARNING -# define ENABLE_HARD_LINK_TO_SYMLINK_WARNING 0 -#endif - /* 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 @@ -150,7 +146,12 @@ do_link (const char *source, const char *dest) /* 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. */ + 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) @@ -159,14 +160,6 @@ do_link (const char *source, const char *dest) return false; } - if (ENABLE_HARD_LINK_TO_SYMLINK_WARNING - && S_ISLNK (source_stats.st_mode)) - { - error (0, 0, _("%s: warning: making a hard link to a symbolic link\ - is not portable"), - quote (source)); - } - if (S_ISDIR (source_stats.st_mode)) { source_is_dir = true; @@ -271,14 +264,14 @@ do_link (const char *source, const char *dest) /* If the attempt to create a link failed and we are removing or backing up destinations, unlink the destination and try again. - POSIX 1003.1-2004 requires that ln -f A B must unlink B even on - failure (e.g., when A does not exist). This is counterintuitive, - and we submitted a defect report - <http://www.opengroup.org/austin/mailarchives/ag-review/msg01794.html> - (2004-06-24). If the committee does not fix the standard we'll - have to change the behavior of ln -f, at least if POSIXLY_CORRECT - is set. In the meantime ln -f A B will not unlink B unless the - attempt to link A to B failed because B already existed. + On the surface, POSIX describes an algorithm that states that + 'ln -f A B' will call unlink() on B before ever attempting + link() on A. But strictly following this has the counterintuitive + effect of losing the contents of B, if A does not exist. + Fortunately, POSIX 2008 clarified that an application is free + to fail early if it can prove continuing onwards cannot succeed, + so we are justified in trying link() before blindly removing B, + and calling link() twice for a successful 'ln -f'. Try to unlink DEST even if we may have backed it up successfully. In some unusual cases (when DEST and DEST_BACKUP are hard-links -- 1.6.3.3.334.g916e1
