]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
smb: client: handle lack of EA support in smb2_query_path_info()
authorPaulo Alcantara <pc@manguebit.com>
Tue, 21 Jan 2025 18:25:36 +0000 (15:25 -0300)
committerSteve French <stfrench@microsoft.com>
Thu, 23 Jan 2025 02:13:49 +0000 (20:13 -0600)
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>
fs/smb/client/smb2inode.c

index 7d3685dd655a2f5684764bf19d34833a8e87ff08..c97f14757c27c3b9e58fe30b2a1c2ecbc2f577b2 100644 (file)
@@ -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 */
@@ -639,7 +639,14 @@ finished:
                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;
@@ -805,6 +812,7 @@ finished:
                }
        }
        SMB2_close_free(&rqst[num_rqst]);
+       rc = tmp_rc;
 
        num_cmds += 2;
        if (out_iov && out_buftype) {
@@ -860,22 +868,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;
@@ -945,14 +983,14 @@ 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 |
@@ -960,9 +998,18 @@ int smb2_query_path_info(const unsigned int xid,
                                     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;
@@ -980,8 +1027,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;
 }