#[cfg(unix)]
use super::types::FxpStatus;
pub const EXT_POSIX_RENAME: &str = "posix-rename@openssh.com";
pub const EXT_STATVFS: &str = "statvfs@openssh.com";
pub const EXT_FSTATVFS: &str = "fstatvfs@openssh.com";
pub const EXT_HARDLINK: &str = "hardlink@openssh.com";
pub const EXT_FSYNC: &str = "fsync@openssh.com";
pub const SSH_FXE_STATVFS_ST_RDONLY: u64 = 0x1;
pub const SSH_FXE_STATVFS_ST_NOSUID: u64 = 0x2;
pub const ADVERTISED_EXTENSIONS: &[(&str, &str)] = &[
(EXT_POSIX_RENAME, "1"),
(EXT_STATVFS, "2"),
(EXT_FSTATVFS, "2"),
(EXT_HARDLINK, "1"),
(EXT_FSYNC, "1"),
];
#[cfg(unix)]
#[derive(Debug, Clone, Copy)]
pub struct StatvfsReply {
pub bsize: u64,
pub frsize: u64,
pub blocks: u64,
pub bfree: u64,
pub bavail: u64,
pub files: u64,
pub ffree: u64,
pub favail: u64,
pub fsid: u64,
pub flag: u64,
pub namemax: u64,
}
#[cfg(unix)]
impl StatvfsReply {
pub fn from_nix(s: &nix::sys::statvfs::Statvfs) -> Self {
let nix_flags = s.flags();
let mut flag: u64 = 0;
if nix_flags.contains(nix::sys::statvfs::FsFlags::ST_RDONLY) {
flag |= SSH_FXE_STATVFS_ST_RDONLY;
}
if nix_flags.contains(nix::sys::statvfs::FsFlags::ST_NOSUID) {
flag |= SSH_FXE_STATVFS_ST_NOSUID;
}
#[allow(clippy::unnecessary_cast)]
Self {
bsize: s.block_size() as u64,
frsize: s.fragment_size() as u64,
blocks: s.blocks() as u64,
bfree: s.blocks_free() as u64,
bavail: s.blocks_available() as u64,
files: s.files() as u64,
ffree: s.files_free() as u64,
favail: s.files_available() as u64,
fsid: s.filesystem_id() as u64,
flag,
namemax: s.name_max() as u64,
}
}
pub fn encode(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(8 * 11);
for v in [
self.bsize,
self.frsize,
self.blocks,
self.bfree,
self.bavail,
self.files,
self.ffree,
self.favail,
self.fsid,
self.flag,
self.namemax,
] {
out.extend_from_slice(&v.to_be_bytes());
}
out
}
}
#[cfg(unix)]
pub fn fxp_status_from_errno(e: nix::errno::Errno) -> FxpStatus {
use nix::errno::Errno;
match e {
Errno::ENOENT => FxpStatus::NoSuchFile,
Errno::EACCES | Errno::EPERM => FxpStatus::PermissionDenied,
_ => FxpStatus::Failure,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn advertised_extensions_include_all_five() {
let names: Vec<&str> = ADVERTISED_EXTENSIONS.iter().map(|(n, _)| *n).collect();
assert!(names.contains(&EXT_POSIX_RENAME));
assert!(names.contains(&EXT_STATVFS));
assert!(names.contains(&EXT_FSTATVFS));
assert!(names.contains(&EXT_HARDLINK));
assert!(names.contains(&EXT_FSYNC));
}
#[cfg(unix)]
#[test]
fn statvfs_reply_encodes_eleven_u64s() {
let r = StatvfsReply {
bsize: 1,
frsize: 2,
blocks: 3,
bfree: 4,
bavail: 5,
files: 6,
ffree: 7,
favail: 8,
fsid: 9,
flag: 10,
namemax: 11,
};
let bytes = r.encode();
assert_eq!(bytes.len(), 8 * 11);
assert_eq!(&bytes[80..], &11u64.to_be_bytes());
}
}