ksmbd: synthesize empty AFP_AfpInfo xattr on first probe

Once a server advertises the AAPL COPYFILE capability, macOS requires
an AFP_AfpInfo stream on every file it looks at for Finder type/
creator/icon resolution. smb2_set_stream_name_xattr() currently
returns -EBADF (STATUS_OBJECT_NAME_NOT_FOUND) when a client opens
AFP_AfpInfo with FILE_OPEN disposition and the xattr doesn't exist
yet, which macOS treats as fatal for that file: Finder falls back to
showing a generic icon, and file operations that depend on succeeding
against this stream (e.g. Cmd+D duplication) fail.

Synthesize a 60-byte zeroed AFP_AfpInfo xattr (magic 0x00051607,
version 0x00020000, both big-endian per the AFP_AfpInfo wire format)
on first FILE_OPEN probe instead. type=0/creator=0 tells macOS to fall
back to extension-based type detection, which is correct for files with
no explicit Finder metadata. The synthesized xattr persists on disk, so
this only pays the extra write once per file; a later genuine write from
macOS (e.g. after the user assigns a custom icon) overwrites it normally.

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:
Gael Blivet 2026-07-09 02:06:08 +02:00 committed by Namjae Jeon
parent 8f1b796ff1
commit eaff8e924f

View File

@ -2857,6 +2857,30 @@ static noinline int smb2_set_stream_name_xattr(const struct path *path,
return 0;
if (fp->cdoption == FILE_OPEN_LE) {
if (!strcmp(stream_name, "AFP_AfpInfo") &&
test_share_config_flag(fp->tcon->share_conf,
KSMBD_SHARE_FLAG_TIME_MACHINE)) {
/*
* Synthesize an empty AFP_AfpInfo xattr on first access.
* type=0/creator=0 tells macOS to use the file extension
* for icon and type detection.
*
* Scoped to TIME_MACHINE shares, matching the rest of
* the AAPL series -- conn->is_aapl alone isn't a safe
* gate here, since the pre-existing narrow UniqueId=0
* path can also set it on ordinary, non-Time-Machine
* shares whenever a Mac client happens to negotiate
* AAPL there too.
*/
static const u8 afpinfo_empty[60] = {
0x00, 0x05, 0x16, 0x07, /* magic 0x00051607 BE */
0x00, 0x02, 0x00, 0x00, /* version 0x00020000 BE */
};
rc = ksmbd_vfs_setxattr(idmap, path, xattr_stream_name,
(void *)afpinfo_empty,
sizeof(afpinfo_empty), 0, false);
return rc < 0 ? rc : 0;
}
ksmbd_debug(SMB, "XATTR stream name lookup failed: %d\n", rc);
return -EBADF;
}