Skip to content

Commit 7449d73

Browse files
smfrenchgregkh
authored andcommitted
smb: client: introduce SMB2_OP_QUERY_WSL_EA
[ Upstream commit ea41367 ] Add a new command to smb2_compound_op() for querying WSL extended attributes from reparse points. Signed-off-by: Paulo Alcantara <[email protected]> Signed-off-by: Steve French <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent b93ac66 commit 7449d73

File tree

6 files changed

+190
-25
lines changed

6 files changed

+190
-25
lines changed

fs/smb/client/cifsglob.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,10 @@ struct cifs_open_info_data {
214214
struct reparse_posix_data *posix;
215215
};
216216
} reparse;
217+
struct {
218+
__u8 eas[SMB2_WSL_MAX_QUERY_EA_RESP_SIZE];
219+
unsigned int eas_len;
220+
} wsl;
217221
char *symlink_target;
218222
struct cifs_sid posix_owner;
219223
struct cifs_sid posix_group;
@@ -2304,6 +2308,7 @@ struct smb2_compound_vars {
23042308
struct kvec close_iov;
23052309
struct smb2_file_rename_info rename_info;
23062310
struct smb2_file_link_info link_info;
2311+
struct kvec ea_iov;
23072312
};
23082313

23092314
static inline bool cifs_ses_exiting(struct cifs_ses *ses)

fs/smb/client/reparse.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -205,15 +205,15 @@ static int wsl_set_xattrs(struct inode *inode, umode_t _mode,
205205
__le64 dev = cpu_to_le64(((u64)MINOR(_dev) << 32) | MAJOR(_dev));
206206
__le64 mode = cpu_to_le64(_mode);
207207
struct wsl_xattr xattrs[] = {
208-
{ .name = "$LXUID", .value = uid, .size = 4, },
209-
{ .name = "$LXGID", .value = gid, .size = 4, },
210-
{ .name = "$LXMOD", .value = mode, .size = 4, },
211-
{ .name = "$LXDEV", .value = dev, .size = 8, },
208+
{ .name = SMB2_WSL_XATTR_UID, .value = uid, .size = SMB2_WSL_XATTR_UID_SIZE, },
209+
{ .name = SMB2_WSL_XATTR_GID, .value = gid, .size = SMB2_WSL_XATTR_GID_SIZE, },
210+
{ .name = SMB2_WSL_XATTR_MODE, .value = mode, .size = SMB2_WSL_XATTR_MODE_SIZE, },
211+
{ .name = SMB2_WSL_XATTR_DEV, .value = dev, .size = SMB2_WSL_XATTR_DEV_SIZE, },
212212
};
213213
size_t cc_len;
214214
u32 dlen = 0, next = 0;
215215
int i, num_xattrs;
216-
u8 name_size = strlen(xattrs[0].name) + 1;
216+
u8 name_size = SMB2_WSL_XATTR_NAME_LEN + 1;
217217

218218
memset(iov, 0, sizeof(*iov));
219219

fs/smb/client/smb2glob.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ enum smb2_compound_ops {
3636
SMB2_OP_RMDIR,
3737
SMB2_OP_POSIX_QUERY_INFO,
3838
SMB2_OP_SET_REPARSE,
39-
SMB2_OP_GET_REPARSE
39+
SMB2_OP_GET_REPARSE,
40+
SMB2_OP_QUERY_WSL_EA,
4041
};
4142

4243
/* Used when constructing chained read requests. */

fs/smb/client/smb2inode.c

Lines changed: 151 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,82 @@ static int parse_posix_sids(struct cifs_open_info_data *data,
8585
return 0;
8686
}
8787

88+
struct wsl_query_ea {
89+
__le32 next;
90+
__u8 name_len;
91+
__u8 name[SMB2_WSL_XATTR_NAME_LEN + 1];
92+
} __packed;
93+
94+
#define NEXT_OFF cpu_to_le32(sizeof(struct wsl_query_ea))
95+
96+
static const struct wsl_query_ea wsl_query_eas[] = {
97+
{ .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_UID, },
98+
{ .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_GID, },
99+
{ .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_MODE, },
100+
{ .next = 0, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_DEV, },
101+
};
102+
103+
static int check_wsl_eas(struct kvec *rsp_iov)
104+
{
105+
struct smb2_file_full_ea_info *ea;
106+
struct smb2_query_info_rsp *rsp = rsp_iov->iov_base;
107+
unsigned long addr;
108+
u32 outlen, next;
109+
u16 vlen;
110+
u8 nlen;
111+
u8 *end;
112+
113+
outlen = le32_to_cpu(rsp->OutputBufferLength);
114+
if (outlen < SMB2_WSL_MIN_QUERY_EA_RESP_SIZE ||
115+
outlen > SMB2_WSL_MAX_QUERY_EA_RESP_SIZE)
116+
return -EINVAL;
117+
118+
ea = (void *)((u8 *)rsp_iov->iov_base +
119+
le16_to_cpu(rsp->OutputBufferOffset));
120+
end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len;
121+
for (;;) {
122+
if ((u8 *)ea > end - sizeof(*ea))
123+
return -EINVAL;
124+
125+
nlen = ea->ea_name_length;
126+
vlen = le16_to_cpu(ea->ea_value_length);
127+
if (nlen != SMB2_WSL_XATTR_NAME_LEN ||
128+
(u8 *)ea + nlen + 1 + vlen > end)
129+
return -EINVAL;
130+
131+
switch (vlen) {
132+
case 4:
133+
if (strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) &&
134+
strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) &&
135+
strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen))
136+
return -EINVAL;
137+
break;
138+
case 8:
139+
if (strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen))
140+
return -EINVAL;
141+
break;
142+
case 0:
143+
if (!strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) ||
144+
!strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) ||
145+
!strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen) ||
146+
!strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen))
147+
break;
148+
fallthrough;
149+
default:
150+
return -EINVAL;
151+
}
152+
153+
next = le32_to_cpu(ea->next_entry_offset);
154+
if (!next)
155+
break;
156+
if (!IS_ALIGNED(next, 4) ||
157+
check_add_overflow((unsigned long)ea, next, &addr))
158+
return -EINVAL;
159+
ea = (void *)addr;
160+
}
161+
return 0;
162+
}
163+
88164
/*
89165
* note: If cfile is passed, the reference to it is dropped here.
90166
* 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,
119195
__u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0};
120196
unsigned int size[2];
121197
void *data[2];
122-
int len;
198+
unsigned int len;
123199
int retries = 0, cur_sleep = 1;
124200

125201
replay_again:
@@ -476,6 +552,39 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
476552
trace_smb3_get_reparse_compound_enter(xid, ses->Suid,
477553
tcon->tid, full_path);
478554
break;
555+
case SMB2_OP_QUERY_WSL_EA:
556+
rqst[num_rqst].rq_iov = &vars->ea_iov;
557+
rqst[num_rqst].rq_nvec = 1;
558+
559+
if (cfile) {
560+
rc = SMB2_query_info_init(tcon, server,
561+
&rqst[num_rqst],
562+
cfile->fid.persistent_fid,
563+
cfile->fid.volatile_fid,
564+
FILE_FULL_EA_INFORMATION,
565+
SMB2_O_INFO_FILE, 0,
566+
SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
567+
sizeof(wsl_query_eas),
568+
(void *)wsl_query_eas);
569+
} else {
570+
rc = SMB2_query_info_init(tcon, server,
571+
&rqst[num_rqst],
572+
COMPOUND_FID,
573+
COMPOUND_FID,
574+
FILE_FULL_EA_INFORMATION,
575+
SMB2_O_INFO_FILE, 0,
576+
SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
577+
sizeof(wsl_query_eas),
578+
(void *)wsl_query_eas);
579+
}
580+
if (!rc && (!cfile || num_rqst > 1)) {
581+
smb2_set_next_command(tcon, &rqst[num_rqst]);
582+
smb2_set_related(&rqst[num_rqst]);
583+
} else if (rc) {
584+
goto finished;
585+
}
586+
num_rqst++;
587+
break;
479588
default:
480589
cifs_dbg(VFS, "Invalid command\n");
481590
rc = -EINVAL;
@@ -665,11 +774,32 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
665774
memset(iov, 0, sizeof(*iov));
666775
resp_buftype[i + 1] = CIFS_NO_BUFFER;
667776
} else {
668-
trace_smb3_set_reparse_compound_err(xid, ses->Suid,
777+
trace_smb3_set_reparse_compound_err(xid, ses->Suid,
669778
tcon->tid, rc);
670779
}
671780
SMB2_ioctl_free(&rqst[num_rqst++]);
672781
break;
782+
case SMB2_OP_QUERY_WSL_EA:
783+
if (!rc) {
784+
idata = in_iov[i].iov_base;
785+
qi_rsp = rsp_iov[i + 1].iov_base;
786+
data[0] = (u8 *)qi_rsp + le16_to_cpu(qi_rsp->OutputBufferOffset);
787+
size[0] = le32_to_cpu(qi_rsp->OutputBufferLength);
788+
rc = check_wsl_eas(&rsp_iov[i + 1]);
789+
if (!rc) {
790+
memcpy(idata->wsl.eas, data[0], size[0]);
791+
idata->wsl.eas_len = size[0];
792+
}
793+
}
794+
if (!rc) {
795+
trace_smb3_query_wsl_ea_compound_done(xid, ses->Suid,
796+
tcon->tid);
797+
} else {
798+
trace_smb3_query_wsl_ea_compound_err(xid, ses->Suid,
799+
tcon->tid, rc);
800+
}
801+
SMB2_query_info_free(&rqst[num_rqst++]);
802+
break;
673803
}
674804
}
675805
SMB2_close_free(&rqst[num_rqst]);
@@ -737,11 +867,11 @@ int smb2_query_path_info(const unsigned int xid,
737867
struct cifsFileInfo *cfile;
738868
struct cached_fid *cfid = NULL;
739869
struct smb2_hdr *hdr;
740-
struct kvec in_iov[2], out_iov[3] = {};
870+
struct kvec in_iov[3], out_iov[3] = {};
741871
int out_buftype[3] = {};
742-
int cmds[2];
872+
int cmds[3];
743873
bool islink;
744-
int i, num_cmds;
874+
int i, num_cmds = 0;
745875
int rc, rc2;
746876

747877
data->adjust_tz = false;
@@ -774,21 +904,22 @@ int smb2_query_path_info(const unsigned int xid,
774904
close_cached_dir(cfid);
775905
return rc;
776906
}
777-
cmds[0] = SMB2_OP_QUERY_INFO;
907+
cmds[num_cmds++] = SMB2_OP_QUERY_INFO;
778908
} else {
779-
cmds[0] = SMB2_OP_POSIX_QUERY_INFO;
909+
cmds[num_cmds++] = SMB2_OP_POSIX_QUERY_INFO;
780910
}
781911

782912
in_iov[0].iov_base = data;
783913
in_iov[0].iov_len = sizeof(*data);
784914
in_iov[1] = in_iov[0];
915+
in_iov[2] = in_iov[0];
785916

786917
cifs_get_readable_path(tcon, full_path, &cfile);
787918
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_READ_ATTRIBUTES,
788919
FILE_OPEN, create_options, ACL_NO_MODE);
789920
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
790-
&oparms, in_iov, cmds, 1, cfile,
791-
out_iov, out_buftype, NULL);
921+
&oparms, in_iov, cmds, num_cmds,
922+
cfile, out_iov, out_buftype, NULL);
792923
hdr = out_iov[0].iov_base;
793924
/*
794925
* 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,
808939
if (rc || !data->reparse_point)
809940
goto out;
810941

811-
if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK) {
812-
/* symlink already parsed in create response */
813-
num_cmds = 1;
814-
} else {
815-
cmds[1] = SMB2_OP_GET_REPARSE;
816-
num_cmds = 2;
817-
}
942+
cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;
943+
/*
944+
* Skip SMB2_OP_GET_REPARSE if symlink already parsed in create
945+
* response.
946+
*/
947+
if (data->reparse.tag != IO_REPARSE_TAG_SYMLINK)
948+
cmds[num_cmds++] = SMB2_OP_GET_REPARSE;
949+
818950
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
819-
FILE_READ_ATTRIBUTES, FILE_OPEN,
820-
create_options | OPEN_REPARSE_POINT,
821-
ACL_NO_MODE);
951+
FILE_READ_ATTRIBUTES | FILE_READ_EA,
952+
FILE_OPEN, create_options |
953+
OPEN_REPARSE_POINT, ACL_NO_MODE);
822954
cifs_get_readable_path(tcon, full_path, &cfile);
823955
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
824956
&oparms, in_iov, cmds, num_cmds,

fs/smb/client/smb2pdu.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,4 +420,29 @@ struct smb2_create_ea_ctx {
420420
struct smb2_file_full_ea_info ea;
421421
} __packed;
422422

423+
#define SMB2_WSL_XATTR_UID "$LXUID"
424+
#define SMB2_WSL_XATTR_GID "$LXGID"
425+
#define SMB2_WSL_XATTR_MODE "$LXMOD"
426+
#define SMB2_WSL_XATTR_DEV "$LXDEV"
427+
#define SMB2_WSL_XATTR_NAME_LEN 6
428+
#define SMB2_WSL_NUM_XATTRS 4
429+
430+
#define SMB2_WSL_XATTR_UID_SIZE 4
431+
#define SMB2_WSL_XATTR_GID_SIZE 4
432+
#define SMB2_WSL_XATTR_MODE_SIZE 4
433+
#define SMB2_WSL_XATTR_DEV_SIZE 8
434+
435+
#define SMB2_WSL_MIN_QUERY_EA_RESP_SIZE \
436+
(ALIGN((SMB2_WSL_NUM_XATTRS - 1) * \
437+
(SMB2_WSL_XATTR_NAME_LEN + 1 + \
438+
sizeof(struct smb2_file_full_ea_info)), 4) + \
439+
SMB2_WSL_XATTR_NAME_LEN + 1 + sizeof(struct smb2_file_full_ea_info))
440+
441+
#define SMB2_WSL_MAX_QUERY_EA_RESP_SIZE \
442+
(ALIGN(SMB2_WSL_MIN_QUERY_EA_RESP_SIZE + \
443+
SMB2_WSL_XATTR_UID_SIZE + \
444+
SMB2_WSL_XATTR_GID_SIZE + \
445+
SMB2_WSL_XATTR_MODE_SIZE + \
446+
SMB2_WSL_XATTR_DEV_SIZE, 4))
447+
423448
#endif /* _SMB2PDU_H */

fs/smb/client/trace.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_eof_done);
411411
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_info_compound_done);
412412
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_reparse_compound_done);
413413
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(get_reparse_compound_done);
414+
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(query_wsl_ea_compound_done);
414415
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done);
415416
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done);
416417
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(tdis_done);
@@ -456,6 +457,7 @@ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_eof_err);
456457
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_info_compound_err);
457458
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_reparse_compound_err);
458459
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(get_reparse_compound_err);
460+
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(query_wsl_ea_compound_err);
459461
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err);
460462
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err);
461463
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(tdis_err);

0 commit comments

Comments
 (0)