Skip to content

Commit bacd704

Browse files
Paulo Alcantara (SUSE)smfrench
authored andcommitted
cifs: handle prefix paths in reconnect
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) <[email protected]> Acked-by: Ronnie Sahlberg <[email protected]> Reviewed-by: Aurelien Aptel <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent ffdec8d commit bacd704

File tree

7 files changed

+169
-59
lines changed

7 files changed

+169
-59
lines changed

fs/cifs/cifsproto.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,11 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,
602602
int resp_buftype,
603603
struct cifs_search_info *srch_inf);
604604

605+
struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
606+
void cifs_put_tcp_super(struct super_block *sb);
607+
int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
608+
size_t prefix_len);
609+
605610
#ifdef CONFIG_CIFS_DFS_UPCALL
606611
static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
607612
const char *old_path,

fs/cifs/cifssmb.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,18 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
162162

163163
for (it = dfs_cache_get_tgt_iterator(&tl); it;
164164
it = dfs_cache_get_next_tgt(&tl, it)) {
165-
const char *tgt = dfs_cache_get_tgt_name(it);
165+
const char *share, *prefix;
166+
size_t share_len, prefix_len;
166167

167-
extract_unc_hostname(tgt, &dfs_host, &dfs_host_len);
168+
rc = dfs_cache_get_tgt_share(it, &share, &share_len, &prefix,
169+
&prefix_len);
170+
if (rc) {
171+
cifs_dbg(VFS, "%s: failed to parse target share %d\n",
172+
__func__, rc);
173+
continue;
174+
}
175+
176+
extract_unc_hostname(share, &dfs_host, &dfs_host_len);
168177

169178
if (dfs_host_len != tcp_host_len
170179
|| strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
@@ -175,11 +184,13 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
175184
continue;
176185
}
177186

178-
scnprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);
187+
scnprintf(tree, MAX_TREE_SIZE, "\\%.*s", (int)share_len, share);
179188

180189
rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
181-
if (!rc)
190+
if (!rc) {
191+
rc = update_super_prepath(tcon, prefix, prefix_len);
182192
break;
193+
}
183194
if (rc == -EREMOTE)
184195
break;
185196
}

fs/cifs/connect.c

Lines changed: 12 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@
5757
#include "smb2proto.h"
5858
#include "smbdirect.h"
5959
#include "dns_resolve.h"
60-
#include "cifsfs.h"
6160
#ifdef CONFIG_CIFS_DFS_UPCALL
6261
#include "dfs_cache.h"
6362
#endif
@@ -389,54 +388,7 @@ static inline int reconn_set_ipaddr(struct TCP_Server_Info *server)
389388
#endif
390389

391390
#ifdef CONFIG_CIFS_DFS_UPCALL
392-
struct super_cb_data {
393-
struct TCP_Server_Info *server;
394-
struct super_block *sb;
395-
};
396-
397391
/* These functions must be called with server->srv_mutex held */
398-
399-
static void super_cb(struct super_block *sb, void *arg)
400-
{
401-
struct super_cb_data *d = arg;
402-
struct cifs_sb_info *cifs_sb;
403-
struct cifs_tcon *tcon;
404-
405-
if (d->sb)
406-
return;
407-
408-
cifs_sb = CIFS_SB(sb);
409-
tcon = cifs_sb_master_tcon(cifs_sb);
410-
if (tcon->ses->server == d->server)
411-
d->sb = sb;
412-
}
413-
414-
static struct super_block *get_tcp_super(struct TCP_Server_Info *server)
415-
{
416-
struct super_cb_data d = {
417-
.server = server,
418-
.sb = NULL,
419-
};
420-
421-
iterate_supers_type(&cifs_fs_type, super_cb, &d);
422-
423-
if (unlikely(!d.sb))
424-
return ERR_PTR(-ENOENT);
425-
/*
426-
* Grab an active reference in order to prevent automounts (DFS links)
427-
* of expiring and then freeing up our cifs superblock pointer while
428-
* we're doing failover.
429-
*/
430-
cifs_sb_active(d.sb);
431-
return d.sb;
432-
}
433-
434-
static inline void put_tcp_super(struct super_block *sb)
435-
{
436-
if (!IS_ERR_OR_NULL(sb))
437-
cifs_sb_deactive(sb);
438-
}
439-
440392
static void reconn_inval_dfs_target(struct TCP_Server_Info *server,
441393
struct cifs_sb_info *cifs_sb,
442394
struct dfs_cache_tgt_list *tgt_list,
@@ -508,7 +460,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
508460
server->nr_targets = 1;
509461
#ifdef CONFIG_CIFS_DFS_UPCALL
510462
spin_unlock(&GlobalMid_Lock);
511-
sb = get_tcp_super(server);
463+
sb = cifs_get_tcp_super(server);
512464
if (IS_ERR(sb)) {
513465
rc = PTR_ERR(sb);
514466
cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n",
@@ -535,7 +487,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
535487
spin_unlock(&GlobalMid_Lock);
536488
#ifdef CONFIG_CIFS_DFS_UPCALL
537489
dfs_cache_free_tgts(&tgt_list);
538-
put_tcp_super(sb);
490+
cifs_put_tcp_super(sb);
539491
#endif
540492
return rc;
541493
} else
@@ -666,7 +618,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
666618

667619
}
668620

669-
put_tcp_super(sb);
621+
cifs_put_tcp_super(sb);
670622
#endif
671623
if (server->tcpStatus == CifsNeedNegotiate)
672624
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)
49994951
* dentry revalidation to think the dentry are stale (ESTALE).
50004952
*/
50014953
cifs_autodisable_serverino(cifs_sb);
4954+
/*
4955+
* Force the use of prefix path to support failover on DFS paths that
4956+
* resolve to targets that have different prefix paths.
4957+
*/
4958+
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
4959+
kfree(cifs_sb->prepath);
4960+
cifs_sb->prepath = vol->prepath;
4961+
vol->prepath = NULL;
4962+
50024963
out:
50034964
free_xid(xid);
50044965
cifs_try_adding_channels(ses);

fs/cifs/dfs_cache.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,6 +1260,44 @@ void dfs_cache_del_vol(const char *fullpath)
12601260
kref_put(&vi->refcnt, vol_release);
12611261
}
12621262

1263+
/**
1264+
* dfs_cache_get_tgt_share - parse a DFS target
1265+
*
1266+
* @it: DFS target iterator.
1267+
* @share: tree name.
1268+
* @share_len: length of tree name.
1269+
* @prefix: prefix path.
1270+
* @prefix_len: length of prefix path.
1271+
*
1272+
* Return zero if target was parsed correctly, otherwise non-zero.
1273+
*/
1274+
int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
1275+
const char **share, size_t *share_len,
1276+
const char **prefix, size_t *prefix_len)
1277+
{
1278+
char *s, sep;
1279+
1280+
if (!it || !share || !share_len || !prefix || !prefix_len)
1281+
return -EINVAL;
1282+
1283+
sep = it->it_name[0];
1284+
if (sep != '\\' && sep != '/')
1285+
return -EINVAL;
1286+
1287+
s = strchr(it->it_name + 1, sep);
1288+
if (!s)
1289+
return -EINVAL;
1290+
1291+
s = strchrnul(s + 1, sep);
1292+
1293+
*share = it->it_name;
1294+
*share_len = s - it->it_name;
1295+
*prefix = *s ? s + 1 : s;
1296+
*prefix_len = &it->it_name[strlen(it->it_name)] - *prefix;
1297+
1298+
return 0;
1299+
}
1300+
12631301
/* Get all tcons that are within a DFS namespace and can be refreshed */
12641302
static void get_tcons(struct TCP_Server_Info *server, struct list_head *head)
12651303
{

fs/cifs/dfs_cache.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ extern int dfs_cache_update_vol(const char *fullpath,
4949
struct TCP_Server_Info *server);
5050
extern void dfs_cache_del_vol(const char *fullpath);
5151

52+
extern int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
53+
const char **share, size_t *share_len,
54+
const char **prefix, size_t *prefix_len);
55+
5256
static inline struct dfs_cache_tgt_iterator *
5357
dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl,
5458
struct dfs_cache_tgt_iterator *it)

fs/cifs/misc.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "nterr.h"
3232
#include "cifs_unicode.h"
3333
#include "smb2pdu.h"
34+
#include "cifsfs.h"
3435

3536
extern mempool_t *cifs_sm_req_poolp;
3637
extern mempool_t *cifs_req_poolp;
@@ -1022,3 +1023,82 @@ int copy_path_name(char *dst, const char *src)
10221023
name_len++;
10231024
return name_len;
10241025
}
1026+
1027+
struct super_cb_data {
1028+
struct TCP_Server_Info *server;
1029+
struct super_block *sb;
1030+
};
1031+
1032+
static void super_cb(struct super_block *sb, void *arg)
1033+
{
1034+
struct super_cb_data *d = arg;
1035+
struct cifs_sb_info *cifs_sb;
1036+
struct cifs_tcon *tcon;
1037+
1038+
if (d->sb)
1039+
return;
1040+
1041+
cifs_sb = CIFS_SB(sb);
1042+
tcon = cifs_sb_master_tcon(cifs_sb);
1043+
if (tcon->ses->server == d->server)
1044+
d->sb = sb;
1045+
}
1046+
1047+
struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server)
1048+
{
1049+
struct super_cb_data d = {
1050+
.server = server,
1051+
.sb = NULL,
1052+
};
1053+
1054+
iterate_supers_type(&cifs_fs_type, super_cb, &d);
1055+
1056+
if (unlikely(!d.sb))
1057+
return ERR_PTR(-ENOENT);
1058+
/*
1059+
* Grab an active reference in order to prevent automounts (DFS links)
1060+
* of expiring and then freeing up our cifs superblock pointer while
1061+
* we're doing failover.
1062+
*/
1063+
cifs_sb_active(d.sb);
1064+
return d.sb;
1065+
}
1066+
1067+
void cifs_put_tcp_super(struct super_block *sb)
1068+
{
1069+
if (!IS_ERR_OR_NULL(sb))
1070+
cifs_sb_deactive(sb);
1071+
}
1072+
1073+
int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
1074+
size_t prefix_len)
1075+
{
1076+
struct super_block *sb;
1077+
struct cifs_sb_info *cifs_sb;
1078+
int rc = 0;
1079+
1080+
sb = cifs_get_tcp_super(tcon->ses->server);
1081+
if (IS_ERR(sb))
1082+
return PTR_ERR(sb);
1083+
1084+
cifs_sb = CIFS_SB(sb);
1085+
1086+
kfree(cifs_sb->prepath);
1087+
1088+
if (*prefix && prefix_len) {
1089+
cifs_sb->prepath = kstrndup(prefix, prefix_len, GFP_ATOMIC);
1090+
if (!cifs_sb->prepath) {
1091+
rc = -ENOMEM;
1092+
goto out;
1093+
}
1094+
1095+
convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
1096+
} else
1097+
cifs_sb->prepath = NULL;
1098+
1099+
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
1100+
1101+
out:
1102+
cifs_put_tcp_super(sb);
1103+
return rc;
1104+
}

fs/cifs/smb2pdu.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,18 @@ static int __smb2_reconnect(const struct nls_table *nlsc,
193193

194194
for (it = dfs_cache_get_tgt_iterator(&tl); it;
195195
it = dfs_cache_get_next_tgt(&tl, it)) {
196-
const char *tgt = dfs_cache_get_tgt_name(it);
196+
const char *share, *prefix;
197+
size_t share_len, prefix_len;
197198

198-
extract_unc_hostname(tgt, &dfs_host, &dfs_host_len);
199+
rc = dfs_cache_get_tgt_share(it, &share, &share_len, &prefix,
200+
&prefix_len);
201+
if (rc) {
202+
cifs_dbg(VFS, "%s: failed to parse target share %d\n",
203+
__func__, rc);
204+
continue;
205+
}
206+
207+
extract_unc_hostname(share, &dfs_host, &dfs_host_len);
199208

200209
if (dfs_host_len != tcp_host_len
201210
|| strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
@@ -206,11 +215,13 @@ static int __smb2_reconnect(const struct nls_table *nlsc,
206215
continue;
207216
}
208217

209-
scnprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);
218+
scnprintf(tree, MAX_TREE_SIZE, "\\%.*s", (int)share_len, share);
210219

211220
rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc);
212-
if (!rc)
221+
if (!rc) {
222+
rc = update_super_prepath(tcon, prefix, prefix_len);
213223
break;
224+
}
214225
if (rc == -EREMOTE)
215226
break;
216227
}

0 commit comments

Comments
 (0)