AspectUnk / russh-sftp

SFTP subsystem supported server and client for Russh
Apache License 2.0
64 stars 21 forks source link

`FileAttributes` needed while opening file on some `sftp` server #57

Closed Wykiki closed 2 weeks ago

Wykiki commented 3 weeks ago

Hello !

I am fetching file on a remote SFTP server, for which I don't have much information regarding its version, its platform, or anything else. I am able to sftp ls and sftp get files.

When I try to session.metadata() a file, it works, but when I try to session.open() it, it does fail with a PermissionDenied error.

// Some session creation operations...
let session: SftpSession = ...;
let path = "some_file.txt";
let metadata = session.metadata(path).await.unwrap(); // Works
let file = session.open(path).await.unwrap(); // Does not work, PermissionDenied

After digging and trying things on the russh_sftp codebase, I successfully read the file only if I send the whole file metadata when opening it, like this :

// I've created the following method inside a local copy of `russh_sftp`
impl SftpSession {
    pub async fn open_with_flags_and_attributes<T: Into<String>>(
        &self,
        filename: T,
        flags: OpenFlags,
        attributes: FileAttributes,
    ) -> SftpResult<File> {
        let handle = self.session.open(filename, flags, attributes).await?.handle;

        Ok(File::new(
            self.session.clone(),
            handle,
            self.extensions.clone(),
        ))
    }

}

// Some session creation operations...
let session: SftpSession = ...;
let path = "some_file.txt";
let metadata = session.metadata(path).await.unwrap(); // Works
let file = session.open_with_flags_and_attributes(path, OpenFlags::READ, metadata).await.unwrap(); // Works

I'm absolutely not a sftp expert, so I'm not sure if this behaviour is expected on some server implementations, and I don't know if I can submit a PR to fix this issue on russh_sftp, or if I should fix it on my side via a fork (because I don't see how I can do it without forking).

If you're willing to accept a PR, would you prefer :

Thanks for reading !

Wykiki commented 3 weeks ago

Created PR #58 in case you're willing to accept the contribution.

AspectUnk commented 3 weeks ago

Try replacing 3rd argument with

FileAttributes {
    size: None,
    uid: None,
    user: None,
    gid: None,
    group: None,
    permissions: None,
    atime: None,
    mtime: None
}

and let me know if this solves the problem

Wykiki commented 2 weeks ago

Thanks for guidance, here is what I tried :

// Works
let metadata = session.metadata(&file).await.unwrap();
// Works
let metadata = FileAttributes {
    size: None,
    uid: None,
    user: None,
    gid: None,
    group: None,
    permissions: None,
    atime: None,
    mtime: None,
};
// Works
let metadata = FileAttributes {
    size: None,
    uid: None,
    user: None,
    gid: None,
    group: None,
    permissions: Some(0o755 | OpenFlags::READ.bits()),
    atime: None,
    mtime: None,
};
// Does not work
let metadata = FileAttributes {
    permissions: Some(0o755 | OpenFlags::READ.bits()),
    ..Default::default()
};

Above results have been tested against the unknown remote SFTP server.

I also tried them against the following SFTP server, working for every cases : https://github.com/atmoz/sftp

AspectUnk commented 2 weeks ago

Now, by default, the setting of attributes is delegated to the server, since according to the standard, it should set them at its own discretion by default. https://github.com/AspectUnk/russh-sftp/commit/ce779cec66c876ef9da32c38e61143d590b3899e

Your problem is strange, because attributes are initial and should be processed by the server only when creating the file, but not when opening it, anyway everything should work for you now

Wykiki commented 2 weeks ago

I confirm that the new behaviour makes the session.open(&file) work as expected on my side.

Thanks a lot for your time !