Skip to content

Commit 3ae872d

Browse files
Paulo AlcantaraSteve French
authored andcommitted
smb: client: fix shared DFS root mounts with different prefixes
When having two DFS root mounts that are connected to same namespace, same mount options but different prefix paths, we can't really use the shared @server->origin_fullpath when chasing DFS links in them. Move the origin_fullpath field to cifs_tcon structure so when having shared DFS root mounts with different prefix paths, and we need to chase any DFS links, dfs_get_automount_devname() will pick up the correct full path out of the @tcon that will be used for the new mount. Before patch mount.cifs //dom/dfs/dir /mnt/1 -o ... mount.cifs //dom/dfs /mnt/2 -o ... # shared server, ses, tcon # server: origin_fullpath=//dom/dfs/dir # @server->origin_fullpath + '/dir/link1' $ ls /mnt/2/dir/link1 ls: cannot open directory '/mnt/2/dir/link1': No such file or directory After patch mount.cifs //dom/dfs/dir /mnt/1 -o ... mount.cifs //dom/dfs /mnt/2 -o ... # shared server & ses # tcon_1: origin_fullpath=//dom/dfs/dir # tcon_2: origin_fullpath=//dom/dfs # @tcon_2->origin_fullpath + '/dir/link1' $ ls /mnt/2/dir/link1 dir0 dir1 dir10 dir3 dir5 dir6 dir7 dir9 target2_file.txt tsub Fixes: 8e35541 ("cifs: fix sharing of DFS connections") Signed-off-by: Paulo Alcantara (SUSE) <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 49024ec commit 3ae872d

File tree

8 files changed

+118
-100
lines changed

8 files changed

+118
-100
lines changed

fs/smb/client/cifs_debug.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ static void cifs_debug_tcon(struct seq_file *m, struct cifs_tcon *tcon)
122122
seq_puts(m, " nosparse");
123123
if (tcon->need_reconnect)
124124
seq_puts(m, "\tDISCONNECTED ");
125+
spin_lock(&tcon->tc_lock);
126+
if (tcon->origin_fullpath) {
127+
seq_printf(m, "\n\tDFS origin fullpath: %s",
128+
tcon->origin_fullpath);
129+
}
130+
spin_unlock(&tcon->tc_lock);
125131
seq_putc(m, '\n');
126132
}
127133

@@ -428,13 +434,9 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
428434
seq_printf(m, "\nIn Send: %d In MaxReq Wait: %d",
429435
atomic_read(&server->in_send),
430436
atomic_read(&server->num_waiters));
431-
if (IS_ENABLED(CONFIG_CIFS_DFS_UPCALL)) {
432-
if (server->origin_fullpath)
433-
seq_printf(m, "\nDFS origin full path: %s",
434-
server->origin_fullpath);
435-
if (server->leaf_fullpath)
436-
seq_printf(m, "\nDFS leaf full path: %s",
437-
server->leaf_fullpath);
437+
if (server->leaf_fullpath) {
438+
seq_printf(m, "\nDFS leaf full path: %s",
439+
server->leaf_fullpath);
438440
}
439441

440442
seq_printf(m, "\n\n\tSessions: ");

fs/smb/client/cifsglob.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -736,23 +736,20 @@ struct TCP_Server_Info {
736736
#endif
737737
struct mutex refpath_lock; /* protects leaf_fullpath */
738738
/*
739-
* origin_fullpath: Canonical copy of smb3_fs_context::source.
740-
* It is used for matching existing DFS tcons.
741-
*
742739
* leaf_fullpath: Canonical DFS referral path related to this
743740
* connection.
744741
* It is used in DFS cache refresher, reconnect and may
745742
* change due to nested DFS links.
746743
*
747-
* Both protected by @refpath_lock and @srv_lock. The @refpath_lock is
748-
* mosly used for not requiring a copy of @leaf_fullpath when getting
744+
* Protected by @refpath_lock and @srv_lock. The @refpath_lock is
745+
* mostly used for not requiring a copy of @leaf_fullpath when getting
749746
* cached or new DFS referrals (which might also sleep during I/O).
750747
* While @srv_lock is held for making string and NULL comparions against
751748
* both fields as in mount(2) and cache refresh.
752749
*
753750
* format: \\HOST\SHARE[\OPTIONAL PATH]
754751
*/
755-
char *origin_fullpath, *leaf_fullpath;
752+
char *leaf_fullpath;
756753
};
757754

758755
static inline bool is_smb1(struct TCP_Server_Info *server)
@@ -1205,6 +1202,7 @@ struct cifs_tcon {
12051202
struct delayed_work dfs_cache_work;
12061203
#endif
12071204
struct delayed_work query_interfaces; /* query interfaces workqueue job */
1205+
char *origin_fullpath; /* canonical copy of smb3_fs_context::source */
12081206
};
12091207

12101208
/*

fs/smb/client/cifsproto.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,
652652
int resp_buftype,
653653
struct cifs_search_info *srch_inf);
654654

655-
struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
655+
struct super_block *cifs_get_dfs_tcon_super(struct cifs_tcon *tcon);
656656
void cifs_put_tcp_super(struct super_block *sb);
657657
int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix);
658658
char *extract_hostname(const char *unc);

fs/smb/client/connect.c

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -996,7 +996,6 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
996996
*/
997997
}
998998

999-
kfree(server->origin_fullpath);
1000999
kfree(server->leaf_fullpath);
10011000
kfree(server);
10021001

@@ -1436,7 +1435,9 @@ match_security(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
14361435
}
14371436

14381437
/* this function must be called with srv_lock held */
1439-
static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
1438+
static int match_server(struct TCP_Server_Info *server,
1439+
struct smb3_fs_context *ctx,
1440+
bool match_super)
14401441
{
14411442
struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr;
14421443

@@ -1467,36 +1468,38 @@ static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context *
14671468
(struct sockaddr *)&server->srcaddr))
14681469
return 0;
14691470
/*
1470-
* - Match for an DFS tcon (@server->origin_fullpath).
1471-
* - Match for an DFS root server connection (@server->leaf_fullpath).
1472-
* - If none of the above and @ctx->leaf_fullpath is set, then
1473-
* it is a new DFS connection.
1474-
* - If 'nodfs' mount option was passed, then match only connections
1475-
* that have no DFS referrals set
1476-
* (e.g. can't failover to other targets).
1471+
* When matching cifs.ko superblocks (@match_super == true), we can't
1472+
* really match either @server->leaf_fullpath or @server->dstaddr
1473+
* directly since this @server might belong to a completely different
1474+
* server -- in case of domain-based DFS referrals or DFS links -- as
1475+
* provided earlier by mount(2) through 'source' and 'ip' options.
1476+
*
1477+
* Otherwise, match the DFS referral in @server->leaf_fullpath or the
1478+
* destination address in @server->dstaddr.
1479+
*
1480+
* When using 'nodfs' mount option, we avoid sharing it with DFS
1481+
* connections as they might failover.
14771482
*/
1478-
if (!ctx->nodfs) {
1479-
if (ctx->source && server->origin_fullpath) {
1480-
if (!dfs_src_pathname_equal(ctx->source,
1481-
server->origin_fullpath))
1483+
if (!match_super) {
1484+
if (!ctx->nodfs) {
1485+
if (server->leaf_fullpath) {
1486+
if (!ctx->leaf_fullpath ||
1487+
strcasecmp(server->leaf_fullpath,
1488+
ctx->leaf_fullpath))
1489+
return 0;
1490+
} else if (ctx->leaf_fullpath) {
14821491
return 0;
1492+
}
14831493
} else if (server->leaf_fullpath) {
1484-
if (!ctx->leaf_fullpath ||
1485-
strcasecmp(server->leaf_fullpath,
1486-
ctx->leaf_fullpath))
1487-
return 0;
1488-
} else if (ctx->leaf_fullpath) {
14891494
return 0;
14901495
}
1491-
} else if (server->origin_fullpath || server->leaf_fullpath) {
1492-
return 0;
14931496
}
14941497

14951498
/*
14961499
* Match for a regular connection (address/hostname/port) which has no
14971500
* DFS referrals set.
14981501
*/
1499-
if (!server->origin_fullpath && !server->leaf_fullpath &&
1502+
if (!server->leaf_fullpath &&
15001503
(strcasecmp(server->hostname, ctx->server_hostname) ||
15011504
!match_server_address(server, addr) ||
15021505
!match_port(server, addr)))
@@ -1532,7 +1535,8 @@ cifs_find_tcp_session(struct smb3_fs_context *ctx)
15321535
* Skip ses channels since they're only handled in lower layers
15331536
* (e.g. cifs_send_recv).
15341537
*/
1535-
if (CIFS_SERVER_IS_CHAN(server) || !match_server(server, ctx)) {
1538+
if (CIFS_SERVER_IS_CHAN(server) ||
1539+
!match_server(server, ctx, false)) {
15361540
spin_unlock(&server->srv_lock);
15371541
continue;
15381542
}
@@ -2320,10 +2324,16 @@ static int match_tcon(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
23202324

23212325
if (tcon->status == TID_EXITING)
23222326
return 0;
2323-
/* Skip UNC validation when matching DFS connections or superblocks */
2324-
if (!server->origin_fullpath && !server->leaf_fullpath &&
2325-
strncmp(tcon->tree_name, ctx->UNC, MAX_TREE_SIZE))
2327+
2328+
if (tcon->origin_fullpath) {
2329+
if (!ctx->source ||
2330+
!dfs_src_pathname_equal(ctx->source,
2331+
tcon->origin_fullpath))
2332+
return 0;
2333+
} else if (!server->leaf_fullpath &&
2334+
strncmp(tcon->tree_name, ctx->UNC, MAX_TREE_SIZE)) {
23262335
return 0;
2336+
}
23272337
if (tcon->seal != ctx->seal)
23282338
return 0;
23292339
if (tcon->snapshot_time != ctx->snapshot_time)
@@ -2722,7 +2732,7 @@ compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data)
27222732
}
27232733

27242734
static int match_prepath(struct super_block *sb,
2725-
struct TCP_Server_Info *server,
2735+
struct cifs_tcon *tcon,
27262736
struct cifs_mnt_data *mnt_data)
27272737
{
27282738
struct smb3_fs_context *ctx = mnt_data->ctx;
@@ -2733,8 +2743,8 @@ static int match_prepath(struct super_block *sb,
27332743
bool new_set = (new->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) &&
27342744
new->prepath;
27352745

2736-
if (server->origin_fullpath &&
2737-
dfs_src_pathname_equal(server->origin_fullpath, ctx->source))
2746+
if (tcon->origin_fullpath &&
2747+
dfs_src_pathname_equal(tcon->origin_fullpath, ctx->source))
27382748
return 1;
27392749

27402750
if (old_set && new_set && !strcmp(new->prepath, old->prepath))
@@ -2783,10 +2793,10 @@ cifs_match_super(struct super_block *sb, void *data)
27832793
spin_lock(&ses->ses_lock);
27842794
spin_lock(&ses->chan_lock);
27852795
spin_lock(&tcon->tc_lock);
2786-
if (!match_server(tcp_srv, ctx) ||
2796+
if (!match_server(tcp_srv, ctx, true) ||
27872797
!match_session(ses, ctx) ||
27882798
!match_tcon(tcon, ctx) ||
2789-
!match_prepath(sb, tcp_srv, mnt_data)) {
2799+
!match_prepath(sb, tcon, mnt_data)) {
27902800
rc = 0;
27912801
goto out;
27922802
}

fs/smb/client/dfs.c

Lines changed: 21 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -217,14 +217,12 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
217217
server = mnt_ctx->server;
218218
tcon = mnt_ctx->tcon;
219219

220-
mutex_lock(&server->refpath_lock);
221-
spin_lock(&server->srv_lock);
222-
if (!server->origin_fullpath) {
223-
server->origin_fullpath = origin_fullpath;
220+
spin_lock(&tcon->tc_lock);
221+
if (!tcon->origin_fullpath) {
222+
tcon->origin_fullpath = origin_fullpath;
224223
origin_fullpath = NULL;
225224
}
226-
spin_unlock(&server->srv_lock);
227-
mutex_unlock(&server->refpath_lock);
225+
spin_unlock(&tcon->tc_lock);
228226

229227
if (list_empty(&tcon->dfs_ses_list)) {
230228
list_replace_init(&mnt_ctx->dfs_ses_list,
@@ -247,18 +245,13 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
247245
{
248246
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
249247
struct cifs_ses *ses;
250-
char *source = ctx->source;
251248
bool nodfs = ctx->nodfs;
252249
int rc;
253250

254251
*isdfs = false;
255-
/* Temporarily set @ctx->source to NULL as we're not matching DFS
256-
* superblocks yet. See cifs_match_super() and match_server().
257-
*/
258-
ctx->source = NULL;
259252
rc = get_session(mnt_ctx, NULL);
260253
if (rc)
261-
goto out;
254+
return rc;
262255

263256
ctx->dfs_root_ses = mnt_ctx->ses;
264257
/*
@@ -272,15 +265,15 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
272265
rc = dfs_get_referral(mnt_ctx, ctx->UNC + 1, NULL, NULL);
273266
if (rc) {
274267
if (rc != -ENOENT && rc != -EOPNOTSUPP && rc != -EIO)
275-
goto out;
268+
return rc;
276269
nodfs = true;
277270
}
278271
}
279272
if (nodfs) {
280273
rc = cifs_mount_get_tcon(mnt_ctx);
281274
if (!rc)
282275
rc = cifs_is_path_remote(mnt_ctx);
283-
goto out;
276+
return rc;
284277
}
285278

286279
*isdfs = true;
@@ -296,12 +289,7 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
296289
rc = __dfs_mount_share(mnt_ctx);
297290
if (ses == ctx->dfs_root_ses)
298291
cifs_put_smb_ses(ses);
299-
out:
300-
/*
301-
* Restore previous value of @ctx->source so DFS superblock can be
302-
* matched in cifs_match_super().
303-
*/
304-
ctx->source = source;
292+
305293
return rc;
306294
}
307295

@@ -535,11 +523,11 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
535523
int rc;
536524
struct TCP_Server_Info *server = tcon->ses->server;
537525
const struct smb_version_operations *ops = server->ops;
538-
struct super_block *sb = NULL;
539-
struct cifs_sb_info *cifs_sb;
540526
struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
541-
char *tree;
527+
struct cifs_sb_info *cifs_sb = NULL;
528+
struct super_block *sb = NULL;
542529
struct dfs_info3_param ref = {0};
530+
char *tree;
543531

544532
/* only send once per connect */
545533
spin_lock(&tcon->tc_lock);
@@ -571,19 +559,18 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
571559
goto out;
572560
}
573561

574-
sb = cifs_get_tcp_super(server);
575-
if (IS_ERR(sb)) {
576-
rc = PTR_ERR(sb);
577-
cifs_dbg(VFS, "%s: could not find superblock: %d\n", __func__, rc);
578-
goto out;
579-
}
580-
581-
cifs_sb = CIFS_SB(sb);
562+
sb = cifs_get_dfs_tcon_super(tcon);
563+
if (!IS_ERR(sb))
564+
cifs_sb = CIFS_SB(sb);
582565

583-
/* If it is not dfs or there was no cached dfs referral, then reconnect to same share */
584-
if (!server->leaf_fullpath ||
566+
/*
567+
* Tree connect to last share in @tcon->tree_name whether dfs super or
568+
* cached dfs referral was not found.
569+
*/
570+
if (!cifs_sb || !server->leaf_fullpath ||
585571
dfs_cache_noreq_find(server->leaf_fullpath + 1, &ref, &tl)) {
586-
rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, cifs_sb->local_nls);
572+
rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon,
573+
cifs_sb ? cifs_sb->local_nls : nlsc);
587574
goto out;
588575
}
589576

fs/smb/client/dfs.h

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,15 @@ static inline char *dfs_get_automount_devname(struct dentry *dentry, void *page)
3939
{
4040
struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb);
4141
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
42-
struct TCP_Server_Info *server = tcon->ses->server;
4342
size_t len;
4443
char *s;
4544

46-
spin_lock(&server->srv_lock);
47-
if (unlikely(!server->origin_fullpath)) {
48-
spin_unlock(&server->srv_lock);
45+
spin_lock(&tcon->tc_lock);
46+
if (unlikely(!tcon->origin_fullpath)) {
47+
spin_unlock(&tcon->tc_lock);
4948
return ERR_PTR(-EREMOTE);
5049
}
51-
spin_unlock(&server->srv_lock);
50+
spin_unlock(&tcon->tc_lock);
5251

5352
s = dentry_path_raw(dentry, page, PATH_MAX);
5453
if (IS_ERR(s))
@@ -57,16 +56,16 @@ static inline char *dfs_get_automount_devname(struct dentry *dentry, void *page)
5756
if (!s[1])
5857
s++;
5958

60-
spin_lock(&server->srv_lock);
61-
len = strlen(server->origin_fullpath);
59+
spin_lock(&tcon->tc_lock);
60+
len = strlen(tcon->origin_fullpath);
6261
if (s < (char *)page + len) {
63-
spin_unlock(&server->srv_lock);
62+
spin_unlock(&tcon->tc_lock);
6463
return ERR_PTR(-ENAMETOOLONG);
6564
}
6665

6766
s -= len;
68-
memcpy(s, server->origin_fullpath, len);
69-
spin_unlock(&server->srv_lock);
67+
memcpy(s, tcon->origin_fullpath, len);
68+
spin_unlock(&tcon->tc_lock);
7069
convert_delimiter(s, '/');
7170

7271
return s;

fs/smb/client/dfs_cache.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,18 +1248,20 @@ static int refresh_tcon(struct cifs_tcon *tcon, bool force_refresh)
12481248
int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
12491249
{
12501250
struct cifs_tcon *tcon;
1251-
struct TCP_Server_Info *server;
12521251

12531252
if (!cifs_sb || !cifs_sb->master_tlink)
12541253
return -EINVAL;
12551254

12561255
tcon = cifs_sb_master_tcon(cifs_sb);
1257-
server = tcon->ses->server;
12581256

1259-
if (!server->origin_fullpath) {
1257+
spin_lock(&tcon->tc_lock);
1258+
if (!tcon->origin_fullpath) {
1259+
spin_unlock(&tcon->tc_lock);
12601260
cifs_dbg(FYI, "%s: not a dfs mount\n", __func__);
12611261
return 0;
12621262
}
1263+
spin_unlock(&tcon->tc_lock);
1264+
12631265
/*
12641266
* After reconnecting to a different server, unique ids won't match anymore, so we disable
12651267
* serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE).

0 commit comments

Comments
 (0)