cifs: Query EA $LXMOD in cifs_query_path_info() for WSL reparse points

[ Upstream commit 057ac50638bcece64b3b436d3a61b70ed6c01a34 ]

EA $LXMOD is required for WSL non-symlink reparse points.

Fixes: ef86ab131d91 ("cifs: Fix querying of WSL CHR and BLK reparse points over SMB1")
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Pali Rohár
2025-06-07 18:11:10 +02:00
committed by Greg Kroah-Hartman
parent a133e2699f
commit 436cfdbc57

View File

@@ -671,14 +671,72 @@ static int cifs_query_path_info(const unsigned int xid,
}
#ifdef CONFIG_CIFS_XATTR
/*
* For non-symlink WSL reparse points it is required to fetch
* EA $LXMOD which contains in its S_DT part the mandatory file type.
*/
if (!rc && data->reparse_point) {
struct smb2_file_full_ea_info *ea;
u32 next = 0;
ea = (struct smb2_file_full_ea_info *)data->wsl.eas;
do {
ea = (void *)((u8 *)ea + next);
next = le32_to_cpu(ea->next_entry_offset);
} while (next);
if (le16_to_cpu(ea->ea_value_length)) {
ea->next_entry_offset = cpu_to_le32(ALIGN(sizeof(*ea) +
ea->ea_name_length + 1 +
le16_to_cpu(ea->ea_value_length), 4));
ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset));
}
rc = CIFSSMBQAllEAs(xid, tcon, full_path, SMB2_WSL_XATTR_MODE,
&ea->ea_data[SMB2_WSL_XATTR_NAME_LEN + 1],
SMB2_WSL_XATTR_MODE_SIZE, cifs_sb);
if (rc == SMB2_WSL_XATTR_MODE_SIZE) {
ea->next_entry_offset = cpu_to_le32(0);
ea->flags = 0;
ea->ea_name_length = SMB2_WSL_XATTR_NAME_LEN;
ea->ea_value_length = cpu_to_le16(SMB2_WSL_XATTR_MODE_SIZE);
memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_MODE, SMB2_WSL_XATTR_NAME_LEN + 1);
data->wsl.eas_len += ALIGN(sizeof(*ea) + SMB2_WSL_XATTR_NAME_LEN + 1 +
SMB2_WSL_XATTR_MODE_SIZE, 4);
rc = 0;
} else if (rc >= 0) {
/* It is an error if EA $LXMOD has wrong size. */
rc = -EINVAL;
} else {
/*
* In all other cases ignore error if fetching
* of EA $LXMOD failed. It is needed only for
* non-symlink WSL reparse points and wsl_to_fattr()
* handle the case when EA is missing.
*/
rc = 0;
}
}
/*
* For WSL CHR and BLK reparse points it is required to fetch
* EA $LXDEV which contains major and minor device numbers.
*/
if (!rc && data->reparse_point) {
struct smb2_file_full_ea_info *ea;
u32 next = 0;
ea = (struct smb2_file_full_ea_info *)data->wsl.eas;
do {
ea = (void *)((u8 *)ea + next);
next = le32_to_cpu(ea->next_entry_offset);
} while (next);
if (le16_to_cpu(ea->ea_value_length)) {
ea->next_entry_offset = cpu_to_le32(ALIGN(sizeof(*ea) +
ea->ea_name_length + 1 +
le16_to_cpu(ea->ea_value_length), 4));
ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset));
}
rc = CIFSSMBQAllEAs(xid, tcon, full_path, SMB2_WSL_XATTR_DEV,
&ea->ea_data[SMB2_WSL_XATTR_NAME_LEN + 1],
SMB2_WSL_XATTR_DEV_SIZE, cifs_sb);
@@ -688,8 +746,8 @@ static int cifs_query_path_info(const unsigned int xid,
ea->ea_name_length = SMB2_WSL_XATTR_NAME_LEN;
ea->ea_value_length = cpu_to_le16(SMB2_WSL_XATTR_DEV_SIZE);
memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_DEV, SMB2_WSL_XATTR_NAME_LEN + 1);
data->wsl.eas_len = sizeof(*ea) + SMB2_WSL_XATTR_NAME_LEN + 1 +
SMB2_WSL_XATTR_DEV_SIZE;
data->wsl.eas_len += ALIGN(sizeof(*ea) + SMB2_WSL_XATTR_NAME_LEN + 1 +
SMB2_WSL_XATTR_MODE_SIZE, 4);
rc = 0;
} else if (rc >= 0) {
/* It is an error if EA $LXDEV has wrong size. */