From: "Gaël Blivet-Bailly" <gael.blivet@gmail.com>
To: Namjae Jeon <linkinjeon@kernel.org>
Cc: Gael Blivet <gael.blivet@gmail.com>, linux-cifs@vger.kernel.org
Subject: [PATCH v2 3/6] ksmbd: send inline FinderInfo in FIND responses when READDIR_ATTR negotiated
Date: Thu, 9 Jul 2026 02:06:09 +0200 [thread overview]
Message-ID: <20260709000618.66534-4-g.blivet@me.com> (raw)
In-Reply-To: <20260709000618.66534-1-g.blivet@me.com>
From: Gael Blivet <gael.blivet@gmail.com>
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 Samba, 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 Samba's vfs_fruit marshalling
(source3/smbd/smb2_trans2.c, SMB_FIND_ID_BOTH_DIRECTORY_INFO). Also
confirmed against Apple's published SMBClient kernel source
(apple-oss-distributions/SMBClient, smb_smb_2.c) -- 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 Samba, not
because macOS requires it.
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
---
v1 -> v2: Cross-checked wire format against Apple's published
SMBClient kernel source -- confirmed every field matches, and
corrected an inaccurate claim about ShortNameLength=24 being
something real clients require (they don't; a comment-only fix).
fs/smb/server/smb2pdu.c | 60 +++++++++++++++++++++++++++++++++++++----
1 file changed, 55 insertions(+), 5 deletions(-)
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 8fed5fd6d..120c2c80d 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -4733,17 +4733,67 @@ 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 Samba's
+ * vfs_fruit marshalling (source3/smbd/smb2_trans2.c):
+ * 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
+ * Samba's vfs_fruit marshalling (smb2_trans2.c) for
+ * server-to-server wire parity. Apple's own SMBClient
+ * source (smb_smb_2.c) confirms real macOS clients
+ * read this field but never examine its value for a
+ * V1 (non-READDIR_ATTR_V2) connection -- 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);
--
2.43.0
next prev parent reply other threads:[~2026-07-09 0:07 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-09 0:06 [PATCH v2 0/6] ksmbd: add macOS Time Machine / AAPL SMB2 extension support Gaël Blivet-Bailly
2026-07-09 0:06 ` [PATCH v2 1/6] ksmbd: add Apple AAPL kAAPL_SERVER_QUERY create context support Gaël Blivet-Bailly
2026-07-09 0:06 ` [PATCH v2 2/6] ksmbd: synthesize empty AFP_AfpInfo xattr on first probe Gaël Blivet-Bailly
2026-07-09 0:06 ` Gaël Blivet-Bailly [this message]
2026-07-09 0:06 ` [PATCH v2 4/6] ksmbd: defer CHANGE_NOTIFY completion instead of STATUS_NOT_IMPLEMENTED Gaël Blivet-Bailly
2026-07-09 7:03 ` ChenXiaoSong
2026-07-09 9:24 ` ChenXiaoSong
2026-07-10 5:59 ` Gaël Blivet
2026-07-10 6:48 ` ChenXiaoSong
2026-07-10 7:04 ` ChenXiaoSong
2026-07-10 8:54 ` Gaël Blivet
2026-07-10 9:15 ` ChenXiaoSong
2026-07-10 13:28 ` Gaël Blivet
2026-07-09 0:06 ` [PATCH v2 5/6] ksmbd: implement full-file copy for AAPL ChunkCount=0 COPYCHUNK Gaël Blivet-Bailly
2026-07-09 0:06 ` [PATCH v2 6/6] ksmbd: add AAPL READDIR_ATTR V2 support Gaël Blivet-Bailly
2026-07-09 13:15 ` [PATCH v2 0/6] ksmbd: add macOS Time Machine / AAPL SMB2 extension support Namjae Jeon
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260709000618.66534-4-g.blivet@me.com \
--to=gael.blivet@gmail.com \
--cc=linkinjeon@kernel.org \
--cc=linux-cifs@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox