New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
shutil.move fails to move symlink (Invalid cross-device link) #70978
Comments
Hi everyone, I'm not really sure if this is a new issue but digging through the bug reports from the past I couldn't find an answer. The problem can be visualized with the following code. import shutil
import os
TMPDIR = "/tmp/tmpdir"
TESTLINK = "test_dir"
if not os.path.isdir(TMPDIR):
os.mkdir(TMPDIR)
if not os.path.islink(TESTLINK):
os.symlink(TMPDIR, TESTLINK)
shutil.move(TESTLINK, TMPDIR) When executed it gives me: % python3 test.py
Traceback (most recent call last):
File "test.py", line 14, in <module>
shutil.move(TESTLINK, TMPDIR)
File "/usr/lib64/python3.4/shutil.py", line 516, in move
os.rename(src, dst)
OSError: [Errno 18] Invalid cross-device link: 'test_dir' -> '/tmp/tmpdir' This happens because /tmp is: tmpfs on /tmp type tmpfs (rw,nosuid,nodev,noatime,nodiratime) In the past the recommendation to handle this problem was to stop using os.rename and use shutil.move instead. If one searches for this exception there's plenty of advice [1][2][3][4] in the same direction. On the other end doing the equivalent action in the shell with 'mv' works fine. [1] - http://stackoverflow.com/a/15300474 |
Also related to http://bugs.python.org/issue212317 |
This seems to be only triggered when moving a symlink into its destination. Assumption in the code is that when _samefile returns True, it must be due to case-insensitive filesystem, rather than this edge case. |
It also happens when moving a file in linux from an xfs filesystem to a NFS mounted filesystem. |
SilentGhost's analysis is correct. The provided example code causes the error because it is trying to move the symlink into its target when the target is a directory. Any cross-device moving issues are unrelated to this example code. Here is the relevant code in the master branch: if os.path.isdir(dst):
if _samefile(src, dst):
# We might be on a case insensitive filesystem,
# perform the rename anyway.
os.rename(src, dst)
return shutil._samefile() considers the example link and its target to be the same. When _samefile() returns False, this code gets executed: real_dst = os.path.join(dst, _basename(src))
if os.path.exists(real_dst):
raise Error("Destination path '%s' already exists" % real_dst)
try:
os.rename(src, real_dst)
except OSError:
if os.path.islink(src):
linkto = os.readlink(src)
os.symlink(linkto, real_dst)
os.unlink(src) A simple fix is to check whether src is a symlink when _samefile() returns True. The "Destination path...already exists" error isn't a problem for our symlink case because the shell mv command also returns an error. $ ls -l /tmp/tmpdir/
total 0
lrwxr-xr-x 1 jeff staff 11 Aug 5 23:36 test_dir -> /tmp/tmpdir
$ mv test_dir /tmp/tmpdir
mv: test_dir and /tmp/tmpdir/test_dir are identical I will generate a pull request. |
Removing old versions. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: