mirror of
https://github.com/torvalds/linux.git
synced 2026-10-10 12:28:02 +02:00
ksmbd: send inline FinderInfo in FIND responses when READDIR_ATTR negotiated
Without READDIR_ATTR, macOS Finder resolves type/creator/icon for
every file in a directory listing by opening its AFP_AfpInfo stream
individually -- one extra CREATE+QUERY_INFO+CLOSE round trip per file,
which is the dominant cost of browsing a large directory over SMB from
a Mac.
When the client negotiates READDIR_ATTR (conn->aapl_readdir_attr, set
during CREATE's AAPL context exchange), inline the same information
directly into each FILEID_BOTH_DIRECTORY_INFORMATION FIND entry:
EaSize = max_access, expanded specific rights
(GENERIC_ALL_FLAGS), not the raw FILE_GENERIC_ALL_LE
"generic" meta-bit -- that bit has none of the
specific FILE_* rights macOS's smbfs.kext checks
bit-by-bit, so reporting it directly would fail
every access check and show Finder's "no entry"
badge on every file/folder.
ShortNameLength = 24 (fixed; the spec says 0 when there's no short
name; kept for wire parity with reference server,
see below)
ShortName[0..7] = resource fork size (0 -- no resource forks)
ShortName[8..23] = compressed FinderInfo (all zero: type/creator
unset, client falls back to extension-based
icon/type detection, consistent with the
AFP_AfpInfo synthesis this mirrors)
Reserved2 = Unix mode bits
Reparse-point status is still carried via ExtFileAttributes rather
than EaSize once READDIR_ATTR is active, since EaSize is repurposed
for max_access.
Reverse-engineered from macOS smbfs.kext network behavior and cross-checked
against reference implementation marshalling (reference implementation
behavior). Also confirmed against AAPL's published public client behavior
(public client behavior reference) -- every field here matches exactly,
except ShortNameLength=24: real V1 clients read but never examine that
field, so it's kept for wire parity with reference server, not because
macOS requires it.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
eaff8e924f
commit
9dfb281383
|
|
@ -4742,17 +4742,65 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level,
|
|||
|
||||
fibdinfo = (struct file_id_both_directory_info *)kstat;
|
||||
fibdinfo->FileNameLength = cpu_to_le32(conv_len);
|
||||
fibdinfo->EaSize =
|
||||
smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode);
|
||||
if (fibdinfo->EaSize)
|
||||
fibdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE;
|
||||
if (conn->is_aapl)
|
||||
fibdinfo->UniqueId = 0;
|
||||
else
|
||||
fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino);
|
||||
fibdinfo->ShortNameLength = 0;
|
||||
fibdinfo->Reserved = 0;
|
||||
fibdinfo->Reserved2 = cpu_to_le16(0);
|
||||
if (conn->aapl_readdir_attr) {
|
||||
/*
|
||||
* READDIR_ATTR wire format, confirmed against reference server's
|
||||
* reference implementation marshalling (reference implementation behavior):
|
||||
* EaSize = max_access (expanded specific
|
||||
* rights, simplified to "grant all")
|
||||
* ShortNameLength = 24 (fixed; not 0, despite the spec)
|
||||
* ShortName[0..7] = resource fork size (uint64 LE, 0 = no rfork)
|
||||
* ShortName[8..23] = compressed FinderInfo (type+creator+flags+
|
||||
* ext_flags+date_added, 16 bytes LE; all
|
||||
* zeros means type=0/creator=0, i.e. use
|
||||
* the file extension for icon lookup)
|
||||
* Reserved2 = Unix mode bits (uint16 LE)
|
||||
* Reparse-point tag is indicated via ExtFileAttributes, not EaSize.
|
||||
*/
|
||||
__le32 reparse_tag =
|
||||
smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode);
|
||||
|
||||
if (reparse_tag)
|
||||
fibdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE;
|
||||
/*
|
||||
* FILE_GENERIC_ALL_LE (0x10000000) is the raw
|
||||
* "generic all" meta-bit -- valid only in a
|
||||
* client's requested access mask, for the server
|
||||
* to expand. It has none of the specific FILE_*
|
||||
* rights bits set (FILE_LIST_DIRECTORY, FILE_TRAVERSE,
|
||||
* etc.), so reporting it here as max_access would make
|
||||
* macOS's bit-by-bit access checks fail on every
|
||||
* entry -> permanent "no entry" badges in Finder.
|
||||
* Report the actual expanded rights instead, same
|
||||
* as smb_map_generic_desired_access() does when
|
||||
* translating a client's GENERIC_ALL request.
|
||||
*/
|
||||
fibdinfo->EaSize = cpu_to_le32(GENERIC_ALL_FLAGS);
|
||||
/*
|
||||
* The spec says ShortNameLength should be 0 when
|
||||
* there's no short name; 24 here instead matches
|
||||
* reference implementation marshalling (reference
|
||||
* behavior) for server-to-server wire parity.
|
||||
* V2 repurposes it as a flags field that is
|
||||
* interpreted; V1 doesn't. Either value is safe
|
||||
* here, so keep 24 for parity.
|
||||
*/
|
||||
fibdinfo->ShortNameLength = 24;
|
||||
memset(fibdinfo->ShortName, 0, sizeof(fibdinfo->ShortName));
|
||||
fibdinfo->Reserved2 = cpu_to_le16(ksmbd_kstat->kstat->mode & 0xffff);
|
||||
} else {
|
||||
fibdinfo->EaSize =
|
||||
smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode);
|
||||
if (fibdinfo->EaSize)
|
||||
fibdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE;
|
||||
fibdinfo->Reserved2 = cpu_to_le16(0);
|
||||
}
|
||||
if (d_info->hide_dot_file && d_info->name[0] == '.')
|
||||
fibdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE;
|
||||
memcpy(fibdinfo->FileName, conv_name, conv_len);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user