Skip to content

Commit c88f7dc

Browse files
Paulo Alcantarasmfrench
authored andcommitted
cifs: support nested dfs links over reconnect
Mounting a dfs link that has nested links was already supported at mount(2), so make it work over reconnect as well. Make the following case work: * mount //root/dfs/link /mnt -o ... - final share: /server/share * in server settings - change target folder of /root/dfs/link3 to /server/share2 - change target folder of /root/dfs/link2 to /root/dfs/link3 - change target folder of /root/dfs/link to /root/dfs/link2 * mount -o remount,... /mnt - refresh all dfs referrals - mark current connection for failover - cifs_reconnect() reconnects to root server - tree_connect() * checks that /root/dfs/link2 is a link, then chase it * checks that root/dfs/link3 is a link, then chase it * finally tree connect to /server/share2 If the mounted share is no longer accessible and a reconnect had been triggered, the client will retry it from both last referral path (/root/dfs/link3) and original referral path (/root/dfs/link). Any new referral paths found while chasing dfs links over reconnect, it will be updated to TCP_Server_Info::leaf_fullpath, accordingly. Signed-off-by: Paulo Alcantara (SUSE) <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 71e6864 commit c88f7dc

File tree

9 files changed

+660
-693
lines changed

9 files changed

+660
-693
lines changed

fs/cifs/cifs_dfs_ref.c

Lines changed: 2 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -307,12 +307,8 @@ static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt,
307307
static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
308308
{
309309
struct cifs_sb_info *cifs_sb;
310-
struct cifs_ses *ses;
311-
struct cifs_tcon *tcon;
312310
void *page;
313-
char *full_path, *root_path;
314-
unsigned int xid;
315-
int rc;
311+
char *full_path;
316312
struct vfsmount *mnt;
317313

318314
cifs_dbg(FYI, "in %s\n", __func__);
@@ -324,8 +320,6 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
324320
* the double backslashes usually used in the UNC. This function
325321
* gives us the latter, so we must adjust the result.
326322
*/
327-
mnt = ERR_PTR(-ENOMEM);
328-
329323
cifs_sb = CIFS_SB(mntpt->d_sb);
330324
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) {
331325
mnt = ERR_PTR(-EREMOTE);
@@ -341,60 +335,11 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
341335
}
342336

343337
convert_delimiter(full_path, '\\');
344-
345338
cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path);
346339

347-
if (!cifs_sb_master_tlink(cifs_sb)) {
348-
cifs_dbg(FYI, "%s: master tlink is NULL\n", __func__);
349-
goto free_full_path;
350-
}
351-
352-
tcon = cifs_sb_master_tcon(cifs_sb);
353-
if (!tcon) {
354-
cifs_dbg(FYI, "%s: master tcon is NULL\n", __func__);
355-
goto free_full_path;
356-
}
357-
358-
root_path = kstrdup(tcon->treeName, GFP_KERNEL);
359-
if (!root_path) {
360-
mnt = ERR_PTR(-ENOMEM);
361-
goto free_full_path;
362-
}
363-
cifs_dbg(FYI, "%s: root path: %s\n", __func__, root_path);
364-
365-
ses = tcon->ses;
366-
xid = get_xid();
367-
368-
/*
369-
* If DFS root has been expired, then unconditionally fetch it again to
370-
* refresh DFS referral cache.
371-
*/
372-
rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb),
373-
root_path + 1, NULL, NULL);
374-
if (!rc) {
375-
rc = dfs_cache_find(xid, ses, cifs_sb->local_nls,
376-
cifs_remap(cifs_sb), full_path + 1,
377-
NULL, NULL);
378-
}
379-
380-
free_xid(xid);
381-
382-
if (rc) {
383-
mnt = ERR_PTR(rc);
384-
goto free_root_path;
385-
}
386-
/*
387-
* OK - we were able to get and cache a referral for @full_path.
388-
*
389-
* Now, pass it down to cifs_mount() and it will retry every available
390-
* node server in case of failures - no need to do it here.
391-
*/
392340
mnt = cifs_dfs_do_mount(mntpt, cifs_sb, full_path);
393-
cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__,
394-
full_path + 1, mnt);
341+
cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__, full_path + 1, mnt);
395342

396-
free_root_path:
397-
kfree(root_path);
398343
free_full_path:
399344
free_dentry_path(page);
400345
cdda_exit:

fs/cifs/cifs_fs_sb.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,6 @@ struct cifs_sb_info {
6161
/* only used when CIFS_MOUNT_USE_PREFIX_PATH is set */
6262
char *prepath;
6363

64-
/*
65-
* Canonical DFS path initially provided by the mount call. We might connect to something
66-
* different via DFS but we want to keep it to do failover properly.
67-
*/
68-
char *origin_fullpath; /* \\HOST\SHARE\[OPTIONAL PATH] */
6964
/* randomly generated 128-bit number for indexing dfs mount groups in referral cache */
7065
uuid_t dfs_mount_id;
7166
/*

fs/cifs/cifsglob.h

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,19 @@ struct TCP_Server_Info {
697697
#endif
698698
#ifdef CONFIG_CIFS_DFS_UPCALL
699699
bool is_dfs_conn; /* if a dfs connection */
700+
struct mutex refpath_lock; /* protects leaf_fullpath */
701+
/*
702+
* Canonical DFS full paths that were used to chase referrals in mount and reconnect.
703+
*
704+
* origin_fullpath: first or original referral path
705+
* leaf_fullpath: last referral path (might be changed due to nested links in reconnect)
706+
*
707+
* current_fullpath: pointer to either origin_fullpath or leaf_fullpath
708+
* NOTE: cannot be accessed outside cifs_reconnect() and smb2_reconnect()
709+
*
710+
* format: \\HOST\SHARE\[OPTIONAL PATH]
711+
*/
712+
char *origin_fullpath, *leaf_fullpath, *current_fullpath;
700713
#endif
701714
};
702715

@@ -1097,7 +1110,6 @@ struct cifs_tcon {
10971110
struct cached_fid crfid; /* Cached root fid */
10981111
/* BB add field for back pointer to sb struct(s)? */
10991112
#ifdef CONFIG_CIFS_DFS_UPCALL
1100-
char *dfs_path; /* canonical DFS path */
11011113
struct list_head ulist; /* cache update list */
11021114
#endif
11031115
};
@@ -1948,4 +1960,14 @@ static inline bool is_tcon_dfs(struct cifs_tcon *tcon)
19481960
tcon->share_flags & (SHI1005_FLAGS_DFS | SHI1005_FLAGS_DFS_ROOT);
19491961
}
19501962

1963+
static inline bool cifs_is_referral_server(struct cifs_tcon *tcon,
1964+
const struct dfs_info3_param *ref)
1965+
{
1966+
/*
1967+
* Check if all targets are capable of handling DFS referrals as per
1968+
* MS-DFSC 2.2.4 RESP_GET_DFS_REFERRAL.
1969+
*/
1970+
return is_tcon_dfs(tcon) || (ref && (ref->flags & DFSREF_REFERRAL_SERVER));
1971+
}
1972+
19511973
#endif /* _CIFS_GLOB_H */

fs/cifs/cifsproto.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,
607607

608608
struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
609609
void cifs_put_tcp_super(struct super_block *sb);
610-
int update_super_prepath(struct cifs_tcon *tcon, char *prefix);
610+
int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix);
611611
char *extract_hostname(const char *unc);
612612
char *extract_sharename(const char *unc);
613613

@@ -634,4 +634,7 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
634634
return options;
635635
}
636636

637+
struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
638+
void cifs_put_tcon_super(struct super_block *sb);
639+
637640
#endif /* _CIFSPROTO_H */

0 commit comments

Comments
 (0)