Skip to contentSkip to conversation

Add support for DFS in SMB2+

PATCH In review Aurelien Aptel 3 participants 19 messages Feb 28, 2017 – Jun 23, 2017 10 messages not in the archive

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

v3 cover Add support for DFS in SMB2+

This series of patch tries to implement the get_dfs_refer operation for SMB2+.

In SMB2+, DFS resolving is now done through an FSCTL (patch #7). The relevant Microsoft specifications for this are:

MS-SMB2: 3.2.4.20 Application Requests an IO Control Code Operation
MS-SMB2: 3.2.4.20.3 Application Requests DFS Referral Information
MS-SMB2: 3.2.5.14 Receiving an SMB2 IOCTL Response
MS-SMB2: 3.2.5.14.4 Handling a DFS Referral Information Response

MS-DFSC: 2.2 Message Syntax (but really the whole document is useful)

The DFS response payload however is identical. Patch #1 moves the DFS response parsing out of SMB1 code and makes it work on any version of SMB.

DFS code has 2 "main" entry points: initial mounting and automount (when a DFS link is accessed/traversed).

When automounting, cifs.ko calls build_path_from_dentry() which only makes treename-prefixed paths when the tcon has the SMB_IN_DFS_SHARE. This flag is checked in tcon->Flags which is never touched by SMB2 code as it sets tcon->share_flags on connexion.

* CIFS requires to prefix all pathnames with the treename prefix when connected to a DFS server, so the current build_path_from_dentry() makes sense for CIFS.
* For SMB2+ it seems only the Create request requires the treename prefix. Simply making the function check for both CIFS SMB2+ flag for DFS to add a prefix does not work as the server has different expectations about which packet can have/require a DFS pathname.

Patch #2 adds build_path_from_dentry_optional_prefix() with an extra bool arg to decide to prefix or not. The automount code path always ask for it. Patch #6 modifies SMB2_open() to add the treename prefix to the given path if the server requires it.

We try to use an IPC connection first for the DFS request. Patch #3 adds a flag to SMB2_ioctl() to force the usage of ipc_tid. The SMB2 get_dfs_refer operation tries with the flag set and fallback to a share connection if that doesn't work.

Patch #4 updates the type of ipc_tid from u16 to u32 as that is what a Tid is in SMB2+. This is correct and really needed since in SMB2 Samba doesn't use sequential tree id but random ones in the 32bit space.

As part of the mouting process a IPC tcon is made (I suspect we don't need it anymore in SMB3). This tcon doesn't respect the signing requirement. This is fixed by patch #5.

Finally the SMB2+ implementation of the get_dfs_referral operation is added in all supported SMB versions in patch #7.

I've sucessfuly tested this (v1.0 and v3.0) against a Windows Server 2016 DFS setup and a Samba DFS setup.

Samba used to only accepts DFS referrals requests on an IPC connexion, which is in violation of the spec. A patch was sent on samba-technical and merged. It is not required anymore.

https://lists.samba.org/archive/samba-technical/2017-February/118859.html

Changes since v2:

* add use_ipc flag to SMB2_open()
* make smb2_get_dfs_refer() try IPC connections first
* use first_entry_or_null() instead of first_entry() on the tcon list
* protect tcon list access with spinlock
* add inc/dec of the tcon reference number, protected by spinlock

Aurelien Aptel (7):
CIFS: move DFS response parsing out of SMB1 code
CIFS: add build_path_from_dentry_optional_prefix()
CIFS: add use_ipc flag to SMB2_ioctl()
CIFS: let ses->ipc_tid hold smb2 TreeIds
CIFS: set signing flag in SMB2+ TreeConnect if needed
CIFS: use DFS pathnames in SMB2+ Create requests
CIFS: implement get_dfs_refer for SMB2+

 fs/cifs/cifs_dfs_ref.c |   4 +-
 fs/cifs/cifsglob.h     |   2 +-
 fs/cifs/cifspdu.h      |  16 ++++---
 fs/cifs/cifsproto.h    |   7 +++
 fs/cifs/cifssmb.c      | 119 ++---------------------------------------------
 fs/cifs/dir.c          |  13 +++++-
 fs/cifs/misc.c         | 105 +++++++++++++++++++++++++++++++++++++++++
 fs/cifs/smb2file.c     |   3 +-
 fs/cifs/smb2ops.c      | 123 ++++++++++++++++++++++++++++++++++++++++++++++---
 fs/cifs/smb2pdu.c      | 114 +++++++++++++++++++++++++++++++++++++--------
 fs/cifs/smb2pdu.h      |   8 ++++
 fs/cifs/smb2proto.h    |   3 +-
 12 files changed, 365 insertions(+), 152 deletions(-)
Signature · 1 line
2.10.2

A message that is not in the archive (sent off-list or not yet received): 20170228184034.18771-1-aaptel-IBi9RG/[email protected]

#2 lore raw

PATCH v3 1/7 CIFS: move DFS response parsing out of SMB1 code

since the DFS payload is not tied to the SMB version we can:
* isolate the DFS payload in its own struct, and include that struct in packet structs
* move the function that parses the response to misc.c and make it work on the new DFS payload struct (add payload size and utf16 flag as a result).

---
 fs/cifs/cifspdu.h   |  16 ++++---
 fs/cifs/cifsproto.h |   5 +++
 fs/cifs/cifssmb.c   | 119 +++-------------------------------------------------
 fs/cifs/misc.c      | 105 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 125 insertions(+), 120 deletions(-)
fs/cifs/cifspdu.h +10 -6
index f5b8730..1ce733f 100644
@@ -2086,17 +2086,21 @@ typedef struct dfs_referral_level_3 { /* version 4 is same, + one flag bit */
 	__u8   ServiceSiteGuid[16];  /* MBZ, ignored */
 } __attribute__((packed)) REFERRAL3;
 
-typedef struct smb_com_transaction_get_dfs_refer_rsp {
-	struct smb_hdr hdr;	/* wct = 10 */
-	struct trans2_resp t2;
-	__u16 ByteCount;
-	__u8 Pad;
+struct get_dfs_referral_rsp {
 	__le16 PathConsumed;
 	__le16 NumberOfReferrals;
 	__le32 DFSFlags;
 	REFERRAL3 referrals[1];	/* array of level 3 dfs_referral structures */
 	/* followed by the strings pointed to by the referral structures */
-} __attribute__((packed)) TRANSACTION2_GET_DFS_REFER_RSP;
+} __packed;
+
+typedef struct smb_com_transaction_get_dfs_refer_rsp {
+	struct smb_hdr hdr;	/* wct = 10 */
+	struct trans2_resp t2;
+	__u16 ByteCount;
+	__u8 Pad;
+	struct get_dfs_referral_rsp dfs_data;
+} __packed TRANSACTION2_GET_DFS_REFER_RSP;
 
 /* DFS Flags */
 #define DFSREF_REFERRAL_SERVER  0x00000001 /* all targets are DFS roots */
fs/cifs/cifsproto.h +5 -0
index 406d2c1..c097830 100644
@@ -284,6 +284,11 @@ extern int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
 			const struct nls_table *nls_codepage,
 			unsigned int *num_referrals,
 			struct dfs_info3_param **referrals, int remap);
+extern int parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
+			       unsigned int *num_of_nodes,
+			       struct dfs_info3_param **target_nodes,
+			       const struct nls_table *nls_codepage, int remap,
+			       const char *searchName, bool is_unicode);
 extern void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
 				 struct cifs_sb_info *cifs_sb,
 				 struct smb_vol *vol);
fs/cifs/cifssmb.c +5 -114

131 lines not on this page · raw message

fs/cifs/misc.c +105 -0

108 lines not on this page · raw message

Signature · 1 line
2.10.2

A message that is not in the archive (sent off-list or not yet received): 20170228184034.18771-2-aaptel-IBi9RG/[email protected]

#4 lore raw

Re: [PATCH v3 1/7] CIFS: move DFS response parsing out of SMB1 code

2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel-IBi9RG/[email protected]>:

Quoted 316 lines

since the DFS payload is not tied to the SMB version we can:
* isolate the DFS payload in its own struct, and include that struct in packet structs
* move the function that parses the response to misc.c and make it work on the new DFS payload struct (add payload size and utf16 flag as a result).

---
 fs/cifs/cifspdu.h   |  16 ++++---
 fs/cifs/cifsproto.h |   5 +++
 fs/cifs/cifssmb.c   | 119 +++-------------------------------------------------
 fs/cifs/misc.c      | 105 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 125 insertions(+), 120 deletions(-)
fs/cifs/cifspdu.h +10 -6
index f5b8730..1ce733f 100644
@@ -2086,17 +2086,21 @@ typedef struct dfs_referral_level_3 { /* version 4 is same, + one flag bit */
        __u8   ServiceSiteGuid[16];  /* MBZ, ignored */
 } __attribute__((packed)) REFERRAL3;
 
-typedef struct smb_com_transaction_get_dfs_refer_rsp {
-       struct smb_hdr hdr;     /* wct = 10 */
-       struct trans2_resp t2;
-       __u16 ByteCount;
-       __u8 Pad;
+struct get_dfs_referral_rsp {
        __le16 PathConsumed;
        __le16 NumberOfReferrals;
        __le32 DFSFlags;
        REFERRAL3 referrals[1]; /* array of level 3 dfs_referral structures */
        /* followed by the strings pointed to by the referral structures */
-} __attribute__((packed)) TRANSACTION2_GET_DFS_REFER_RSP;
+} __packed;
+
+typedef struct smb_com_transaction_get_dfs_refer_rsp {
+       struct smb_hdr hdr;     /* wct = 10 */
+       struct trans2_resp t2;
+       __u16 ByteCount;
+       __u8 Pad;
+       struct get_dfs_referral_rsp dfs_data;
+} __packed TRANSACTION2_GET_DFS_REFER_RSP;
 
 /* DFS Flags */
 #define DFSREF_REFERRAL_SERVER  0x00000001 /* all targets are DFS roots */
fs/cifs/cifsproto.h +5 -0
index 406d2c1..c097830 100644
@@ -284,6 +284,11 @@ extern int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
                        const struct nls_table *nls_codepage,
                        unsigned int *num_referrals,
                        struct dfs_info3_param **referrals, int remap);
+extern int parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
+                              unsigned int *num_of_nodes,
+                              struct dfs_info3_param **target_nodes,
+                              const struct nls_table *nls_codepage, int remap,
+                              const char *searchName, bool is_unicode);
 extern void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
                                 struct cifs_sb_info *cifs_sb,
                                 struct smb_vol *vol);
fs/cifs/cifssmb.c +5 -114
index f5099fb..5005c79 100644
@@ -4786,117 +4786,6 @@ CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon,
        return rc;
 }
 
-/* parses DFS refferal V3 structure
- * caller is responsible for freeing target_nodes
- * returns:
- *     on success - 0
- *     on failure - errno
- */
-static int
-parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
-               unsigned int *num_of_nodes,
-               struct dfs_info3_param **target_nodes,
-               const struct nls_table *nls_codepage, int remap,
-               const char *searchName)
-{
-       int i, rc = 0;
-       char *data_end;
-       bool is_unicode;
-       struct dfs_referral_level_3 *ref;
-
-       if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
-               is_unicode = true;
-       else
-               is_unicode = false;
-       *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);
-
-       if (*num_of_nodes < 1) {
-               cifs_dbg(VFS, "num_referrals: must be at least > 0, but we get num_referrals = %d\n",
-                        *num_of_nodes);
-               rc = -EINVAL;
-               goto parse_DFS_referrals_exit;
-       }
-
-       ref = (struct dfs_referral_level_3 *) &(pSMBr->referrals);
-       if (ref->VersionNumber != cpu_to_le16(3)) {
-               cifs_dbg(VFS, "Referrals of V%d version are not supported, should be V3\n",
-                        le16_to_cpu(ref->VersionNumber));
-               rc = -EINVAL;
-               goto parse_DFS_referrals_exit;
-       }
-
-       /* get the upper boundary of the resp buffer */
-       data_end = (char *)(&(pSMBr->PathConsumed)) +
-                               le16_to_cpu(pSMBr->t2.DataCount);
-
-       cifs_dbg(FYI, "num_referrals: %d dfs flags: 0x%x ...\n",
-                *num_of_nodes, le32_to_cpu(pSMBr->DFSFlags));
-
-       *target_nodes = kcalloc(*num_of_nodes, sizeof(struct dfs_info3_param),
-                               GFP_KERNEL);
-       if (*target_nodes == NULL) {
-               rc = -ENOMEM;
-               goto parse_DFS_referrals_exit;
-       }
-
-       /* collect necessary data from referrals */
-       for (i = 0; i < *num_of_nodes; i++) {
-               char *temp;
-               int max_len;
-               struct dfs_info3_param *node = (*target_nodes)+i;
-
-               node->flags = le32_to_cpu(pSMBr->DFSFlags);
-               if (is_unicode) {
-                       __le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
-                                               GFP_KERNEL);
-                       if (tmp == NULL) {
-                               rc = -ENOMEM;
-                               goto parse_DFS_referrals_exit;
-                       }
-                       cifsConvertToUTF16((__le16 *) tmp, searchName,
-                                          PATH_MAX, nls_codepage, remap);
-                       node->path_consumed = cifs_utf16_bytes(tmp,
-                                       le16_to_cpu(pSMBr->PathConsumed),
-                                       nls_codepage);
-                       kfree(tmp);
-               } else
-                       node->path_consumed = le16_to_cpu(pSMBr->PathConsumed);
-
-               node->server_type = le16_to_cpu(ref->ServerType);
-               node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
-
-               /* copy DfsPath */
-               temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
-               max_len = data_end - temp;
-               node->path_name = cifs_strndup_from_utf16(temp, max_len,
-                                               is_unicode, nls_codepage);
-               if (!node->path_name) {
-                       rc = -ENOMEM;
-                       goto parse_DFS_referrals_exit;
-               }
-
-               /* copy link target UNC */
-               temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
-               max_len = data_end - temp;
-               node->node_name = cifs_strndup_from_utf16(temp, max_len,
-                                               is_unicode, nls_codepage);
-               if (!node->node_name) {
-                       rc = -ENOMEM;
-                       goto parse_DFS_referrals_exit;
-               }
-
-               ref++;
-       }
-
-parse_DFS_referrals_exit:
-       if (rc) {
-               free_dfs_info_array(*target_nodes, *num_of_nodes);
-               *target_nodes = NULL;
-               *num_of_nodes = 0;
-       }
-       return rc;
-}
-
 int
 CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses,
                const char *search_name, struct dfs_info3_param **target_nodes,
@@ -4993,9 +4882,11 @@ CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses,
                 get_bcc(&pSMBr->hdr), le16_to_cpu(pSMBr->t2.DataOffset));
 
        /* parse returned result into more usable form */
-       rc = parse_DFS_referrals(pSMBr, num_of_nodes,
-                                target_nodes, nls_codepage, remap,
-                                search_name);
+       rc = parse_dfs_referrals(&pSMBr->dfs_data,
+                                le16_to_cpu(pSMBr->t2.DataCount),
+                                num_of_nodes, target_nodes, nls_codepage,
+                                remap, search_name,
+                                pSMBr->hdr.Flags2 & SMBFLG2_UNICODE);
 
 GetDFSRefExit:
        cifs_buf_release(pSMB);
fs/cifs/misc.c +105 -1
index c672915..d3fb115 100644
@@ -640,3 +640,108 @@ cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
        cifs_add_pending_open_locked(fid, tlink, open);
        spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
 }
+
+/* parses DFS refferal V3 structure
+ * caller is responsible for freeing target_nodes
+ * returns:
+ * - on success - 0
+ * - on failure - errno
+ */
+int
+parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
+                   unsigned int *num_of_nodes,
+                   struct dfs_info3_param **target_nodes,
+                   const struct nls_table *nls_codepage, int remap,
+                   const char *searchName, bool is_unicode)
+{
+       int i, rc = 0;
+       char *data_end;
+       struct dfs_referral_level_3 *ref;
+
+       *num_of_nodes = le16_to_cpu(rsp->NumberOfReferrals);
+
+       if (*num_of_nodes < 1) {
+               cifs_dbg(VFS, "num_referrals: must be at least > 0, but we get num_referrals = %d\n",
+                        *num_of_nodes);
+               rc = -EINVAL;
+               goto parse_DFS_referrals_exit;
+       }
+
+       ref = (struct dfs_referral_level_3 *) &(rsp->referrals);
+       if (ref->VersionNumber != cpu_to_le16(3)) {
+               cifs_dbg(VFS, "Referrals of V%d version are not supported, should be V3\n",
+                        le16_to_cpu(ref->VersionNumber));
+               rc = -EINVAL;
+               goto parse_DFS_referrals_exit;
+       }
+
+       /* get the upper boundary of the resp buffer */
+       data_end = (char *)rsp + rsp_size;
+
+       cifs_dbg(FYI, "num_referrals: %d dfs flags: 0x%x ...\n",
+                *num_of_nodes, le32_to_cpu(rsp->DFSFlags));
+
+       *target_nodes = kcalloc(*num_of_nodes, sizeof(struct dfs_info3_param),
+                               GFP_KERNEL);
+       if (*target_nodes == NULL) {
+               rc = -ENOMEM;
+               goto parse_DFS_referrals_exit;
+       }
+
+       /* collect necessary data from referrals */
+       for (i = 0; i < *num_of_nodes; i++) {
+               char *temp;
+               int max_len;
+               struct dfs_info3_param *node = (*target_nodes)+i;
+
+               node->flags = le32_to_cpu(rsp->DFSFlags);
+               if (is_unicode) {
+                       __le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
+                                               GFP_KERNEL);
+                       if (tmp == NULL) {
+                               rc = -ENOMEM;
+                               goto parse_DFS_referrals_exit;
+                       }
+                       cifsConvertToUTF16((__le16 *) tmp, searchName,
+                                          PATH_MAX, nls_codepage, remap);
+                       node->path_consumed = cifs_utf16_bytes(tmp,
+                                       le16_to_cpu(rsp->PathConsumed),
+                                       nls_codepage);
+                       kfree(tmp);
+               } else
+                       node->path_consumed = le16_to_cpu(rsp->PathConsumed);
+
+               node->server_type = le16_to_cpu(ref->ServerType);
+               node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
+
+               /* copy DfsPath */
+               temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
+               max_len = data_end - temp;
+               node->path_name = cifs_strndup_from_utf16(temp, max_len,
+                                               is_unicode, nls_codepage);
+               if (!node->path_name) {
+                       rc = -ENOMEM;
+                       goto parse_DFS_referrals_exit;
+               }
+
+               /* copy link target UNC */
+               temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
+               max_len = data_end - temp;
+               node->node_name = cifs_strndup_from_utf16(temp, max_len,
+                                               is_unicode, nls_codepage);
+               if (!node->node_name) {
+                       rc = -ENOMEM;
+                       goto parse_DFS_referrals_exit;
+               }
+
+               ref++;
+       }
+
+parse_DFS_referrals_exit:
+       if (rc) {
+               free_dfs_info_array(*target_nodes, *num_of_nodes);
+               *target_nodes = NULL;
+               *num_of_nodes = 0;
+       }
+       return rc;
+}
--

2.10.2

Quoted 4 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

Signature · 2 lines
Best regards,
Pavel Shilovsky
#5 lore raw

PATCH v3 2/7 CIFS: add build_path_from_dentry_optional_prefix()

In reply to a message not in the archive

this function does the same thing as add build_path_from_dentry() but takes a boolean parameter to decide whether or not to prefix the path with the tree name.

we cannot rely on tcon->Flags & SMB_SHARE_IS_IN_DFS for SMB2 as smb2 code never sets tcon->Flags but it sets tcon->share_flags and it seems the SMB_SHARE_IS_IN_DFS has different semantics in SMB2: the prefix shouldn't be added everytime it was in SMB1.

---
 fs/cifs/cifs_dfs_ref.c |  4 +++-
 fs/cifs/cifsproto.h    |  2 ++
 fs/cifs/dir.c          | 13 ++++++++++++-
 3 files changed, 17 insertions(+), 2 deletions(-)
fs/cifs/cifs_dfs_ref.c +3 -1
index ec9dbbc..8a9ebfd 100644
@@ -302,7 +302,9 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
 	 * gives us the latter, so we must adjust the result.
 	 */
 	mnt = ERR_PTR(-ENOMEM);
-	full_path = build_path_from_dentry(mntpt);
+
+	/* always use tree name prefix */
+	full_path = build_path_from_dentry_optional_prefix(mntpt, true);
 	if (full_path == NULL)
 		goto cdda_exit;
 
fs/cifs/cifsproto.h +2 -0
index c097830..9ee46c1 100644
@@ -61,6 +61,8 @@ extern void exit_cifs_idmap(void);
 extern int init_cifs_spnego(void);
 extern void exit_cifs_spnego(void);
 extern char *build_path_from_dentry(struct dentry *);
+extern char *build_path_from_dentry_optional_prefix(struct dentry *direntry,
+						    bool prefix);
 extern char *cifs_build_path_to_root(struct smb_vol *vol,
 				     struct cifs_sb_info *cifs_sb,
 				     struct cifs_tcon *tcon,
fs/cifs/dir.c +12 -1
index 2c227a9..56366e9 100644
@@ -81,6 +81,17 @@ cifs_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
 char *
 build_path_from_dentry(struct dentry *direntry)
 {
+	struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
+	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+	bool prefix = tcon->Flags & SMB_SHARE_IS_IN_DFS;
+
+	return build_path_from_dentry_optional_prefix(direntry,
+						      prefix);
+}
+
+char *
+build_path_from_dentry_optional_prefix(struct dentry *direntry, bool prefix)
+{
 	struct dentry *temp;
 	int namelen;
 	int dfsplen;
@@ -92,7 +103,7 @@ build_path_from_dentry(struct dentry *direntry)
 	unsigned seq;
 
 	dirsep = CIFS_DIR_SEP(cifs_sb);
-	if (tcon->Flags & SMB_SHARE_IS_IN_DFS)
+	if (prefix)
 		dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
 	else
 		dfsplen = 0;
Signature · 1 line
2.10.2

A message that is not in the archive (sent off-list or not yet received): 20170228184034.18771-3-aaptel-IBi9RG/[email protected]

#7 lore raw

Re: [PATCH v3 2/7] CIFS: add build_path_from_dentry_optional_prefix()

2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel-IBi9RG/[email protected]>:

Quoted 78 lines

this function does the same thing as add build_path_from_dentry() but takes a boolean parameter to decide whether or not to prefix the path with the tree name.

we cannot rely on tcon->Flags & SMB_SHARE_IS_IN_DFS for SMB2 as smb2 code never sets tcon->Flags but it sets tcon->share_flags and it seems the SMB_SHARE_IS_IN_DFS has different semantics in SMB2: the prefix shouldn't be added everytime it was in SMB1.

---
 fs/cifs/cifs_dfs_ref.c |  4 +++-
 fs/cifs/cifsproto.h    |  2 ++
 fs/cifs/dir.c          | 13 ++++++++++++-
 3 files changed, 17 insertions(+), 2 deletions(-)
fs/cifs/cifs_dfs_ref.c +3 -1
index ec9dbbc..8a9ebfd 100644
@@ -302,7 +302,9 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
         * gives us the latter, so we must adjust the result.
         */
        mnt = ERR_PTR(-ENOMEM);
-       full_path = build_path_from_dentry(mntpt);
+
+       /* always use tree name prefix */
+       full_path = build_path_from_dentry_optional_prefix(mntpt, true);
        if (full_path == NULL)
                goto cdda_exit;
 
fs/cifs/cifsproto.h +2 -0
index c097830..9ee46c1 100644
@@ -61,6 +61,8 @@ extern void exit_cifs_idmap(void);
 extern int init_cifs_spnego(void);
 extern void exit_cifs_spnego(void);
 extern char *build_path_from_dentry(struct dentry *);
+extern char *build_path_from_dentry_optional_prefix(struct dentry *direntry,
+                                                   bool prefix);
 extern char *cifs_build_path_to_root(struct smb_vol *vol,
                                     struct cifs_sb_info *cifs_sb,
                                     struct cifs_tcon *tcon,
fs/cifs/dir.c +12 -2
index 2c227a9..56366e9 100644
@@ -81,6 +81,17 @@ cifs_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
 char *
 build_path_from_dentry(struct dentry *direntry)
 {
+       struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
+       struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+       bool prefix = tcon->Flags & SMB_SHARE_IS_IN_DFS;
+
+       return build_path_from_dentry_optional_prefix(direntry,
+                                                     prefix);
+}
+
+char *
+build_path_from_dentry_optional_prefix(struct dentry *direntry, bool prefix)
+{
        struct dentry *temp;
        int namelen;
        int dfsplen;
@@ -92,7 +103,7 @@ build_path_from_dentry(struct dentry *direntry)
        unsigned seq;
 
        dirsep = CIFS_DIR_SEP(cifs_sb);
-       if (tcon->Flags & SMB_SHARE_IS_IN_DFS)
+       if (prefix)
                dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
        else
                dfsplen = 0;
--

2.10.2

Quoted 4 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

Signature · 2 lines
Best regards,
Pavel Shilovsky
#8 lore raw

PATCH v3 3/7 CIFS: add use_ipc flag to SMB2_ioctl()

In reply to a message not in the archive

when set, use the session IPC tree id instead of the tid in the provided tcon.

---
 fs/cifs/smb2file.c  |  3 ++-
 fs/cifs/smb2ops.c   | 23 ++++++++++++++++-------
 fs/cifs/smb2pdu.c   | 17 +++++++++++++++--
 fs/cifs/smb2proto.h |  3 ++-
 4 files changed, 35 insertions(+), 11 deletions(-)
fs/cifs/smb2file.c +2 -1
index b2aff0c..b4b1f03 100644
@@ -73,7 +73,8 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
 		nr_ioctl_req.Timeout = 0; /* use server default (120 seconds) */
 		nr_ioctl_req.Reserved = 0;
 		rc = SMB2_ioctl(xid, oparms->tcon, fid->persistent_fid,
-			fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY, true,
+			fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY,
+			true /* is_fsctl */, false /* use_ipc */,
 			(char *)&nr_ioctl_req, sizeof(nr_ioctl_req),
 			NULL, NULL /* no return info */);
 		if (rc == -EOPNOTSUPP) {
fs/cifs/smb2ops.c +16 -7
index a44b4db..02937cc 100644
@@ -282,6 +282,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
 
 	rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
 			FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */,
+			false /* use_ipc */,
 			NULL /* no data input */, 0 /* no data input */,
 			(char **)&out_buf, &ret_data_len);
 	if (rc != 0)
@@ -571,6 +572,7 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
 
 	rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
 			FSCTL_SRV_REQUEST_RESUME_KEY, true /* is_fsctl */,
+			false /* use_ipc */,
 			NULL, 0 /* no input */,
 			(char **)&res_key, &ret_data_len);
 
@@ -635,7 +637,8 @@ smb2_clone_range(const unsigned int xid,
 		/* Request server copy to target from src identified by key */
 		rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
 			trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
-			true /* is_fsctl */, (char *)pcchunk,
+			true /* is_fsctl */, false /* use_ipc */,
+			(char *)pcchunk,
 			sizeof(struct copychunk_ioctl),	(char **)&retbuf,
 			&ret_data_len);
 		if (rc == 0) {
@@ -787,7 +790,8 @@ static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
 
 	rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
 			cfile->fid.volatile_fid, FSCTL_SET_SPARSE,
-			true /* is_fctl */, &setsparse, 1, NULL, NULL);
+			true /* is_fctl */, false /* use_ipc */,
+			&setsparse, 1, NULL, NULL);
 	if (rc) {
 		tcon->broken_sparse_sup = true;
 		cifs_dbg(FYI, "set sparse rc = %d\n", rc);
@@ -857,7 +861,8 @@ smb2_duplicate_extents(const unsigned int xid,
 	rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
 			trgtfile->fid.volatile_fid,
 			FSCTL_DUPLICATE_EXTENTS_TO_FILE,
-			true /* is_fsctl */, (char *)&dup_ext_buf,
+			true /* is_fsctl */, false /* use_ipc */,
+			(char *)&dup_ext_buf,
 			sizeof(struct duplicate_extents_to_file),
 			NULL,
 			&ret_data_len);
@@ -891,7 +896,8 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
 	return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
 			cfile->fid.volatile_fid,
 			FSCTL_SET_INTEGRITY_INFORMATION,
-			true /* is_fsctl */, (char *)&integr_info,
+			true /* is_fsctl */, false /* use_ipc */,
+			(char *)&integr_info,
 			sizeof(struct fsctl_set_integrity_information_req),
 			NULL,
 			&ret_data_len);
@@ -910,7 +916,8 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
 	rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
 			cfile->fid.volatile_fid,
 			FSCTL_SRV_ENUMERATE_SNAPSHOTS,
-			true /* is_fsctl */, NULL, 0 /* no input data */,
+			true /* is_fsctl */, false /* use_ipc */,
+			NULL, 0 /* no input data */,
 			(char **)&retbuf,
 			&ret_data_len);
 	cifs_dbg(FYI, "enum snaphots ioctl returned %d and ret buflen is %d\n",
@@ -1220,7 +1227,8 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
 
 	rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
 			cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
-			true /* is_fctl */, (char *)&fsctl_buf,
+			true /* is_fctl */, false /* use_ipc */,
+			(char *)&fsctl_buf,
 			sizeof(struct file_zero_data_information), NULL, NULL);
 	free_xid(xid);
 	return rc;
@@ -1254,7 +1262,8 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
 
 	rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
 			cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
-			true /* is_fctl */, (char *)&fsctl_buf,
+			true /* is_fctl */, false /* use_ipc */,
+			(char *)&fsctl_buf,
 			sizeof(struct file_zero_data_information), NULL, NULL);
 	free_xid(xid);
 	return rc;
fs/cifs/smb2pdu.c +15 -2
index ad83b3d..8c4532d 100644
@@ -620,6 +620,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
 
 	rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
 		FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */,
+		false /* use_ipc */,
 		(char *)&vneg_inbuf, sizeof(struct validate_negotiate_info_req),
 		(char **)&pneg_rsp, &rsplen);
 
@@ -1683,8 +1684,9 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
  */
 int
 SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
-	   u64 volatile_fid, u32 opcode, bool is_fsctl, char *in_data,
-	   u32 indatalen, char **out_data, u32 *plen /* returned data len */)
+	   u64 volatile_fid, u32 opcode, bool is_fsctl, bool use_ipc,
+	   char *in_data, u32 indatalen,
+	   char **out_data, u32 *plen /* returned data len */)
 {
 	struct smb2_ioctl_req *req;
 	struct smb2_ioctl_rsp *rsp;
@@ -1721,6 +1723,16 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
 	if (rc)
 		return rc;
 
+	if (use_ipc) {
+		if (ses->ipc_tid == 0) {
+			cifs_small_buf_release(req);
+			return -ENOTCONN;
+		}
+
+		cifs_dbg(FYI, "replacing tid 0x%x with IPC tid 0x%x\n",
+			 req->hdr.sync_hdr.TreeId, ses->ipc_tid);
+		req->hdr.sync_hdr.TreeId = ses->ipc_tid;
+	}
 	if (encryption_required(tcon))
 		flags |= CIFS_TRANSFORM_REQ;
 
@@ -1843,6 +1855,7 @@ SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
 
 	rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
 			FSCTL_SET_COMPRESSION, true /* is_fsctl */,
+			false /* use_ipc */,
 			(char *)&fsctl_input /* data input */,
 			2 /* in data len */, &ret_data /* out data */, NULL);
 
fs/cifs/smb2proto.h +2 -1
index 85fc7a7..11d9f30 100644
@@ -121,7 +121,8 @@ extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms,
 		     struct smb2_err_rsp **err_buf);
 extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon,
 		     u64 persistent_fid, u64 volatile_fid, u32 opcode,
-		     bool is_fsctl, char *in_data, u32 indatalen,
+		     bool is_fsctl, bool use_ipc,
+		     char *in_data, u32 indatalen,
 		     char **out_data, u32 *plen /* returned data len */);
 extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
 		      u64 persistent_file_id, u64 volatile_file_id);
Signature · 1 line
2.10.2

A message that is not in the archive (sent off-list or not yet received): 20170228184034.18771-4-aaptel-IBi9RG/[email protected]

Reviewed-by
#10 lore raw

Re: [PATCH v3 3/7] CIFS: add use_ipc flag to SMB2_ioctl()

2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel-IBi9RG/[email protected]>:

Quoted 181 lines

when set, use the session IPC tree id instead of the tid in the provided tcon.

---
 fs/cifs/smb2file.c  |  3 ++-
 fs/cifs/smb2ops.c   | 23 ++++++++++++++++-------
 fs/cifs/smb2pdu.c   | 17 +++++++++++++++--
 fs/cifs/smb2proto.h |  3 ++-
 4 files changed, 35 insertions(+), 11 deletions(-)
fs/cifs/smb2file.c +2 -1
index b2aff0c..b4b1f03 100644
@@ -73,7 +73,8 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
                nr_ioctl_req.Timeout = 0; /* use server default (120 seconds) */
                nr_ioctl_req.Reserved = 0;
                rc = SMB2_ioctl(xid, oparms->tcon, fid->persistent_fid,
-                       fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY, true,
+                       fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY,
+                       true /* is_fsctl */, false /* use_ipc */,
                        (char *)&nr_ioctl_req, sizeof(nr_ioctl_req),
                        NULL, NULL /* no return info */);
                if (rc == -EOPNOTSUPP) {
fs/cifs/smb2ops.c +16 -7
index a44b4db..02937cc 100644
@@ -282,6 +282,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
 
        rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
                        FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */,
+                       false /* use_ipc */,
                        NULL /* no data input */, 0 /* no data input */,
                        (char **)&out_buf, &ret_data_len);
        if (rc != 0)
@@ -571,6 +572,7 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
 
        rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
                        FSCTL_SRV_REQUEST_RESUME_KEY, true /* is_fsctl */,
+                       false /* use_ipc */,
                        NULL, 0 /* no input */,
                        (char **)&res_key, &ret_data_len);
 
@@ -635,7 +637,8 @@ smb2_clone_range(const unsigned int xid,
                /* Request server copy to target from src identified by key */
                rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
                        trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
-                       true /* is_fsctl */, (char *)pcchunk,
+                       true /* is_fsctl */, false /* use_ipc */,
+                       (char *)pcchunk,
                        sizeof(struct copychunk_ioctl), (char **)&retbuf,
                        &ret_data_len);
                if (rc == 0) {
@@ -787,7 +790,8 @@ static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
 
        rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
                        cfile->fid.volatile_fid, FSCTL_SET_SPARSE,
-                       true /* is_fctl */, &setsparse, 1, NULL, NULL);
+                       true /* is_fctl */, false /* use_ipc */,
+                       &setsparse, 1, NULL, NULL);
        if (rc) {
                tcon->broken_sparse_sup = true;
                cifs_dbg(FYI, "set sparse rc = %d\n", rc);
@@ -857,7 +861,8 @@ smb2_duplicate_extents(const unsigned int xid,
        rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
                        trgtfile->fid.volatile_fid,
                        FSCTL_DUPLICATE_EXTENTS_TO_FILE,
-                       true /* is_fsctl */, (char *)&dup_ext_buf,
+                       true /* is_fsctl */, false /* use_ipc */,
+                       (char *)&dup_ext_buf,
                        sizeof(struct duplicate_extents_to_file),
                        NULL,
                        &ret_data_len);
@@ -891,7 +896,8 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
        return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
                        cfile->fid.volatile_fid,
                        FSCTL_SET_INTEGRITY_INFORMATION,
-                       true /* is_fsctl */, (char *)&integr_info,
+                       true /* is_fsctl */, false /* use_ipc */,
+                       (char *)&integr_info,
                        sizeof(struct fsctl_set_integrity_information_req),
                        NULL,
                        &ret_data_len);
@@ -910,7 +916,8 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
        rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
                        cfile->fid.volatile_fid,
                        FSCTL_SRV_ENUMERATE_SNAPSHOTS,
-                       true /* is_fsctl */, NULL, 0 /* no input data */,
+                       true /* is_fsctl */, false /* use_ipc */,
+                       NULL, 0 /* no input data */,
                        (char **)&retbuf,
                        &ret_data_len);
        cifs_dbg(FYI, "enum snaphots ioctl returned %d and ret buflen is %d\n",
@@ -1220,7 +1227,8 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
 
        rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
                        cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
-                       true /* is_fctl */, (char *)&fsctl_buf,
+                       true /* is_fctl */, false /* use_ipc */,
+                       (char *)&fsctl_buf,
                        sizeof(struct file_zero_data_information), NULL, NULL);
        free_xid(xid);
        return rc;
@@ -1254,7 +1262,8 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
 
        rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
                        cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
-                       true /* is_fctl */, (char *)&fsctl_buf,
+                       true /* is_fctl */, false /* use_ipc */,
+                       (char *)&fsctl_buf,
                        sizeof(struct file_zero_data_information), NULL, NULL);
        free_xid(xid);
        return rc;
fs/cifs/smb2pdu.c +15 -2
index ad83b3d..8c4532d 100644
@@ -620,6 +620,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
 
        rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
                FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */,
+               false /* use_ipc */,
                (char *)&vneg_inbuf, sizeof(struct validate_negotiate_info_req),
                (char **)&pneg_rsp, &rsplen);
 
@@ -1683,8 +1684,9 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
  */
 int
 SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
-          u64 volatile_fid, u32 opcode, bool is_fsctl, char *in_data,
-          u32 indatalen, char **out_data, u32 *plen /* returned data len */)
+          u64 volatile_fid, u32 opcode, bool is_fsctl, bool use_ipc,
+          char *in_data, u32 indatalen,
+          char **out_data, u32 *plen /* returned data len */)
 {
        struct smb2_ioctl_req *req;
        struct smb2_ioctl_rsp *rsp;
@@ -1721,6 +1723,16 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
        if (rc)
                return rc;
 
+       if (use_ipc) {
+               if (ses->ipc_tid == 0) {
+                       cifs_small_buf_release(req);
+                       return -ENOTCONN;
+               }
+
+               cifs_dbg(FYI, "replacing tid 0x%x with IPC tid 0x%x\n",
+                        req->hdr.sync_hdr.TreeId, ses->ipc_tid);
+               req->hdr.sync_hdr.TreeId = ses->ipc_tid;
+       }
        if (encryption_required(tcon))
                flags |= CIFS_TRANSFORM_REQ;
 
@@ -1843,6 +1855,7 @@ SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
 
        rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
                        FSCTL_SET_COMPRESSION, true /* is_fsctl */,
+                       false /* use_ipc */,
                        (char *)&fsctl_input /* data input */,
                        2 /* in data len */, &ret_data /* out data */, NULL);
 
fs/cifs/smb2proto.h +2 -2
index 85fc7a7..11d9f30 100644
@@ -121,7 +121,8 @@ extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms,
                     struct smb2_err_rsp **err_buf);
 extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon,
                     u64 persistent_fid, u64 volatile_fid, u32 opcode,
-                    bool is_fsctl, char *in_data, u32 indatalen,
+                    bool is_fsctl, bool use_ipc,
+                    char *in_data, u32 indatalen,
                     char **out_data, u32 *plen /* returned data len */);
 extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
                      u64 persistent_file_id, u64 volatile_file_id);
--

2.10.2

Quoted 4 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

Signature · 2 lines
Best regards,
Pavel Shilovsky
#11 lore raw

PATCH v3 4/7 CIFS: let ses->ipc_tid hold smb2 TreeIds

In reply to a message not in the archive

the TreeId field went from 2 bytes in CIFS to 4 bytes in SMB2+. this commit updates the size of the ipc_tid field of a cifs_ses, which was still using 2 bytes.

---
 fs/cifs/cifsglob.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/cifs/cifsglob.h +1 -1
index 1a90bb3..af224cd 100644
@@ -822,7 +822,7 @@ struct cifs_ses {
 	int ses_count;		/* reference counter */
 	enum statusEnum status;
 	unsigned overrideSecFlg;  /* if non-zero override global sec flags */
-	__u16 ipc_tid;		/* special tid for connection to IPC share */
+	__u32 ipc_tid;		/* special tid for connection to IPC share */
 	char *serverOS;		/* name of operating system underlying server */
 	char *serverNOS;	/* name of network operating system of server */
 	char *serverDomain;	/* security realm of server */
Signature · 1 line
2.10.2

A message that is not in the archive (sent off-list or not yet received): 20170228184034.18771-5-aaptel-IBi9RG/[email protected]

Reviewed-by
#13 lore raw

Re: [PATCH v3 4/7] CIFS: let ses->ipc_tid hold smb2 TreeIds

2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel-IBi9RG/[email protected]>:

Quoted 25 lines

the TreeId field went from 2 bytes in CIFS to 4 bytes in SMB2+. this commit updates the size of the ipc_tid field of a cifs_ses, which was still using 2 bytes.

---
 fs/cifs/cifsglob.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/cifs/cifsglob.h +1 -2
index 1a90bb3..af224cd 100644
@@ -822,7 +822,7 @@ struct cifs_ses {
        int ses_count;          /* reference counter */
        enum statusEnum status;
        unsigned overrideSecFlg;  /* if non-zero override global sec flags */
-       __u16 ipc_tid;          /* special tid for connection to IPC share */
+       __u32 ipc_tid;          /* special tid for connection to IPC share */
        char *serverOS;         /* name of operating system underlying server */
        char *serverNOS;        /* name of network operating system of server */
        char *serverDomain;     /* security realm of server */
--

2.10.2

Quoted 4 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

Signature · 2 lines
Best regards,
Pavel Shilovsky
#14 lore raw

PATCH v3 5/7 CIFS: set signing flag in SMB2+ TreeConnect if needed

In reply to a message not in the archive

cifs_enable_signing() already sets server->sign according to what the server requires/offers and what mount options allows/forbids, so use that.

this is required for IPC tcon that connects to signing-required servers.

---
 fs/cifs/smb2pdu.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
fs/cifs/smb2pdu.c +2 -2
index 8c4532d..2fd93ee 100644
@@ -1168,8 +1168,8 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
 
 		/* since no tcon, smb2_init can not do this, so do here */
 		req->hdr.sync_hdr.SessionId = ses->Suid;
-		/* if (ses->server->sec_mode & SECMODE_SIGN_REQUIRED)
-			req->hdr.Flags |= SMB2_FLAGS_SIGNED; */
+		if (ses->server->sign)
+			req->hdr.sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
 	} else if (encryption_required(tcon))
 		flags |= CIFS_TRANSFORM_REQ;
 
Signature · 1 line
2.10.2

A message that is not in the archive (sent off-list or not yet received): 20170228184034.18771-6-aaptel-IBi9RG/[email protected]

Reviewed-by
#16 lore raw

Re: [PATCH v3 5/7] CIFS: set signing flag in SMB2+ TreeConnect if needed

2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel-IBi9RG/[email protected]>:

Quoted 29 lines

cifs_enable_signing() already sets server->sign according to what the server requires/offers and what mount options allows/forbids, so use that.

this is required for IPC tcon that connects to signing-required servers.

---
 fs/cifs/smb2pdu.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
fs/cifs/smb2pdu.c +2 -3
index 8c4532d..2fd93ee 100644
@@ -1168,8 +1168,8 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
 
                /* since no tcon, smb2_init can not do this, so do here */
                req->hdr.sync_hdr.SessionId = ses->Suid;
-               /* if (ses->server->sec_mode & SECMODE_SIGN_REQUIRED)
-                       req->hdr.Flags |= SMB2_FLAGS_SIGNED; */
+               if (ses->server->sign)
+                       req->hdr.sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
        } else if (encryption_required(tcon))
                flags |= CIFS_TRANSFORM_REQ;
 
--

2.10.2

Quoted 4 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

Signature · 2 lines
Best regards,
Pavel Shilovsky
#17 lore raw

PATCH v3 6/7 CIFS: use DFS pathnames in SMB2+ Create requests

In reply to a message not in the archive

When connected to a DFS capable share, the client must set the SMB2_FLAGS_DFS_OPERATIONS flag in the SMB2 header and use DFS path names: "<server>\<share>\<path>" *without* leading \\.

Sources:

[MS-SMB2] 3.2.5.5 Receiving an SMB2 TREE_CONNECT Response

Quoted 2 lines

TreeConnect.IsDfsShare MUST be set to TRUE, if the SMB2_SHARE_CAP_DFS bit is set in the Capabilities field of the response.

[MS-SMB2] 3.2.4.3 Application Requests Opening a File

Quoted 2 lines

If TreeConnect.IsDfsShare is TRUE, the SMB2_FLAGS_DFS_OPERATIONS flag is set in the Flags field.

[MS-SMB2] 2.2.13 SMB2 CREATE Request, NameOffset:

Quoted 5 lines

If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of the SMB2 header, the file name includes a prefix that will be processed during DFS name normalization as specified in section 3.3.5.9. Otherwise, the file name is relative to the share that is identified by the TreeId in the SMB2 header.

---
 fs/cifs/smb2pdu.c | 93 +++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 77 insertions(+), 16 deletions(-)
fs/cifs/smb2pdu.c +77 -16
index 2fd93ee..059da05 100644
@@ -1528,6 +1528,48 @@ add_durable_context(struct kvec *iov, unsigned int *num_iovec,
 	return 0;
 }
 
+static int
+alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
+			    const char *treename, const __le16 *path)
+{
+	int treename_len, path_len;
+	const __le16 sep[] = {cpu_to_le16('\\'), cpu_to_le16(0x0000)};
+
+	/*
+	 * skip leading "\\"
+	 */
+	treename_len = strlen(treename);
+	if (treename_len < 2 || !(treename[0] == '\\' && treename[1] == '\\'))
+		return -EINVAL;
+
+	treename += 2;
+	treename_len -= 2;
+
+	path_len = UniStrnlen((wchar_t *)path, PATH_MAX);
+
+	/*
+	 * make room for one path separator between the treename and
+	 * path
+	 */
+	*out_len = treename_len + 1 + path_len;
+
+	/*
+	 * final path needs to be null-terminated UTF16 with a
+	 * size aligned to 8
+	 */
+
+	*out_size = roundup((*out_len+1)*2, 8);
+	*out_path = kzalloc(*out_size, GFP_KERNEL);
+	if (!*out_path)
+		return -ENOMEM;
+
+	cifs_strtoUTF16(*out_path, treename, treename_len, load_nls_default());
+	UniStrcat(*out_path, sep);
+	UniStrcat(*out_path, path);
+
+	return 0;
+}
+
 int
 SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
 	  __u8 *oplock, struct smb2_file_all_info *buf,
@@ -1576,30 +1618,49 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
 	req->ShareAccess = FILE_SHARE_ALL_LE;
 	req->CreateDisposition = cpu_to_le32(oparms->disposition);
 	req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK);
-	uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
-	/* do not count rfc1001 len field */
-	req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 4);
 
 	iov[0].iov_base = (char *)req;
 	/* 4 for rfc1002 length field */
 	iov[0].iov_len = get_rfc1002_length(req) + 4;
-
-	/* MUST set path len (NameLength) to 0 opening root of share */
-	req->NameLength = cpu_to_le16(uni_path_len - 2);
 	/* -1 since last byte is buf[0] which is sent below (path) */
 	iov[0].iov_len--;
-	if (uni_path_len % 8 != 0) {
-		copy_size = uni_path_len / 8 * 8;
-		if (copy_size < uni_path_len)
-			copy_size += 8;
-
-		copy_path = kzalloc(copy_size, GFP_KERNEL);
-		if (!copy_path)
-			return -ENOMEM;
-		memcpy((char *)copy_path, (const char *)path,
-			uni_path_len);
+
+	req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 4);
+
+	/* [MS-SMB2] 2.2.13 NameOffset:
+	 * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of
+	 * the SMB2 header, the file name includes a prefix that will
+	 * be processed during DFS name normalization as specified in
+	 * section 3.3.5.9. Otherwise, the file name is relative to
+	 * the share that is identified by the TreeId in the SMB2
+	 * header.
+	 */
+	if (tcon->share_flags & SHI1005_FLAGS_DFS) {
+		int name_len;
+
+		req->hdr.sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS;
+		rc = alloc_path_with_tree_prefix(&copy_path, &copy_size,
+						 &name_len,
+						 tcon->treeName, path);
+		if (rc)
+			return rc;
+		req->NameLength = cpu_to_le16(name_len * 2);
 		uni_path_len = copy_size;
 		path = copy_path;
+	} else {
+		uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
+		/* MUST set path len (NameLength) to 0 opening root of share */
+		req->NameLength = cpu_to_le16(uni_path_len - 2);
+		if (uni_path_len % 8 != 0) {
+			copy_size = roundup(uni_path_len, 8);
+			copy_path = kzalloc(copy_size, GFP_KERNEL);
+			if (!copy_path)
+				return -ENOMEM;
+			memcpy((char *)copy_path, (const char *)path,
+			       uni_path_len);
+			uni_path_len = copy_size;
+			path = copy_path;
+		}
 	}
 
 	iov[1].iov_len = uni_path_len;
Signature · 1 line
2.10.2

A message that is not in the archive (sent off-list or not yet received): 20170228184034.18771-7-aaptel-IBi9RG/[email protected]

#19 lore raw

Re: [PATCH v3 6/7] CIFS: use DFS pathnames in SMB2+ Create requests

2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel-IBi9RG/[email protected]>:

Quoted 148 lines

When connected to a DFS capable share, the client must set the SMB2_FLAGS_DFS_OPERATIONS flag in the SMB2 header and use DFS path names: "<server>\<share>\<path>" *without* leading \\.

Sources:

[MS-SMB2] 3.2.5.5 Receiving an SMB2 TREE_CONNECT Response

Quoted 2 lines

TreeConnect.IsDfsShare MUST be set to TRUE, if the SMB2_SHARE_CAP_DFS bit is set in the Capabilities field of the response.

[MS-SMB2] 3.2.4.3 Application Requests Opening a File

Quoted 2 lines

If TreeConnect.IsDfsShare is TRUE, the SMB2_FLAGS_DFS_OPERATIONS flag is set in the Flags field.

[MS-SMB2] 2.2.13 SMB2 CREATE Request, NameOffset:

Quoted 5 lines

If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of the SMB2 header, the file name includes a prefix that will be processed during DFS name normalization as specified in section 3.3.5.9. Otherwise, the file name is relative to the share that is identified by the TreeId in the SMB2 header.

---
 fs/cifs/smb2pdu.c | 93 +++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 77 insertions(+), 16 deletions(-)
fs/cifs/smb2pdu.c +77 -17
index 2fd93ee..059da05 100644
@@ -1528,6 +1528,48 @@ add_durable_context(struct kvec *iov, unsigned int *num_iovec,
        return 0;
 }
 
+static int
+alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
+                           const char *treename, const __le16 *path)
+{
+       int treename_len, path_len;
+       const __le16 sep[] = {cpu_to_le16('\\'), cpu_to_le16(0x0000)};
+
+       /*
+        * skip leading "\\"
+        */
+       treename_len = strlen(treename);
+       if (treename_len < 2 || !(treename[0] == '\\' && treename[1] == '\\'))
+               return -EINVAL;
+
+       treename += 2;
+       treename_len -= 2;
+
+       path_len = UniStrnlen((wchar_t *)path, PATH_MAX);
+
+       /*
+        * make room for one path separator between the treename and
+        * path
+        */
+       *out_len = treename_len + 1 + path_len;
+
+       /*
+        * final path needs to be null-terminated UTF16 with a
+        * size aligned to 8
+        */
+
+       *out_size = roundup((*out_len+1)*2, 8);
+       *out_path = kzalloc(*out_size, GFP_KERNEL);
+       if (!*out_path)
+               return -ENOMEM;
+
+       cifs_strtoUTF16(*out_path, treename, treename_len, load_nls_default());
+       UniStrcat(*out_path, sep);
+       UniStrcat(*out_path, path);
+
+       return 0;
+}
+
 int
 SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
          __u8 *oplock, struct smb2_file_all_info *buf,
@@ -1576,30 +1618,49 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
        req->ShareAccess = FILE_SHARE_ALL_LE;
        req->CreateDisposition = cpu_to_le32(oparms->disposition);
        req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK);
-       uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
-       /* do not count rfc1001 len field */
-       req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 4);
 
        iov[0].iov_base = (char *)req;
        /* 4 for rfc1002 length field */
        iov[0].iov_len = get_rfc1002_length(req) + 4;
-
-       /* MUST set path len (NameLength) to 0 opening root of share */
-       req->NameLength = cpu_to_le16(uni_path_len - 2);
        /* -1 since last byte is buf[0] which is sent below (path) */
        iov[0].iov_len--;
-       if (uni_path_len % 8 != 0) {
-               copy_size = uni_path_len / 8 * 8;
-               if (copy_size < uni_path_len)
-                       copy_size += 8;
-
-               copy_path = kzalloc(copy_size, GFP_KERNEL);
-               if (!copy_path)
-                       return -ENOMEM;
-               memcpy((char *)copy_path, (const char *)path,
-                       uni_path_len);
+
+       req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 4);
+
+       /* [MS-SMB2] 2.2.13 NameOffset:
+        * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of
+        * the SMB2 header, the file name includes a prefix that will
+        * be processed during DFS name normalization as specified in
+        * section 3.3.5.9. Otherwise, the file name is relative to
+        * the share that is identified by the TreeId in the SMB2
+        * header.
+        */
+       if (tcon->share_flags & SHI1005_FLAGS_DFS) {
+               int name_len;
+
+               req->hdr.sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS;
+               rc = alloc_path_with_tree_prefix(&copy_path, &copy_size,
+                                                &name_len,
+                                                tcon->treeName, path);
+               if (rc)
+                       return rc;
+               req->NameLength = cpu_to_le16(name_len * 2);
                uni_path_len = copy_size;
                path = copy_path;
+       } else {
+               uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
+               /* MUST set path len (NameLength) to 0 opening root of share */
+               req->NameLength = cpu_to_le16(uni_path_len - 2);
+               if (uni_path_len % 8 != 0) {
+                       copy_size = roundup(uni_path_len, 8);
+                       copy_path = kzalloc(copy_size, GFP_KERNEL);
+                       if (!copy_path)
+                               return -ENOMEM;
+                       memcpy((char *)copy_path, (const char *)path,
+                              uni_path_len);
+                       uni_path_len = copy_size;
+                       path = copy_path;
+               }
        }
 
        iov[1].iov_len = uni_path_len;
--

2.10.2

Quoted 4 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

Signature · 2 lines
Best regards,
Pavel Shilovsky
#20 lore raw

PATCH v3 7/7 CIFS: implement get_dfs_refer for SMB2+

In reply to a message not in the archive

in SMB2+ the get_dfs_refer operation uses a FSCTL. The request can be made on any Tree Connection according to the specs. Since Samba only accepted it on an IPC connection until recently, try that first.

https://lists.samba.org/archive/samba-technical/2017-February/118859.html

3.2.4.20.3 Application Requests DFS Referral Information:

Quoted 6 lines

The client MUST search for an existing Session and TreeConnect to any share on the server identified by ServerName for the user identified by UserCredentials. If no Session and TreeConnect are found, the client MUST establish a new Session and TreeConnect to IPC$ on the target server as described in section 3.2.4.2 using the supplied ServerName and UserCredentials.

---
 fs/cifs/smb2ops.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/cifs/smb2pdu.h |   8 +++++
 2 files changed, 108 insertions(+)
fs/cifs/smb2ops.c +100 -0
index 02937cc..fcd524d 100644
@@ -1104,6 +1104,102 @@ smb2_new_lease_key(struct cifs_fid *fid)
 	generate_random_uuid(fid->lease_key);
 }
 
+static int
+smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
+		   const char *search_name,
+		   struct dfs_info3_param **target_nodes,
+		   unsigned int *num_of_nodes,
+		   const struct nls_table *nls_codepage, int remap)
+{
+	int rc;
+	__le16 *utf16_path = NULL;
+	int utf16_path_len = 0;
+	struct cifs_tcon *tcon;
+	struct fsctl_get_dfs_referral_req *dfs_req = NULL;
+	struct get_dfs_referral_rsp *dfs_rsp = NULL;
+	u32 dfs_req_size = 0, dfs_rsp_size = 0;
+
+	cifs_dbg(FYI, "smb2_get_dfs_refer path <%s>\n", search_name);
+
+	/*
+	 * Use any tcon from the current session. Here, the first one.
+	 */
+	spin_lock(&cifs_tcp_ses_lock);
+	tcon = list_first_entry_or_null(&ses->tcon_list, struct cifs_tcon, tcon_list);
+	if (tcon)
+		tcon->tc_count++;
+	spin_unlock(&cifs_tcp_ses_lock);
+
+	if (!tcon) {
+		cifs_dbg(VFS, "session %p has no tcon available for a dfs referral request\n",
+			 ses);
+		rc = -ENOTCONN;
+		goto out;
+	}
+
+	utf16_path = cifs_strndup_to_utf16(search_name, PATH_MAX,
+					   &utf16_path_len,
+					   nls_codepage, remap);
+	if (!utf16_path) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	dfs_req_size = sizeof(*dfs_req) + utf16_path_len;
+	dfs_req = kzalloc(dfs_req_size, GFP_KERNEL);
+	if (!dfs_req) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	/* Highest DFS referral version understood */
+	dfs_req->MaxReferralLevel = cpu_to_le16(DFS_VERSION);
+
+	/* Path to resolve in an UTF-16 null-terminated string */
+	memcpy(dfs_req->RequestFileName, utf16_path, utf16_path_len);
+
+	do {
+		/* try first with IPC */
+		rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
+				FSCTL_DFS_GET_REFERRALS,
+				true /* is_fsctl */, true /* use_ipc */,
+				(char *)dfs_req, dfs_req_size,
+				(char **)&dfs_rsp, &dfs_rsp_size);
+		if (rc == -ENOTCONN) {
+			/* try with normal tcon */
+			rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
+					FSCTL_DFS_GET_REFERRALS,
+					true /* is_fsctl */, false /*use_ipc*/,
+					(char *)dfs_req, dfs_req_size,
+					(char **)&dfs_rsp, &dfs_rsp_size);
+		}
+	} while (rc == -EAGAIN);
+
+	if (rc) {
+		cifs_dbg(VFS, "ioctl error in smb2_get_dfs_refer rc=%d\n", rc);
+		goto out;
+	}
+
+	rc = parse_dfs_referrals(dfs_rsp, dfs_rsp_size,
+				 num_of_nodes, target_nodes,
+				 nls_codepage, remap, search_name,
+				 true /* is_unicode */);
+	if (rc) {
+		cifs_dbg(VFS, "parse error in smb2_get_dfs_refer rc=%d\n", rc);
+		goto out;
+	}
+
+ out:
+	if (tcon) {
+		spin_lock(&cifs_tcp_ses_lock);
+		tcon->tc_count--;
+		spin_unlock(&cifs_tcp_ses_lock);
+	}
+	kfree(utf16_path);
+	kfree(dfs_req);
+	kfree(dfs_rsp);
+	return rc;
+}
 #define SMB2_SYMLINK_STRUCT_SIZE \
 	(sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp))
 
@@ -2263,6 +2359,7 @@ struct smb_version_operations smb20_operations = {
 	.clone_range = smb2_clone_range,
 	.wp_retry_size = smb2_wp_retry_size,
 	.dir_needs_close = smb2_dir_needs_close,
+	.get_dfs_refer = smb2_get_dfs_refer,
 };
 
 struct smb_version_operations smb21_operations = {
@@ -2344,6 +2441,7 @@ struct smb_version_operations smb21_operations = {
 	.wp_retry_size = smb2_wp_retry_size,
 	.dir_needs_close = smb2_dir_needs_close,
 	.enum_snapshots = smb3_enum_snapshots,
+	.get_dfs_refer = smb2_get_dfs_refer,
 };
 
 struct smb_version_operations smb30_operations = {
@@ -2435,6 +2533,7 @@ struct smb_version_operations smb30_operations = {
 	.free_transform_rq = smb3_free_transform_rq,
 	.is_transform_hdr = smb3_is_transform_hdr,
 	.receive_transform = smb3_receive_transform,
+	.get_dfs_refer = smb2_get_dfs_refer,
 };
 
 #ifdef CONFIG_CIFS_SMB311
@@ -2527,6 +2626,7 @@ struct smb_version_operations smb311_operations = {
 	.free_transform_rq = smb3_free_transform_rq,
 	.is_transform_hdr = smb3_is_transform_hdr,
 	.receive_transform = smb3_receive_transform,
+	.get_dfs_refer = smb2_get_dfs_refer,
 };
 #endif /* CIFS_SMB311 */
 
fs/cifs/smb2pdu.h +8 -0
index c03b252..18700fd 100644
@@ -695,6 +695,14 @@ struct fsctl_get_integrity_information_rsp {
 /* Integrity flags for above */
 #define FSCTL_INTEGRITY_FLAG_CHECKSUM_ENFORCEMENT_OFF	0x00000001
 
+/* See MS-DFSC 2.2.2 */
+struct fsctl_get_dfs_referral_req {
+	__le16 MaxReferralLevel;
+	__u8 RequestFileName[];
+} __packed;
+
+/* DFS response is struct get_dfs_refer_rsp */
+
 /* See MS-SMB2 2.2.31.3 */
 struct network_resiliency_req {
 	__le32 Timeout;
Signature · 1 line
2.10.2

A message that is not in the archive (sent off-list or not yet received): 20170228184034.18771-8-aaptel-IBi9RG/[email protected]

Reviewed-by
#22 lore raw

Re: [PATCH v3 7/7] CIFS: implement get_dfs_refer for SMB2+

2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel-IBi9RG/[email protected]>:

Quoted 181 lines

in SMB2+ the get_dfs_refer operation uses a FSCTL. The request can be made on any Tree Connection according to the specs. Since Samba only accepted it on an IPC connection until recently, try that first.

https://lists.samba.org/archive/samba-technical/2017-February/118859.html

3.2.4.20.3 Application Requests DFS Referral Information:

Quoted 6 lines

The client MUST search for an existing Session and TreeConnect to any share on the server identified by ServerName for the user identified by UserCredentials. If no Session and TreeConnect are found, the client MUST establish a new Session and TreeConnect to IPC$ on the target server as described in section 3.2.4.2 using the supplied ServerName and UserCredentials.

---
 fs/cifs/smb2ops.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/cifs/smb2pdu.h |   8 +++++
 2 files changed, 108 insertions(+)
fs/cifs/smb2ops.c +100 -0
index 02937cc..fcd524d 100644
@@ -1104,6 +1104,102 @@ smb2_new_lease_key(struct cifs_fid *fid)
        generate_random_uuid(fid->lease_key);
 }
 
+static int
+smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
+                  const char *search_name,
+                  struct dfs_info3_param **target_nodes,
+                  unsigned int *num_of_nodes,
+                  const struct nls_table *nls_codepage, int remap)
+{
+       int rc;
+       __le16 *utf16_path = NULL;
+       int utf16_path_len = 0;
+       struct cifs_tcon *tcon;
+       struct fsctl_get_dfs_referral_req *dfs_req = NULL;
+       struct get_dfs_referral_rsp *dfs_rsp = NULL;
+       u32 dfs_req_size = 0, dfs_rsp_size = 0;
+
+       cifs_dbg(FYI, "smb2_get_dfs_refer path <%s>\n", search_name);
+
+       /*
+        * Use any tcon from the current session. Here, the first one.
+        */
+       spin_lock(&cifs_tcp_ses_lock);
+       tcon = list_first_entry_or_null(&ses->tcon_list, struct cifs_tcon, tcon_list);
+       if (tcon)
+               tcon->tc_count++;
+       spin_unlock(&cifs_tcp_ses_lock);
+
+       if (!tcon) {
+               cifs_dbg(VFS, "session %p has no tcon available for a dfs referral request\n",
+                        ses);
+               rc = -ENOTCONN;
+               goto out;
+       }
+
+       utf16_path = cifs_strndup_to_utf16(search_name, PATH_MAX,
+                                          &utf16_path_len,
+                                          nls_codepage, remap);
+       if (!utf16_path) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       dfs_req_size = sizeof(*dfs_req) + utf16_path_len;
+       dfs_req = kzalloc(dfs_req_size, GFP_KERNEL);
+       if (!dfs_req) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       /* Highest DFS referral version understood */
+       dfs_req->MaxReferralLevel = cpu_to_le16(DFS_VERSION);
+
+       /* Path to resolve in an UTF-16 null-terminated string */
+       memcpy(dfs_req->RequestFileName, utf16_path, utf16_path_len);
+
+       do {
+               /* try first with IPC */
+               rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
+                               FSCTL_DFS_GET_REFERRALS,
+                               true /* is_fsctl */, true /* use_ipc */,
+                               (char *)dfs_req, dfs_req_size,
+                               (char **)&dfs_rsp, &dfs_rsp_size);
+               if (rc == -ENOTCONN) {
+                       /* try with normal tcon */
+                       rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
+                                       FSCTL_DFS_GET_REFERRALS,
+                                       true /* is_fsctl */, false /*use_ipc*/,
+                                       (char *)dfs_req, dfs_req_size,
+                                       (char **)&dfs_rsp, &dfs_rsp_size);
+               }
+       } while (rc == -EAGAIN);
+
+       if (rc) {
+               cifs_dbg(VFS, "ioctl error in smb2_get_dfs_refer rc=%d\n", rc);
+               goto out;
+       }
+
+       rc = parse_dfs_referrals(dfs_rsp, dfs_rsp_size,
+                                num_of_nodes, target_nodes,
+                                nls_codepage, remap, search_name,
+                                true /* is_unicode */);
+       if (rc) {
+               cifs_dbg(VFS, "parse error in smb2_get_dfs_refer rc=%d\n", rc);
+               goto out;
+       }
+
+ out:
+       if (tcon) {
+               spin_lock(&cifs_tcp_ses_lock);
+               tcon->tc_count--;
+               spin_unlock(&cifs_tcp_ses_lock);
+       }
+       kfree(utf16_path);
+       kfree(dfs_req);
+       kfree(dfs_rsp);
+       return rc;
+}
 #define SMB2_SYMLINK_STRUCT_SIZE \
        (sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp))
 
@@ -2263,6 +2359,7 @@ struct smb_version_operations smb20_operations = {
        .clone_range = smb2_clone_range,
        .wp_retry_size = smb2_wp_retry_size,
        .dir_needs_close = smb2_dir_needs_close,
+       .get_dfs_refer = smb2_get_dfs_refer,
 };
 
 struct smb_version_operations smb21_operations = {
@@ -2344,6 +2441,7 @@ struct smb_version_operations smb21_operations = {
        .wp_retry_size = smb2_wp_retry_size,
        .dir_needs_close = smb2_dir_needs_close,
        .enum_snapshots = smb3_enum_snapshots,
+       .get_dfs_refer = smb2_get_dfs_refer,
 };
 
 struct smb_version_operations smb30_operations = {
@@ -2435,6 +2533,7 @@ struct smb_version_operations smb30_operations = {
        .free_transform_rq = smb3_free_transform_rq,
        .is_transform_hdr = smb3_is_transform_hdr,
        .receive_transform = smb3_receive_transform,
+       .get_dfs_refer = smb2_get_dfs_refer,
 };
 
 #ifdef CONFIG_CIFS_SMB311
@@ -2527,6 +2626,7 @@ struct smb_version_operations smb311_operations = {
        .free_transform_rq = smb3_free_transform_rq,
        .is_transform_hdr = smb3_is_transform_hdr,
        .receive_transform = smb3_receive_transform,
+       .get_dfs_refer = smb2_get_dfs_refer,
 };
 #endif /* CIFS_SMB311 */
 
fs/cifs/smb2pdu.h +8 -1
index c03b252..18700fd 100644
@@ -695,6 +695,14 @@ struct fsctl_get_integrity_information_rsp {
 /* Integrity flags for above */
 #define FSCTL_INTEGRITY_FLAG_CHECKSUM_ENFORCEMENT_OFF  0x00000001
 
+/* See MS-DFSC 2.2.2 */
+struct fsctl_get_dfs_referral_req {
+       __le16 MaxReferralLevel;
+       __u8 RequestFileName[];
+} __packed;
+
+/* DFS response is struct get_dfs_refer_rsp */
+
 /* See MS-SMB2 2.2.31.3 */
 struct network_resiliency_req {
        __le32 Timeout;
--

2.10.2

Quoted 4 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

Signature · 2 lines
Best regards,
Pavel Shilovsky
#23 lore raw

Re: [PATCH v3 7/7] CIFS: implement get_dfs_refer for SMB2+

In reply to a message not in the archive

2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel-IBi9RG/[email protected]>:

Quoted 93 lines

in SMB2+ the get_dfs_refer operation uses a FSCTL. The request can be made on any Tree Connection according to the specs. Since Samba only accepted it on an IPC connection until recently, try that first.

https://lists.samba.org/archive/samba-technical/2017-February/118859.html

3.2.4.20.3 Application Requests DFS Referral Information:

Quoted 6 lines

The client MUST search for an existing Session and TreeConnect to any share on the server identified by ServerName for the user identified by UserCredentials. If no Session and TreeConnect are found, the client MUST establish a new Session and TreeConnect to IPC$ on the target server as described in section 3.2.4.2 using the supplied ServerName and UserCredentials.

---
 fs/cifs/smb2ops.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/cifs/smb2pdu.h |   8 +++++
 2 files changed, 108 insertions(+)
fs/cifs/smb2ops.c +65 -0
index 02937cc..fcd524d 100644
@@ -1104,6 +1104,102 @@ smb2_new_lease_key(struct cifs_fid *fid)
        generate_random_uuid(fid->lease_key);
 }
 
+static int
+smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
+                  const char *search_name,
+                  struct dfs_info3_param **target_nodes,
+                  unsigned int *num_of_nodes,
+                  const struct nls_table *nls_codepage, int remap)
+{
+       int rc;
+       __le16 *utf16_path = NULL;
+       int utf16_path_len = 0;
+       struct cifs_tcon *tcon;
+       struct fsctl_get_dfs_referral_req *dfs_req = NULL;
+       struct get_dfs_referral_rsp *dfs_rsp = NULL;
+       u32 dfs_req_size = 0, dfs_rsp_size = 0;
+
+       cifs_dbg(FYI, "smb2_get_dfs_refer path <%s>\n", search_name);
+
+       /*
+        * Use any tcon from the current session. Here, the first one.
+        */
+       spin_lock(&cifs_tcp_ses_lock);
+       tcon = list_first_entry_or_null(&ses->tcon_list, struct cifs_tcon, tcon_list);
+       if (tcon)
+               tcon->tc_count++;
+       spin_unlock(&cifs_tcp_ses_lock);
+
+       if (!tcon) {
+               cifs_dbg(VFS, "session %p has no tcon available for a dfs referral request\n",
+                        ses);
+               rc = -ENOTCONN;
+               goto out;
+       }
+
+       utf16_path = cifs_strndup_to_utf16(search_name, PATH_MAX,
+                                          &utf16_path_len,
+                                          nls_codepage, remap);
+       if (!utf16_path) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       dfs_req_size = sizeof(*dfs_req) + utf16_path_len;
+       dfs_req = kzalloc(dfs_req_size, GFP_KERNEL);
+       if (!dfs_req) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       /* Highest DFS referral version understood */
+       dfs_req->MaxReferralLevel = cpu_to_le16(DFS_VERSION);
+
+       /* Path to resolve in an UTF-16 null-terminated string */
+       memcpy(dfs_req->RequestFileName, utf16_path, utf16_path_len);
+
+       do {
+               /* try first with IPC */
+               rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
+                               FSCTL_DFS_GET_REFERRALS,
+                               true /* is_fsctl */, true /* use_ipc */,
+                               (char *)dfs_req, dfs_req_size,
+                               (char **)&dfs_rsp, &dfs_rsp_size);
+               if (rc == -ENOTCONN) {
+                       /* try with normal tcon */
+                       rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
+                                       FSCTL_DFS_GET_REFERRALS,

fs/cifs/smb2ops.c smb2_new_lease_key(struct cifs_fid *fid) · line 1181

Quoted 10 lines
fs/cifs/smb2ops.c +10 -0
+                                       true /* is_fsctl */, false /*use_ipc*/,
+                                       (char *)dfs_req, dfs_req_size,
+                                       (char **)&dfs_rsp, &dfs_rsp_size);
+               }
+       } while (rc == -EAGAIN);
+
+       if (rc) {
+               cifs_dbg(VFS, "ioctl error in smb2_get_dfs_refer rc=%d\n", rc);
+               goto out;
+       }

During the local testing with cthon and xfstests with DFS is being involved I see a bunch of error messages like the one below:

CIFS VFS: ioctl error in smb2_get_dfs_refer rc=-2

Should we lower a debug level to FYI here?

--
Best regards,
Pavel Shilovsky

A message that is not in the archive (sent off-list or not yet received): CAKywueTx4b4+MDYeTm+jBsUZfmYe+Sa1HT9cUx-d4043hKxr+A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org

#25 lore raw

Re: [PATCH v3 7/7] CIFS: implement get_dfs_refer for SMB2+

2017-06-22 16:03 GMT-07:00 Pavel Shilovsky <[email protected]>:

Quoted 98 lines

2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel-IBi9RG/[email protected]>:

Quoted 97 lines

in SMB2+ the get_dfs_refer operation uses a FSCTL. The request can be made on any Tree Connection according to the specs. Since Samba only accepted it on an IPC connection until recently, try that first.

https://lists.samba.org/archive/samba-technical/2017-February/118859.html

3.2.4.20.3 Application Requests DFS Referral Information:

Quoted 6 lines

The client MUST search for an existing Session and TreeConnect to any share on the server identified by ServerName for the user identified by UserCredentials. If no Session and TreeConnect are found, the client MUST establish a new Session and TreeConnect to IPC$ on the target server as described in section 3.2.4.2 using the supplied ServerName and UserCredentials.

---
 fs/cifs/smb2ops.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/cifs/smb2pdu.h |   8 +++++
 2 files changed, 108 insertions(+)
fs/cifs/smb2ops.c +69 -0
index 02937cc..fcd524d 100644
@@ -1104,6 +1104,102 @@ smb2_new_lease_key(struct cifs_fid *fid)
        generate_random_uuid(fid->lease_key);
 }
 
+static int
+smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
+                  const char *search_name,
+                  struct dfs_info3_param **target_nodes,
+                  unsigned int *num_of_nodes,
+                  const struct nls_table *nls_codepage, int remap)
+{
+       int rc;
+       __le16 *utf16_path = NULL;
+       int utf16_path_len = 0;
+       struct cifs_tcon *tcon;
+       struct fsctl_get_dfs_referral_req *dfs_req = NULL;
+       struct get_dfs_referral_rsp *dfs_rsp = NULL;
+       u32 dfs_req_size = 0, dfs_rsp_size = 0;
+
+       cifs_dbg(FYI, "smb2_get_dfs_refer path <%s>\n", search_name);
+
+       /*
+        * Use any tcon from the current session. Here, the first one.
+        */
+       spin_lock(&cifs_tcp_ses_lock);
+       tcon = list_first_entry_or_null(&ses->tcon_list, struct cifs_tcon, tcon_list);
+       if (tcon)
+               tcon->tc_count++;
+       spin_unlock(&cifs_tcp_ses_lock);
+
+       if (!tcon) {
+               cifs_dbg(VFS, "session %p has no tcon available for a dfs referral request\n",
+                        ses);
+               rc = -ENOTCONN;
+               goto out;
+       }
+
+       utf16_path = cifs_strndup_to_utf16(search_name, PATH_MAX,
+                                          &utf16_path_len,
+                                          nls_codepage, remap);
+       if (!utf16_path) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       dfs_req_size = sizeof(*dfs_req) + utf16_path_len;
+       dfs_req = kzalloc(dfs_req_size, GFP_KERNEL);
+       if (!dfs_req) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       /* Highest DFS referral version understood */
+       dfs_req->MaxReferralLevel = cpu_to_le16(DFS_VERSION);
+
+       /* Path to resolve in an UTF-16 null-terminated string */
+       memcpy(dfs_req->RequestFileName, utf16_path, utf16_path_len);
+
+       do {
+               /* try first with IPC */
+               rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
+                               FSCTL_DFS_GET_REFERRALS,
+                               true /* is_fsctl */, true /* use_ipc */,
+                               (char *)dfs_req, dfs_req_size,
+                               (char **)&dfs_rsp, &dfs_rsp_size);
+               if (rc == -ENOTCONN) {
+                       /* try with normal tcon */
+                       rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
+                                       FSCTL_DFS_GET_REFERRALS,
+                                       true /* is_fsctl */, false /*use_ipc*/,
+                                       (char *)dfs_req, dfs_req_size,
+                                       (char **)&dfs_rsp, &dfs_rsp_size);
+               }
Quoted 8 lines

fs/cifs/smb2ops.c smb2_new_lease_key(struct cifs_fid *fid) · line 1181

Quoted 6 lines
fs/cifs/smb2ops.c +6 -0
+       } while (rc == -EAGAIN);
+
+       if (rc) {
+               cifs_dbg(VFS, "ioctl error in smb2_get_dfs_refer rc=%d\n", rc);
+               goto out;
+       }

During the local testing with cthon and xfstests with DFS is being

                                                                             ^^^

Sorry, the above should be "without DFS being involved".

--
Best regards,
Pavel Shilovsky

A message that is not in the archive (sent off-list or not yet received): CAKywueSy1Y1PNLHkRnDbxgc+Kf1qe_H65DPkWpX-=k=Enu-VUw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org

#27 lore raw

Re: [PATCH v3 7/7] CIFS: implement get_dfs_refer for SMB2+

Hi Pavel,

Quoted 1 line · Pavel Shilovsky
Quoted 1 line

Should we lower a debug level to FYI here?

Sounds good.

Signature · 4 lines
Aurélien Aptel / SUSE Labs Samba Team
GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
SUSE Linux GmbH, Maxfeldstraße 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
#28 lore raw
In reply to a message not in the archive

Merged v4 of this into cifs-2.6.git for-next

Quoted 101 lines · Aurelien Aptel

This series of patch tries to implement the get_dfs_refer operation for SMB2+.

In SMB2+, DFS resolving is now done through an FSCTL (patch #7). The relevant Microsoft specifications for this are:

MS-SMB2: 3.2.4.20 Application Requests an IO Control Code Operation
MS-SMB2: 3.2.4.20.3 Application Requests DFS Referral Information
MS-SMB2: 3.2.5.14 Receiving an SMB2 IOCTL Response
MS-SMB2: 3.2.5.14.4 Handling a DFS Referral Information Response

MS-DFSC: 2.2 Message Syntax (but really the whole document is useful)

The DFS response payload however is identical. Patch #1 moves the DFS response parsing out of SMB1 code and makes it work on any version of SMB.

DFS code has 2 "main" entry points: initial mounting and automount (when a DFS link is accessed/traversed).

When automounting, cifs.ko calls build_path_from_dentry() which only makes treename-prefixed paths when the tcon has the SMB_IN_DFS_SHARE. This flag is checked in tcon->Flags which is never touched by SMB2 code as it sets tcon->share_flags on connexion.

* CIFS requires to prefix all pathnames with the treename prefix when connected to a DFS server, so the current build_path_from_dentry() makes sense for CIFS.
* For SMB2+ it seems only the Create request requires the treename prefix. Simply making the function check for both CIFS SMB2+ flag for DFS to add a prefix does not work as the server has different expectations about which packet can have/require a DFS pathname.

Patch #2 adds build_path_from_dentry_optional_prefix() with an extra bool arg to decide to prefix or not. The automount code path always ask for it. Patch #6 modifies SMB2_open() to add the treename prefix to the given path if the server requires it.

We try to use an IPC connection first for the DFS request. Patch #3 adds a flag to SMB2_ioctl() to force the usage of ipc_tid. The SMB2 get_dfs_refer operation tries with the flag set and fallback to a share connection if that doesn't work.

Patch #4 updates the type of ipc_tid from u16 to u32 as that is what a Tid is in SMB2+. This is correct and really needed since in SMB2 Samba doesn't use sequential tree id but random ones in the 32bit space.

As part of the mouting process a IPC tcon is made (I suspect we don't need it anymore in SMB3). This tcon doesn't respect the signing requirement. This is fixed by patch #5.

Finally the SMB2+ implementation of the get_dfs_referral operation is added in all supported SMB versions in patch #7.

I've sucessfuly tested this (v1.0 and v3.0) against a Windows Server 2016 DFS setup and a Samba DFS setup.

Samba used to only accepts DFS referrals requests on an IPC connexion, which is in violation of the spec. A patch was sent on samba-technical and merged. It is not required anymore.

https://lists.samba.org/archive/samba-technical/2017-February/118859.html

Changes since v2:

* add use_ipc flag to SMB2_open()
* make smb2_get_dfs_refer() try IPC connections first
* use first_entry_or_null() instead of first_entry() on the tcon list
* protect tcon list access with spinlock
* add inc/dec of the tcon reference number, protected by spinlock

Aurelien Aptel (7):
CIFS: move DFS response parsing out of SMB1 code
CIFS: add build_path_from_dentry_optional_prefix()
CIFS: add use_ipc flag to SMB2_ioctl()
CIFS: let ses->ipc_tid hold smb2 TreeIds
CIFS: set signing flag in SMB2+ TreeConnect if needed
CIFS: use DFS pathnames in SMB2+ Create requests
CIFS: implement get_dfs_refer for SMB2+

 fs/cifs/cifs_dfs_ref.c |   4 +-
 fs/cifs/cifsglob.h     |   2 +-
 fs/cifs/cifspdu.h      |  16 ++++---
 fs/cifs/cifsproto.h    |   7 +++
 fs/cifs/cifssmb.c      | 119 ++---------------------------------------------
 fs/cifs/dir.c          |  13 +++++-
 fs/cifs/misc.c         | 105 +++++++++++++++++++++++++++++++++++++++++
 fs/cifs/smb2file.c     |   3 +-
 fs/cifs/smb2ops.c      | 123 ++++++++++++++++++++++++++++++++++++++++++++++---
 fs/cifs/smb2pdu.c      | 114 +++++++++++++++++++++++++++++++++++++--------
 fs/cifs/smb2pdu.h      |   8 ++++
 fs/cifs/smb2proto.h    |   3 +-
 12 files changed, 365 insertions(+), 152 deletions(-)

--
2.10.2

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

Signature · 3 lines
Thanks,

Steve