Open eyalgolan1337 opened 6 days ago
Thanks for the report!
Unfortunately, the SFTP protocol doesn't provide the ability to do the equivalent of FXP_SETSTAT (used for doing a chown, chmod, or utime on a file) without following symlinks. So, even if I tried to make copy()
aware of this, it would only work when copying data from a remote system to the local system, and not vice-versa. It also wouldn't work when running as a server and getting a copy request from a remote system, and even when copying to the local system it seems that os.chmod()
not following links is not supported on all OSes. In particular, Linux explicitly forbids this for os.chmod()
, and Python raises NotImplementedError
there:
>>> os.chmod('file', 0o555, follow_symlinks=False)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NotImplementedError: chmod: follow_symlinks unavailable on this platform
The Linux chmod man page says the following:
chmod never changes the permissions of symbolic links; the chmod system call cannot change their permissions. This is not a problem since the permissions of symbolic links are never used. However, for each symbolic link listed on the command line, chmod changes the permissions of the pointed-to file. In contrast, chmod ignores symbolic links encoun‐ tered during recursive directory traversals.
So, there's really no way to make this work in all cases with follow_symlinks=False
and preserve=True
. I could make it work better when copying from remote to local, and potentially ignore the exception on Linux to avoid the other issue you mentioned about attempting to follow the link and getting an error that the file doesn't exist, but I don't see a good way in SFTP to set attributes on a remote symlink without following the link. I'd probably need to completely skip doing the preserve and not try to set attributes at all when copying symlinks to a remote system.
I think just skipping the call to setstat
for symlinks makes the most sense, with a note in the documentation clarifying this behavior (and possibly an info
log).
(OpenSSH does have the lsetstat@openssh.com
extension, but I'm not sure that it makes sense to implement it.)
Ah, thanks - I hadn't noticed that OpenSSH added lsetstat@openssh.com
. That actually makers me feel better about trying to support this. If this extension is added to AsyncSSH, both it and OpenSSH should have no problem fully implementing update of uid, gid, and timestamps on symlinks on all platforms, and even permissions on symlinks on BSD and macOS.
The only time we'd need to skip updating the attributes is when follow_symlinks=False
and preserve=True
are set and the peer doesn't support lsetstat. We'd effectively be ignoring preserve=True
in this case, but only on symlinks.
If I get some time this weekend, I'll take a closer look.
When copying a symlink with
preserve=True
, the attributes of the target file are copied, instead of the link, even iffollow_symlinks
isFalse
.The attributes are also copied to the target on the destination, which causes copies to fail with
SFTPNoSuchFile
when the symlink is copied before the target.