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@xxxxxxxxx> 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@xxxxxxxxx> wrote: > > > > Acked-by: Ronnie Sahlberg <lsahlber@xxxxxxxxxx> > > > > On Fri, Feb 21, 2020 at 8:50 AM Paulo Alcantara (SUSE) <pc@xxxxxx> 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@xxxxxx> > > > --- > > > 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