All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/7] Add support for DFS in SMB2+
@ 2017-02-28 18:40 Aurelien Aptel
       [not found] ` <20170228184034.18771-1-aaptel-IBi9RG/b67k@public.gmane.org>
  0 siblings, 1 reply; 19+ messages in thread
From: Aurelien Aptel @ 2017-02-28 18:40 UTC (permalink / raw)
  To: linux-cifs-u79uwXL29TY76Z2rM5mHXA; +Cc: 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

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v3 1/7] CIFS: move DFS response parsing out of SMB1 code
       [not found] ` <20170228184034.18771-1-aaptel-IBi9RG/b67k@public.gmane.org>
@ 2017-02-28 18:40   ` Aurelien Aptel
       [not found]     ` <20170228184034.18771-2-aaptel-IBi9RG/b67k@public.gmane.org>
  2017-02-28 18:40   ` [PATCH v3 2/7] CIFS: add build_path_from_dentry_optional_prefix() Aurelien Aptel
                     ` (6 subsequent siblings)
  7 siblings, 1 reply; 19+ messages in thread
From: Aurelien Aptel @ 2017-02-28 18:40 UTC (permalink / raw)
  To: linux-cifs-u79uwXL29TY76Z2rM5mHXA; +Cc: Aurelien Aptel

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).

Signed-off-by: Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>
---
 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(-)

diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
index f5b8730..1ce733f 100644
--- a/fs/cifs/cifspdu.h
+++ b/fs/cifs/cifspdu.h
@@ -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 */
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 406d2c1..c097830 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -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);
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index f5099fb..5005c79 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -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);
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index c672915..d3fb115 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -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

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v3 2/7] CIFS: add build_path_from_dentry_optional_prefix()
       [not found] ` <20170228184034.18771-1-aaptel-IBi9RG/b67k@public.gmane.org>
  2017-02-28 18:40   ` [PATCH v3 1/7] CIFS: move DFS response parsing out of SMB1 code Aurelien Aptel
@ 2017-02-28 18:40   ` Aurelien Aptel
       [not found]     ` <20170228184034.18771-3-aaptel-IBi9RG/b67k@public.gmane.org>
  2017-02-28 18:40   ` [PATCH v3 3/7] CIFS: add use_ipc flag to SMB2_ioctl() Aurelien Aptel
                     ` (5 subsequent siblings)
  7 siblings, 1 reply; 19+ messages in thread
From: Aurelien Aptel @ 2017-02-28 18:40 UTC (permalink / raw)
  To: linux-cifs-u79uwXL29TY76Z2rM5mHXA; +Cc: Aurelien Aptel

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.

Signed-off-by: Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>
---
 fs/cifs/cifs_dfs_ref.c |  4 +++-
 fs/cifs/cifsproto.h    |  2 ++
 fs/cifs/dir.c          | 13 ++++++++++++-
 3 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
index ec9dbbc..8a9ebfd 100644
--- a/fs/cifs/cifs_dfs_ref.c
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -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;
 
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index c097830..9ee46c1 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -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,
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 2c227a9..56366e9 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -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

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v3 3/7] CIFS: add use_ipc flag to SMB2_ioctl()
       [not found] ` <20170228184034.18771-1-aaptel-IBi9RG/b67k@public.gmane.org>
  2017-02-28 18:40   ` [PATCH v3 1/7] CIFS: move DFS response parsing out of SMB1 code Aurelien Aptel
  2017-02-28 18:40   ` [PATCH v3 2/7] CIFS: add build_path_from_dentry_optional_prefix() Aurelien Aptel
@ 2017-02-28 18:40   ` Aurelien Aptel
       [not found]     ` <20170228184034.18771-4-aaptel-IBi9RG/b67k@public.gmane.org>
  2017-02-28 18:40   ` [PATCH v3 4/7] CIFS: let ses->ipc_tid hold smb2 TreeIds Aurelien Aptel
                     ` (4 subsequent siblings)
  7 siblings, 1 reply; 19+ messages in thread
From: Aurelien Aptel @ 2017-02-28 18:40 UTC (permalink / raw)
  To: linux-cifs-u79uwXL29TY76Z2rM5mHXA; +Cc: Aurelien Aptel

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

Signed-off-by: Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>
---
 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(-)

diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
index b2aff0c..b4b1f03 100644
--- a/fs/cifs/smb2file.c
+++ b/fs/cifs/smb2file.c
@@ -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) {
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index a44b4db..02937cc 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -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;
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index ad83b3d..8c4532d 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -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);
 
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 85fc7a7..11d9f30 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -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

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v3 4/7] CIFS: let ses->ipc_tid hold smb2 TreeIds
       [not found] ` <20170228184034.18771-1-aaptel-IBi9RG/b67k@public.gmane.org>
                     ` (2 preceding siblings ...)
  2017-02-28 18:40   ` [PATCH v3 3/7] CIFS: add use_ipc flag to SMB2_ioctl() Aurelien Aptel
@ 2017-02-28 18:40   ` Aurelien Aptel
       [not found]     ` <20170228184034.18771-5-aaptel-IBi9RG/b67k@public.gmane.org>
  2017-02-28 18:40   ` [PATCH v3 5/7] CIFS: set signing flag in SMB2+ TreeConnect if needed Aurelien Aptel
                     ` (3 subsequent siblings)
  7 siblings, 1 reply; 19+ messages in thread
From: Aurelien Aptel @ 2017-02-28 18:40 UTC (permalink / raw)
  To: linux-cifs-u79uwXL29TY76Z2rM5mHXA; +Cc: Aurelien Aptel

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.

Signed-off-by: Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>
---
 fs/cifs/cifsglob.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 1a90bb3..af224cd 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -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

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v3 5/7] CIFS: set signing flag in SMB2+ TreeConnect if needed
       [not found] ` <20170228184034.18771-1-aaptel-IBi9RG/b67k@public.gmane.org>
                     ` (3 preceding siblings ...)
  2017-02-28 18:40   ` [PATCH v3 4/7] CIFS: let ses->ipc_tid hold smb2 TreeIds Aurelien Aptel
@ 2017-02-28 18:40   ` Aurelien Aptel
       [not found]     ` <20170228184034.18771-6-aaptel-IBi9RG/b67k@public.gmane.org>
  2017-02-28 18:40   ` [PATCH v3 6/7] CIFS: use DFS pathnames in SMB2+ Create requests Aurelien Aptel
                     ` (2 subsequent siblings)
  7 siblings, 1 reply; 19+ messages in thread
From: Aurelien Aptel @ 2017-02-28 18:40 UTC (permalink / raw)
  To: linux-cifs-u79uwXL29TY76Z2rM5mHXA; +Cc: Aurelien Aptel

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.

Signed-off-by: Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>
---
 fs/cifs/smb2pdu.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 8c4532d..2fd93ee 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -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

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v3 6/7] CIFS: use DFS pathnames in SMB2+ Create requests
       [not found] ` <20170228184034.18771-1-aaptel-IBi9RG/b67k@public.gmane.org>
                     ` (4 preceding siblings ...)
  2017-02-28 18:40   ` [PATCH v3 5/7] CIFS: set signing flag in SMB2+ TreeConnect if needed Aurelien Aptel
@ 2017-02-28 18:40   ` Aurelien Aptel
       [not found]     ` <20170228184034.18771-7-aaptel-IBi9RG/b67k@public.gmane.org>
  2017-02-28 18:40   ` [PATCH v3 7/7] CIFS: implement get_dfs_refer for SMB2+ Aurelien Aptel
  2017-03-02  4:41   ` [PATCH v3 0/7] Add support for DFS in SMB2+ Steve French
  7 siblings, 1 reply; 19+ messages in thread
From: Aurelien Aptel @ 2017-02-28 18:40 UTC (permalink / raw)
  To: linux-cifs-u79uwXL29TY76Z2rM5mHXA; +Cc: Aurelien Aptel

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
> 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
> 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:
> 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.

Signed-off-by: Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>
---
 fs/cifs/smb2pdu.c | 93 +++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 77 insertions(+), 16 deletions(-)

diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 2fd93ee..059da05 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -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

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v3 7/7] CIFS: implement get_dfs_refer for SMB2+
       [not found] ` <20170228184034.18771-1-aaptel-IBi9RG/b67k@public.gmane.org>
                     ` (5 preceding siblings ...)
  2017-02-28 18:40   ` [PATCH v3 6/7] CIFS: use DFS pathnames in SMB2+ Create requests Aurelien Aptel
@ 2017-02-28 18:40   ` Aurelien Aptel
       [not found]     ` <20170228184034.18771-8-aaptel-IBi9RG/b67k@public.gmane.org>
  2017-03-02  4:41   ` [PATCH v3 0/7] Add support for DFS in SMB2+ Steve French
  7 siblings, 1 reply; 19+ messages in thread
From: Aurelien Aptel @ 2017-02-28 18:40 UTC (permalink / raw)
  To: linux-cifs-u79uwXL29TY76Z2rM5mHXA; +Cc: Aurelien Aptel

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:
> 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.

Signed-off-by: Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>
---
 fs/cifs/smb2ops.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/cifs/smb2pdu.h |   8 +++++
 2 files changed, 108 insertions(+)

diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 02937cc..fcd524d 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -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 */
 
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index c03b252..18700fd 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -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

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v3 1/7] CIFS: move DFS response parsing out of SMB1 code
       [not found]     ` <20170228184034.18771-2-aaptel-IBi9RG/b67k@public.gmane.org>
@ 2017-03-01  1:58       ` Pavel Shilovsky
  0 siblings, 0 replies; 19+ messages in thread
From: Pavel Shilovsky @ 2017-03-01  1:58 UTC (permalink / raw)
  To: Aurelien Aptel; +Cc: linux-cifs

2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>:
> 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).
>
> Signed-off-by: Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>
> ---
>  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(-)
>
> diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
> index f5b8730..1ce733f 100644
> --- a/fs/cifs/cifspdu.h
> +++ b/fs/cifs/cifspdu.h
> @@ -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 */
> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> index 406d2c1..c097830 100644
> --- a/fs/cifs/cifsproto.h
> +++ b/fs/cifs/cifsproto.h
> @@ -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);
> diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
> index f5099fb..5005c79 100644
> --- a/fs/cifs/cifssmb.c
> +++ b/fs/cifs/cifssmb.c
> @@ -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);
> diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
> index c672915..d3fb115 100644
> --- a/fs/cifs/misc.c
> +++ b/fs/cifs/misc.c
> @@ -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
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

Acked-by: Pavel Shilovsky <pshilov-0li6OtcxBFHby3iVrkZq2A@public.gmane.org>

-- 
Best regards,
Pavel Shilovsky

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v3 2/7] CIFS: add build_path_from_dentry_optional_prefix()
       [not found]     ` <20170228184034.18771-3-aaptel-IBi9RG/b67k@public.gmane.org>
@ 2017-03-01  2:35       ` Pavel Shilovsky
  0 siblings, 0 replies; 19+ messages in thread
From: Pavel Shilovsky @ 2017-03-01  2:35 UTC (permalink / raw)
  To: Aurelien Aptel; +Cc: linux-cifs

2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>:
> 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.
>
> Signed-off-by: Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>
> ---
>  fs/cifs/cifs_dfs_ref.c |  4 +++-
>  fs/cifs/cifsproto.h    |  2 ++
>  fs/cifs/dir.c          | 13 ++++++++++++-
>  3 files changed, 17 insertions(+), 2 deletions(-)
>
> diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
> index ec9dbbc..8a9ebfd 100644
> --- a/fs/cifs/cifs_dfs_ref.c
> +++ b/fs/cifs/cifs_dfs_ref.c
> @@ -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;
>
> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> index c097830..9ee46c1 100644
> --- a/fs/cifs/cifsproto.h
> +++ b/fs/cifs/cifsproto.h
> @@ -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,
> diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
> index 2c227a9..56366e9 100644
> --- a/fs/cifs/dir.c
> +++ b/fs/cifs/dir.c
> @@ -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
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

Acked-by: Pavel Shilovsky <pshilov-0li6OtcxBFHby3iVrkZq2A@public.gmane.org>

-- 
Best regards,
Pavel Shilovsky

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v3 3/7] CIFS: add use_ipc flag to SMB2_ioctl()
       [not found]     ` <20170228184034.18771-4-aaptel-IBi9RG/b67k@public.gmane.org>
@ 2017-03-01  2:36       ` Pavel Shilovsky
  0 siblings, 0 replies; 19+ messages in thread
From: Pavel Shilovsky @ 2017-03-01  2:36 UTC (permalink / raw)
  To: Aurelien Aptel; +Cc: linux-cifs

2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>:
> when set, use the session IPC tree id instead of the tid in the provided
> tcon.
>
> Signed-off-by: Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>
> ---
>  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(-)
>
> diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
> index b2aff0c..b4b1f03 100644
> --- a/fs/cifs/smb2file.c
> +++ b/fs/cifs/smb2file.c
> @@ -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) {
> diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
> index a44b4db..02937cc 100644
> --- a/fs/cifs/smb2ops.c
> +++ b/fs/cifs/smb2ops.c
> @@ -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;
> diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> index ad83b3d..8c4532d 100644
> --- a/fs/cifs/smb2pdu.c
> +++ b/fs/cifs/smb2pdu.c
> @@ -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);
>
> diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
> index 85fc7a7..11d9f30 100644
> --- a/fs/cifs/smb2proto.h
> +++ b/fs/cifs/smb2proto.h
> @@ -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
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reviewed-by: Pavel Shilovsky <pshilov-0li6OtcxBFHby3iVrkZq2A@public.gmane.org>

-- 
Best regards,
Pavel Shilovsky

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v3 4/7] CIFS: let ses->ipc_tid hold smb2 TreeIds
       [not found]     ` <20170228184034.18771-5-aaptel-IBi9RG/b67k@public.gmane.org>
@ 2017-03-01  2:36       ` Pavel Shilovsky
  0 siblings, 0 replies; 19+ messages in thread
From: Pavel Shilovsky @ 2017-03-01  2:36 UTC (permalink / raw)
  To: Aurelien Aptel; +Cc: linux-cifs

2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>:
> 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.
>
> Signed-off-by: Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>
> ---
>  fs/cifs/cifsglob.h | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> index 1a90bb3..af224cd 100644
> --- a/fs/cifs/cifsglob.h
> +++ b/fs/cifs/cifsglob.h
> @@ -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
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reviewed-by: Pavel Shilovsky <pshilov-0li6OtcxBFHby3iVrkZq2A@public.gmane.org>

-- 
Best regards,
Pavel Shilovsky

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v3 5/7] CIFS: set signing flag in SMB2+ TreeConnect if needed
       [not found]     ` <20170228184034.18771-6-aaptel-IBi9RG/b67k@public.gmane.org>
@ 2017-03-01  2:37       ` Pavel Shilovsky
  0 siblings, 0 replies; 19+ messages in thread
From: Pavel Shilovsky @ 2017-03-01  2:37 UTC (permalink / raw)
  To: Aurelien Aptel; +Cc: linux-cifs

2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>:
> 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.
>
> Signed-off-by: Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>
> ---
>  fs/cifs/smb2pdu.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> index 8c4532d..2fd93ee 100644
> --- a/fs/cifs/smb2pdu.c
> +++ b/fs/cifs/smb2pdu.c
> @@ -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
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reviewed-by: Pavel Shilovsky <pshilov-0li6OtcxBFHby3iVrkZq2A@public.gmane.org>

-- 
Best regards,
Pavel Shilovsky

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v3 6/7] CIFS: use DFS pathnames in SMB2+ Create requests
       [not found]     ` <20170228184034.18771-7-aaptel-IBi9RG/b67k@public.gmane.org>
@ 2017-03-01  2:40       ` Pavel Shilovsky
  0 siblings, 0 replies; 19+ messages in thread
From: Pavel Shilovsky @ 2017-03-01  2:40 UTC (permalink / raw)
  To: Aurelien Aptel; +Cc: linux-cifs

2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>:
> 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
>> 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
>> 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:
>> 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.
>
> Signed-off-by: Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>
> ---
>  fs/cifs/smb2pdu.c | 93 +++++++++++++++++++++++++++++++++++++++++++++----------
>  1 file changed, 77 insertions(+), 16 deletions(-)
>
> diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> index 2fd93ee..059da05 100644
> --- a/fs/cifs/smb2pdu.c
> +++ b/fs/cifs/smb2pdu.c
> @@ -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
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

Acked-by: Pavel Shilovsky <pshilov-0li6OtcxBFHby3iVrkZq2A@public.gmane.org>

-- 
Best regards,
Pavel Shilovsky

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v3 7/7] CIFS: implement get_dfs_refer for SMB2+
       [not found]     ` <20170228184034.18771-8-aaptel-IBi9RG/b67k@public.gmane.org>
@ 2017-03-01  2:42       ` Pavel Shilovsky
  2017-06-22 23:03       ` Pavel Shilovsky
  1 sibling, 0 replies; 19+ messages in thread
From: Pavel Shilovsky @ 2017-03-01  2:42 UTC (permalink / raw)
  To: Aurelien Aptel; +Cc: linux-cifs

2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>:
> 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:
>> 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.
>
> Signed-off-by: Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>
> ---
>  fs/cifs/smb2ops.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/cifs/smb2pdu.h |   8 +++++
>  2 files changed, 108 insertions(+)
>
> diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
> index 02937cc..fcd524d 100644
> --- a/fs/cifs/smb2ops.c
> +++ b/fs/cifs/smb2ops.c
> @@ -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 */
>
> diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
> index c03b252..18700fd 100644
> --- a/fs/cifs/smb2pdu.h
> +++ b/fs/cifs/smb2pdu.h
> @@ -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
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reviewed-by: Pavel Shilovsky <pshilov-0li6OtcxBFHby3iVrkZq2A@public.gmane.org>

-- 
Best regards,
Pavel Shilovsky

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v3 0/7] Add support for DFS in SMB2+
       [not found] ` <20170228184034.18771-1-aaptel-IBi9RG/b67k@public.gmane.org>
                     ` (6 preceding siblings ...)
  2017-02-28 18:40   ` [PATCH v3 7/7] CIFS: implement get_dfs_refer for SMB2+ Aurelien Aptel
@ 2017-03-02  4:41   ` Steve French
  7 siblings, 0 replies; 19+ messages in thread
From: Steve French @ 2017-03-02  4:41 UTC (permalink / raw)
  To: Aurelien Aptel; +Cc: linux-cifs-u79uwXL29TY76Z2rM5mHXA

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

On Tue, Feb 28, 2017 at 12:40 PM, Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org> wrote:
> 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 majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



-- 
Thanks,

Steve

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v3 7/7] CIFS: implement get_dfs_refer for SMB2+
       [not found]     ` <20170228184034.18771-8-aaptel-IBi9RG/b67k@public.gmane.org>
  2017-03-01  2:42       ` Pavel Shilovsky
@ 2017-06-22 23:03       ` Pavel Shilovsky
       [not found]         ` <CAKywueTx4b4+MDYeTm+jBsUZfmYe+Sa1HT9cUx-d4043hKxr+A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  1 sibling, 1 reply; 19+ messages in thread
From: Pavel Shilovsky @ 2017-06-22 23:03 UTC (permalink / raw)
  To: Aurelien Aptel; +Cc: linux-cifs

2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>:
> 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:
>> 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.
>
> Signed-off-by: Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>
> ---
>  fs/cifs/smb2ops.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/cifs/smb2pdu.h |   8 +++++
>  2 files changed, 108 insertions(+)
>
> diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
> index 02937cc..fcd524d 100644
> --- a/fs/cifs/smb2ops.c
> +++ b/fs/cifs/smb2ops.c
> @@ -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;
> +       }

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

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v3 7/7] CIFS: implement get_dfs_refer for SMB2+
       [not found]         ` <CAKywueTx4b4+MDYeTm+jBsUZfmYe+Sa1HT9cUx-d4043hKxr+A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2017-06-22 23:05           ` Pavel Shilovsky
       [not found]             ` <CAKywueSy1Y1PNLHkRnDbxgc+Kf1qe_H65DPkWpX-=k=Enu-VUw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  0 siblings, 1 reply; 19+ messages in thread
From: Pavel Shilovsky @ 2017-06-22 23:05 UTC (permalink / raw)
  To: Aurelien Aptel; +Cc: linux-cifs

2017-06-22 16:03 GMT-07:00 Pavel Shilovsky <piastryyy-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>:
> 2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>:
>> 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:
>>> 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.
>>
>> Signed-off-by: Aurelien Aptel <aaptel-IBi9RG/b67k@public.gmane.org>
>> ---
>>  fs/cifs/smb2ops.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  fs/cifs/smb2pdu.h |   8 +++++
>>  2 files changed, 108 insertions(+)
>>
>> diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
>> index 02937cc..fcd524d 100644
>> --- a/fs/cifs/smb2ops.c
>> +++ b/fs/cifs/smb2ops.c
>> @@ -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;
>> +       }
>
> 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

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v3 7/7] CIFS: implement get_dfs_refer for SMB2+
       [not found]             ` <CAKywueSy1Y1PNLHkRnDbxgc+Kf1qe_H65DPkWpX-=k=Enu-VUw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2017-06-23  9:19               ` Aurélien Aptel
  0 siblings, 0 replies; 19+ messages in thread
From: Aurélien Aptel @ 2017-06-23  9:19 UTC (permalink / raw)
  To: Pavel Shilovsky; +Cc: linux-cifs

Hi Pavel,

Pavel Shilovsky <piastryyy-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> writes:
>> Should we lower a debug level to FYI here?

Sounds good.

-- 
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)

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2017-06-23  9:19 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-28 18:40 [PATCH v3 0/7] Add support for DFS in SMB2+ Aurelien Aptel
     [not found] ` <20170228184034.18771-1-aaptel-IBi9RG/b67k@public.gmane.org>
2017-02-28 18:40   ` [PATCH v3 1/7] CIFS: move DFS response parsing out of SMB1 code Aurelien Aptel
     [not found]     ` <20170228184034.18771-2-aaptel-IBi9RG/b67k@public.gmane.org>
2017-03-01  1:58       ` Pavel Shilovsky
2017-02-28 18:40   ` [PATCH v3 2/7] CIFS: add build_path_from_dentry_optional_prefix() Aurelien Aptel
     [not found]     ` <20170228184034.18771-3-aaptel-IBi9RG/b67k@public.gmane.org>
2017-03-01  2:35       ` Pavel Shilovsky
2017-02-28 18:40   ` [PATCH v3 3/7] CIFS: add use_ipc flag to SMB2_ioctl() Aurelien Aptel
     [not found]     ` <20170228184034.18771-4-aaptel-IBi9RG/b67k@public.gmane.org>
2017-03-01  2:36       ` Pavel Shilovsky
2017-02-28 18:40   ` [PATCH v3 4/7] CIFS: let ses->ipc_tid hold smb2 TreeIds Aurelien Aptel
     [not found]     ` <20170228184034.18771-5-aaptel-IBi9RG/b67k@public.gmane.org>
2017-03-01  2:36       ` Pavel Shilovsky
2017-02-28 18:40   ` [PATCH v3 5/7] CIFS: set signing flag in SMB2+ TreeConnect if needed Aurelien Aptel
     [not found]     ` <20170228184034.18771-6-aaptel-IBi9RG/b67k@public.gmane.org>
2017-03-01  2:37       ` Pavel Shilovsky
2017-02-28 18:40   ` [PATCH v3 6/7] CIFS: use DFS pathnames in SMB2+ Create requests Aurelien Aptel
     [not found]     ` <20170228184034.18771-7-aaptel-IBi9RG/b67k@public.gmane.org>
2017-03-01  2:40       ` Pavel Shilovsky
2017-02-28 18:40   ` [PATCH v3 7/7] CIFS: implement get_dfs_refer for SMB2+ Aurelien Aptel
     [not found]     ` <20170228184034.18771-8-aaptel-IBi9RG/b67k@public.gmane.org>
2017-03-01  2:42       ` Pavel Shilovsky
2017-06-22 23:03       ` Pavel Shilovsky
     [not found]         ` <CAKywueTx4b4+MDYeTm+jBsUZfmYe+Sa1HT9cUx-d4043hKxr+A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2017-06-22 23:05           ` Pavel Shilovsky
     [not found]             ` <CAKywueSy1Y1PNLHkRnDbxgc+Kf1qe_H65DPkWpX-=k=Enu-VUw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2017-06-23  9:19               ` Aurélien Aptel
2017-03-02  4:41   ` [PATCH v3 0/7] Add support for DFS in SMB2+ Steve French

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.