diff --git a/vendor/smb-server/src/backend.rs b/vendor/smb-server/src/backend.rs index 97848d8..f7ef707 100644 --- a/vendor/smb-server/src/backend.rs +++ b/vendor/smb-server/src/backend.rs @@ -253,7 +253,7 @@ impl ShareBackend for NotSupportedBackend { } fn capabilities(&self) -> BackendCapabilities { BackendCapabilities { - is_read_only: true, + is_read_only: false, // IPC$ share 允许 named pipe communication case_sensitive: false, } } diff --git a/vendor/smb-server/src/dispatch.rs b/vendor/smb-server/src/dispatch.rs index b72b602..1c74b70 100644 --- a/vendor/smb-server/src/dispatch.rs +++ b/vendor/smb-server/src/dispatch.rs @@ -439,6 +439,13 @@ async fn dispatch_one( } 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 let Some(sid) = resp.take_preauth_snapshot_for_session { diff --git a/vendor/smb-server/src/fs/local.rs b/vendor/smb-server/src/fs/local.rs index 340692b..93ce9ca 100644 --- a/vendor/smb-server/src/fs/local.rs +++ b/vendor/smb-server/src/fs/local.rs @@ -306,16 +306,20 @@ impl ShareBackend for LocalFsBackend { } match opts.intent { OpenIntent::Open | OpenIntent::OpenOrCreate => { - let root = Arc::clone(&self.root); - let rel = rel.clone(); - let dir_handle = spawn_blocking(move || root.open_dir(&rel)) - .await - .map_err(join_to_io) - .map_err(io_to_smb)? - .map_err(io_to_smb)?; + let dir_handle = if path.is_root() { + Arc::clone(&self.root) + } else { + let root = Arc::clone(&self.root); + let rel = rel.clone(); + Arc::new(spawn_blocking(move || root.open_dir(&rel)) + .await + .map_err(join_to_io) + .map_err(io_to_smb)? + .map_err(io_to_smb)?) + }; return Ok(Box::new(LocalHandle::Dir { name: file_name_for(path), - dir_handle: Arc::new(dir_handle), + dir_handle, })); } OpenIntent::Create => return Err(SmbError::Exists), @@ -696,21 +700,19 @@ impl Handle for LocalHandle { let entry = entry?; let os_name = entry.file_name(); 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; }; 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)) { continue; } } let md = entry.metadata()?; 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 }); } + tracing::debug!(count = %out.len(), "list_dir complete"); Ok(out) }) .await diff --git a/vendor/smb-server/src/handlers/query_directory.rs b/vendor/smb-server/src/handlers/query_directory.rs index 48e98b1..55a993a 100644 --- a/vendor/smb-server/src/handlers/query_directory.rs +++ b/vendor/smb-server/src/handlers/query_directory.rs @@ -123,6 +123,11 @@ pub async fn handle( if buf.is_empty() { 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 { structure_size: 9, diff --git a/vendor/smb-server/src/handlers/tree_connect.rs b/vendor/smb-server/src/handlers/tree_connect.rs index 49ed22b..38d599c 100644 --- a/vendor/smb-server/src/handlers/tree_connect.rs +++ b/vendor/smb-server/src/handlers/tree_connect.rs @@ -21,6 +21,11 @@ const FILE_GENERIC_READ: u32 = 0x0012_0089; const FILE_GENERIC_EXECUTE: u32 = 0x0012_00A0; 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( server: &Arc, conn: &Arc, @@ -119,6 +124,24 @@ pub async fn handle( 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 { structure_size: 16, share_type: if share.is_ipc { @@ -127,8 +150,8 @@ pub async fn handle( SHARE_TYPE_DISK }, reserved: 0, - share_flags: 0, - capabilities: 0, + share_flags, + capabilities, maximal_access, }; let mut buf = Vec::new(); diff --git a/vendor/smb-server/src/info_class.rs b/vendor/smb-server/src/info_class.rs index 42a3e9d..179e519 100644 --- a/vendor/smb-server/src/info_class.rs +++ b/vendor/smb-server/src/info_class.rs @@ -371,8 +371,7 @@ pub fn encode_dir_entry(class: u8, entry: &DirEntry, file_index: u64) -> Vec out.push(0); // ShortNameLength out.push(0); // Reserved1 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 + out.extend_from_slice(&file_index.to_le_bytes()); // FileId (8 bytes) out.extend_from_slice(&name_u16); out } diff --git a/vendor/smb-server/src/server.rs b/vendor/smb-server/src/server.rs index ffa4780..f566c2a 100644 --- a/vendor/smb-server/src/server.rs +++ b/vendor/smb-server/src/server.rs @@ -78,7 +78,7 @@ impl ShareBindings { Self::new( "IPC$".to_string(), Arc::new(crate::backend::NotSupportedBackend), - ShareMode::PublicReadOnly, + ShareMode::Public, // 允许 named pipe communication (ReadWrite) HashMap::new(), true, false, diff --git a/vendor/smb-server/src/unicode_mapping.rs b/vendor/smb-server/src/unicode_mapping.rs index 7190b4a..5c3fc30 100644 --- a/vendor/smb-server/src/unicode_mapping.rs +++ b/vendor/smb-server/src/unicode_mapping.rs @@ -8,7 +8,7 @@ pub const FRUIT_ENC_PRIVATE: bool = false; const APPLE_SLASH: u16 = 0xF026; const APPLE_COLON: u16 = 0xF02A; -const APPLE_ASTERISK: u16 = 0xF02A; +const APPLE_ASTERISK: u16 = 0xF02B; const APPLE_QUESTION: u16 = 0xF03F; const APPLE_QUOTE: u16 = 0xF022; const APPLE_LESS_THAN: u16 = 0xF03C;