[Python-Dev] shutil.copy() and hard links

Christian Heimes lists at cheimes.de
Wed Jan 11 10:49:46 CET 2012


here is another fun fact about links, this time hard links and the
shutil.copy() function.

The shutil.copy() functions behaves like the Unix cp(1) command. Both
don't unlink the destination file if it already exists. As a consequence
all hard links point to the updated file data. This behavior may
surprise some users. Perhaps the docs should point out how shutil.copy()
works when hard links join the party.

It might be worth to add a function that works similar to install(1).
The install(1) command unlinks the destination first and opens it with
exclusive create flags. This compensates for possible symlink attacks, too.


Shell session example of cp and install

$ echo "test1" > test1
$ echo "test2" > test2
$ ln test1 test_hardlink

now test_hardlink points to the same inodes as test1
$ cat test_hardlink

test_hardlink still points to the same inodes
$ cp test2 test1
$ cat test_hardlink

$ echo "test1" > test1
$ cat test_hardlink

install unlinks the file first, test1 and test_hardlink point to
different inodes
$ install test2 test1
$ cat test_hardlink

strace of install test2 test1

stat("test1", {st_mode=S_IFREG|0755, st_size=6, ...}) = 0
stat("test2", {st_mode=S_IFREG|0664, st_size=6, ...}) = 0
lstat("test1", {st_mode=S_IFREG|0755, st_size=6, ...}) = 0
unlink("test1")                         = 0
open("test2", O_RDONLY)                 = 3
fstat(3, {st_mode=S_IFREG|0664, st_size=6, ...}) = 0
open("test1", O_WRONLY|O_CREAT|O_EXCL, 0664) = 4
fstat(4, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0

More information about the Python-Dev mailing list