### Sftp::new Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Creates an Sftp instance by connecting to an already-spawned sftp subsystem process. It accepts any AsyncWrite+Send stdin and AsyncRead+Send stdout, plus SftpOptions for tuning. Internally, it spawns a flush task and a read task. ```APIDOC ## Sftp::new — Create an SFTP session from raw stdin/stdout ### Description Creates an `Sftp` instance by connecting to an already-spawned `sftp` subsystem process. Accepts any `AsyncWrite`+`Send` stdin and `AsyncRead`+`Send` stdout, plus `SftpOptions` for tuning. Internally spawns a flush task and a read task. ### Method `Sftp::new(stdin: impl AsyncWrite + Send, stdout: impl AsyncRead + Send, options: SftpOptions) -> impl Future>` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```rust use openssh_sftp_client::{Sftp, SftpOptions}; use tokio::process::Command; #[tokio::main] async fn main() -> Result<(), Box> { let mut child = Command::new("ssh") .args(["-s", "user@hostname", "sftp"]) .stdin(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped()) .spawn()?; let stdin = child.stdin.take().unwrap(); let stdout = child.stdout.take().unwrap(); let sftp = Sftp::new(stdin, stdout, SftpOptions::default()).await?; // Use sftp ... sftp.close().await?; Ok(()) } ``` ### Response #### Success Response (200) An `Sftp` instance representing the active SFTP session. #### Response Example None provided. ``` -------------------------------- ### Create SFTP Session from Raw Stdin/Stdout Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Use this to create an Sftp instance by connecting to an already-spawned sftp subsystem process. Ensure the child process is spawned with piped stdin and stdout. ```rust use openssh_sftp_client::{Sftp, SftpOptions}; use tokio::process::Command; #[tokio::main] async fn main() -> Result<(), Box> { let mut child = Command::new("ssh") .args(["-s", "user@hostname", "sftp"]) .stdin(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped()) .spawn()?; let stdin = child.stdin.take().unwrap(); let stdout = child.stdout.take().unwrap(); let sftp = Sftp::new(stdin, stdout, SftpOptions::default()).await?; // Use sftp ... sftp.close().await?; Ok(()) } ``` -------------------------------- ### Open or Create Remote Files with Sftp::open and Sftp::create Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt `open` opens a remote file in read-only mode. `create` opens in write-only mode, creating it if absent or truncating if present. Both return a `File` handle. ```rust use openssh_sftp_client::{Sftp, SftpOptions}; async fn example(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { // Read an existing file let mut file = sftp.open("remote/data.txt").await?; let buf = bytes::BytesMut::with_capacity(1024); if let Some(data) = file.read(1024, buf).await? { println!("Read {} bytes", data.len()); } file.close().await?; // Create/truncate a file and write to it let mut file = sftp.create("remote/output.txt").await?; file.write_all(b"Hello, remote world!\n").await?; file.close().await?; Ok(()) } ``` -------------------------------- ### Tokio Compatibility Adapter (`TokioCompatFile`) Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Wraps an SFTP `File` to implement standard Tokio async traits, enabling use with Tokio's I/O utilities. ```APIDOC ## `TokioCompatFile` — Tokio `AsyncRead` / `AsyncWrite` / `AsyncBufRead` / `AsyncSeek` adapter Wraps a `File` with standard tokio async traits, making it compatible with any tokio I/O pipeline (e.g., `tokio::io::copy`, `BufReader`, codec framing). ### Methods - `new(file: File) -> TokioCompatFile` - `into_inner(self) -> File` ### Traits Implemented - `tokio::io::AsyncRead` - `tokio::io::AsyncWrite` - `tokio::io::AsyncBufRead` - `tokio::io::AsyncSeek` ### Request Example ```rust use openssh_sftp_client::{Sftp, SftpOptions, file::TokioCompatFile}; use tokio::io::{AsyncReadExt, AsyncWriteExt, AsyncSeekExt}; use std::io::SeekFrom; async fn tokio_compat(sftp: &Sftp) -> Result<(), Box> { // Write using AsyncWrite let write_file = sftp.create("remote/tokio.txt").await?; let mut compat = TokioCompatFile::new(write_file); compat.write_all(b"Hello via AsyncWrite").await?; compat.flush().await?; let write_file = compat.into_inner(); write_file.close().await?; // Read using AsyncRead + seek let read_file = sftp.open("remote/tokio.txt").await?; let mut compat = TokioCompatFile::new(read_file); compat.seek(SeekFrom::Start(6)).await?; let mut buf = String::new(); tokio::io::AsyncBufReadExt::read_line(&mut compat, &mut buf).await?; println!("After seek: {buf}"); // "via AsyncWrite" // Use with tokio::io::copy let src = sftp.open("remote/tokio.txt").await?; let dst = sftp.create("remote/copy.txt").await?; let mut src_compat = TokioCompatFile::new(src); let mut dst_compat = TokioCompatFile::new(dst); tokio::io::copy(&mut src_compat, &mut dst_compat).await?; dst_compat.flush().await?; Ok(()) } ``` ### Response - **Success Response**: `Ok(())` on successful operations. - **Error Response**: `Err(Box)` if any Tokio I/O operation fails. ``` -------------------------------- ### Build and Inspect Metadata with MetaDataBuilder Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Demonstrates how to construct metadata for a setstat call using MetaDataBuilder and Permissions. Shows setting individual permission bits, using octal shorthand, and inspecting created metadata attributes like size, UID, and modification time. ```rust use openssh_sftp_client::metadata::{MetaDataBuilder, Permissions, FileType}; fn build_metadata_example() { // Build metadata for a setstat call let mut perm = Permissions::new(); perm.set_read_by_owner(true); perm.set_write_by_owner(true); perm.set_read_by_group(true); perm.set_read_by_other(true); assert!(!perm.readonly()); perm.set_readonly(true); assert!(perm.readonly()); // From octal shorthand let perm644 = Permissions::from(0o644_u16); assert!(perm644.read_by_owner()); assert!(perm644.write_by_owner()); assert!(!perm644.execute_by_owner()); let metadata = MetaDataBuilder::new() .permissions(perm644) .id((1000, 1000)) .create(); // Inspect println!("Size: {:?}", metadata.len()); println!("UID: {:?}", metadata.uid()); println!("Modified: {:?}", metadata.modified()); } ``` -------------------------------- ### Configure SFTP Session Parameters with SftpOptions Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Use SftpOptions to tune flush interval, buffer sizes, and pending request limits. All methods are const and return Self for chaining. ```rust use openssh_sftp_client::SftpOptions; use std::{num::{NonZeroU16, NonZeroUsize}, time::Duration}; let options = SftpOptions::new() // Max time a request sits in the write buffer before being sent (default: 0.5ms) .flush_interval(Duration::from_millis(1)) // Flush immediately if this many requests are pending (default: 100) .max_pending_requests(NonZeroU16::new(50).unwrap()) // Initial capacity of the request write buffer (default: 100) .requests_buffer_size(NonZeroUsize::new(256).unwrap()) // Initial capacity of the response read buffer in bytes (default: 1024) .responses_buffer_size(NonZeroUsize::new(4096).unwrap()) // Write buffer limit for TokioCompatFile before it flushes (default: 640 KB) .tokio_compat_file_write_limit(NonZeroUsize::new(1024 * 1024).unwrap()); // Use options when creating Sftp // let sftp = Sftp::new(stdin, stdout, options).await?; ``` -------------------------------- ### Create SFTP Session from openssh Session Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Launches the SFTP subsystem over an existing openssh::Session. Ensure the 'openssh' feature is enabled for openssh-sftp-client and openssh. Calling close() on the Sftp instance also awaits the remote child process and session close. ```toml # Cargo.toml [dependencies] openssh-sftp-client = { version = "0.15.7", features = ["openssh"] } openssh = { version = "0.11.0", features = ["native-mux"] } tokio = { version = "1", features = ["rt", "macros"] } ``` ```rust use openssh::{KnownHosts, Session}; use openssh_sftp_client::{Sftp, SftpOptions}; #[tokio::main] async fn main() -> Result<(), Box> { let session = Session::connect_mux("ssh://user@hostname:22", KnownHosts::Strict).await?; let sftp = Sftp::from_session(session, SftpOptions::default()).await?; let mut fs = sftp.fs(); let contents = fs.read("remote/file.txt").await?; println!("File contents: {}", String::from_utf8_lossy(&contents)); sftp.close().await?; Ok(()) } ``` -------------------------------- ### SftpOptions Configuration Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Configure SFTP session parameters such as flush interval, buffer sizes, and pending request limits using a builder pattern. ```APIDOC ## `SftpOptions` — Configure SFTP session parameters Builder-style struct for tuning flush interval, buffer sizes, pending request limits, and tokio-compat write limits. All methods are `const` and return `Self` for chaining. ```rust use openssh_sftp_client::SftpOptions; use std::{num::{NonZeroU16, NonZeroUsize}, time::Duration}; let options = SftpOptions::new() // Max time a request sits in the write buffer before being sent (default: 0.5ms) .flush_interval(Duration::from_millis(1)) // Flush immediately if this many requests are pending (default: 100) .max_pending_requests(NonZeroU16::new(50).unwrap()) // Initial capacity of the request write buffer (default: 100) .requests_buffer_size(NonZeroUsize::new(256).unwrap()) // Initial capacity of the response read buffer in bytes (default: 1024) .responses_buffer_size(NonZeroUsize::new(4096).unwrap()) // Write buffer limit for TokioCompatFile before it flushes (default: 640 KB) .tokio_compat_file_write_limit(NonZeroUsize::new(1024 * 1024).unwrap()); // Use options when creating Sftp // let sftp = Sftp::new(stdin, stdout, options).await?; ``` ``` -------------------------------- ### Fine-grained File Open Flags with OpenOptions Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Mirrors `std::fs::OpenOptions` for remote files, supporting read, write, append, truncate, create, and create_new modes. ```rust use openssh_sftp_client::{Sftp, SftpOptions}; async fn example(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { // Open for appending, creating if not present let mut file = sftp.options() .append(true) .create(true) .open("remote/log.txt") .await?; file.write_all(b"log entry\n").await?; file.close().await?; // Exclusively create a new file (fails if it already exists) let mut new_file = sftp.options() .write(true) .create_new(true) .open("remote/unique.txt") .await?; new_file.write_all(b"new content").await?; new_file.close().await?; Ok(()) } ``` -------------------------------- ### Check Supported SFTP Extensions with Sftp::support_* Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Query which optional SFTP extensions the remote server supports before calling extension-dependent APIs. This helps ensure compatibility and graceful degradation if an extension is not available. ```rust use openssh_sftp_client::{Sftp, SftpOptions}; async fn check_extensions(sftp: &Sftp) { println!("expand-path : {}", sftp.support_expand_path()); println!("fsync : {}", sftp.support_fsync()); println!("hardlink : {}", sftp.support_hardlink()); println!("posix-rename: {}", sftp.support_posix_rename()); println!("copy-data : {}", sftp.support_copy()); } ``` -------------------------------- ### Sftp::open and Sftp::create Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Open a remote file in read-only mode (`open`) or write-only mode (`create`), creating it if absent or truncating if present. Both return a `File` handle. ```APIDOC ## `Sftp::open` and `Sftp::create` — Open or create remote files `open` opens a remote file in read-only mode. `create` opens in write-only mode, creating it if absent or truncating if present. Both return a `File` handle. ```rust use openssh_sftp_client::{Sftp, SftpOptions}; async fn example(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { // Read an existing file let mut file = sftp.open("remote/data.txt").await?; let buf = bytes::BytesMut::with_capacity(1024); if let Some(data) = file.read(1024, buf).await? { println!("Read {} bytes", data.len()); } file.close().await?; // Create/truncate a file and write to it let mut file = sftp.create("remote/output.txt").await?; file.write_all(b"Hello, remote world!\n").await?; file.close().await?; Ok(()) } ``` ``` -------------------------------- ### Create Directories with Custom Permissions using DirBuilder Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Use `DirBuilder` to create remote directories with specific uid/gid and permissions. This allows for fine-grained control over directory access. ```rust use openssh_sftp_client::{Sftp, SftpOptions, metadata::Permissions}; async fn create_dir_with_perms(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { let mut fs = sftp.fs(); let mut perm = Permissions::new(); perm.set_read_by_owner(true); perm.set_write_by_owner(true); perm.set_execute_by_owner(true); perm.set_read_by_group(true); perm.set_execute_by_group(true); fs.dir_builder() .permissions(perm) .id((1000, 1000)) // uid, gid .create("/tmp/mydir") .await?; Ok(()) } ``` -------------------------------- ### Tokio Compatibility Adapter for SFTP Files Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Wraps an SFTP `File` to implement standard Tokio async traits (`AsyncRead`, `AsyncWrite`, `AsyncBufRead`, `AsyncSeek`). Enables integration with Tokio's I/O utilities like `tokio::io::copy`. ```rust use openssh_sftp_client::{Sftp, SftpOptions, file::TokioCompatFile}; use tokio::io::{AsyncReadExt, AsyncWriteExt, AsyncSeekExt}; use std::io::SeekFrom; async fn tokio_compat(sftp: &Sftp) -> Result<(), Box> { // Write using AsyncWrite let write_file = sftp.create("remote/tokio.txt").await?; let mut compat = TokioCompatFile::new(write_file); compat.write_all(b"Hello via AsyncWrite").await?; compat.flush().await?; let write_file = compat.into_inner(); write_file.close().await?; // Read using AsyncRead + seek let read_file = sftp.open("remote/tokio.txt").await?; let mut compat = TokioCompatFile::new(read_file); compat.seek(SeekFrom::Start(6)).await?; let mut buf = String::new(); tokio::io::AsyncBufReadExt::read_line(&mut compat, &mut buf).await?; println!("After seek: {buf}"); // "via AsyncWrite" // Use with tokio::io::copy let src = sftp.open("remote/tokio.txt").await?; let dst = sftp.create("remote/copy.txt").await?; let mut src_compat = TokioCompatFile::new(src); let mut dst_compat = TokioCompatFile::new(dst); tokio::io::copy(&mut src_compat, &mut dst_compat).await?; dst_compat.flush().await?; Ok(()) } ``` -------------------------------- ### Force Flush to Disk (`sync_all`) Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Forces all in-core data to the remote filesystem. Requires the `fsync` extension. ```APIDOC ## `File::sync_all` — Flush to disk (extension: `fsync`) Forces all in-core data to the remote filesystem. Requires the `fsync` extension. Check with `Sftp::support_fsync()`. ### Method - `sync_all(&mut self) -> impl Future>` ### Parameters This method does not take any parameters. ### Request Example ```rust use openssh_sftp_client::{Sftp, SftpOptions}; async fn sync_example(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { if !sftp.support_fsync() { eprintln!("fsync extension not available"); return Ok(()); } let mut file = sftp.create("remote/important.dat").await?; file.write_all(b"critical data").await?; file.sync_all().await?; // ensures data reaches the remote disk file.close().await?; Ok(()) } ``` ### Response - **Success Response**: `Ok(())` on successful flush. - **Error Response**: `Err(openssh_sftp_client::Error)` if an error occurs during the flush operation. ``` -------------------------------- ### File Attribute Operations: Size, Permissions, Metadata Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Demonstrates how to manipulate file attributes including setting length, changing permissions, and reading/writing metadata such as UID, GID, and timestamps. ```rust use openssh_sftp_client::{Sftp, SftpOptions, metadata::{MetaDataBuilder, Permissions}}; async fn file_attrs(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { let mut file = sftp.options().read(true).write(true).create(true).open("remote/sized.bin").await?; // Extend file to 1 MB file.set_len(1024 * 1024).await?; // Set permissions to rw-r--r-- (0o644) let mut perm = Permissions::new(); perm.set_read_by_owner(true); perm.set_write_by_owner(true); perm.set_read_by_group(true); perm.set_read_by_other(true); file.set_permissions(perm).await?; // Query metadata let meta = file.metadata().await?; println!("Size: {:?}", meta.len()); println!("UID: {:?}", meta.uid()); println!("GID: {:?}", meta.gid()); if let Some(ft) = meta.file_type() { println!("Is file: {}", ft.is_file()); } // Set via MetaDataBuilder (uid/gid + timestamps) let new_meta = MetaDataBuilder::new().id((1000, 1000)).create(); file.set_metadata(new_meta).await?; file.close().await?; Ok(()) } ``` -------------------------------- ### File::read / File::read_all / File::write / File::write_all Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Perform low-level file I/O operations including reading specified amounts of data and writing data at the current offset. Supports vectorized writes. ```APIDOC ## `File::read` / `File::read_all` / `File::write` / `File::write_all` — Low-level file I/O `read` reads up to `n` bytes at the current offset, returning `None` on EOF. `read_all` reads exactly `n` bytes, erroring on EOF. `write` and `write_all` write data at the current offset; large writes are automatically split into multiple SFTP requests. ```rust use bytes::BytesMut; use openssh_sftp_client::{Sftp, SftpOptions}; async fn copy_section(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { let mut src = sftp.open("remote/source.bin").await?; let mut dst = sftp.create("remote/dest.bin").await?; // Read exactly 4096 bytes let data = src.read_all(4096, BytesMut::new()).await?; println!("Read {} bytes", data.len()); // Write them all to destination dst.write_all(&data).await?; // Vectorized write from multiple buffers use std::io::IoSlice; let header = b"HEADER"; let body = b"BODY_DATA"; let mut bufs = [IoSlice::new(header), IoSlice::new(body)]; dst.write_all_vectorized(&mut bufs).await?; src.close().await?; dst.close().await?; Ok(()) } ``` ``` -------------------------------- ### Create SFTP Session with Periodic Liveness Checks Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Similar to Sftp::from_session, but includes an async closure for continuous SSH connection liveness checks. The checker should only return on error and is cancelled when the SFTP session closes. Ensure the 'openssh' feature is enabled. ```rust use openssh::{KnownHosts, Session}; use openssh_sftp_client::{Sftp, SftpOptions}; use std::{pin::Pin, time::Duration}; fn check_connection<'s>( session: &'s Session, ) -> Pin> + Send + Sync + 's>> { Box::pin(async move { loop { tokio::time::sleep(Duration::from_secs(10)).await; session.check().await?; } }) } #[tokio::main] async fn main() -> Result<(), Box> { let session = Session::connect_mux("me@ssh.example.com", KnownHosts::Strict).await?; let sftp = Sftp::from_session_with_check_connection( session, SftpOptions::default(), check_connection, ).await?; sftp.close().await?; Ok(()) } ``` -------------------------------- ### Low-level File I/O with File::read, File::read_all, File::write, File::write_all Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt `read` reads up to `n` bytes at the current offset, returning `None` on EOF. `read_all` reads exactly `n` bytes, erroring on EOF. `write` and `write_all` write data at the current offset; large writes are automatically split into multiple SFTP requests. ```rust use bytes::BytesMut; use openssh_sftp_client::{Sftp, SftpOptions}; async fn copy_section(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { let mut src = sftp.open("remote/source.bin").await?; let mut dst = sftp.create("remote/dest.bin").await?; // Read exactly 4096 bytes let data = src.read_all(4096, BytesMut::new()).await?; println!("Read {} bytes", data.len()); // Write them all to destination dst.write_all(&data).await?; // Vectorized write from multiple buffers use std::io::IoSlice; let header = b"HEADER"; let body = b"BODY_DATA"; let mut bufs = [IoSlice::new(header), IoSlice::new(body)]; dst.write_all_vectorized(&mut bufs).await?; src.close().await?; dst.close().await?; Ok(()) } ``` -------------------------------- ### List Directory Entries with Fs::open_dir and Dir::read_dir Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Opens a remote directory and returns a `ReadDir` stream. Each `DirEntry` provides filename, metadata, and file type. This is useful for iterating through the contents of a remote directory. ```rust use futures_util::StreamExt; use openssh_sftp_client::{Sftp, SftpOptions}; async fn list_dir(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { let mut fs = sftp.fs(); let dir = fs.open_dir("/home/user/documents").await?; let mut read_dir = dir.read_dir(); while let Some(entry) = read_dir.next().await { let entry = entry?; let name = entry.filename().display().to_string(); let meta = entry.metadata(); let file_type = entry.file_type(); let size = meta.len().unwrap_or(0); match file_type { Some(ft) if ft.is_dir() => println!("DIR {name}"), Some(ft) if ft.is_file() => println!("FILE {name} ({size} bytes)"), Some(ft) if ft.is_symlink() => println!("LINK {name}"), _ => println!(" {name}"), } } Ok(()) } ``` -------------------------------- ### Fs::dir_builder / DirBuilder — Create directories with custom permissions Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Returns a `DirBuilder` for creating remote directories. This builder allows specifying custom uid, gid, and permissions for the newly created directories. ```APIDOC ## `Fs::dir_builder` / `DirBuilder` — Create directories with custom permissions Returns a `DirBuilder` for creating remote directories with specific uid/gid and permissions. ```rust use openssh_sftp_client::{Sftp, SftpOptions, metadata::Permissions}; async fn create_dir_with_perms(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { let mut fs = sftp.fs(); let mut perm = Permissions::new(); perm.set_read_by_owner(true); perm.set_write_by_owner(true); perm.set_execute_by_owner(true); perm.set_read_by_group(true); perm.set_execute_by_group(true); fs.dir_builder() .permissions(perm) .id((1000, 1000)) // uid, gid .create("/tmp/mydir") .await?; Ok(()) } ``` ``` -------------------------------- ### Flush File Data to Disk with `fsync` Extension Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Ensures that all buffered data is written to the remote filesystem. Requires the `fsync` extension. Check availability with `Sftp::support_fsync()`. ```rust use openssh_sftp_client::{Sftp, SftpOptions}; async fn sync_example(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { if !sftp.support_fsync() { eprintln!("fsync extension not available"); return Ok(()); } let mut file = sftp.create("remote/important.dat").await?; file.write_all(b"critical data").await?; file.sync_all().await?; // ensures data reaches the remote disk file.close().await?; Ok(()) } ``` -------------------------------- ### Sftp::from_session Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Launches the SFTP subsystem over an existing openssh Session. Calling close() on the returned Sftp also awaits the remote child process and session close, propagating all errors. ```APIDOC ## Sftp::from_session — Create an SFTP session from an openssh Session (feature: `openssh`) ### Description Launches the SFTP subsystem over an existing `openssh::Session`. Calling `close()` on the returned `Sftp` also awaits the remote child process and session close, propagating all errors. ### Method `Sftp::from_session(session: Session, options: SftpOptions) -> impl Future>` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```rust use openssh::{KnownHosts, Session}; use openssh_sftp_client::{Sftp, SftpOptions}; #[tokio::main] async fn main() -> Result<(), Box> { let session = Session::connect_mux("ssh://user@hostname:22", KnownHosts::Strict).await?; let sftp = Sftp::from_session(session, SftpOptions::default()).await?; let mut fs = sftp.fs(); let contents = fs.read("remote/file.txt").await?; println!("File contents: {}", String::from_utf8_lossy(&contents)); sftp.close().await?; Ok(()) } ``` ### Response #### Success Response (200) An `Sftp` instance representing the active SFTP session. #### Response Example None provided. ``` -------------------------------- ### Perform Remote Filesystem Operations with Sftp::fs Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Use the `Fs` handle for directory and path operations. The working directory defaults to the remote home directory and can be changed with `set_cwd`. Supports a wide range of operations like read, write, create_dir, remove_dir, remove_file, rename, symlink, hard_link, canonicalize, metadata, symlink_metadata, and read_link. ```rust use openssh_sftp_client::{Sftp, SftpOptions, metadata::{Permissions, MetaDataBuilder}}; async fn fs_operations(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { let mut fs = sftp.fs(); // Change working directory fs.set_cwd("/tmp/workspace"); // Create a directory fs.create_dir("subdir").await?; // Write a file (relative to cwd) fs.write("subdir/hello.txt", b"Hello from Fs::write").await?; // Read the file back let bytes = fs.read("subdir/hello.txt").await?; println!("{}", String::from_utf8_lossy(&bytes)); // Canonicalize path (expands ~ if expand-path extension is supported) let canonical = fs.canonicalize("subdir/hello.txt").await?; println!("Canonical: {}", canonical.display()); // Query metadata and symlink metadata let meta = fs.metadata("subdir/hello.txt").await?; println!("Size: {:?}", meta.len()); // Create symlink fs.symlink("subdir/hello.txt", "subdir/link.txt").await?; let target = fs.read_link("subdir/link.txt").await?; println!("Link target: {}", target.display()); // Rename fs.rename("subdir/hello.txt", "subdir/renamed.txt").await?; // Hard link (requires hardlink extension) if sftp.support_hardlink() { fs.hard_link("subdir/renamed.txt", "subdir/hardlink.txt").await?; } // Set permissions via Fs let mut perm = Permissions::new(); perm.set_read_by_owner(true); perm.set_write_by_owner(true); fs.set_permissions("subdir/renamed.txt", perm).await?; // Remove file and directory fs.remove_file("subdir/renamed.txt").await?; fs.remove_dir("subdir").await?; Ok(()) } ``` -------------------------------- ### OpenOptions for Fine-grained File Control Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Use `OpenOptions` to specify fine-grained flags for opening remote files, mirroring `std::fs::OpenOptions`. ```APIDOC ## `OpenOptions` — Fine-grained file open flags Mirrors `std::fs::OpenOptions` for remote files. Supports read, write, append, truncate, create, and create_new modes. ```rust use openssh_sftp_client::{Sftp, SftpOptions}; async fn example(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { // Open for appending, creating if not present let mut file = sftp.options() .append(true) .create(true) .open("remote/log.txt") .await?; file.write_all(b"log entry\n").await?; file.close().await?; // Exclusively create a new file (fails if it already exists) let mut new_file = sftp.options() .write(true) .create_new(true) .open("remote/unique.txt") .await?; new_file.write_all(b"new content").await?; new_file.close().await?; Ok(()) } ``` ``` -------------------------------- ### Fs::open_dir / Dir::read_dir / ReadDir — List directory entries Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Opens a remote directory and returns a `ReadDir` stream. Each `DirEntry` in the stream provides filename, metadata, and file type, allowing for detailed listing of directory contents. ```APIDOC ## `Fs::open_dir` / `Dir::read_dir` / `ReadDir` — List directory entries Opens a remote directory and returns a `ReadDir` stream implementing `futures::Stream>`. Each `DirEntry` provides filename, metadata, and file type. ```rust use futures_util::StreamExt; use openssh_sftp_client::{Sftp, SftpOptions}; async fn list_dir(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { let mut fs = sftp.fs(); let dir = fs.open_dir("/home/user/documents").await?; let mut read_dir = dir.read_dir(); while let Some(entry) = read_dir.next().await { let entry = entry?; let name = entry.filename().display().to_string(); let meta = entry.metadata(); let file_type = entry.file_type(); let size = meta.len().unwrap_or(0); match file_type { Some(ft) if ft.is_dir() => println!("DIR {name}"), Some(ft) if ft.is_file() => println!("FILE {name} ({size} bytes)"), Some(ft) if ft.is_symlink() => println!("LINK {name}"), _ => println!(" {name}"), } } Ok(()) } ``` ``` -------------------------------- ### Extension support checks — Sftp::support_* Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Allows querying which optional SFTP extensions the remote server supports. This is useful for determining if extension-dependent APIs can be safely called. ```APIDOC ## Extension support checks — `Sftp::support_*` Query which optional SFTP extensions the remote server supports before calling extension-dependent APIs. ```rust use openssh_sftp_client::{Sftp, SftpOptions}; async fn check_extensions(sftp: &Sftp) { println!("expand-path : {}", sftp.support_expand_path()); println!("fsync : {}", sftp.support_fsync()); println!("hardlink : {}", sftp.support_hardlink()); println!("posix-rename: {}", sftp.support_posix_rename()); println!("copy-data : {}", sftp.support_copy()); } ``` ``` -------------------------------- ### Sftp::fs — Remote filesystem operations Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Provides an Fs handle for performing various remote filesystem operations such as creating directories, writing/reading files, renaming, and querying metadata. The working directory defaults to the remote home directory and can be changed using `set_cwd`. ```APIDOC ## `Sftp::fs` / `Fs` — Remote filesystem operations Returns an `Fs` handle for directory and path operations. The working directory defaults to the remote home directory; set it with `set_cwd`. Supports: read, write, create_dir, remove_dir, remove_file, rename, symlink, hard_link, canonicalize, metadata, symlink_metadata, read_link. ```rust use openssh_sftp_client::{Sftp, SftpOptions, metadata::{Permissions, MetaDataBuilder}}; async fn fs_operations(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { let mut fs = sftp.fs(); // Change working directory fs.set_cwd("/tmp/workspace"); // Create a directory fs.create_dir("subdir").await?; // Write a file (relative to cwd) fs.write("subdir/hello.txt", b"Hello from Fs::write").await?; // Read the file back let bytes = fs.read("subdir/hello.txt").await?; println!("{}", String::from_utf8_lossy(&bytes)); // Canonicalize path (expands ~ if expand-path extension is supported) let canonical = fs.canonicalize("subdir/hello.txt").await?; println!("Canonical: {}", canonical.display()); // Query metadata and symlink metadata let meta = fs.metadata("subdir/hello.txt").await?; println!("Size: {:?}", meta.len()); // Create symlink fs.symlink("subdir/hello.txt", "subdir/link.txt").await?; let target = fs.read_link("subdir/link.txt").await?; println!("Link target: {}", target.display()); // Rename fs.rename("subdir/hello.txt", "subdir/renamed.txt").await?; // Hard link (requires hardlink extension) if sftp.support_hardlink() { fs.hard_link("subdir/renamed.txt", "subdir/hardlink.txt").await?; } // Set permissions via Fs let mut perm = Permissions::new(); perm.set_read_by_owner(true); perm.set_write_by_owner(true); fs.set_permissions("subdir/renamed.txt", perm).await?; // Remove file and directory fs.remove_file("subdir/renamed.txt").await?; fs.remove_dir("subdir").await?; Ok(()) } ``` ``` -------------------------------- ### File::write_zero_copy / File::write_all_zero_copy Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Perform zero-copy writes directly from `bytes::Bytes` slices, avoiding intermediate data copying for efficient serving of pre-computed buffers. ```APIDOC ## `File::write_zero_copy` / `File::write_all_zero_copy` — Zero-copy write with `bytes::Bytes` Makes it possible to write from a slice of `bytes::Bytes` without copying the data into an intermediate buffer, ideal for serving pre-computed byte buffers efficiently. ```rust use bytes::Bytes; use openssh_sftp_client::{Sftp, SftpOptions}; async fn zero_copy_write(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { let mut file = sftp.create("remote/zeroCopy.bin").await?; let chunk1 = Bytes::from_static(b"chunk-one-data"); let chunk2 = Bytes::from("chunk-two-data".to_string()); let mut bufs = [chunk1, chunk2]; // Write all chunks without copying file.write_all_zero_copy(&mut bufs).await?; file.close().await?; Ok(()) } ``` ``` -------------------------------- ### Zero-copy Write with File::write_zero_copy / File::write_all_zero_copy Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Writes from a slice of `bytes::Bytes` without copying the data into an intermediate buffer, ideal for serving pre-computed byte buffers efficiently. ```rust use bytes::Bytes; use openssh_sftp_client::{Sftp, SftpOptions}; async fn zero_copy_write(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { let mut file = sftp.create("remote/zeroCopy.bin").await?; let chunk1 = Bytes::from_static(b"chunk-one-data"); let chunk2 = Bytes::from("chunk-two-data".to_string()); let mut bufs = [chunk1, chunk2]; // Write all chunks without copying file.write_all_zero_copy(&mut bufs).await?; file.close().await?; Ok(()) } ``` -------------------------------- ### Server-side File Copy (`copy_to`, `copy_all_to`) Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Copies data between two open remote file handles entirely on the server side, avoiding round-trips. Requires the `copy-data` extension. ```APIDOC ## `File::copy_to` / `File::copy_all_to` — Server-side file copy (extension: `copy-data`) Copies data between two open remote file handles entirely on the server side, avoiding round-trips. Requires the `copy-data` extension (available in OpenSSH ≥ V_9_0_P1). Use `Sftp::support_copy()` to check availability. ### Method - `copy_to(&mut self, dst: &mut File, len: NonZeroU64) -> impl Future>` - `copy_all_to(&mut self, dst: &mut File) -> impl Future>` ### Parameters #### `copy_to` - **dst** (`&mut File`) - The destination file handle. - **len** (`NonZeroU64`) - The number of bytes to copy. #### `copy_all_to` - **dst** (`&mut File`) - The destination file handle. ### Request Example ```rust use std::num::NonZeroU64; use openssh_sftp_client::{Sftp, SftpOptions}; async fn server_side_copy(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { if !sftp.support_copy() { eprintln!("copy-data extension not supported"); return Ok(()); } let mut src = sftp.open("remote/source.dat").await?; let mut dst = sftp.create("remote/dest.dat").await?; // Copy first 1024 bytes src.copy_to(&mut dst, NonZeroU64::new(1024).unwrap()).await?; // Copy everything from current position to EOF src.copy_all_to(&mut dst).await?; src.close().await?; dst.close().await?; Ok(()) } ``` ### Response - **Success Response**: `Ok(())` on successful copy. - **Error Response**: `Err(openssh_sftp_client::Error)` if an error occurs during the copy operation. ``` -------------------------------- ### File Attribute Operations (`set_len`, `set_permissions`, `set_metadata`, `metadata`) Source: https://context7.com/openssh-rust/openssh-sftp-client/llms.txt Allows modification and retrieval of file attributes including size, permissions, and metadata. ```APIDOC ## `File::set_len` / `File::set_permissions` / `File::set_metadata` / `File::metadata` — File attribute operations Truncates or extends a file to a given size, changes permissions, or reads/writes arbitrary `MetaData` (size, uid, gid, permissions, timestamps). ### Methods - `set_len(&mut self, len: u64) -> impl Future>` - `set_permissions(&mut self, perm: Permissions) -> impl Future>` - `set_metadata(&mut self, meta: MetaData) -> impl Future>` - `metadata(&mut self) -> impl Future>` ### Parameters #### `set_len` - **len** (`u64`) - The desired new size of the file. #### `set_permissions` - **perm** (`Permissions`) - The new permissions to set for the file. #### `set_metadata` - **meta** (`MetaData`) - The metadata to set, can include UID, GID, and timestamps. ### Request Example ```rust use openssh_sftp_client::{Sftp, SftpOptions, metadata::{MetaDataBuilder, Permissions}}; async fn file_attrs(sftp: &Sftp) -> Result<(), openssh_sftp_client::Error> { let mut file = sftp.options().read(true).write(true).create(true).open("remote/sized.bin").await?; // Extend file to 1 MB file.set_len(1024 * 1024).await?; // Set permissions to rw-r--r-- (0o644) let mut perm = Permissions::new(); perm.set_read_by_owner(true); perm.set_write_by_owner(true); perm.set_read_by_group(true); perm.set_read_by_other(true); file.set_permissions(perm).await?; // Query metadata let meta = file.metadata().await?; println!("Size: {:?}", meta.len()); println!("UID: {:?}", meta.uid()); println!("GID: {:?}", meta.gid()); if let Some(ft) = meta.file_type() { println!("Is file: {}", ft.is_file()); } // Set via MetaDataBuilder (uid/gid + timestamps) let new_meta = MetaDataBuilder::new().id((1000, 1000)).create(); file.set_metadata(new_meta).await?; file.close().await?; Ok(()) } ``` ### Response - **Success Response**: `Ok(())` for setters, `Ok(MetaData)` for `metadata()`. - **Error Response**: `Err(openssh_sftp_client::Error)` if an attribute operation fails. ```