Commit 057ac506 authored by Pali Rohár's avatar Pali Rohár Committed by Steve French
Browse files

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



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

Fixes: ef86ab13 ("cifs: Fix querying of WSL CHR and BLK reparse points over SMB1")
Signed-off-by: default avatarPali Rohár <pali@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 7ae6152b
Loading
Loading
Loading
Loading
+60 −2
Original line number Diff line number Diff line
@@ -651,14 +651,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);
@@ -668,8 +726,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. */