New submission from Jo Henke <free.softw...@gmx.com>:
Regarding link() POSIX states (https://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html): "If path1 names a symbolic link, it is implementation-defined whether link() follows the symbolic link, or creates a new link to the symbolic link itself." In Linux, link() does _not_ follow symlinks (http://man7.org/linux/man-pages/man2/link.2.html): "By default, linkat(), does not dereference oldpath if it is a symbolic link (like link())." But Python 3 assumes the opposite to be always the case: https://github.com/python/cpython/blob/8cb65d1381b027f0b09ee36bfed7f35bb4dec9a9/Modules/posixmodule.c#L3517 ...which suits e.g. NetBSD (https://netbsd.gw.com/cgi-bin/man-cgi?link+2): "When operating on a symlink, link() resolves the symlink and creates a hard link on the target." Therefore, I recommend to always call linkat(), if the platform provides it. That's the modern superset of link() with clearly defined behavior. Here are some commands to reproduce the issue on Linux (should hard link 'file' -> 'link', but tries '/tmp/symlink' -> 'link'): ~$ : >file ~$ ln -s "$PWD/file" /tmp/symlink ~$ strace -e link,linkat python -c 'import os; os.link("/tmp/symlink", "link", follow_symlinks=True)' link("/tmp/symlink", "link") = -1 EXDEV (Cross-device link) Traceback (most recent call last): File "<string>", line 1, in <module> OSError: [Errno 18] Cross-device link: '/tmp/symlink' -> 'link' +++ exited with 1 +++ For comparison, calling linkat() without AT_SYMLINK_FOLLOW results in the same error: ~$ strace -e link,linkat python -c 'import os; os.link("/tmp/symlink", "link", follow_symlinks=False)' linkat(AT_FDCWD, "/tmp/symlink", AT_FDCWD, "link", 0) = -1 EXDEV (Cross-device link) Traceback (most recent call last): File "<string>", line 1, in <module> OSError: [Errno 18] Cross-device link: '/tmp/symlink' -> 'link' +++ exited with 1 +++ Currently, the only way to call linkat() with AT_SYMLINK_FOLLOW from Python, is to provide a directory file descriptor != AT_FDCWD: ~$ strace -e link,linkat python -c 'import os; d=os.open(".", 0); os.link("/tmp/symlink", "link", dst_dir_fd=d, follow_symlinks=True)' linkat(AT_FDCWD, "/tmp/symlink", 3, "link", AT_SYMLINK_FOLLOW) = 0 +++ exited with 0 +++ ---------- components: Library (Lib) messages: 348086 nosy: jo-he priority: normal severity: normal status: open title: os.link(..., follow_symlinks=True) broken on Linux type: behavior versions: Python 3.5, Python 3.6, Python 3.7, Python 3.8, Python 3.9 _______________________________________ Python tracker <rep...@bugs.python.org> <https://bugs.python.org/issue37612> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com