Merge tag '4.14-smb3-xattr-enable' of git://git.samba.org/sfrench/cifs-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 7 Sep 2017 23:06:14 +0000 (16:06 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 7 Sep 2017 23:06:14 +0000 (16:06 -0700)
Pull cifs update from Steve French:
 "Enable xattr support for smb3 and also a bugfix"

* tag '4.14-smb3-xattr-enable' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: Check for timeout on Negotiate stage
  cifs: Add support for writing attributes on SMB2+
  cifs: Add support for reading attributes on SMB2+

fs/cifs/cifsglob.h
fs/cifs/cifsproto.h
fs/cifs/cifssmb.c
fs/cifs/connect.c
fs/cifs/smb2ops.c
fs/cifs/smb2pdu.c
fs/cifs/smb2pdu.h
fs/cifs/smb2proto.h
fs/cifs/xattr.c

index 221693fe49ec8114d307f3f3974f0420e871b73c..808486c29f0dcb40dbda7a86e626d77908d82001 100644 (file)
@@ -421,7 +421,7 @@ struct smb_version_operations {
                        size_t, struct cifs_sb_info *);
        int (*set_EA)(const unsigned int, struct cifs_tcon *, const char *,
                        const char *, const void *, const __u16,
-                       const struct nls_table *, int);
+                       const struct nls_table *, struct cifs_sb_info *);
        struct cifs_ntsd * (*get_acl)(struct cifs_sb_info *, struct inode *,
                        const char *, u32 *);
        struct cifs_ntsd * (*get_acl_by_fid)(struct cifs_sb_info *,
index 6eb3147132e30c2225b610b01281111bb896c617..4143c9dec46398328aa4c78af849c6032a0edfd3 100644 (file)
@@ -484,7 +484,8 @@ extern ssize_t CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,
 extern int CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
                const char *fileName, const char *ea_name,
                const void *ea_value, const __u16 ea_value_len,
-               const struct nls_table *nls_codepage, int remap_special_chars);
+               const struct nls_table *nls_codepage,
+               struct cifs_sb_info *cifs_sb);
 extern int CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon,
                        __u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen);
 extern int CIFSSMBSetCIFSACL(const unsigned int, struct cifs_tcon *, __u16,
index 118a63e7e221fa54630a6149d3e664d688a805ba..35dc5bf01ee2ab96f62a793c466e033bf0659de4 100644 (file)
@@ -178,6 +178,18 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
         * reconnect the same SMB session
         */
        mutex_lock(&ses->session_mutex);
+
+       /*
+        * Recheck after acquire mutex. If another thread is negotiating
+        * and the server never sends an answer the socket will be closed
+        * and tcpStatus set to reconnect.
+        */
+       if (server->tcpStatus == CifsNeedReconnect) {
+               rc = -EHOSTDOWN;
+               mutex_unlock(&ses->session_mutex);
+               goto out;
+       }
+
        rc = cifs_negotiate_protocol(0, ses);
        if (rc == 0 && ses->need_reconnect)
                rc = cifs_setup_session(0, ses, nls_codepage);
@@ -6264,7 +6276,7 @@ int
 CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
             const char *fileName, const char *ea_name, const void *ea_value,
             const __u16 ea_value_len, const struct nls_table *nls_codepage,
-            int remap)
+            struct cifs_sb_info *cifs_sb)
 {
        struct smb_com_transaction2_spi_req *pSMB = NULL;
        struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
@@ -6273,6 +6285,7 @@ CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
        int rc = 0;
        int bytes_returned = 0;
        __u16 params, param_offset, byte_count, offset, count;
+       int remap = cifs_remap(cifs_sb);
 
        cifs_dbg(FYI, "In SetEA\n");
 SetEARetry:
index 83a8f52cd879204d7847a77a6a543a8892ecf1db..5aa2d278ca841f7aa7d4de0bb892dbdfc7a48645 100644 (file)
@@ -509,7 +509,8 @@ server_unresponsive(struct TCP_Server_Info *server)
         * 65s kernel_recvmsg times out, and we see that we haven't gotten
         *     a response in >60s.
         */
-       if (server->tcpStatus == CifsGood &&
+       if ((server->tcpStatus == CifsGood ||
+           server->tcpStatus == CifsNeedNegotiate) &&
            time_after(jiffies, server->lstrp + 2 * server->echo_interval)) {
                cifs_dbg(VFS, "Server %s has not responded in %lu seconds. Reconnecting...\n",
                         server->hostname, (2 * server->echo_interval) / HZ);
index cfacf2c97e9418c991b7264f7b75ee271039b260..fb2934b9b97cf56f16359702a7bcb81cd6624252 100644 (file)
@@ -426,6 +426,194 @@ smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
        return rc;
 }
 
+static ssize_t
+move_smb2_ea_to_cifs(char *dst, size_t dst_size,
+                    struct smb2_file_full_ea_info *src, size_t src_size,
+                    const unsigned char *ea_name)
+{
+       int rc = 0;
+       unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0;
+       char *name, *value;
+       size_t name_len, value_len, user_name_len;
+
+       while (src_size > 0) {
+               name = &src->ea_data[0];
+               name_len = (size_t)src->ea_name_length;
+               value = &src->ea_data[src->ea_name_length + 1];
+               value_len = (size_t)le16_to_cpu(src->ea_value_length);
+
+               if (name_len == 0) {
+                       break;
+               }
+
+               if (src_size < 8 + name_len + 1 + value_len) {
+                       cifs_dbg(FYI, "EA entry goes beyond length of list\n");
+                       rc = -EIO;
+                       goto out;
+               }
+
+               if (ea_name) {
+                       if (ea_name_len == name_len &&
+                           memcmp(ea_name, name, name_len) == 0) {
+                               rc = value_len;
+                               if (dst_size == 0)
+                                       goto out;
+                               if (dst_size < value_len) {
+                                       rc = -ERANGE;
+                                       goto out;
+                               }
+                               memcpy(dst, value, value_len);
+                               goto out;
+                       }
+               } else {
+                       /* 'user.' plus a terminating null */
+                       user_name_len = 5 + 1 + name_len;
+
+                       rc += user_name_len;
+
+                       if (dst_size >= user_name_len) {
+                               dst_size -= user_name_len;
+                               memcpy(dst, "user.", 5);
+                               dst += 5;
+                               memcpy(dst, src->ea_data, name_len);
+                               dst += name_len;
+                               *dst = 0;
+                               ++dst;
+                       } else if (dst_size == 0) {
+                               /* skip copy - calc size only */
+                       } else {
+                               /* stop before overrun buffer */
+                               rc = -ERANGE;
+                               break;
+                       }
+               }
+
+               if (!src->next_entry_offset)
+                       break;
+
+               if (src_size < le32_to_cpu(src->next_entry_offset)) {
+                       /* stop before overrun buffer */
+                       rc = -ERANGE;
+                       break;
+               }
+               src_size -= le32_to_cpu(src->next_entry_offset);
+               src = (void *)((char *)src +
+                              le32_to_cpu(src->next_entry_offset));
+       }
+
+       /* didn't find the named attribute */
+       if (ea_name)
+               rc = -ENODATA;
+
+out:
+       return (ssize_t)rc;
+}
+
+static ssize_t
+smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
+              const unsigned char *path, const unsigned char *ea_name,
+              char *ea_data, size_t buf_size,
+              struct cifs_sb_info *cifs_sb)
+{
+       int rc;
+       __le16 *utf16_path;
+       __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+       struct cifs_open_parms oparms;
+       struct cifs_fid fid;
+       struct smb2_file_full_ea_info *smb2_data;
+
+       utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
+       if (!utf16_path)
+               return -ENOMEM;
+
+       oparms.tcon = tcon;
+       oparms.desired_access = FILE_READ_EA;
+       oparms.disposition = FILE_OPEN;
+       oparms.create_options = 0;
+       oparms.fid = &fid;
+       oparms.reconnect = false;
+
+       rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
+       kfree(utf16_path);
+       if (rc) {
+               cifs_dbg(FYI, "open failed rc=%d\n", rc);
+               return rc;
+       }
+
+       smb2_data = kzalloc(SMB2_MAX_EA_BUF, GFP_KERNEL);
+       if (smb2_data == NULL) {
+               SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+               return -ENOMEM;
+       }
+
+       rc = SMB2_query_eas(xid, tcon, fid.persistent_fid, fid.volatile_fid,
+                           smb2_data);
+       SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+
+       if (!rc)
+               rc = move_smb2_ea_to_cifs(ea_data, buf_size, smb2_data,
+                                         SMB2_MAX_EA_BUF, ea_name);
+
+       kfree(smb2_data);
+       return rc;
+}
+
+
+static int
+smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
+           const char *path, const char *ea_name, const void *ea_value,
+           const __u16 ea_value_len, const struct nls_table *nls_codepage,
+           struct cifs_sb_info *cifs_sb)
+{
+       int rc;
+       __le16 *utf16_path;
+       __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+       struct cifs_open_parms oparms;
+       struct cifs_fid fid;
+       struct smb2_file_full_ea_info *ea;
+       int ea_name_len = strlen(ea_name);
+       int len;
+
+       if (ea_name_len > 255)
+               return -EINVAL;
+
+       utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
+       if (!utf16_path)
+               return -ENOMEM;
+
+       oparms.tcon = tcon;
+       oparms.desired_access = FILE_WRITE_EA;
+       oparms.disposition = FILE_OPEN;
+       oparms.create_options = 0;
+       oparms.fid = &fid;
+       oparms.reconnect = false;
+
+       rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
+       kfree(utf16_path);
+       if (rc) {
+               cifs_dbg(FYI, "open failed rc=%d\n", rc);
+               return rc;
+       }
+
+       len = sizeof(ea) + ea_name_len + ea_value_len + 1;
+       ea = kzalloc(len, GFP_KERNEL);
+       if (ea == NULL) {
+               SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+               return -ENOMEM;
+       }
+
+       ea->ea_name_length = ea_name_len;
+       ea->ea_value_length = cpu_to_le16(ea_value_len);
+       memcpy(ea->ea_data, ea_name, ea_name_len + 1);
+       memcpy(ea->ea_data + ea_name_len + 1, ea_value, ea_value_len);
+
+       rc = SMB2_set_ea(xid, tcon, fid.persistent_fid, fid.volatile_fid, ea,
+                        len);
+       SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+
+       return rc;
+}
+
 static bool
 smb2_can_echo(struct TCP_Server_Info *server)
 {
@@ -2572,6 +2760,10 @@ struct smb_version_operations smb20_operations = {
        .dir_needs_close = smb2_dir_needs_close,
        .get_dfs_refer = smb2_get_dfs_refer,
        .select_sectype = smb2_select_sectype,
+#ifdef CONFIG_CIFS_XATTR
+       .query_all_EAs = smb2_query_eas,
+       .set_EA = smb2_set_ea,
+#endif /* CIFS_XATTR */
 #ifdef CONFIG_CIFS_ACL
        .get_acl = get_smb2_acl,
        .get_acl_by_fid = get_smb2_acl_by_fid,
@@ -2662,6 +2854,10 @@ struct smb_version_operations smb21_operations = {
        .enum_snapshots = smb3_enum_snapshots,
        .get_dfs_refer = smb2_get_dfs_refer,
        .select_sectype = smb2_select_sectype,
+#ifdef CONFIG_CIFS_XATTR
+       .query_all_EAs = smb2_query_eas,
+       .set_EA = smb2_set_ea,
+#endif /* CIFS_XATTR */
 #ifdef CONFIG_CIFS_ACL
        .get_acl = get_smb2_acl,
        .get_acl_by_fid = get_smb2_acl_by_fid,
@@ -2762,6 +2958,10 @@ struct smb_version_operations smb30_operations = {
        .receive_transform = smb3_receive_transform,
        .get_dfs_refer = smb2_get_dfs_refer,
        .select_sectype = smb2_select_sectype,
+#ifdef CONFIG_CIFS_XATTR
+       .query_all_EAs = smb2_query_eas,
+       .set_EA = smb2_set_ea,
+#endif /* CIFS_XATTR */
 #ifdef CONFIG_CIFS_ACL
        .get_acl = get_smb2_acl,
        .get_acl_by_fid = get_smb2_acl_by_fid,
@@ -2863,6 +3063,10 @@ struct smb_version_operations smb311_operations = {
        .receive_transform = smb3_receive_transform,
        .get_dfs_refer = smb2_get_dfs_refer,
        .select_sectype = smb2_select_sectype,
+#ifdef CONFIG_CIFS_XATTR
+       .query_all_EAs = smb2_query_eas,
+       .set_EA = smb2_set_ea,
+#endif /* CIFS_XATTR */
 };
 #endif /* CIFS_SMB311 */
 
index 7aa67206f6da2fe3e8ed4c1913a561948614db0a..5531e7ee1210eba48915a6a0d4d01b348b386cde 100644 (file)
@@ -238,6 +238,18 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
         * the same SMB session
         */
        mutex_lock(&tcon->ses->session_mutex);
+
+       /*
+        * Recheck after acquire mutex. If another thread is negotiating
+        * and the server never sends an answer the socket will be closed
+        * and tcpStatus set to reconnect.
+        */
+       if (server->tcpStatus == CifsNeedReconnect) {
+               rc = -EHOSTDOWN;
+               mutex_unlock(&tcon->ses->session_mutex);
+               goto out;
+       }
+
        rc = cifs_negotiate_protocol(0, tcon->ses);
        if (!rc && tcon->ses->need_reconnect)
                rc = cifs_setup_session(0, tcon->ses, nls_codepage);
@@ -2145,6 +2157,18 @@ qinf_exit:
        return rc;
 }
 
+int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
+       u64 persistent_fid, u64 volatile_fid,
+       struct smb2_file_full_ea_info *data)
+{
+       return query_info(xid, tcon, persistent_fid, volatile_fid,
+                         FILE_FULL_EA_INFORMATION, SMB2_O_INFO_FILE, 0,
+                         SMB2_MAX_EA_BUF,
+                         sizeof(struct smb2_file_full_ea_info),
+                         (void **)&data,
+                         NULL);
+}
+
 int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
        u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data)
 {
@@ -3184,6 +3208,16 @@ SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
                        1, (void **)&pnntsd, &pacllen);
 }
 
+int
+SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
+           u64 persistent_fid, u64 volatile_fid,
+           struct smb2_file_full_ea_info *buf, int len)
+{
+       return send_set_info(xid, tcon, persistent_fid, volatile_fid,
+               current->tgid, FILE_FULL_EA_INFORMATION, SMB2_O_INFO_FILE,
+               0, 1, (void **)&buf, &len);
+}
+
 int
 SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
                  const u64 persistent_fid, const u64 volatile_fid,
index 2826882c81d14f138b393824fac9c9091311cd1f..393ed5f4e1b6516b40f30f64890a385a43b44c9a 100644 (file)
@@ -1178,6 +1178,16 @@ struct smb2_file_link_info { /* encoding of request for level 11 */
        char   FileName[0];     /* Name to be assigned to new link */
 } __packed; /* level 11 Set */
 
+#define SMB2_MAX_EA_BUF 2048
+
+struct smb2_file_full_ea_info { /* encoding of response for level 15 */
+       __le32 next_entry_offset;
+       __u8   flags;
+       __u8   ea_name_length;
+       __le16 ea_value_length;
+       char   ea_data[0]; /* \0 terminated name plus value */
+} __packed; /* level 15 Set */
+
 /*
  * This level 18, although with struct with same name is different from cifs
  * level 0x107. Level 0x107 has an extra u64 between AccessFlags and
index 1cadaf9f3c588ea3acc673f883baacfbf8c70b5f..003217099ef3e6831a36ed81f7c13f1f611efb9d 100644 (file)
@@ -132,6 +132,9 @@ extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
                      u64 persistent_file_id, u64 volatile_file_id);
 extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon,
                      u64 persistent_file_id, u64 volatile_file_id);
+extern int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
+                         u64 persistent_file_id, u64 volatile_file_id,
+                         struct smb2_file_full_ea_info *data);
 extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
                           u64 persistent_file_id, u64 volatile_file_id,
                           struct smb2_file_all_info *data);
@@ -169,6 +172,9 @@ extern int SMB2_set_info(const unsigned int xid, struct cifs_tcon *tcon,
 extern int SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
                        u64 persistent_fid, u64 volatile_fid,
                        struct cifs_ntsd *pnntsd, int pacllen, int aclflag);
+extern int SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
+                      u64 persistent_fid, u64 volatile_fid,
+                      struct smb2_file_full_ea_info *buf, int len);
 extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
                                u64 persistent_fid, u64 volatile_fid);
 extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
index de50e749ff058d79c67f7462962614c8c835ecdb..52f975d848a076e6873d55b6429c0c62588ecb51 100644 (file)
@@ -84,7 +84,7 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
                if (pTcon->ses->server->ops->set_EA)
                        rc = pTcon->ses->server->ops->set_EA(xid, pTcon,
                                full_path, name, value, (__u16)size,
-                               cifs_sb->local_nls, cifs_remap(cifs_sb));
+                               cifs_sb->local_nls, cifs_sb);
                break;
 
        case XATTR_CIFS_ACL: {