smb: client: introduce SMB2_OP_QUERY_WSL_EA

[ Upstream commit ea41367b2a ]

Add a new command to smb2_compound_op() for querying WSL extended
attributes from reparse points.

Signed-off-by: Paulo Alcantara <pc@manguebit.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Steve French 2024-05-01 01:01:06 -05:00 committed by Greg Kroah-Hartman
parent b93ac66fa2
commit 7449d736bb
6 changed files with 190 additions and 25 deletions

View File

@ -214,6 +214,10 @@ struct cifs_open_info_data {
struct reparse_posix_data *posix; struct reparse_posix_data *posix;
}; };
} reparse; } reparse;
struct {
__u8 eas[SMB2_WSL_MAX_QUERY_EA_RESP_SIZE];
unsigned int eas_len;
} wsl;
char *symlink_target; char *symlink_target;
struct cifs_sid posix_owner; struct cifs_sid posix_owner;
struct cifs_sid posix_group; struct cifs_sid posix_group;
@ -2304,6 +2308,7 @@ struct smb2_compound_vars {
struct kvec close_iov; struct kvec close_iov;
struct smb2_file_rename_info rename_info; struct smb2_file_rename_info rename_info;
struct smb2_file_link_info link_info; struct smb2_file_link_info link_info;
struct kvec ea_iov;
}; };
static inline bool cifs_ses_exiting(struct cifs_ses *ses) static inline bool cifs_ses_exiting(struct cifs_ses *ses)

View File

@ -205,15 +205,15 @@ static int wsl_set_xattrs(struct inode *inode, umode_t _mode,
__le64 dev = cpu_to_le64(((u64)MINOR(_dev) << 32) | MAJOR(_dev)); __le64 dev = cpu_to_le64(((u64)MINOR(_dev) << 32) | MAJOR(_dev));
__le64 mode = cpu_to_le64(_mode); __le64 mode = cpu_to_le64(_mode);
struct wsl_xattr xattrs[] = { struct wsl_xattr xattrs[] = {
{ .name = "$LXUID", .value = uid, .size = 4, }, { .name = SMB2_WSL_XATTR_UID, .value = uid, .size = SMB2_WSL_XATTR_UID_SIZE, },
{ .name = "$LXGID", .value = gid, .size = 4, }, { .name = SMB2_WSL_XATTR_GID, .value = gid, .size = SMB2_WSL_XATTR_GID_SIZE, },
{ .name = "$LXMOD", .value = mode, .size = 4, }, { .name = SMB2_WSL_XATTR_MODE, .value = mode, .size = SMB2_WSL_XATTR_MODE_SIZE, },
{ .name = "$LXDEV", .value = dev, .size = 8, }, { .name = SMB2_WSL_XATTR_DEV, .value = dev, .size = SMB2_WSL_XATTR_DEV_SIZE, },
}; };
size_t cc_len; size_t cc_len;
u32 dlen = 0, next = 0; u32 dlen = 0, next = 0;
int i, num_xattrs; int i, num_xattrs;
u8 name_size = strlen(xattrs[0].name) + 1; u8 name_size = SMB2_WSL_XATTR_NAME_LEN + 1;
memset(iov, 0, sizeof(*iov)); memset(iov, 0, sizeof(*iov));

View File

@ -36,7 +36,8 @@ enum smb2_compound_ops {
SMB2_OP_RMDIR, SMB2_OP_RMDIR,
SMB2_OP_POSIX_QUERY_INFO, SMB2_OP_POSIX_QUERY_INFO,
SMB2_OP_SET_REPARSE, SMB2_OP_SET_REPARSE,
SMB2_OP_GET_REPARSE SMB2_OP_GET_REPARSE,
SMB2_OP_QUERY_WSL_EA,
}; };
/* Used when constructing chained read requests. */ /* Used when constructing chained read requests. */

View File

@ -85,6 +85,82 @@ static int parse_posix_sids(struct cifs_open_info_data *data,
return 0; return 0;
} }
struct wsl_query_ea {
__le32 next;
__u8 name_len;
__u8 name[SMB2_WSL_XATTR_NAME_LEN + 1];
} __packed;
#define NEXT_OFF cpu_to_le32(sizeof(struct wsl_query_ea))
static const struct wsl_query_ea wsl_query_eas[] = {
{ .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_UID, },
{ .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_GID, },
{ .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_MODE, },
{ .next = 0, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_DEV, },
};
static int check_wsl_eas(struct kvec *rsp_iov)
{
struct smb2_file_full_ea_info *ea;
struct smb2_query_info_rsp *rsp = rsp_iov->iov_base;
unsigned long addr;
u32 outlen, next;
u16 vlen;
u8 nlen;
u8 *end;
outlen = le32_to_cpu(rsp->OutputBufferLength);
if (outlen < SMB2_WSL_MIN_QUERY_EA_RESP_SIZE ||
outlen > SMB2_WSL_MAX_QUERY_EA_RESP_SIZE)
return -EINVAL;
ea = (void *)((u8 *)rsp_iov->iov_base +
le16_to_cpu(rsp->OutputBufferOffset));
end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len;
for (;;) {
if ((u8 *)ea > end - sizeof(*ea))
return -EINVAL;
nlen = ea->ea_name_length;
vlen = le16_to_cpu(ea->ea_value_length);
if (nlen != SMB2_WSL_XATTR_NAME_LEN ||
(u8 *)ea + nlen + 1 + vlen > end)
return -EINVAL;
switch (vlen) {
case 4:
if (strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) &&
strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) &&
strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen))
return -EINVAL;
break;
case 8:
if (strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen))
return -EINVAL;
break;
case 0:
if (!strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) ||
!strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) ||
!strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen) ||
!strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen))
break;
fallthrough;
default:
return -EINVAL;
}
next = le32_to_cpu(ea->next_entry_offset);
if (!next)
break;
if (!IS_ALIGNED(next, 4) ||
check_add_overflow((unsigned long)ea, next, &addr))
return -EINVAL;
ea = (void *)addr;
}
return 0;
}
/* /*
* note: If cfile is passed, the reference to it is dropped here. * note: If cfile is passed, the reference to it is dropped here.
* So make sure that you do not reuse cfile after return from this func. * So make sure that you do not reuse cfile after return from this func.
@ -119,7 +195,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
__u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0}; __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0};
unsigned int size[2]; unsigned int size[2];
void *data[2]; void *data[2];
int len; unsigned int len;
int retries = 0, cur_sleep = 1; int retries = 0, cur_sleep = 1;
replay_again: replay_again:
@ -476,6 +552,39 @@ replay_again:
trace_smb3_get_reparse_compound_enter(xid, ses->Suid, trace_smb3_get_reparse_compound_enter(xid, ses->Suid,
tcon->tid, full_path); tcon->tid, full_path);
break; break;
case SMB2_OP_QUERY_WSL_EA:
rqst[num_rqst].rq_iov = &vars->ea_iov;
rqst[num_rqst].rq_nvec = 1;
if (cfile) {
rc = SMB2_query_info_init(tcon, server,
&rqst[num_rqst],
cfile->fid.persistent_fid,
cfile->fid.volatile_fid,
FILE_FULL_EA_INFORMATION,
SMB2_O_INFO_FILE, 0,
SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
sizeof(wsl_query_eas),
(void *)wsl_query_eas);
} else {
rc = SMB2_query_info_init(tcon, server,
&rqst[num_rqst],
COMPOUND_FID,
COMPOUND_FID,
FILE_FULL_EA_INFORMATION,
SMB2_O_INFO_FILE, 0,
SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
sizeof(wsl_query_eas),
(void *)wsl_query_eas);
}
if (!rc && (!cfile || num_rqst > 1)) {
smb2_set_next_command(tcon, &rqst[num_rqst]);
smb2_set_related(&rqst[num_rqst]);
} else if (rc) {
goto finished;
}
num_rqst++;
break;
default: default:
cifs_dbg(VFS, "Invalid command\n"); cifs_dbg(VFS, "Invalid command\n");
rc = -EINVAL; rc = -EINVAL;
@ -665,11 +774,32 @@ finished:
memset(iov, 0, sizeof(*iov)); memset(iov, 0, sizeof(*iov));
resp_buftype[i + 1] = CIFS_NO_BUFFER; resp_buftype[i + 1] = CIFS_NO_BUFFER;
} else { } else {
trace_smb3_set_reparse_compound_err(xid, ses->Suid, trace_smb3_set_reparse_compound_err(xid, ses->Suid,
tcon->tid, rc); tcon->tid, rc);
} }
SMB2_ioctl_free(&rqst[num_rqst++]); SMB2_ioctl_free(&rqst[num_rqst++]);
break; break;
case SMB2_OP_QUERY_WSL_EA:
if (!rc) {
idata = in_iov[i].iov_base;
qi_rsp = rsp_iov[i + 1].iov_base;
data[0] = (u8 *)qi_rsp + le16_to_cpu(qi_rsp->OutputBufferOffset);
size[0] = le32_to_cpu(qi_rsp->OutputBufferLength);
rc = check_wsl_eas(&rsp_iov[i + 1]);
if (!rc) {
memcpy(idata->wsl.eas, data[0], size[0]);
idata->wsl.eas_len = size[0];
}
}
if (!rc) {
trace_smb3_query_wsl_ea_compound_done(xid, ses->Suid,
tcon->tid);
} else {
trace_smb3_query_wsl_ea_compound_err(xid, ses->Suid,
tcon->tid, rc);
}
SMB2_query_info_free(&rqst[num_rqst++]);
break;
} }
} }
SMB2_close_free(&rqst[num_rqst]); SMB2_close_free(&rqst[num_rqst]);
@ -737,11 +867,11 @@ int smb2_query_path_info(const unsigned int xid,
struct cifsFileInfo *cfile; struct cifsFileInfo *cfile;
struct cached_fid *cfid = NULL; struct cached_fid *cfid = NULL;
struct smb2_hdr *hdr; struct smb2_hdr *hdr;
struct kvec in_iov[2], out_iov[3] = {}; struct kvec in_iov[3], out_iov[3] = {};
int out_buftype[3] = {}; int out_buftype[3] = {};
int cmds[2]; int cmds[3];
bool islink; bool islink;
int i, num_cmds; int i, num_cmds = 0;
int rc, rc2; int rc, rc2;
data->adjust_tz = false; data->adjust_tz = false;
@ -774,21 +904,22 @@ int smb2_query_path_info(const unsigned int xid,
close_cached_dir(cfid); close_cached_dir(cfid);
return rc; return rc;
} }
cmds[0] = SMB2_OP_QUERY_INFO; cmds[num_cmds++] = SMB2_OP_QUERY_INFO;
} else { } else {
cmds[0] = SMB2_OP_POSIX_QUERY_INFO; cmds[num_cmds++] = SMB2_OP_POSIX_QUERY_INFO;
} }
in_iov[0].iov_base = data; in_iov[0].iov_base = data;
in_iov[0].iov_len = sizeof(*data); in_iov[0].iov_len = sizeof(*data);
in_iov[1] = in_iov[0]; in_iov[1] = in_iov[0];
in_iov[2] = in_iov[0];
cifs_get_readable_path(tcon, full_path, &cfile); cifs_get_readable_path(tcon, full_path, &cfile);
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_READ_ATTRIBUTES, oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_READ_ATTRIBUTES,
FILE_OPEN, create_options, ACL_NO_MODE); FILE_OPEN, create_options, ACL_NO_MODE);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
&oparms, in_iov, cmds, 1, cfile, &oparms, in_iov, cmds, num_cmds,
out_iov, out_buftype, NULL); cfile, out_iov, out_buftype, NULL);
hdr = out_iov[0].iov_base; hdr = out_iov[0].iov_base;
/* /*
* If first iov is unset, then SMB session was dropped or we've got a * If first iov is unset, then SMB session was dropped or we've got a
@ -808,17 +939,18 @@ int smb2_query_path_info(const unsigned int xid,
if (rc || !data->reparse_point) if (rc || !data->reparse_point)
goto out; goto out;
if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK) { cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;
/* symlink already parsed in create response */ /*
num_cmds = 1; * Skip SMB2_OP_GET_REPARSE if symlink already parsed in create
} else { * response.
cmds[1] = SMB2_OP_GET_REPARSE; */
num_cmds = 2; if (data->reparse.tag != IO_REPARSE_TAG_SYMLINK)
} cmds[num_cmds++] = SMB2_OP_GET_REPARSE;
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
FILE_READ_ATTRIBUTES, FILE_OPEN, FILE_READ_ATTRIBUTES | FILE_READ_EA,
create_options | OPEN_REPARSE_POINT, FILE_OPEN, create_options |
ACL_NO_MODE); OPEN_REPARSE_POINT, ACL_NO_MODE);
cifs_get_readable_path(tcon, full_path, &cfile); cifs_get_readable_path(tcon, full_path, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
&oparms, in_iov, cmds, num_cmds, &oparms, in_iov, cmds, num_cmds,

View File

@ -420,4 +420,29 @@ struct smb2_create_ea_ctx {
struct smb2_file_full_ea_info ea; struct smb2_file_full_ea_info ea;
} __packed; } __packed;
#define SMB2_WSL_XATTR_UID "$LXUID"
#define SMB2_WSL_XATTR_GID "$LXGID"
#define SMB2_WSL_XATTR_MODE "$LXMOD"
#define SMB2_WSL_XATTR_DEV "$LXDEV"
#define SMB2_WSL_XATTR_NAME_LEN 6
#define SMB2_WSL_NUM_XATTRS 4
#define SMB2_WSL_XATTR_UID_SIZE 4
#define SMB2_WSL_XATTR_GID_SIZE 4
#define SMB2_WSL_XATTR_MODE_SIZE 4
#define SMB2_WSL_XATTR_DEV_SIZE 8
#define SMB2_WSL_MIN_QUERY_EA_RESP_SIZE \
(ALIGN((SMB2_WSL_NUM_XATTRS - 1) * \
(SMB2_WSL_XATTR_NAME_LEN + 1 + \
sizeof(struct smb2_file_full_ea_info)), 4) + \
SMB2_WSL_XATTR_NAME_LEN + 1 + sizeof(struct smb2_file_full_ea_info))
#define SMB2_WSL_MAX_QUERY_EA_RESP_SIZE \
(ALIGN(SMB2_WSL_MIN_QUERY_EA_RESP_SIZE + \
SMB2_WSL_XATTR_UID_SIZE + \
SMB2_WSL_XATTR_GID_SIZE + \
SMB2_WSL_XATTR_MODE_SIZE + \
SMB2_WSL_XATTR_DEV_SIZE, 4))
#endif /* _SMB2PDU_H */ #endif /* _SMB2PDU_H */

View File

@ -411,6 +411,7 @@ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_eof_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_info_compound_done); DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_info_compound_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_reparse_compound_done); DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_reparse_compound_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(get_reparse_compound_done); DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(get_reparse_compound_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(query_wsl_ea_compound_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done); DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done); DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(tdis_done); DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(tdis_done);
@ -456,6 +457,7 @@ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_eof_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_info_compound_err); DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_info_compound_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_reparse_compound_err); DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_reparse_compound_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(get_reparse_compound_err); DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(get_reparse_compound_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(query_wsl_ea_compound_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err); DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err); DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(tdis_err); DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(tdis_err);