mirror of
https://github.com/torvalds/linux.git
synced 2026-10-08 03:26:02 +02:00
ksmbd: add AAPL READDIR_ATTR V2 support
Extends the existing V1 inline-FinderInfo mechanism
(SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) with the V2 variant:
byte-identical layout otherwise, except the ShortNameLength+Reserved
bytes (ignored outright by V1 clients) become a single flags field that
V2 clients actually interpret.
Negotiation: when a client's own client_caps requests V2
(SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR_V2), advertise V2 instead of
V1 in the server's own server_caps reply -- they're mutually exclusive
on the wire, not both set together.
Wire format: the only currently-defined V2 flag,
AAPL_READDIR_ATTR_V2_NO_XATTR, signals that an item has no
xattrs/streams so the client can skip a separate query. Compute this
per-entry in ksmbd_vfs_fill_dentry_attrs() by checking for any xattr
under the XATTR_NAME_STREAM ("user.DosStream.") prefix -- a reliable,
distinct marker for genuine ADS/stream xattrs, unlike DOSATTRIB or ACL
xattrs which live under different prefixes, so this can't
false-positive into telling Finder a file has no extra data when it
actually does. Only computed when a V2 connection is active, to avoid
the extra listxattr() call otherwise.
V1's fixed ShortNameLength=24 convention (real macOS clients ignore
the value outright per the same client source, so it's cosmetic
parity with other real servers, not a functional requirement) is kept
V1-only rather than reused as a V2 base value -- V2 clients do
interpret this field, so it needs a clean 0-or-flag value, not a
leftover V1 constant that happens not to collide with the one defined
flag bit today.
Confirmed via live diagnostics that macOS actually negotiates and
uses V2 (client_caps bit 0x10 set) rather than falling back to V1 or
ignoring the capability.
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
aa38147e4f
commit
9907ed8457
|
|
@ -1261,6 +1261,14 @@ struct create_mxac_req {
|
|||
#define SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE 2
|
||||
#define SMB2_CRTCTX_AAPL_UNIX_BASED 4
|
||||
#define SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE 8
|
||||
/*
|
||||
* V2 extends the same inline-FinderInfo mechanism as
|
||||
* SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR with an added flags field,
|
||||
* confirmed byte-identical to V1 otherwise against AAPL's actual
|
||||
* public client behavior. Mutually exclusive with the V1 bit on
|
||||
* the wire, not both set together.
|
||||
*/
|
||||
#define SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR_V2 16
|
||||
|
||||
/* "AAPL" Volume Capabilities bitmap */
|
||||
#define SMB2_CRTCTX_AAPL_SUPPORT_RESOLVE_ID 1
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ struct ksmbd_conn {
|
|||
atomic_t refcnt;
|
||||
bool is_aapl;
|
||||
bool aapl_readdir_attr; /* READDIR_ATTR negotiated */
|
||||
bool aapl_readdir_attr_v2; /* V2 specifically */
|
||||
struct work_struct release_work;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2158,11 +2158,15 @@ void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp)
|
|||
*
|
||||
* Sending reply_bitmap with MODEL_INFO set but no model string causes
|
||||
* smbfs.kext to enter a broken disconnect path requiring a macOS reboot.
|
||||
* @readdir_attr_v2: advertise SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR_V2
|
||||
* instead of the V1 bit
|
||||
*/
|
||||
void create_aapl_rsp_buf(char *cc, __u64 vol_caps, __u64 req_bitmap)
|
||||
void create_aapl_rsp_buf(char *cc, __u64 vol_caps, __u64 req_bitmap,
|
||||
bool readdir_attr_v2)
|
||||
{
|
||||
struct create_aapl_rsp *buf;
|
||||
u64 reply_bitmap;
|
||||
u64 server_caps;
|
||||
u32 data_len;
|
||||
|
||||
buf = (struct create_aapl_rsp *)cc;
|
||||
|
|
@ -2188,8 +2192,12 @@ void create_aapl_rsp_buf(char *cc, __u64 vol_caps, __u64 req_bitmap)
|
|||
|
||||
buf->cmd = cpu_to_le32(SMB2_CRTCTX_AAPL_SERVER_QUERY);
|
||||
buf->reply_bitmap = cpu_to_le64(reply_bitmap);
|
||||
server_caps = AAPL_SERVER_CAPS_KSMBD;
|
||||
if (readdir_attr_v2)
|
||||
server_caps = (server_caps & ~SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) |
|
||||
SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR_V2;
|
||||
buf->server_caps = (reply_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) ?
|
||||
cpu_to_le64(AAPL_SERVER_CAPS_KSMBD) : 0;
|
||||
cpu_to_le64(server_caps) : 0;
|
||||
buf->vol_caps = (reply_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) ?
|
||||
cpu_to_le64(vol_caps) : 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -125,7 +125,8 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp);
|
|||
void create_mxac_rsp_buf(char *cc, int maximal_access);
|
||||
void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id);
|
||||
void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp);
|
||||
void create_aapl_rsp_buf(char *cc, __u64 vol_caps, __u64 req_bitmap);
|
||||
void create_aapl_rsp_buf(char *cc, __u64 vol_caps, __u64 req_bitmap,
|
||||
bool readdir_attr_v2);
|
||||
struct create_context *smb2_find_context_vals(void *open_req, const char *tag, int tag_len);
|
||||
struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn,
|
||||
char *lease_key);
|
||||
|
|
|
|||
|
|
@ -4413,12 +4413,23 @@ int smb2_open(struct ksmbd_work *work)
|
|||
if (aapl_ctxt) {
|
||||
if (aapl_client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR)
|
||||
conn->aapl_readdir_attr = true;
|
||||
/*
|
||||
* V2 extends the same inline-FinderInfo mechanism (see
|
||||
* smb2pdu.h), so a V2-requesting client also gets
|
||||
* aapl_readdir_attr treatment -- the reply just advertises
|
||||
* the V2 bit instead of the V1 one (create_aapl_rsp_buf).
|
||||
*/
|
||||
if (aapl_client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR_V2) {
|
||||
conn->aapl_readdir_attr = true;
|
||||
conn->aapl_readdir_attr_v2 = true;
|
||||
}
|
||||
|
||||
contxt_cnt++;
|
||||
create_aapl_rsp_buf(rsp->Buffer +
|
||||
le32_to_cpu(rsp->CreateContextsLength),
|
||||
SMB2_CRTCTX_AAPL_FULL_SYNC,
|
||||
aapl_req_bitmap);
|
||||
aapl_req_bitmap,
|
||||
conn->aapl_readdir_attr_v2);
|
||||
le32_add_cpu(&rsp->CreateContextsLength,
|
||||
conn->vals->create_aapl_size);
|
||||
iov_len += conn->vals->create_aapl_size;
|
||||
|
|
@ -4762,6 +4773,11 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level,
|
|||
* the file extension for icon lookup)
|
||||
* Reserved2 = Unix mode bits (uint16 LE)
|
||||
* Reparse-point tag is indicated via ExtFileAttributes, not EaSize.
|
||||
*
|
||||
* V2 (conn->aapl_readdir_attr_v2): ShortNameLength+Reserved
|
||||
* are read as a single flags field instead of being ignored
|
||||
* -- see smb2pdu.h for the wire-format confirmation and
|
||||
* AAPL_READDIR_ATTR_V2_NO_XATTR's meaning.
|
||||
*/
|
||||
__le32 reparse_tag =
|
||||
smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode);
|
||||
|
|
@ -4791,7 +4807,20 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level,
|
|||
* interpreted; V1 doesn't. Either value is safe
|
||||
* here, so keep 24 for parity.
|
||||
*/
|
||||
fibdinfo->ShortNameLength = 24;
|
||||
if (conn->aapl_readdir_attr_v2) {
|
||||
/*
|
||||
* V2 repurposes this field as flags (see comment
|
||||
* above) -- 24 is a V1-only convention that real
|
||||
* macOS clients ignore outright, so don't reuse it
|
||||
* here as a base value for a field V2 clients
|
||||
* actually interpret.
|
||||
*/
|
||||
fibdinfo->ShortNameLength = 0;
|
||||
if (!ksmbd_kstat->has_ads_stream)
|
||||
fibdinfo->ShortNameLength = AAPL_READDIR_ATTR_V2_NO_XATTR;
|
||||
} else {
|
||||
fibdinfo->ShortNameLength = 24;
|
||||
}
|
||||
memset(fibdinfo->ShortName, 0, sizeof(fibdinfo->ShortName));
|
||||
fibdinfo->Reserved2 = cpu_to_le16(ksmbd_kstat->kstat->mode & 0xffff);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -95,6 +95,21 @@ struct preauth_integrity_info {
|
|||
SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE | \
|
||||
SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR)
|
||||
|
||||
/*
|
||||
* READDIR_ATTR_V2 (SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR_V2, see
|
||||
* fs/smb/common/smb2pdu.h) extends the same inline-FinderInfo mechanism
|
||||
* above with a flags field, confirmed byte-identical to V1 otherwise
|
||||
* against AAPL's actual public client behavior. When a client's own
|
||||
* client_caps requests V2, the server advertises V2 instead of V1 in
|
||||
* its own server_caps reply; V1 and V2 are mutually exclusive on the
|
||||
* wire, not both set together. The wire format's ShortNameLength+Reserved
|
||||
* (ignored in V1) become a single flags field in V2 --
|
||||
* AAPL_READDIR_ATTR_V2_NO_XATTR is the only flag bit currently defined,
|
||||
* signaling the item has no xattrs/streams so the client can skip a
|
||||
* separate query.
|
||||
*/
|
||||
#define AAPL_READDIR_ATTR_V2_NO_XATTR 0x01
|
||||
|
||||
/* Model string: up to 31 ASCII chars */
|
||||
#define AAPL_MODEL_MAX_CHARS 31
|
||||
#define AAPL_MODEL_UTF16_BYTES (AAPL_MODEL_MAX_CHARS * 2)
|
||||
|
|
|
|||
|
|
@ -1684,6 +1684,35 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work,
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Only pay for this when it'll actually be used: AAPL
|
||||
* READDIR_ATTR_V2's flags field (AAPL_READDIR_ATTR_V2_NO_XATTR) is
|
||||
* the only consumer. XATTR_NAME_STREAM ("user.DosStream.") is a
|
||||
* reliable, distinct prefix for genuine ADS/stream xattrs -- unlike
|
||||
* DOSATTRIB or ACL xattrs, which live under different prefixes, so
|
||||
* this can't false-positive into telling Finder a file has no extra
|
||||
* data when it actually does.
|
||||
*/
|
||||
ksmbd_kstat->has_ads_stream = false;
|
||||
if (work->conn->aapl_readdir_attr_v2) {
|
||||
char *xattr_list = NULL, *name;
|
||||
ssize_t xattr_list_len;
|
||||
|
||||
xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list);
|
||||
if (xattr_list_len > 0) {
|
||||
for (name = xattr_list;
|
||||
name - xattr_list < xattr_list_len;
|
||||
name += strlen(name) + 1) {
|
||||
if (!strncmp(name, XATTR_NAME_STREAM,
|
||||
XATTR_NAME_STREAM_LEN)) {
|
||||
ksmbd_kstat->has_ads_stream = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
kvfree(xattr_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ struct ksmbd_kstat {
|
|||
struct kstat *kstat;
|
||||
unsigned long long create_time;
|
||||
__le32 file_attributes;
|
||||
bool has_ads_stream; /* AAPL READDIR_ATTR V2 xattr-presence flag */
|
||||
};
|
||||
|
||||
int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user