From fada664df7a294d1d1a9ca3e7cd3637069122f17 Mon Sep 17 00:00:00 2001 From: Cheolhee Lee Date: Thu, 1 Oct 2026 00:50:12 +0900 Subject: [PATCH] fix(clipboard): unix, keep Windows directories searchable (#16406) A directory pasted from Windows with FILE_ATTRIBUTE_READONLY or FILE_ATTRIBUTE_HIDDEN (Git for Windows hides `.git`) was given mode 0444 or 0400 in the FUSE list, because the read-only and hidden branches came before the directory branch. Without `x` the directory cannot be entered once a mode-preserving copy lands it on disk. Windows does not restrict access to a directory for either attribute, so give every directory 0755, as a plain one already gets. Co-authored-by: Claude Opus 5.5 --- libs/clipboard/src/platform/unix/filetype.rs | 30 ++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/libs/clipboard/src/platform/unix/filetype.rs b/libs/clipboard/src/platform/unix/filetype.rs index 2ff48174e..408daaecc 100644 --- a/libs/clipboard/src/platform/unix/filetype.rs +++ b/libs/clipboard/src/platform/unix/filetype.rs @@ -140,12 +140,13 @@ impl FileDescription { // cannot set as is... } else if normal { PERM_RWX + } else if directory { + // Windows ignores read-only and hidden on a directory; without `x` it cannot be entered. + PERM_RWX } else if readonly { PERM_READ } else if hidden { PERM_SELF_RO - } else if directory { - PERM_RWX } else { PERM_RW }; @@ -327,6 +328,31 @@ mod tests { )); } + fn parse_perm(attributes: u32) -> u16 { + let mut pdu = descriptor_pdu("folder"); + pdu[ATTRIBUTES_OFFSET..ATTRIBUTES_OFFSET + size_of::()] + .copy_from_slice(&attributes.to_le_bytes()); + FileDescription::parse_file_descriptors(pdu, 0).unwrap()[0].perm + } + + #[test] + fn windows_directories_stay_searchable() { + const FILE_ATTRIBUTE_READONLY: u32 = 0x01; + const FILE_ATTRIBUTE_HIDDEN: u32 = 0x02; + const FILE_ATTRIBUTE_DIRECTORY: u32 = 0x10; + for attributes in [ + FILE_ATTRIBUTE_DIRECTORY, + FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY, + FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_HIDDEN, + ] { + assert_eq!( + parse_perm(attributes), + PERM_RWX, + "attributes {attributes:#x}" + ); + } + } + fn parse_last_write_time(filetime: u64) -> SystemTime { let mut pdu = descriptor_pdu("file.txt"); pdu[PDU_HEADER_SIZE..PDU_HEADER_SIZE + size_of::()]