* [PATCH 1/2] cifs: handle prefix paths in reconnect
@ 2020-02-20 22:49 Paulo Alcantara (SUSE)
2020-02-20 22:49 ` [PATCH 2/2] cifs: fix potential mismatch of UNC paths Paulo Alcantara (SUSE)
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Paulo Alcantara (SUSE) @ 2020-02-20 22:49 UTC (permalink / raw)
To: smfrench; +Cc: linux-cifs, Paulo Alcantara (SUSE)
For the case where we have a DFS path like below and we're currently
connected to targetA:
//dfsroot/link -> //targetA/share/foo, //targetB/share/bar
after failover, we should make sure to update cifs_sb->prepath so the
next operations will use the new prefix path "/bar".
Besides, in order to simplify the use of different prefix paths,
enforce CIFS_MOUNT_USE_PREFIX_PATH for DFS mounts so we don't have to
revalidate the root dentry every time we set a new prefix path.
Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
---
fs/cifs/cifsproto.h | 5 +++
fs/cifs/cifssmb.c | 19 ++++++++---
fs/cifs/connect.c | 63 +++++++----------------------------
fs/cifs/dfs_cache.c | 38 +++++++++++++++++++++
fs/cifs/dfs_cache.h | 4 +++
fs/cifs/misc.c | 80 +++++++++++++++++++++++++++++++++++++++++++++
fs/cifs/smb2pdu.c | 19 ++++++++---
7 files changed, 169 insertions(+), 59 deletions(-)
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 89eaaf46d1ca..af8e79b58b94 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -601,6 +601,11 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,
int resp_buftype,
struct cifs_search_info *srch_inf);
+struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
+void cifs_put_tcp_super(struct super_block *sb);
+int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
+ size_t prefix_len);
+
#ifdef CONFIG_CIFS_DFS_UPCALL
static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
const char *old_path,
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 3c89569e7210..b6fa575d2d3f 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -162,9 +162,18 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
for (it = dfs_cache_get_tgt_iterator(&tl); it;
it = dfs_cache_get_next_tgt(&tl, it)) {
- const char *tgt = dfs_cache_get_tgt_name(it);
+ const char *share, *prefix;
+ size_t share_len, prefix_len;
- extract_unc_hostname(tgt, &dfs_host, &dfs_host_len);
+ rc = dfs_cache_get_tgt_share(it, &share, &share_len, &prefix,
+ &prefix_len);
+ if (rc) {
+ cifs_dbg(VFS, "%s: failed to parse target share %d\n",
+ __func__, rc);
+ continue;
+ }
+
+ extract_unc_hostname(share, &dfs_host, &dfs_host_len);
if (dfs_host_len != tcp_host_len
|| strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
@@ -175,11 +184,13 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
continue;
}
- scnprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);
+ scnprintf(tree, MAX_TREE_SIZE, "\\%.*s", (int)share_len, share);
rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
- if (!rc)
+ if (!rc) {
+ rc = update_super_prepath(tcon, prefix, prefix_len);
break;
+ }
if (rc == -EREMOTE)
break;
}
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 4804d1df8c1c..e2196b363765 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -57,7 +57,6 @@
#include "smb2proto.h"
#include "smbdirect.h"
#include "dns_resolve.h"
-#include "cifsfs.h"
#ifdef CONFIG_CIFS_DFS_UPCALL
#include "dfs_cache.h"
#endif
@@ -389,54 +388,7 @@ static inline int reconn_set_ipaddr(struct TCP_Server_Info *server)
#endif
#ifdef CONFIG_CIFS_DFS_UPCALL
-struct super_cb_data {
- struct TCP_Server_Info *server;
- struct super_block *sb;
-};
-
/* These functions must be called with server->srv_mutex held */
-
-static void super_cb(struct super_block *sb, void *arg)
-{
- struct super_cb_data *d = arg;
- struct cifs_sb_info *cifs_sb;
- struct cifs_tcon *tcon;
-
- if (d->sb)
- return;
-
- cifs_sb = CIFS_SB(sb);
- tcon = cifs_sb_master_tcon(cifs_sb);
- if (tcon->ses->server == d->server)
- d->sb = sb;
-}
-
-static struct super_block *get_tcp_super(struct TCP_Server_Info *server)
-{
- struct super_cb_data d = {
- .server = server,
- .sb = NULL,
- };
-
- iterate_supers_type(&cifs_fs_type, super_cb, &d);
-
- if (unlikely(!d.sb))
- return ERR_PTR(-ENOENT);
- /*
- * Grab an active reference in order to prevent automounts (DFS links)
- * of expiring and then freeing up our cifs superblock pointer while
- * we're doing failover.
- */
- cifs_sb_active(d.sb);
- return d.sb;
-}
-
-static inline void put_tcp_super(struct super_block *sb)
-{
- if (!IS_ERR_OR_NULL(sb))
- cifs_sb_deactive(sb);
-}
-
static void reconn_inval_dfs_target(struct TCP_Server_Info *server,
struct cifs_sb_info *cifs_sb,
struct dfs_cache_tgt_list *tgt_list,
@@ -508,7 +460,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
server->nr_targets = 1;
#ifdef CONFIG_CIFS_DFS_UPCALL
spin_unlock(&GlobalMid_Lock);
- sb = get_tcp_super(server);
+ sb = cifs_get_tcp_super(server);
if (IS_ERR(sb)) {
rc = PTR_ERR(sb);
cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n",
@@ -535,7 +487,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
spin_unlock(&GlobalMid_Lock);
#ifdef CONFIG_CIFS_DFS_UPCALL
dfs_cache_free_tgts(&tgt_list);
- put_tcp_super(sb);
+ cifs_put_tcp_super(sb);
#endif
return rc;
} else
@@ -666,7 +618,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
}
- put_tcp_super(sb);
+ cifs_put_tcp_super(sb);
#endif
if (server->tcpStatus == CifsNeedNegotiate)
mod_delayed_work(cifsiod_wq, &server->echo, 0);
@@ -4999,6 +4951,15 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
* dentry revalidation to think the dentry are stale (ESTALE).
*/
cifs_autodisable_serverino(cifs_sb);
+ /*
+ * Force the use of prefix path to support failover on DFS paths that
+ * resolve to targets that have different prefix paths.
+ */
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
+ kfree(cifs_sb->prepath);
+ cifs_sb->prepath = vol->prepath;
+ vol->prepath = NULL;
+
out:
free_xid(xid);
cifs_try_adding_channels(ses);
diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c
index 43c1b43a07ec..a67f88bf7ae1 100644
--- a/fs/cifs/dfs_cache.c
+++ b/fs/cifs/dfs_cache.c
@@ -1260,6 +1260,44 @@ void dfs_cache_del_vol(const char *fullpath)
kref_put(&vi->refcnt, vol_release);
}
+/**
+ * dfs_cache_get_tgt_share - parse a DFS target
+ *
+ * @it: DFS target iterator.
+ * @share: tree name.
+ * @share_len: length of tree name.
+ * @prefix: prefix path.
+ * @prefix_len: length of prefix path.
+ *
+ * Return zero if target was parsed correctly, otherwise non-zero.
+ */
+int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
+ const char **share, size_t *share_len,
+ const char **prefix, size_t *prefix_len)
+{
+ char *s, sep;
+
+ if (!it || !share || !share_len || !prefix || !prefix_len)
+ return -EINVAL;
+
+ sep = it->it_name[0];
+ if (sep != '\\' && sep != '/')
+ return -EINVAL;
+
+ s = strchr(it->it_name + 1, sep);
+ if (!s)
+ return -EINVAL;
+
+ s = strchrnul(s + 1, sep);
+
+ *share = it->it_name;
+ *share_len = s - it->it_name;
+ *prefix = *s ? s + 1 : s;
+ *prefix_len = &it->it_name[strlen(it->it_name)] - *prefix;
+
+ return 0;
+}
+
/* Get all tcons that are within a DFS namespace and can be refreshed */
static void get_tcons(struct TCP_Server_Info *server, struct list_head *head)
{
diff --git a/fs/cifs/dfs_cache.h b/fs/cifs/dfs_cache.h
index 99ee44f8ad07..bf94d08cfb5a 100644
--- a/fs/cifs/dfs_cache.h
+++ b/fs/cifs/dfs_cache.h
@@ -49,6 +49,10 @@ extern int dfs_cache_update_vol(const char *fullpath,
struct TCP_Server_Info *server);
extern void dfs_cache_del_vol(const char *fullpath);
+extern int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
+ const char **share, size_t *share_len,
+ const char **prefix, size_t *prefix_len);
+
static inline struct dfs_cache_tgt_iterator *
dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl,
struct dfs_cache_tgt_iterator *it)
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 40ca394fd5de..a456febd4109 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -31,6 +31,7 @@
#include "nterr.h"
#include "cifs_unicode.h"
#include "smb2pdu.h"
+#include "cifsfs.h"
extern mempool_t *cifs_sm_req_poolp;
extern mempool_t *cifs_req_poolp;
@@ -1022,3 +1023,82 @@ int copy_path_name(char *dst, const char *src)
name_len++;
return name_len;
}
+
+struct super_cb_data {
+ struct TCP_Server_Info *server;
+ struct super_block *sb;
+};
+
+static void super_cb(struct super_block *sb, void *arg)
+{
+ struct super_cb_data *d = arg;
+ struct cifs_sb_info *cifs_sb;
+ struct cifs_tcon *tcon;
+
+ if (d->sb)
+ return;
+
+ cifs_sb = CIFS_SB(sb);
+ tcon = cifs_sb_master_tcon(cifs_sb);
+ if (tcon->ses->server == d->server)
+ d->sb = sb;
+}
+
+struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server)
+{
+ struct super_cb_data d = {
+ .server = server,
+ .sb = NULL,
+ };
+
+ iterate_supers_type(&cifs_fs_type, super_cb, &d);
+
+ if (unlikely(!d.sb))
+ return ERR_PTR(-ENOENT);
+ /*
+ * Grab an active reference in order to prevent automounts (DFS links)
+ * of expiring and then freeing up our cifs superblock pointer while
+ * we're doing failover.
+ */
+ cifs_sb_active(d.sb);
+ return d.sb;
+}
+
+void cifs_put_tcp_super(struct super_block *sb)
+{
+ if (!IS_ERR_OR_NULL(sb))
+ cifs_sb_deactive(sb);
+}
+
+int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
+ size_t prefix_len)
+{
+ struct super_block *sb;
+ struct cifs_sb_info *cifs_sb;
+ int rc = 0;
+
+ sb = cifs_get_tcp_super(tcon->ses->server);
+ if (IS_ERR(sb))
+ return PTR_ERR(sb);
+
+ cifs_sb = CIFS_SB(sb);
+
+ kfree(cifs_sb->prepath);
+
+ if (*prefix && prefix_len) {
+ cifs_sb->prepath = kstrndup(prefix, prefix_len, GFP_ATOMIC);
+ if (!cifs_sb->prepath) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
+ } else
+ cifs_sb->prepath = NULL;
+
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
+
+out:
+ cifs_put_tcp_super(sb);
+ return rc;
+}
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index abc0a0512181..49b15a488ede 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -193,9 +193,18 @@ static int __smb2_reconnect(const struct nls_table *nlsc,
for (it = dfs_cache_get_tgt_iterator(&tl); it;
it = dfs_cache_get_next_tgt(&tl, it)) {
- const char *tgt = dfs_cache_get_tgt_name(it);
+ const char *share, *prefix;
+ size_t share_len, prefix_len;
- extract_unc_hostname(tgt, &dfs_host, &dfs_host_len);
+ rc = dfs_cache_get_tgt_share(it, &share, &share_len, &prefix,
+ &prefix_len);
+ if (rc) {
+ cifs_dbg(VFS, "%s: failed to parse target share %d\n",
+ __func__, rc);
+ continue;
+ }
+
+ extract_unc_hostname(share, &dfs_host, &dfs_host_len);
if (dfs_host_len != tcp_host_len
|| strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
@@ -206,11 +215,13 @@ static int __smb2_reconnect(const struct nls_table *nlsc,
continue;
}
- scnprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);
+ scnprintf(tree, MAX_TREE_SIZE, "\\%.*s", (int)share_len, share);
rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc);
- if (!rc)
+ if (!rc) {
+ rc = update_super_prepath(tcon, prefix, prefix_len);
break;
+ }
if (rc == -EREMOTE)
break;
}
--
2.25.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/2] cifs: fix potential mismatch of UNC paths
2020-02-20 22:49 [PATCH 1/2] cifs: handle prefix paths in reconnect Paulo Alcantara (SUSE)
@ 2020-02-20 22:49 ` Paulo Alcantara (SUSE)
2020-02-21 0:08 ` [PATCH 1/2] cifs: handle prefix paths in reconnect ronnie sahlberg
2020-02-25 16:50 ` Aurélien Aptel
2 siblings, 0 replies; 6+ messages in thread
From: Paulo Alcantara (SUSE) @ 2020-02-20 22:49 UTC (permalink / raw)
To: smfrench; +Cc: linux-cifs, Paulo Alcantara (SUSE)
Ensure that full_path is an UNC path that contains '\\' as delimiter,
which is required by cifs_build_devname().
The build_path_from_dentry_optional_prefix() function may return a
path with '/' as delimiter when using SMB1 UNIX extensions, for
example.
Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
---
fs/cifs/cifs_dfs_ref.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
index 606f26d862dc..cc3ada12848d 100644
--- a/fs/cifs/cifs_dfs_ref.c
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -324,6 +324,8 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
if (full_path == NULL)
goto cdda_exit;
+ convert_delimiter(full_path, '\\');
+
cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path);
if (!cifs_sb_master_tlink(cifs_sb)) {
--
2.25.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] cifs: handle prefix paths in reconnect
2020-02-20 22:49 [PATCH 1/2] cifs: handle prefix paths in reconnect Paulo Alcantara (SUSE)
2020-02-20 22:49 ` [PATCH 2/2] cifs: fix potential mismatch of UNC paths Paulo Alcantara (SUSE)
@ 2020-02-21 0:08 ` ronnie sahlberg
2020-02-21 0:30 ` Steve French
2020-02-25 16:50 ` Aurélien Aptel
2 siblings, 1 reply; 6+ messages in thread
From: ronnie sahlberg @ 2020-02-21 0:08 UTC (permalink / raw)
To: Paulo Alcantara (SUSE); +Cc: Steve French, linux-cifs
Acked-by: Ronnie Sahlberg <lsahlber@redhat.com>
On Fri, Feb 21, 2020 at 8:50 AM Paulo Alcantara (SUSE) <pc@cjr.nz> wrote:
>
> For the case where we have a DFS path like below and we're currently
> connected to targetA:
>
> //dfsroot/link -> //targetA/share/foo, //targetB/share/bar
>
> after failover, we should make sure to update cifs_sb->prepath so the
> next operations will use the new prefix path "/bar".
>
> Besides, in order to simplify the use of different prefix paths,
> enforce CIFS_MOUNT_USE_PREFIX_PATH for DFS mounts so we don't have to
> revalidate the root dentry every time we set a new prefix path.
>
> Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
> ---
> fs/cifs/cifsproto.h | 5 +++
> fs/cifs/cifssmb.c | 19 ++++++++---
> fs/cifs/connect.c | 63 +++++++----------------------------
> fs/cifs/dfs_cache.c | 38 +++++++++++++++++++++
> fs/cifs/dfs_cache.h | 4 +++
> fs/cifs/misc.c | 80 +++++++++++++++++++++++++++++++++++++++++++++
> fs/cifs/smb2pdu.c | 19 ++++++++---
> 7 files changed, 169 insertions(+), 59 deletions(-)
>
> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> index 89eaaf46d1ca..af8e79b58b94 100644
> --- a/fs/cifs/cifsproto.h
> +++ b/fs/cifs/cifsproto.h
> @@ -601,6 +601,11 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,
> int resp_buftype,
> struct cifs_search_info *srch_inf);
>
> +struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
> +void cifs_put_tcp_super(struct super_block *sb);
> +int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
> + size_t prefix_len);
> +
> #ifdef CONFIG_CIFS_DFS_UPCALL
> static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
> const char *old_path,
> diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
> index 3c89569e7210..b6fa575d2d3f 100644
> --- a/fs/cifs/cifssmb.c
> +++ b/fs/cifs/cifssmb.c
> @@ -162,9 +162,18 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
>
> for (it = dfs_cache_get_tgt_iterator(&tl); it;
> it = dfs_cache_get_next_tgt(&tl, it)) {
> - const char *tgt = dfs_cache_get_tgt_name(it);
> + const char *share, *prefix;
> + size_t share_len, prefix_len;
>
> - extract_unc_hostname(tgt, &dfs_host, &dfs_host_len);
> + rc = dfs_cache_get_tgt_share(it, &share, &share_len, &prefix,
> + &prefix_len);
> + if (rc) {
> + cifs_dbg(VFS, "%s: failed to parse target share %d\n",
> + __func__, rc);
> + continue;
> + }
> +
> + extract_unc_hostname(share, &dfs_host, &dfs_host_len);
>
> if (dfs_host_len != tcp_host_len
> || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
> @@ -175,11 +184,13 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
> continue;
> }
>
> - scnprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);
> + scnprintf(tree, MAX_TREE_SIZE, "\\%.*s", (int)share_len, share);
>
> rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
> - if (!rc)
> + if (!rc) {
> + rc = update_super_prepath(tcon, prefix, prefix_len);
> break;
> + }
> if (rc == -EREMOTE)
> break;
> }
> diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
> index 4804d1df8c1c..e2196b363765 100644
> --- a/fs/cifs/connect.c
> +++ b/fs/cifs/connect.c
> @@ -57,7 +57,6 @@
> #include "smb2proto.h"
> #include "smbdirect.h"
> #include "dns_resolve.h"
> -#include "cifsfs.h"
> #ifdef CONFIG_CIFS_DFS_UPCALL
> #include "dfs_cache.h"
> #endif
> @@ -389,54 +388,7 @@ static inline int reconn_set_ipaddr(struct TCP_Server_Info *server)
> #endif
>
> #ifdef CONFIG_CIFS_DFS_UPCALL
> -struct super_cb_data {
> - struct TCP_Server_Info *server;
> - struct super_block *sb;
> -};
> -
> /* These functions must be called with server->srv_mutex held */
> -
> -static void super_cb(struct super_block *sb, void *arg)
> -{
> - struct super_cb_data *d = arg;
> - struct cifs_sb_info *cifs_sb;
> - struct cifs_tcon *tcon;
> -
> - if (d->sb)
> - return;
> -
> - cifs_sb = CIFS_SB(sb);
> - tcon = cifs_sb_master_tcon(cifs_sb);
> - if (tcon->ses->server == d->server)
> - d->sb = sb;
> -}
> -
> -static struct super_block *get_tcp_super(struct TCP_Server_Info *server)
> -{
> - struct super_cb_data d = {
> - .server = server,
> - .sb = NULL,
> - };
> -
> - iterate_supers_type(&cifs_fs_type, super_cb, &d);
> -
> - if (unlikely(!d.sb))
> - return ERR_PTR(-ENOENT);
> - /*
> - * Grab an active reference in order to prevent automounts (DFS links)
> - * of expiring and then freeing up our cifs superblock pointer while
> - * we're doing failover.
> - */
> - cifs_sb_active(d.sb);
> - return d.sb;
> -}
> -
> -static inline void put_tcp_super(struct super_block *sb)
> -{
> - if (!IS_ERR_OR_NULL(sb))
> - cifs_sb_deactive(sb);
> -}
> -
> static void reconn_inval_dfs_target(struct TCP_Server_Info *server,
> struct cifs_sb_info *cifs_sb,
> struct dfs_cache_tgt_list *tgt_list,
> @@ -508,7 +460,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
> server->nr_targets = 1;
> #ifdef CONFIG_CIFS_DFS_UPCALL
> spin_unlock(&GlobalMid_Lock);
> - sb = get_tcp_super(server);
> + sb = cifs_get_tcp_super(server);
> if (IS_ERR(sb)) {
> rc = PTR_ERR(sb);
> cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n",
> @@ -535,7 +487,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
> spin_unlock(&GlobalMid_Lock);
> #ifdef CONFIG_CIFS_DFS_UPCALL
> dfs_cache_free_tgts(&tgt_list);
> - put_tcp_super(sb);
> + cifs_put_tcp_super(sb);
> #endif
> return rc;
> } else
> @@ -666,7 +618,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
>
> }
>
> - put_tcp_super(sb);
> + cifs_put_tcp_super(sb);
> #endif
> if (server->tcpStatus == CifsNeedNegotiate)
> mod_delayed_work(cifsiod_wq, &server->echo, 0);
> @@ -4999,6 +4951,15 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
> * dentry revalidation to think the dentry are stale (ESTALE).
> */
> cifs_autodisable_serverino(cifs_sb);
> + /*
> + * Force the use of prefix path to support failover on DFS paths that
> + * resolve to targets that have different prefix paths.
> + */
> + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
> + kfree(cifs_sb->prepath);
> + cifs_sb->prepath = vol->prepath;
> + vol->prepath = NULL;
> +
> out:
> free_xid(xid);
> cifs_try_adding_channels(ses);
> diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c
> index 43c1b43a07ec..a67f88bf7ae1 100644
> --- a/fs/cifs/dfs_cache.c
> +++ b/fs/cifs/dfs_cache.c
> @@ -1260,6 +1260,44 @@ void dfs_cache_del_vol(const char *fullpath)
> kref_put(&vi->refcnt, vol_release);
> }
>
> +/**
> + * dfs_cache_get_tgt_share - parse a DFS target
> + *
> + * @it: DFS target iterator.
> + * @share: tree name.
> + * @share_len: length of tree name.
> + * @prefix: prefix path.
> + * @prefix_len: length of prefix path.
> + *
> + * Return zero if target was parsed correctly, otherwise non-zero.
> + */
> +int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
> + const char **share, size_t *share_len,
> + const char **prefix, size_t *prefix_len)
> +{
> + char *s, sep;
> +
> + if (!it || !share || !share_len || !prefix || !prefix_len)
> + return -EINVAL;
> +
> + sep = it->it_name[0];
> + if (sep != '\\' && sep != '/')
> + return -EINVAL;
> +
> + s = strchr(it->it_name + 1, sep);
> + if (!s)
> + return -EINVAL;
> +
> + s = strchrnul(s + 1, sep);
> +
> + *share = it->it_name;
> + *share_len = s - it->it_name;
> + *prefix = *s ? s + 1 : s;
> + *prefix_len = &it->it_name[strlen(it->it_name)] - *prefix;
> +
> + return 0;
> +}
> +
> /* Get all tcons that are within a DFS namespace and can be refreshed */
> static void get_tcons(struct TCP_Server_Info *server, struct list_head *head)
> {
> diff --git a/fs/cifs/dfs_cache.h b/fs/cifs/dfs_cache.h
> index 99ee44f8ad07..bf94d08cfb5a 100644
> --- a/fs/cifs/dfs_cache.h
> +++ b/fs/cifs/dfs_cache.h
> @@ -49,6 +49,10 @@ extern int dfs_cache_update_vol(const char *fullpath,
> struct TCP_Server_Info *server);
> extern void dfs_cache_del_vol(const char *fullpath);
>
> +extern int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
> + const char **share, size_t *share_len,
> + const char **prefix, size_t *prefix_len);
> +
> static inline struct dfs_cache_tgt_iterator *
> dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl,
> struct dfs_cache_tgt_iterator *it)
> diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
> index 40ca394fd5de..a456febd4109 100644
> --- a/fs/cifs/misc.c
> +++ b/fs/cifs/misc.c
> @@ -31,6 +31,7 @@
> #include "nterr.h"
> #include "cifs_unicode.h"
> #include "smb2pdu.h"
> +#include "cifsfs.h"
>
> extern mempool_t *cifs_sm_req_poolp;
> extern mempool_t *cifs_req_poolp;
> @@ -1022,3 +1023,82 @@ int copy_path_name(char *dst, const char *src)
> name_len++;
> return name_len;
> }
> +
> +struct super_cb_data {
> + struct TCP_Server_Info *server;
> + struct super_block *sb;
> +};
> +
> +static void super_cb(struct super_block *sb, void *arg)
> +{
> + struct super_cb_data *d = arg;
> + struct cifs_sb_info *cifs_sb;
> + struct cifs_tcon *tcon;
> +
> + if (d->sb)
> + return;
> +
> + cifs_sb = CIFS_SB(sb);
> + tcon = cifs_sb_master_tcon(cifs_sb);
> + if (tcon->ses->server == d->server)
> + d->sb = sb;
> +}
> +
> +struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server)
> +{
> + struct super_cb_data d = {
> + .server = server,
> + .sb = NULL,
> + };
> +
> + iterate_supers_type(&cifs_fs_type, super_cb, &d);
> +
> + if (unlikely(!d.sb))
> + return ERR_PTR(-ENOENT);
> + /*
> + * Grab an active reference in order to prevent automounts (DFS links)
> + * of expiring and then freeing up our cifs superblock pointer while
> + * we're doing failover.
> + */
> + cifs_sb_active(d.sb);
> + return d.sb;
> +}
> +
> +void cifs_put_tcp_super(struct super_block *sb)
> +{
> + if (!IS_ERR_OR_NULL(sb))
> + cifs_sb_deactive(sb);
> +}
> +
> +int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
> + size_t prefix_len)
> +{
> + struct super_block *sb;
> + struct cifs_sb_info *cifs_sb;
> + int rc = 0;
> +
> + sb = cifs_get_tcp_super(tcon->ses->server);
> + if (IS_ERR(sb))
> + return PTR_ERR(sb);
> +
> + cifs_sb = CIFS_SB(sb);
> +
> + kfree(cifs_sb->prepath);
> +
> + if (*prefix && prefix_len) {
> + cifs_sb->prepath = kstrndup(prefix, prefix_len, GFP_ATOMIC);
> + if (!cifs_sb->prepath) {
> + rc = -ENOMEM;
> + goto out;
> + }
> +
> + convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
> + } else
> + cifs_sb->prepath = NULL;
> +
> + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
> +
> +out:
> + cifs_put_tcp_super(sb);
> + return rc;
> +}
> diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> index abc0a0512181..49b15a488ede 100644
> --- a/fs/cifs/smb2pdu.c
> +++ b/fs/cifs/smb2pdu.c
> @@ -193,9 +193,18 @@ static int __smb2_reconnect(const struct nls_table *nlsc,
>
> for (it = dfs_cache_get_tgt_iterator(&tl); it;
> it = dfs_cache_get_next_tgt(&tl, it)) {
> - const char *tgt = dfs_cache_get_tgt_name(it);
> + const char *share, *prefix;
> + size_t share_len, prefix_len;
>
> - extract_unc_hostname(tgt, &dfs_host, &dfs_host_len);
> + rc = dfs_cache_get_tgt_share(it, &share, &share_len, &prefix,
> + &prefix_len);
> + if (rc) {
> + cifs_dbg(VFS, "%s: failed to parse target share %d\n",
> + __func__, rc);
> + continue;
> + }
> +
> + extract_unc_hostname(share, &dfs_host, &dfs_host_len);
>
> if (dfs_host_len != tcp_host_len
> || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
> @@ -206,11 +215,13 @@ static int __smb2_reconnect(const struct nls_table *nlsc,
> continue;
> }
>
> - scnprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);
> + scnprintf(tree, MAX_TREE_SIZE, "\\%.*s", (int)share_len, share);
>
> rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc);
> - if (!rc)
> + if (!rc) {
> + rc = update_super_prepath(tcon, prefix, prefix_len);
> break;
> + }
> if (rc == -EREMOTE)
> break;
> }
> --
> 2.25.0
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] cifs: handle prefix paths in reconnect
2020-02-21 0:08 ` [PATCH 1/2] cifs: handle prefix paths in reconnect ronnie sahlberg
@ 2020-02-21 0:30 ` Steve French
2020-02-21 0:33 ` Steve French
0 siblings, 1 reply; 6+ messages in thread
From: Steve French @ 2020-02-21 0:30 UTC (permalink / raw)
To: ronnie sahlberg; +Cc: Paulo Alcantara (SUSE), linux-cifs
merged both into cifs-2.6.git for-next (the 2nd one earlier, so it
could be sent to a 5.6-rc ie before 5.7)
On Thu, Feb 20, 2020 at 6:08 PM ronnie sahlberg
<ronniesahlberg@gmail.com> wrote:
>
> Acked-by: Ronnie Sahlberg <lsahlber@redhat.com>
>
> On Fri, Feb 21, 2020 at 8:50 AM Paulo Alcantara (SUSE) <pc@cjr.nz> wrote:
> >
> > For the case where we have a DFS path like below and we're currently
> > connected to targetA:
> >
> > //dfsroot/link -> //targetA/share/foo, //targetB/share/bar
> >
> > after failover, we should make sure to update cifs_sb->prepath so the
> > next operations will use the new prefix path "/bar".
> >
> > Besides, in order to simplify the use of different prefix paths,
> > enforce CIFS_MOUNT_USE_PREFIX_PATH for DFS mounts so we don't have to
> > revalidate the root dentry every time we set a new prefix path.
> >
> > Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
> > ---
> > fs/cifs/cifsproto.h | 5 +++
> > fs/cifs/cifssmb.c | 19 ++++++++---
> > fs/cifs/connect.c | 63 +++++++----------------------------
> > fs/cifs/dfs_cache.c | 38 +++++++++++++++++++++
> > fs/cifs/dfs_cache.h | 4 +++
> > fs/cifs/misc.c | 80 +++++++++++++++++++++++++++++++++++++++++++++
> > fs/cifs/smb2pdu.c | 19 ++++++++---
> > 7 files changed, 169 insertions(+), 59 deletions(-)
> >
> > diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> > index 89eaaf46d1ca..af8e79b58b94 100644
> > --- a/fs/cifs/cifsproto.h
> > +++ b/fs/cifs/cifsproto.h
> > @@ -601,6 +601,11 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,
> > int resp_buftype,
> > struct cifs_search_info *srch_inf);
> >
> > +struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
> > +void cifs_put_tcp_super(struct super_block *sb);
> > +int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
> > + size_t prefix_len);
> > +
> > #ifdef CONFIG_CIFS_DFS_UPCALL
> > static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
> > const char *old_path,
> > diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
> > index 3c89569e7210..b6fa575d2d3f 100644
> > --- a/fs/cifs/cifssmb.c
> > +++ b/fs/cifs/cifssmb.c
> > @@ -162,9 +162,18 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
> >
> > for (it = dfs_cache_get_tgt_iterator(&tl); it;
> > it = dfs_cache_get_next_tgt(&tl, it)) {
> > - const char *tgt = dfs_cache_get_tgt_name(it);
> > + const char *share, *prefix;
> > + size_t share_len, prefix_len;
> >
> > - extract_unc_hostname(tgt, &dfs_host, &dfs_host_len);
> > + rc = dfs_cache_get_tgt_share(it, &share, &share_len, &prefix,
> > + &prefix_len);
> > + if (rc) {
> > + cifs_dbg(VFS, "%s: failed to parse target share %d\n",
> > + __func__, rc);
> > + continue;
> > + }
> > +
> > + extract_unc_hostname(share, &dfs_host, &dfs_host_len);
> >
> > if (dfs_host_len != tcp_host_len
> > || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
> > @@ -175,11 +184,13 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
> > continue;
> > }
> >
> > - scnprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);
> > + scnprintf(tree, MAX_TREE_SIZE, "\\%.*s", (int)share_len, share);
> >
> > rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
> > - if (!rc)
> > + if (!rc) {
> > + rc = update_super_prepath(tcon, prefix, prefix_len);
> > break;
> > + }
> > if (rc == -EREMOTE)
> > break;
> > }
> > diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
> > index 4804d1df8c1c..e2196b363765 100644
> > --- a/fs/cifs/connect.c
> > +++ b/fs/cifs/connect.c
> > @@ -57,7 +57,6 @@
> > #include "smb2proto.h"
> > #include "smbdirect.h"
> > #include "dns_resolve.h"
> > -#include "cifsfs.h"
> > #ifdef CONFIG_CIFS_DFS_UPCALL
> > #include "dfs_cache.h"
> > #endif
> > @@ -389,54 +388,7 @@ static inline int reconn_set_ipaddr(struct TCP_Server_Info *server)
> > #endif
> >
> > #ifdef CONFIG_CIFS_DFS_UPCALL
> > -struct super_cb_data {
> > - struct TCP_Server_Info *server;
> > - struct super_block *sb;
> > -};
> > -
> > /* These functions must be called with server->srv_mutex held */
> > -
> > -static void super_cb(struct super_block *sb, void *arg)
> > -{
> > - struct super_cb_data *d = arg;
> > - struct cifs_sb_info *cifs_sb;
> > - struct cifs_tcon *tcon;
> > -
> > - if (d->sb)
> > - return;
> > -
> > - cifs_sb = CIFS_SB(sb);
> > - tcon = cifs_sb_master_tcon(cifs_sb);
> > - if (tcon->ses->server == d->server)
> > - d->sb = sb;
> > -}
> > -
> > -static struct super_block *get_tcp_super(struct TCP_Server_Info *server)
> > -{
> > - struct super_cb_data d = {
> > - .server = server,
> > - .sb = NULL,
> > - };
> > -
> > - iterate_supers_type(&cifs_fs_type, super_cb, &d);
> > -
> > - if (unlikely(!d.sb))
> > - return ERR_PTR(-ENOENT);
> > - /*
> > - * Grab an active reference in order to prevent automounts (DFS links)
> > - * of expiring and then freeing up our cifs superblock pointer while
> > - * we're doing failover.
> > - */
> > - cifs_sb_active(d.sb);
> > - return d.sb;
> > -}
> > -
> > -static inline void put_tcp_super(struct super_block *sb)
> > -{
> > - if (!IS_ERR_OR_NULL(sb))
> > - cifs_sb_deactive(sb);
> > -}
> > -
> > static void reconn_inval_dfs_target(struct TCP_Server_Info *server,
> > struct cifs_sb_info *cifs_sb,
> > struct dfs_cache_tgt_list *tgt_list,
> > @@ -508,7 +460,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
> > server->nr_targets = 1;
> > #ifdef CONFIG_CIFS_DFS_UPCALL
> > spin_unlock(&GlobalMid_Lock);
> > - sb = get_tcp_super(server);
> > + sb = cifs_get_tcp_super(server);
> > if (IS_ERR(sb)) {
> > rc = PTR_ERR(sb);
> > cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n",
> > @@ -535,7 +487,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
> > spin_unlock(&GlobalMid_Lock);
> > #ifdef CONFIG_CIFS_DFS_UPCALL
> > dfs_cache_free_tgts(&tgt_list);
> > - put_tcp_super(sb);
> > + cifs_put_tcp_super(sb);
> > #endif
> > return rc;
> > } else
> > @@ -666,7 +618,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
> >
> > }
> >
> > - put_tcp_super(sb);
> > + cifs_put_tcp_super(sb);
> > #endif
> > if (server->tcpStatus == CifsNeedNegotiate)
> > mod_delayed_work(cifsiod_wq, &server->echo, 0);
> > @@ -4999,6 +4951,15 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
> > * dentry revalidation to think the dentry are stale (ESTALE).
> > */
> > cifs_autodisable_serverino(cifs_sb);
> > + /*
> > + * Force the use of prefix path to support failover on DFS paths that
> > + * resolve to targets that have different prefix paths.
> > + */
> > + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
> > + kfree(cifs_sb->prepath);
> > + cifs_sb->prepath = vol->prepath;
> > + vol->prepath = NULL;
> > +
> > out:
> > free_xid(xid);
> > cifs_try_adding_channels(ses);
> > diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c
> > index 43c1b43a07ec..a67f88bf7ae1 100644
> > --- a/fs/cifs/dfs_cache.c
> > +++ b/fs/cifs/dfs_cache.c
> > @@ -1260,6 +1260,44 @@ void dfs_cache_del_vol(const char *fullpath)
> > kref_put(&vi->refcnt, vol_release);
> > }
> >
> > +/**
> > + * dfs_cache_get_tgt_share - parse a DFS target
> > + *
> > + * @it: DFS target iterator.
> > + * @share: tree name.
> > + * @share_len: length of tree name.
> > + * @prefix: prefix path.
> > + * @prefix_len: length of prefix path.
> > + *
> > + * Return zero if target was parsed correctly, otherwise non-zero.
> > + */
> > +int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
> > + const char **share, size_t *share_len,
> > + const char **prefix, size_t *prefix_len)
> > +{
> > + char *s, sep;
> > +
> > + if (!it || !share || !share_len || !prefix || !prefix_len)
> > + return -EINVAL;
> > +
> > + sep = it->it_name[0];
> > + if (sep != '\\' && sep != '/')
> > + return -EINVAL;
> > +
> > + s = strchr(it->it_name + 1, sep);
> > + if (!s)
> > + return -EINVAL;
> > +
> > + s = strchrnul(s + 1, sep);
> > +
> > + *share = it->it_name;
> > + *share_len = s - it->it_name;
> > + *prefix = *s ? s + 1 : s;
> > + *prefix_len = &it->it_name[strlen(it->it_name)] - *prefix;
> > +
> > + return 0;
> > +}
> > +
> > /* Get all tcons that are within a DFS namespace and can be refreshed */
> > static void get_tcons(struct TCP_Server_Info *server, struct list_head *head)
> > {
> > diff --git a/fs/cifs/dfs_cache.h b/fs/cifs/dfs_cache.h
> > index 99ee44f8ad07..bf94d08cfb5a 100644
> > --- a/fs/cifs/dfs_cache.h
> > +++ b/fs/cifs/dfs_cache.h
> > @@ -49,6 +49,10 @@ extern int dfs_cache_update_vol(const char *fullpath,
> > struct TCP_Server_Info *server);
> > extern void dfs_cache_del_vol(const char *fullpath);
> >
> > +extern int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
> > + const char **share, size_t *share_len,
> > + const char **prefix, size_t *prefix_len);
> > +
> > static inline struct dfs_cache_tgt_iterator *
> > dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl,
> > struct dfs_cache_tgt_iterator *it)
> > diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
> > index 40ca394fd5de..a456febd4109 100644
> > --- a/fs/cifs/misc.c
> > +++ b/fs/cifs/misc.c
> > @@ -31,6 +31,7 @@
> > #include "nterr.h"
> > #include "cifs_unicode.h"
> > #include "smb2pdu.h"
> > +#include "cifsfs.h"
> >
> > extern mempool_t *cifs_sm_req_poolp;
> > extern mempool_t *cifs_req_poolp;
> > @@ -1022,3 +1023,82 @@ int copy_path_name(char *dst, const char *src)
> > name_len++;
> > return name_len;
> > }
> > +
> > +struct super_cb_data {
> > + struct TCP_Server_Info *server;
> > + struct super_block *sb;
> > +};
> > +
> > +static void super_cb(struct super_block *sb, void *arg)
> > +{
> > + struct super_cb_data *d = arg;
> > + struct cifs_sb_info *cifs_sb;
> > + struct cifs_tcon *tcon;
> > +
> > + if (d->sb)
> > + return;
> > +
> > + cifs_sb = CIFS_SB(sb);
> > + tcon = cifs_sb_master_tcon(cifs_sb);
> > + if (tcon->ses->server == d->server)
> > + d->sb = sb;
> > +}
> > +
> > +struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server)
> > +{
> > + struct super_cb_data d = {
> > + .server = server,
> > + .sb = NULL,
> > + };
> > +
> > + iterate_supers_type(&cifs_fs_type, super_cb, &d);
> > +
> > + if (unlikely(!d.sb))
> > + return ERR_PTR(-ENOENT);
> > + /*
> > + * Grab an active reference in order to prevent automounts (DFS links)
> > + * of expiring and then freeing up our cifs superblock pointer while
> > + * we're doing failover.
> > + */
> > + cifs_sb_active(d.sb);
> > + return d.sb;
> > +}
> > +
> > +void cifs_put_tcp_super(struct super_block *sb)
> > +{
> > + if (!IS_ERR_OR_NULL(sb))
> > + cifs_sb_deactive(sb);
> > +}
> > +
> > +int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
> > + size_t prefix_len)
> > +{
> > + struct super_block *sb;
> > + struct cifs_sb_info *cifs_sb;
> > + int rc = 0;
> > +
> > + sb = cifs_get_tcp_super(tcon->ses->server);
> > + if (IS_ERR(sb))
> > + return PTR_ERR(sb);
> > +
> > + cifs_sb = CIFS_SB(sb);
> > +
> > + kfree(cifs_sb->prepath);
> > +
> > + if (*prefix && prefix_len) {
> > + cifs_sb->prepath = kstrndup(prefix, prefix_len, GFP_ATOMIC);
> > + if (!cifs_sb->prepath) {
> > + rc = -ENOMEM;
> > + goto out;
> > + }
> > +
> > + convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
> > + } else
> > + cifs_sb->prepath = NULL;
> > +
> > + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
> > +
> > +out:
> > + cifs_put_tcp_super(sb);
> > + return rc;
> > +}
> > diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> > index abc0a0512181..49b15a488ede 100644
> > --- a/fs/cifs/smb2pdu.c
> > +++ b/fs/cifs/smb2pdu.c
> > @@ -193,9 +193,18 @@ static int __smb2_reconnect(const struct nls_table *nlsc,
> >
> > for (it = dfs_cache_get_tgt_iterator(&tl); it;
> > it = dfs_cache_get_next_tgt(&tl, it)) {
> > - const char *tgt = dfs_cache_get_tgt_name(it);
> > + const char *share, *prefix;
> > + size_t share_len, prefix_len;
> >
> > - extract_unc_hostname(tgt, &dfs_host, &dfs_host_len);
> > + rc = dfs_cache_get_tgt_share(it, &share, &share_len, &prefix,
> > + &prefix_len);
> > + if (rc) {
> > + cifs_dbg(VFS, "%s: failed to parse target share %d\n",
> > + __func__, rc);
> > + continue;
> > + }
> > +
> > + extract_unc_hostname(share, &dfs_host, &dfs_host_len);
> >
> > if (dfs_host_len != tcp_host_len
> > || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
> > @@ -206,11 +215,13 @@ static int __smb2_reconnect(const struct nls_table *nlsc,
> > continue;
> > }
> >
> > - scnprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);
> > + scnprintf(tree, MAX_TREE_SIZE, "\\%.*s", (int)share_len, share);
> >
> > rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc);
> > - if (!rc)
> > + if (!rc) {
> > + rc = update_super_prepath(tcon, prefix, prefix_len);
> > break;
> > + }
> > if (rc == -EREMOTE)
> > break;
> > }
> > --
> > 2.25.0
> >
--
Thanks,
Steve
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] cifs: handle prefix paths in reconnect
2020-02-21 0:30 ` Steve French
@ 2020-02-21 0:33 ` Steve French
0 siblings, 0 replies; 6+ messages in thread
From: Steve French @ 2020-02-21 0:33 UTC (permalink / raw)
To: ronnie sahlberg; +Cc: Paulo Alcantara (SUSE), linux-cifs
And kicked off DFS automated tests in buildbot (after updating github
tree as well)
http://smb3-test-rhel-75.southcentralus.cloudapp.azure.com/#/builders/3/builds/69
On Thu, Feb 20, 2020 at 6:30 PM Steve French <smfrench@gmail.com> wrote:
>
> merged both into cifs-2.6.git for-next (the 2nd one earlier, so it
> could be sent to a 5.6-rc ie before 5.7)
>
> On Thu, Feb 20, 2020 at 6:08 PM ronnie sahlberg
> <ronniesahlberg@gmail.com> wrote:
> >
> > Acked-by: Ronnie Sahlberg <lsahlber@redhat.com>
> >
> > On Fri, Feb 21, 2020 at 8:50 AM Paulo Alcantara (SUSE) <pc@cjr.nz> wrote:
> > >
> > > For the case where we have a DFS path like below and we're currently
> > > connected to targetA:
> > >
> > > //dfsroot/link -> //targetA/share/foo, //targetB/share/bar
> > >
> > > after failover, we should make sure to update cifs_sb->prepath so the
> > > next operations will use the new prefix path "/bar".
> > >
> > > Besides, in order to simplify the use of different prefix paths,
> > > enforce CIFS_MOUNT_USE_PREFIX_PATH for DFS mounts so we don't have to
> > > revalidate the root dentry every time we set a new prefix path.
> > >
> > > Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
> > > ---
> > > fs/cifs/cifsproto.h | 5 +++
> > > fs/cifs/cifssmb.c | 19 ++++++++---
> > > fs/cifs/connect.c | 63 +++++++----------------------------
> > > fs/cifs/dfs_cache.c | 38 +++++++++++++++++++++
> > > fs/cifs/dfs_cache.h | 4 +++
> > > fs/cifs/misc.c | 80 +++++++++++++++++++++++++++++++++++++++++++++
> > > fs/cifs/smb2pdu.c | 19 ++++++++---
> > > 7 files changed, 169 insertions(+), 59 deletions(-)
> > >
> > > diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> > > index 89eaaf46d1ca..af8e79b58b94 100644
> > > --- a/fs/cifs/cifsproto.h
> > > +++ b/fs/cifs/cifsproto.h
> > > @@ -601,6 +601,11 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,
> > > int resp_buftype,
> > > struct cifs_search_info *srch_inf);
> > >
> > > +struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
> > > +void cifs_put_tcp_super(struct super_block *sb);
> > > +int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
> > > + size_t prefix_len);
> > > +
> > > #ifdef CONFIG_CIFS_DFS_UPCALL
> > > static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
> > > const char *old_path,
> > > diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
> > > index 3c89569e7210..b6fa575d2d3f 100644
> > > --- a/fs/cifs/cifssmb.c
> > > +++ b/fs/cifs/cifssmb.c
> > > @@ -162,9 +162,18 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
> > >
> > > for (it = dfs_cache_get_tgt_iterator(&tl); it;
> > > it = dfs_cache_get_next_tgt(&tl, it)) {
> > > - const char *tgt = dfs_cache_get_tgt_name(it);
> > > + const char *share, *prefix;
> > > + size_t share_len, prefix_len;
> > >
> > > - extract_unc_hostname(tgt, &dfs_host, &dfs_host_len);
> > > + rc = dfs_cache_get_tgt_share(it, &share, &share_len, &prefix,
> > > + &prefix_len);
> > > + if (rc) {
> > > + cifs_dbg(VFS, "%s: failed to parse target share %d\n",
> > > + __func__, rc);
> > > + continue;
> > > + }
> > > +
> > > + extract_unc_hostname(share, &dfs_host, &dfs_host_len);
> > >
> > > if (dfs_host_len != tcp_host_len
> > > || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
> > > @@ -175,11 +184,13 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
> > > continue;
> > > }
> > >
> > > - scnprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);
> > > + scnprintf(tree, MAX_TREE_SIZE, "\\%.*s", (int)share_len, share);
> > >
> > > rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
> > > - if (!rc)
> > > + if (!rc) {
> > > + rc = update_super_prepath(tcon, prefix, prefix_len);
> > > break;
> > > + }
> > > if (rc == -EREMOTE)
> > > break;
> > > }
> > > diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
> > > index 4804d1df8c1c..e2196b363765 100644
> > > --- a/fs/cifs/connect.c
> > > +++ b/fs/cifs/connect.c
> > > @@ -57,7 +57,6 @@
> > > #include "smb2proto.h"
> > > #include "smbdirect.h"
> > > #include "dns_resolve.h"
> > > -#include "cifsfs.h"
> > > #ifdef CONFIG_CIFS_DFS_UPCALL
> > > #include "dfs_cache.h"
> > > #endif
> > > @@ -389,54 +388,7 @@ static inline int reconn_set_ipaddr(struct TCP_Server_Info *server)
> > > #endif
> > >
> > > #ifdef CONFIG_CIFS_DFS_UPCALL
> > > -struct super_cb_data {
> > > - struct TCP_Server_Info *server;
> > > - struct super_block *sb;
> > > -};
> > > -
> > > /* These functions must be called with server->srv_mutex held */
> > > -
> > > -static void super_cb(struct super_block *sb, void *arg)
> > > -{
> > > - struct super_cb_data *d = arg;
> > > - struct cifs_sb_info *cifs_sb;
> > > - struct cifs_tcon *tcon;
> > > -
> > > - if (d->sb)
> > > - return;
> > > -
> > > - cifs_sb = CIFS_SB(sb);
> > > - tcon = cifs_sb_master_tcon(cifs_sb);
> > > - if (tcon->ses->server == d->server)
> > > - d->sb = sb;
> > > -}
> > > -
> > > -static struct super_block *get_tcp_super(struct TCP_Server_Info *server)
> > > -{
> > > - struct super_cb_data d = {
> > > - .server = server,
> > > - .sb = NULL,
> > > - };
> > > -
> > > - iterate_supers_type(&cifs_fs_type, super_cb, &d);
> > > -
> > > - if (unlikely(!d.sb))
> > > - return ERR_PTR(-ENOENT);
> > > - /*
> > > - * Grab an active reference in order to prevent automounts (DFS links)
> > > - * of expiring and then freeing up our cifs superblock pointer while
> > > - * we're doing failover.
> > > - */
> > > - cifs_sb_active(d.sb);
> > > - return d.sb;
> > > -}
> > > -
> > > -static inline void put_tcp_super(struct super_block *sb)
> > > -{
> > > - if (!IS_ERR_OR_NULL(sb))
> > > - cifs_sb_deactive(sb);
> > > -}
> > > -
> > > static void reconn_inval_dfs_target(struct TCP_Server_Info *server,
> > > struct cifs_sb_info *cifs_sb,
> > > struct dfs_cache_tgt_list *tgt_list,
> > > @@ -508,7 +460,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
> > > server->nr_targets = 1;
> > > #ifdef CONFIG_CIFS_DFS_UPCALL
> > > spin_unlock(&GlobalMid_Lock);
> > > - sb = get_tcp_super(server);
> > > + sb = cifs_get_tcp_super(server);
> > > if (IS_ERR(sb)) {
> > > rc = PTR_ERR(sb);
> > > cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n",
> > > @@ -535,7 +487,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
> > > spin_unlock(&GlobalMid_Lock);
> > > #ifdef CONFIG_CIFS_DFS_UPCALL
> > > dfs_cache_free_tgts(&tgt_list);
> > > - put_tcp_super(sb);
> > > + cifs_put_tcp_super(sb);
> > > #endif
> > > return rc;
> > > } else
> > > @@ -666,7 +618,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
> > >
> > > }
> > >
> > > - put_tcp_super(sb);
> > > + cifs_put_tcp_super(sb);
> > > #endif
> > > if (server->tcpStatus == CifsNeedNegotiate)
> > > mod_delayed_work(cifsiod_wq, &server->echo, 0);
> > > @@ -4999,6 +4951,15 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
> > > * dentry revalidation to think the dentry are stale (ESTALE).
> > > */
> > > cifs_autodisable_serverino(cifs_sb);
> > > + /*
> > > + * Force the use of prefix path to support failover on DFS paths that
> > > + * resolve to targets that have different prefix paths.
> > > + */
> > > + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
> > > + kfree(cifs_sb->prepath);
> > > + cifs_sb->prepath = vol->prepath;
> > > + vol->prepath = NULL;
> > > +
> > > out:
> > > free_xid(xid);
> > > cifs_try_adding_channels(ses);
> > > diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c
> > > index 43c1b43a07ec..a67f88bf7ae1 100644
> > > --- a/fs/cifs/dfs_cache.c
> > > +++ b/fs/cifs/dfs_cache.c
> > > @@ -1260,6 +1260,44 @@ void dfs_cache_del_vol(const char *fullpath)
> > > kref_put(&vi->refcnt, vol_release);
> > > }
> > >
> > > +/**
> > > + * dfs_cache_get_tgt_share - parse a DFS target
> > > + *
> > > + * @it: DFS target iterator.
> > > + * @share: tree name.
> > > + * @share_len: length of tree name.
> > > + * @prefix: prefix path.
> > > + * @prefix_len: length of prefix path.
> > > + *
> > > + * Return zero if target was parsed correctly, otherwise non-zero.
> > > + */
> > > +int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
> > > + const char **share, size_t *share_len,
> > > + const char **prefix, size_t *prefix_len)
> > > +{
> > > + char *s, sep;
> > > +
> > > + if (!it || !share || !share_len || !prefix || !prefix_len)
> > > + return -EINVAL;
> > > +
> > > + sep = it->it_name[0];
> > > + if (sep != '\\' && sep != '/')
> > > + return -EINVAL;
> > > +
> > > + s = strchr(it->it_name + 1, sep);
> > > + if (!s)
> > > + return -EINVAL;
> > > +
> > > + s = strchrnul(s + 1, sep);
> > > +
> > > + *share = it->it_name;
> > > + *share_len = s - it->it_name;
> > > + *prefix = *s ? s + 1 : s;
> > > + *prefix_len = &it->it_name[strlen(it->it_name)] - *prefix;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > /* Get all tcons that are within a DFS namespace and can be refreshed */
> > > static void get_tcons(struct TCP_Server_Info *server, struct list_head *head)
> > > {
> > > diff --git a/fs/cifs/dfs_cache.h b/fs/cifs/dfs_cache.h
> > > index 99ee44f8ad07..bf94d08cfb5a 100644
> > > --- a/fs/cifs/dfs_cache.h
> > > +++ b/fs/cifs/dfs_cache.h
> > > @@ -49,6 +49,10 @@ extern int dfs_cache_update_vol(const char *fullpath,
> > > struct TCP_Server_Info *server);
> > > extern void dfs_cache_del_vol(const char *fullpath);
> > >
> > > +extern int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
> > > + const char **share, size_t *share_len,
> > > + const char **prefix, size_t *prefix_len);
> > > +
> > > static inline struct dfs_cache_tgt_iterator *
> > > dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl,
> > > struct dfs_cache_tgt_iterator *it)
> > > diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
> > > index 40ca394fd5de..a456febd4109 100644
> > > --- a/fs/cifs/misc.c
> > > +++ b/fs/cifs/misc.c
> > > @@ -31,6 +31,7 @@
> > > #include "nterr.h"
> > > #include "cifs_unicode.h"
> > > #include "smb2pdu.h"
> > > +#include "cifsfs.h"
> > >
> > > extern mempool_t *cifs_sm_req_poolp;
> > > extern mempool_t *cifs_req_poolp;
> > > @@ -1022,3 +1023,82 @@ int copy_path_name(char *dst, const char *src)
> > > name_len++;
> > > return name_len;
> > > }
> > > +
> > > +struct super_cb_data {
> > > + struct TCP_Server_Info *server;
> > > + struct super_block *sb;
> > > +};
> > > +
> > > +static void super_cb(struct super_block *sb, void *arg)
> > > +{
> > > + struct super_cb_data *d = arg;
> > > + struct cifs_sb_info *cifs_sb;
> > > + struct cifs_tcon *tcon;
> > > +
> > > + if (d->sb)
> > > + return;
> > > +
> > > + cifs_sb = CIFS_SB(sb);
> > > + tcon = cifs_sb_master_tcon(cifs_sb);
> > > + if (tcon->ses->server == d->server)
> > > + d->sb = sb;
> > > +}
> > > +
> > > +struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server)
> > > +{
> > > + struct super_cb_data d = {
> > > + .server = server,
> > > + .sb = NULL,
> > > + };
> > > +
> > > + iterate_supers_type(&cifs_fs_type, super_cb, &d);
> > > +
> > > + if (unlikely(!d.sb))
> > > + return ERR_PTR(-ENOENT);
> > > + /*
> > > + * Grab an active reference in order to prevent automounts (DFS links)
> > > + * of expiring and then freeing up our cifs superblock pointer while
> > > + * we're doing failover.
> > > + */
> > > + cifs_sb_active(d.sb);
> > > + return d.sb;
> > > +}
> > > +
> > > +void cifs_put_tcp_super(struct super_block *sb)
> > > +{
> > > + if (!IS_ERR_OR_NULL(sb))
> > > + cifs_sb_deactive(sb);
> > > +}
> > > +
> > > +int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
> > > + size_t prefix_len)
> > > +{
> > > + struct super_block *sb;
> > > + struct cifs_sb_info *cifs_sb;
> > > + int rc = 0;
> > > +
> > > + sb = cifs_get_tcp_super(tcon->ses->server);
> > > + if (IS_ERR(sb))
> > > + return PTR_ERR(sb);
> > > +
> > > + cifs_sb = CIFS_SB(sb);
> > > +
> > > + kfree(cifs_sb->prepath);
> > > +
> > > + if (*prefix && prefix_len) {
> > > + cifs_sb->prepath = kstrndup(prefix, prefix_len, GFP_ATOMIC);
> > > + if (!cifs_sb->prepath) {
> > > + rc = -ENOMEM;
> > > + goto out;
> > > + }
> > > +
> > > + convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
> > > + } else
> > > + cifs_sb->prepath = NULL;
> > > +
> > > + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
> > > +
> > > +out:
> > > + cifs_put_tcp_super(sb);
> > > + return rc;
> > > +}
> > > diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> > > index abc0a0512181..49b15a488ede 100644
> > > --- a/fs/cifs/smb2pdu.c
> > > +++ b/fs/cifs/smb2pdu.c
> > > @@ -193,9 +193,18 @@ static int __smb2_reconnect(const struct nls_table *nlsc,
> > >
> > > for (it = dfs_cache_get_tgt_iterator(&tl); it;
> > > it = dfs_cache_get_next_tgt(&tl, it)) {
> > > - const char *tgt = dfs_cache_get_tgt_name(it);
> > > + const char *share, *prefix;
> > > + size_t share_len, prefix_len;
> > >
> > > - extract_unc_hostname(tgt, &dfs_host, &dfs_host_len);
> > > + rc = dfs_cache_get_tgt_share(it, &share, &share_len, &prefix,
> > > + &prefix_len);
> > > + if (rc) {
> > > + cifs_dbg(VFS, "%s: failed to parse target share %d\n",
> > > + __func__, rc);
> > > + continue;
> > > + }
> > > +
> > > + extract_unc_hostname(share, &dfs_host, &dfs_host_len);
> > >
> > > if (dfs_host_len != tcp_host_len
> > > || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
> > > @@ -206,11 +215,13 @@ static int __smb2_reconnect(const struct nls_table *nlsc,
> > > continue;
> > > }
> > >
> > > - scnprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);
> > > + scnprintf(tree, MAX_TREE_SIZE, "\\%.*s", (int)share_len, share);
> > >
> > > rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc);
> > > - if (!rc)
> > > + if (!rc) {
> > > + rc = update_super_prepath(tcon, prefix, prefix_len);
> > > break;
> > > + }
> > > if (rc == -EREMOTE)
> > > break;
> > > }
> > > --
> > > 2.25.0
> > >
>
>
>
> --
> Thanks,
>
> Steve
--
Thanks,
Steve
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] cifs: handle prefix paths in reconnect
2020-02-20 22:49 [PATCH 1/2] cifs: handle prefix paths in reconnect Paulo Alcantara (SUSE)
2020-02-20 22:49 ` [PATCH 2/2] cifs: fix potential mismatch of UNC paths Paulo Alcantara (SUSE)
2020-02-21 0:08 ` [PATCH 1/2] cifs: handle prefix paths in reconnect ronnie sahlberg
@ 2020-02-25 16:50 ` Aurélien Aptel
2 siblings, 0 replies; 6+ messages in thread
From: Aurélien Aptel @ 2020-02-25 16:50 UTC (permalink / raw)
To: Paulo Alcantara (SUSE), smfrench; +Cc: linux-cifs, Paulo Alcantara (SUSE)
Reviewed-by: Aurelien Aptel <aaptel@suse.com>
for both patch
Cheers,
--
Aurélien Aptel / SUSE Labs Samba Team
GPG: 1839 CB5F 9F5B FB9B AA97 8C99 03C8 A49B 521B D5D3
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2020-02-25 16:50 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-20 22:49 [PATCH 1/2] cifs: handle prefix paths in reconnect Paulo Alcantara (SUSE)
2020-02-20 22:49 ` [PATCH 2/2] cifs: fix potential mismatch of UNC paths Paulo Alcantara (SUSE)
2020-02-21 0:08 ` [PATCH 1/2] cifs: handle prefix paths in reconnect ronnie sahlberg
2020-02-21 0:30 ` Steve French
2020-02-21 0:33 ` Steve French
2020-02-25 16:50 ` Aurélien Aptel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).