Howdy folks, We've come across a bug when combining --recursive-unlink with --directory... namely, that --recursive-unlink doesn't work anymore! I'll demonstrate:
$ pwd /home/ubuntu/tmp ## create a tarball containing a symlink 'bar' $ mkdir foo $ touch foo/a $ ln -s foo bar $ tar cf test.tar.gz bar ## now make 'bar' in the filesystem be a nonempty directory $ rm bar $ mv foo bar ## expected: failure to overwrite a nonempty directory with a symlink: $ tar xf test.tar.gz tar: bar: Cannot create symlink to `foo': File exists tar: Exiting with failure status due to previous errors ## unexpected: failure when using --recursive-unlink + --directory: $ (cd /; tar xf /home/ubuntu/tmp/test.tar.gz --directory /home/ubuntu/tmp --recursive-unlink) tar: bar: Cannot create symlink to `foo': File exists tar: Exiting with failure status due to previous errors ## .... but it works as expected if run without --directory: $ tar xf test.tar.gz --recursive-unlink $ ls -l bar lrwxrwxrwx 1 ubuntu ubuntu 3 Mar 12 21:10 bar -> foo stracing the unexpectedly failing tar paints a fairly clear picture of the problem: openat(AT_FDCWD, "/home/ubuntu/tmp", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 4 unlinkat(4, "bar", 0) = -1 EISDIR (Is a directory) unlinkat(4, "bar", AT_REMOVEDIR) = -1 ENOTEMPTY (Directory not empty) openat(AT_FDCWD, "bar", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = -1 ENOENT (No such file or directory) symlinkat("foo", 4, "bar") = -1 EEXIST (File exists) The problem appears to be that the second openat() call is relative to AT_FDCWD (the process' PWD) instead of fd=4 (the --directory argument). FWIW: $ tar --version tar (GNU tar) 1.26 Copyright (C) 2011 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Written by John Gilmore and Jay Fenlason. Thanks, David