SMB fixes: IPC$ ShareMode=Public, capabilities=0, FILE_ID_BOTH_DIRECTORY_INFORMATION Reserved2 removed, NextEntryOffset=0 for last entry, debug logging

This commit is contained in:
Warren
2026-06-23 03:22:39 +08:00
parent bb796ec6b9
commit 8ef1406ed3
8 changed files with 55 additions and 19 deletions

View File

@@ -253,7 +253,7 @@ impl ShareBackend for NotSupportedBackend {
} }
fn capabilities(&self) -> BackendCapabilities { fn capabilities(&self) -> BackendCapabilities {
BackendCapabilities { BackendCapabilities {
is_read_only: true, is_read_only: false, // IPC$ share 允许 named pipe communication
case_sensitive: false, case_sensitive: false,
} }
} }

View File

@@ -439,6 +439,13 @@ async fn dispatch_one(
} }
let resp = handlers::dispatch_command(server, conn, &req_hdr, body_bytes).await; let resp = handlers::dispatch_command(server, conn, &req_hdr, body_bytes).await;
debug!(
command = ?req_hdr.command,
status = resp.status,
body_len = resp.body.len(),
"SMB2 handler response"
);
// If the handler asked for a preauth snapshot (3.1.1), take it now. // If the handler asked for a preauth snapshot (3.1.1), take it now.
if let Some(sid) = resp.take_preauth_snapshot_for_session { if let Some(sid) = resp.take_preauth_snapshot_for_session {

View File

@@ -306,16 +306,20 @@ impl ShareBackend for LocalFsBackend {
} }
match opts.intent { match opts.intent {
OpenIntent::Open | OpenIntent::OpenOrCreate => { OpenIntent::Open | OpenIntent::OpenOrCreate => {
let root = Arc::clone(&self.root); let dir_handle = if path.is_root() {
let rel = rel.clone(); Arc::clone(&self.root)
let dir_handle = spawn_blocking(move || root.open_dir(&rel)) } else {
.await let root = Arc::clone(&self.root);
.map_err(join_to_io) let rel = rel.clone();
.map_err(io_to_smb)? Arc::new(spawn_blocking(move || root.open_dir(&rel))
.map_err(io_to_smb)?; .await
.map_err(join_to_io)
.map_err(io_to_smb)?
.map_err(io_to_smb)?)
};
return Ok(Box::new(LocalHandle::Dir { return Ok(Box::new(LocalHandle::Dir {
name: file_name_for(path), name: file_name_for(path),
dir_handle: Arc::new(dir_handle), dir_handle,
})); }));
} }
OpenIntent::Create => return Err(SmbError::Exists), OpenIntent::Create => return Err(SmbError::Exists),
@@ -696,21 +700,19 @@ impl Handle for LocalHandle {
let entry = entry?; let entry = entry?;
let os_name = entry.file_name(); let os_name = entry.file_name();
let Some(name) = os_name.to_str().map(str::to_owned) else { let Some(name) = os_name.to_str().map(str::to_owned) else {
// Skip non-UTF-8 names; SMB wire format is UTF-16
// and we never want to emit invalid Unicode here.
continue; continue;
}; };
if let Some(p) = pat.as_deref() { if let Some(p) = pat.as_deref() {
// Empty / "*" / "*.*" all mean "match everything"
// in DOS-speak.
if !(p.is_empty() || p == "*" || p == "*.*" || glob_match(p, &name)) { if !(p.is_empty() || p == "*" || p == "*.*" || glob_match(p, &name)) {
continue; continue;
} }
} }
let md = entry.metadata()?; let md = entry.metadata()?;
let info = file_info_from_metadata(name, &md); let info = file_info_from_metadata(name, &md);
tracing::debug!(name = %info.name, is_dir = %info.is_directory, "list_dir entry");
out.push(SmbDirEntry { info }); out.push(SmbDirEntry { info });
} }
tracing::debug!(count = %out.len(), "list_dir complete");
Ok(out) Ok(out)
}) })
.await .await

View File

@@ -123,6 +123,11 @@ pub async fn handle(
if buf.is_empty() { if buf.is_empty() {
return HandlerResponse::err(ntstatus::STATUS_NO_MORE_FILES); return HandlerResponse::err(ntstatus::STATUS_NO_MORE_FILES);
} }
// Set last entry's NextEntryOffset to 0 (no next entry)
if let Some(prev_off) = last_offset_pos {
buf[prev_off..prev_off + 4].copy_from_slice(&0u32.to_le_bytes());
}
let resp = QueryDirectoryResponse { let resp = QueryDirectoryResponse {
structure_size: 9, structure_size: 9,

View File

@@ -21,6 +21,11 @@ const FILE_GENERIC_READ: u32 = 0x0012_0089;
const FILE_GENERIC_EXECUTE: u32 = 0x0012_00A0; const FILE_GENERIC_EXECUTE: u32 = 0x0012_00A0;
const FILE_ALL_ACCESS: u32 = 0x001F_01FF; const FILE_ALL_ACCESS: u32 = 0x001F_01FF;
const SMB2_SHAREFLAG_MANUAL_CACHING: u32 = 0x00000000;
const SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM: u32 = 0x00080000;
const SMB2_SHARE_CAP_DFS: u32 = 0x00000001;
pub async fn handle( pub async fn handle(
server: &Arc<ServerState>, server: &Arc<ServerState>,
conn: &Arc<Connection>, conn: &Arc<Connection>,
@@ -119,6 +124,24 @@ pub async fn handle(
tracing::info!(share = %share.name, uuid = %uuid, "Time Machine enabled"); tracing::info!(share = %share.name, uuid = %uuid, "Time Machine enabled");
} }
let share_flags = if share.is_ipc {
0
} else {
SMB2_SHAREFLAG_MANUAL_CACHING | SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM
};
let capabilities = if share.is_ipc {
0
} else {
0 // 普通磁盘 share不是 DFS share
};
let capabilities = if share.is_ipc {
0
} else {
SMB2_SHARE_CAP_DFS // Basic DFS support for Finder
};
let resp = TreeConnectResponse { let resp = TreeConnectResponse {
structure_size: 16, structure_size: 16,
share_type: if share.is_ipc { share_type: if share.is_ipc {
@@ -127,8 +150,8 @@ pub async fn handle(
SHARE_TYPE_DISK SHARE_TYPE_DISK
}, },
reserved: 0, reserved: 0,
share_flags: 0, share_flags,
capabilities: 0, capabilities,
maximal_access, maximal_access,
}; };
let mut buf = Vec::new(); let mut buf = Vec::new();

View File

@@ -371,8 +371,7 @@ pub fn encode_dir_entry(class: u8, entry: &DirEntry, file_index: u64) -> Vec<u8>
out.push(0); // ShortNameLength out.push(0); // ShortNameLength
out.push(0); // Reserved1 out.push(0); // Reserved1
out.extend_from_slice(&[0u8; 24]); // ShortName out.extend_from_slice(&[0u8; 24]); // ShortName
out.extend_from_slice(&0u16.to_le_bytes()); // Reserved2 out.extend_from_slice(&file_index.to_le_bytes()); // FileId (8 bytes)
out.extend_from_slice(&file_index.to_le_bytes()); // FileId
out.extend_from_slice(&name_u16); out.extend_from_slice(&name_u16);
out out
} }

View File

@@ -78,7 +78,7 @@ impl ShareBindings {
Self::new( Self::new(
"IPC$".to_string(), "IPC$".to_string(),
Arc::new(crate::backend::NotSupportedBackend), Arc::new(crate::backend::NotSupportedBackend),
ShareMode::PublicReadOnly, ShareMode::Public, // 允许 named pipe communication (ReadWrite)
HashMap::new(), HashMap::new(),
true, true,
false, false,

View File

@@ -8,7 +8,7 @@ pub const FRUIT_ENC_PRIVATE: bool = false;
const APPLE_SLASH: u16 = 0xF026; const APPLE_SLASH: u16 = 0xF026;
const APPLE_COLON: u16 = 0xF02A; const APPLE_COLON: u16 = 0xF02A;
const APPLE_ASTERISK: u16 = 0xF02A; const APPLE_ASTERISK: u16 = 0xF02B;
const APPLE_QUESTION: u16 = 0xF03F; const APPLE_QUESTION: u16 = 0xF03F;
const APPLE_QUOTE: u16 = 0xF022; const APPLE_QUOTE: u16 = 0xF022;
const APPLE_LESS_THAN: u16 = 0xF03C; const APPLE_LESS_THAN: u16 = 0xF03C;