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