GNU bug report logs - #32455
cp gets confused by symlinks to parent directory

Previous Next

Package: coreutils;

Reported by: Mike Crowe <mac <at> mcrowe.com>

Date: Thu, 16 Aug 2018 14:48:01 UTC

Severity: normal

Tags: notabug

Done: Assaf Gordon <assafgordon <at> gmail.com>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 32455 in the body.
You can then email your comments to 32455 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-coreutils <at> gnu.org:
bug#32455; Package coreutils. (Thu, 16 Aug 2018 14:48:03 GMT) Full text and rfc822 format available.

Acknowledgement sent to Mike Crowe <mac <at> mcrowe.com>:
New bug report received and forwarded. Copy sent to bug-coreutils <at> gnu.org. (Thu, 16 Aug 2018 14:48:03 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Mike Crowe <mac <at> mcrowe.com>
To: bug-coreutils <at> gnu.org
Subject: cp gets confused by symlinks to parent directory
Date: Thu, 16 Aug 2018 15:47:12 +0100
If cp is passed the -d option and told to copy a symlink to the directory
containing the symlink then it ends up removing the target directory so it
is unable to create the symlink.

Reproduction script:

--8<--
#!/bin/sh
set -x

rm -rf temp
mkdir -p temp/src temp/dest

ln -s . temp/src/self

# This one works
cp -vd temp/src/self temp/dest/self
# This one fails
cp -vd temp/src/self temp/dest/self
# This one works again
cp -vd temp/src/self temp/dest/self
-->8--

Output:

--8<--
+ rm -rf temp
+ mkdir -p temp/src temp/dest
+ ln -s . temp/src/self
+ cp -vd temp/src/self temp/dest/self
'temp/src/self' -> 'temp/dest/self'
+ cp -vd temp/src/self temp/dest/self
removed 'temp/dest/self/self'
'temp/src/self' -> 'temp/dest/self/self'
cp: cannot create symbolic link 'temp/dest/self/self': No such file or directory
+ cp -vd temp/src/self temp/dest/self
'temp/src/self' -> 'temp/dest/self'
-->8--

(coreutils 8.26, 8.28 and 8.30 all appear to behave the same way.)

Expected behaviour:

There should be no error message emitted by the second invocation of cp,
and the target directory should be in the same state as it is after the
first or third attempts to copy the symlink.

Or, maybe I'm fundamentally misunderstanding something. I couldn't find
this mentioned in the FAQ or "gotchas".

Thanks.

Mike.




Information forwarded to bug-coreutils <at> gnu.org:
bug#32455; Package coreutils. (Fri, 18 Jan 2019 10:38:02 GMT) Full text and rfc822 format available.

Message #8 received at 32455 <at> debbugs.gnu.org (full text, mbox):

From: Assaf Gordon <assafgordon <at> gmail.com>
To: Mike Crowe <mac <at> mcrowe.com>, 32455 <at> debbugs.gnu.org
Subject: Re: bug#32455: cp gets confused by symlinks to parent directory
Date: Fri, 18 Jan 2019 03:37:46 -0700
tags 32455 notabug
close 32455
stop

Hello,

It seems your message has not been replied to in a long while.
Sorry about that.

On 2018-08-16 8:47 a.m., Mike Crowe wrote:
> If cp is passed the -d option and told to copy a symlink to the directory
> containing the symlink then it ends up removing the target directory so it
> is unable to create the symlink.

If my understanding is correct, the "-d" flag is not relevant to the
issue. The problem is that "self" is a symlink to a directory:

> 
> Reproduction script:
> 
> --8<--
> #!/bin/sh
> set -x
> 
> rm -rf temp
> mkdir -p temp/src temp/dest
> 
> ln -s . temp/src/self
> 
> # This one works
> cp -vd temp/src/self temp/dest/self

This works because "temp/dest/self" does not exist.
In this case, "temp/dest" is taken as the destination directory,
and "self" is taken as the name of the file/dir/symlink to create.

That is, you could run "cp -vd temp/src/self temp/dest/foobar"
to create "foobar" as a copy of "self".

> # This one fails
> cp -vd temp/src/self temp/dest/self

Here, "temp/dest/self" already exists, and it is a symlink to
a directory.
Meaning, the request is: copy "temp/src/self" into the directory
"temp/dest/self/" (and create "temp/dest/self/self").

This would have gone well, except that because "self" is a symlink
to ".", it can be resolved indefinitely:

  $ file temp/dest/self
  temp/dest/self: symbolic link to .

  $ file temp/dest/self/self/self/self/self/self
  temp/dest/self/self/self/self/self/self: symbolic link to .

  $ file temp/dest/self/self/self/self/self/self/self
  temp/dest/self/self/self/self/self/self/self: symbolic link to .

"cp" first removes "temp/dest/self/self" (which is valid),
but then, "temp/dest/self" is gone (since it is the same file path after 
resolving it).

Hence, "cp" fails by saying "no such directory" on "temp/dest/self/self".

When this step is done, "temp/dest/self" does not exist,
and so:

> # This one works again
> cp -vd temp/src/self temp/dest/self

This works as before.

You can observe what happens on the kernel level
by adding "strace -e trace=file" before the "cp" commands,
this might help in deeper understanding.


To illustrate this differently:

When creating regular directories and files,
then deleting the innermost files, it is naively expected
that the parent directories still exist:

    mkdir -p a/b/c/d
    touch a/b/c/d/e
    rm a/b/c/d/e

That is, a normal program can call "dirname("a/b/c/d/e")"
to get the parent directory of "e", and expect it to still
exist even after "e" is deleted.

But with your case:

   $ mkdir a
   $ ln -s . a/self
   $ rm a/self/self/self/self/self/self

All the "apparent" parent directories ("self/self/self/self/self")
are gone!


> Expected behaviour:
> 
> There should be no error message emitted by the second invocation of cp,
> and the target directory should be in the same state as it is after the
> first or third attempts to copy the symlink.

Not exactly.

What you want is for the DEST parameter of "cp" to always be a file,
never to be considered a directory, i.e. "temp/dest/self"
should always be interpreted as file "self" in directory "temp/dest",
never as directory "temp/dest/self".
Luckily, there is already an option for that:

     -T, --no-target-directory
              treat DEST as a normal file

With "-T", repeated commands work as you expected:

  $ mkdir -p temp/src temp/dest
  $ ln -s . temp/src/self
  $ cp -vdT temp/src/self temp/dest/self
  'temp/src/self' -> 'temp/dest/self'
  $ cp -vdT temp/src/self temp/dest/self
  removed 'temp/dest/self'
  'temp/src/self' -> 'temp/dest/self'
  $ cp -vdT temp/src/self temp/dest/self
  removed 'temp/dest/self'
  'temp/src/self' -> 'temp/dest/self'
  $ cp -vdT temp/src/self temp/dest/self
  removed 'temp/dest/self'
  'temp/src/self' -> 'temp/dest/self'
  [... ad infinitum ...]


I hope this addresses the issue,
I'm closing this as "not a bug", but discussion can continue by replying
to this thread.

regards,
 - assaf





Added tag(s) notabug. Request was from Assaf Gordon <assafgordon <at> gmail.com> to control <at> debbugs.gnu.org. (Fri, 18 Jan 2019 10:39:01 GMT) Full text and rfc822 format available.

bug closed, send any further explanations to 32455 <at> debbugs.gnu.org and Mike Crowe <mac <at> mcrowe.com> Request was from Assaf Gordon <assafgordon <at> gmail.com> to control <at> debbugs.gnu.org. (Fri, 18 Jan 2019 10:39:02 GMT) Full text and rfc822 format available.

bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Fri, 15 Feb 2019 12:24:06 GMT) Full text and rfc822 format available.

This bug report was last modified 5 years and 71 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.