Skip to content

Commit

Permalink
smb: client: handle lack of EA support in smb2_query_path_info()
Browse files Browse the repository at this point in the history
commit 3681c74 upstream.

If the server doesn't support both EAs and reparse point in a file,
the SMB2_QUERY_INFO request will fail with either
STATUS_NO_EAS_ON_FILE or STATUS_EAS_NOT_SUPPORT in the compound chain,
so ignore it as long as reparse point isn't
IO_REPARSE_TAG_LX_(CHR|BLK), which would require the EAs to know about
major/minor numbers.

Reported-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
pcacjr authored and gregkh committed Feb 1, 2025
1 parent d9da7a6 commit 77aefd1
Showing 1 changed file with 69 additions and 23 deletions.
92 changes: 69 additions & 23 deletions fs/smb/client/smb2inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,27 +176,27 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
struct kvec *out_iov, int *out_buftype, struct dentry *dentry)
{

struct reparse_data_buffer *rbuf;
struct smb2_query_info_rsp *qi_rsp = NULL;
struct smb2_compound_vars *vars = NULL;
struct kvec *rsp_iov, *iov;
struct smb_rqst *rqst;
int rc;
__le16 *utf16_path = NULL;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
struct cifs_fid fid;
struct cifs_open_info_data *idata;
struct cifs_ses *ses = tcon->ses;
struct reparse_data_buffer *rbuf;
struct TCP_Server_Info *server;
int num_rqst = 0, i;
int resp_buftype[MAX_COMPOUND];
struct smb2_query_info_rsp *qi_rsp = NULL;
struct cifs_open_info_data *idata;
int retries = 0, cur_sleep = 1;
__u8 delete_pending[8] = {1,};
struct kvec *rsp_iov, *iov;
struct inode *inode = NULL;
int flags = 0;
__u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0};
__le16 *utf16_path = NULL;
struct smb_rqst *rqst;
unsigned int size[2];
void *data[2];
struct cifs_fid fid;
int num_rqst = 0, i;
unsigned int len;
int retries = 0, cur_sleep = 1;
int tmp_rc, rc;
int flags = 0;
void *data[2];

replay_again:
/* reinitialize for possible replay */
Expand Down Expand Up @@ -637,7 +637,14 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
tcon->need_reconnect = true;
}

tmp_rc = rc;
for (i = 0; i < num_cmds; i++) {
char *buf = rsp_iov[i + i].iov_base;

if (buf && resp_buftype[i + 1] != CIFS_NO_BUFFER)
rc = server->ops->map_error(buf, false);
else
rc = tmp_rc;
switch (cmds[i]) {
case SMB2_OP_QUERY_INFO:
idata = in_iov[i].iov_base;
Expand Down Expand Up @@ -803,6 +810,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
}
}
SMB2_close_free(&rqst[num_rqst]);
rc = tmp_rc;

num_cmds += 2;
if (out_iov && out_buftype) {
Expand Down Expand Up @@ -858,22 +866,52 @@ static int parse_create_response(struct cifs_open_info_data *data,
return rc;
}

/* Check only if SMB2_OP_QUERY_WSL_EA command failed in the compound chain */
static bool ea_unsupported(int *cmds, int num_cmds,
struct kvec *out_iov, int *out_buftype)
{
int i;

if (cmds[num_cmds - 1] != SMB2_OP_QUERY_WSL_EA)
return false;

for (i = 1; i < num_cmds - 1; i++) {
struct smb2_hdr *hdr = out_iov[i].iov_base;

if (out_buftype[i] == CIFS_NO_BUFFER || !hdr ||
hdr->Status != STATUS_SUCCESS)
return false;
}
return true;
}

static inline void free_rsp_iov(struct kvec *iovs, int *buftype, int count)
{
int i;

for (i = 0; i < count; i++) {
free_rsp_buf(buftype[i], iovs[i].iov_base);
memset(&iovs[i], 0, sizeof(*iovs));
buftype[i] = CIFS_NO_BUFFER;
}
}

int smb2_query_path_info(const unsigned int xid,
struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
const char *full_path,
struct cifs_open_info_data *data)
{
struct kvec in_iov[3], out_iov[5] = {};
struct cached_fid *cfid = NULL;
struct cifs_open_parms oparms;
__u32 create_options = 0;
struct cifsFileInfo *cfile;
struct cached_fid *cfid = NULL;
__u32 create_options = 0;
int out_buftype[5] = {};
struct smb2_hdr *hdr;
struct kvec in_iov[3], out_iov[3] = {};
int out_buftype[3] = {};
int num_cmds = 0;
int cmds[3];
bool islink;
int i, num_cmds = 0;
int rc, rc2;

data->adjust_tz = false;
Expand Down Expand Up @@ -943,24 +981,33 @@ int smb2_query_path_info(const unsigned int xid,
if (rc || !data->reparse_point)
goto out;

if (!tcon->posix_extensions)
cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;
/*
* Skip SMB2_OP_GET_REPARSE if symlink already parsed in create
* response.
*/
if (data->reparse.tag != IO_REPARSE_TAG_SYMLINK)
cmds[num_cmds++] = SMB2_OP_GET_REPARSE;
if (!tcon->posix_extensions)
cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;

oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
FILE_READ_ATTRIBUTES |
FILE_READ_EA | SYNCHRONIZE,
FILE_OPEN, create_options |
OPEN_REPARSE_POINT, ACL_NO_MODE);
cifs_get_readable_path(tcon, full_path, &cfile);
free_rsp_iov(out_iov, out_buftype, ARRAY_SIZE(out_iov));
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
&oparms, in_iov, cmds, num_cmds,
cfile, NULL, NULL, NULL);
cfile, out_iov, out_buftype, NULL);
if (rc && ea_unsupported(cmds, num_cmds,
out_iov, out_buftype)) {
if (data->reparse.tag != IO_REPARSE_TAG_LX_BLK &&
data->reparse.tag != IO_REPARSE_TAG_LX_CHR)
rc = 0;
else
rc = -EOPNOTSUPP;
}
break;
case -EREMOTE:
break;
Expand All @@ -978,8 +1025,7 @@ int smb2_query_path_info(const unsigned int xid,
}

out:
for (i = 0; i < ARRAY_SIZE(out_buftype); i++)
free_rsp_buf(out_buftype[i], out_iov[i].iov_base);
free_rsp_iov(out_iov, out_buftype, ARRAY_SIZE(out_iov));
return rc;
}

Expand Down

0 comments on commit 77aefd1

Please sign in to comment.