Fix macOS SMB mount: AAPL caps, credit grant, file_index, QueryDirectory padding

- AAPL: Restore UNIX_BASED+NFS_ACE server_caps, RESOLVE_ID+FULL_SYNC volume_caps (Samba baseline)
- Credit: Grant min 1 credit in dispatch response for smbclient compatibility
- file_index: Assign 1-based index per entry in list_dir (both VFS and local backends)
- smb_match(): Add wildcard pattern filter (*/?) for macOS single-entry QueryDirectory probes
- FILE_ID_BOTH_DIR_INFORMATION: Add 2-byte Reserved2 padding between ShortName and FileId
- macOS Sequoia 15.5 mount_smbfs now succeeds (tested: ls, cat, read)
This commit is contained in:
Warren
2026-06-23 09:44:01 +08:00
parent 8ef1406ed3
commit e7863a3034
6 changed files with 94 additions and 15 deletions

View File

@@ -285,18 +285,31 @@ impl Handle for VfsHandle {
}
}
async fn list_dir(&self, _pattern: Option<&str>) -> Result<Vec<DirEntry>, SmbError> {
async fn list_dir(&self, pattern: Option<&str>) -> Result<Vec<DirEntry>, SmbError> {
match self {
Self::File { .. } => Err(SmbError::NotADirectory),
Self::Directory { vfs, path } => {
let entries = vfs.read_dir(path).map_err(map_error)?;
let result = entries
let mut result: Vec<DirEntry> = entries
.into_iter()
.filter(|entry| {
let p = match pattern {
None => return true,
Some(p) => p,
};
if p == "*" || p == "*.*" || p.is_empty() {
return true;
}
smb_match(&entry.name, p)
})
.map(|entry| {
let info = vfs_stat_to_file_info(&entry.stat, &entry.name, path);
DirEntry { info }
})
.collect();
for (i, entry) in result.iter_mut().enumerate() {
entry.info.file_index = (i + 1) as u64;
}
Ok(result)
}
}
@@ -307,6 +320,36 @@ impl Handle for VfsHandle {
}
}
/// Simple SMB wildcard match: `*` matches any sequence, `?` matches one char.
fn smb_match(name: &str, pattern: &str) -> bool {
let name = name.as_bytes();
let pat = pattern.as_bytes();
let mut ni = 0;
let mut pi = 0;
let mut star_idx: Option<usize> = None;
let mut match_idx = 0;
while ni < name.len() {
if pi < pat.len() && (pat[pi] == b'?' || pat[pi] == name[ni]) {
ni += 1;
pi += 1;
} else if pi < pat.len() && pat[pi] == b'*' {
star_idx = Some(pi);
match_idx = ni;
pi += 1;
} else if let Some(si) = star_idx {
pi = si + 1;
match_idx += 1;
ni = match_idx;
} else {
return false;
}
}
while pi < pat.len() && pat[pi] == b'*' {
pi += 1;
}
pi == pat.len()
}
fn filetime_to_systemtime(ft: u64) -> SystemTime {
if ft < FILETIME_OFFSET {
return SystemTime::UNIX_EPOCH;