Skip to content

Commit 8e35541

Browse files
Paulo AlcantaraSteve French
authored andcommitted
cifs: fix sharing of DFS connections
When matching DFS connections, we can't rely on the values set in cifs_sb_info::prepath and cifs_tcon::tree_name as they might change during DFS failover. The DFS referrals related to a specific DFS tcon are already matched earlier in match_server(), therefore we can safely skip those checks altogether as the connection is guaranteed to be unique for the DFS tcon. Besides, when creating or finding an SMB session, make sure to also refcount any DFS root session related to it (cifs_ses::dfs_root_ses), so if a new DFS mount ends up reusing the connection from the old mount while there was an umount(2) still in progress (e.g. umount(2) -> cifs_umount() -> reconnect -> cifs_put_tcon()), the connection could potentially be put right after the umount(2) finished. Patch has minor update to include fix for unused variable issue noted by the kernel test robot Reported-by: kernel test robot <[email protected]> Link: https://lore.kernel.org/oe-kbuild-all/[email protected]/ Cc: [email protected] # v6.2+ Signed-off-by: Paulo Alcantara (SUSE) <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 6be2ea3 commit 8e35541

File tree

6 files changed

+147
-80
lines changed

6 files changed

+147
-80
lines changed

fs/cifs/cifsglob.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1756,7 +1756,6 @@ struct cifs_mount_ctx {
17561756
struct TCP_Server_Info *server;
17571757
struct cifs_ses *ses;
17581758
struct cifs_tcon *tcon;
1759-
char *origin_fullpath, *leaf_fullpath;
17601759
struct list_head dfs_ses_list;
17611760
};
17621761

fs/cifs/cifsproto.h

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#ifndef _CIFSPROTO_H
99
#define _CIFSPROTO_H
1010
#include <linux/nls.h>
11+
#include <linux/ctype.h>
1112
#include "trace.h"
1213
#ifdef CONFIG_CIFS_DFS_UPCALL
1314
#include "dfs_cache.h"
@@ -572,7 +573,7 @@ extern int E_md4hash(const unsigned char *passwd, unsigned char *p16,
572573
extern struct TCP_Server_Info *
573574
cifs_find_tcp_session(struct smb3_fs_context *ctx);
574575

575-
extern void cifs_put_smb_ses(struct cifs_ses *ses);
576+
void __cifs_put_smb_ses(struct cifs_ses *ses);
576577

577578
extern struct cifs_ses *
578579
cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx);
@@ -696,4 +697,45 @@ struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
696697
void cifs_put_tcon_super(struct super_block *sb);
697698
int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry);
698699

700+
/* Put references of @ses and @ses->dfs_root_ses */
701+
static inline void cifs_put_smb_ses(struct cifs_ses *ses)
702+
{
703+
struct cifs_ses *rses = ses->dfs_root_ses;
704+
705+
__cifs_put_smb_ses(ses);
706+
if (rses)
707+
__cifs_put_smb_ses(rses);
708+
}
709+
710+
/* Get an active reference of @ses and @ses->dfs_root_ses.
711+
*
712+
* NOTE: make sure to call this function when incrementing reference count of
713+
* @ses to ensure that any DFS root session attached to it (@ses->dfs_root_ses)
714+
* will also get its reference count incremented.
715+
*
716+
* cifs_put_smb_ses() will put both references, so call it when you're done.
717+
*/
718+
static inline void cifs_smb_ses_inc_refcount(struct cifs_ses *ses)
719+
{
720+
lockdep_assert_held(&cifs_tcp_ses_lock);
721+
722+
ses->ses_count++;
723+
if (ses->dfs_root_ses)
724+
ses->dfs_root_ses->ses_count++;
725+
}
726+
727+
static inline bool dfs_src_pathname_equal(const char *s1, const char *s2)
728+
{
729+
if (strlen(s1) != strlen(s2))
730+
return false;
731+
for (; *s1; s1++, s2++) {
732+
if (*s1 == '/' || *s1 == '\\') {
733+
if (*s2 != '/' && *s2 != '\\')
734+
return false;
735+
} else if (tolower(*s1) != tolower(*s2))
736+
return false;
737+
}
738+
return true;
739+
}
740+
699741
#endif /* _CIFSPROTO_H */

fs/cifs/connect.c

Lines changed: 58 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -996,10 +996,8 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
996996
*/
997997
}
998998

999-
#ifdef CONFIG_CIFS_DFS_UPCALL
1000999
kfree(server->origin_fullpath);
10011000
kfree(server->leaf_fullpath);
1002-
#endif
10031001
kfree(server);
10041002

10051003
length = atomic_dec_return(&tcpSesAllocCount);
@@ -1387,23 +1385,8 @@ match_security(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
13871385
return true;
13881386
}
13891387

1390-
static bool dfs_src_pathname_equal(const char *s1, const char *s2)
1391-
{
1392-
if (strlen(s1) != strlen(s2))
1393-
return false;
1394-
for (; *s1; s1++, s2++) {
1395-
if (*s1 == '/' || *s1 == '\\') {
1396-
if (*s2 != '/' && *s2 != '\\')
1397-
return false;
1398-
} else if (tolower(*s1) != tolower(*s2))
1399-
return false;
1400-
}
1401-
return true;
1402-
}
1403-
14041388
/* this function must be called with srv_lock held */
1405-
static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context *ctx,
1406-
bool dfs_super_cmp)
1389+
static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
14071390
{
14081391
struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr;
14091392

@@ -1434,27 +1417,41 @@ static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context *
14341417
(struct sockaddr *)&server->srcaddr))
14351418
return 0;
14361419
/*
1437-
* When matching DFS superblocks, we only check for original source pathname as the
1438-
* currently connected target might be different than the one parsed earlier in i.e.
1439-
* mount.cifs(8).
1420+
* - Match for an DFS tcon (@server->origin_fullpath).
1421+
* - Match for an DFS root server connection (@server->leaf_fullpath).
1422+
* - If none of the above and @ctx->leaf_fullpath is set, then
1423+
* it is a new DFS connection.
1424+
* - If 'nodfs' mount option was passed, then match only connections
1425+
* that have no DFS referrals set
1426+
* (e.g. can't failover to other targets).
14401427
*/
1441-
if (dfs_super_cmp) {
1442-
if (!ctx->source || !server->origin_fullpath ||
1443-
!dfs_src_pathname_equal(server->origin_fullpath, ctx->source))
1444-
return 0;
1445-
} else {
1446-
/* Skip addr, hostname and port matching for DFS connections */
1447-
if (server->leaf_fullpath) {
1428+
if (!ctx->nodfs) {
1429+
if (ctx->source && server->origin_fullpath) {
1430+
if (!dfs_src_pathname_equal(ctx->source,
1431+
server->origin_fullpath))
1432+
return 0;
1433+
} else if (server->leaf_fullpath) {
14481434
if (!ctx->leaf_fullpath ||
1449-
strcasecmp(server->leaf_fullpath, ctx->leaf_fullpath))
1435+
strcasecmp(server->leaf_fullpath,
1436+
ctx->leaf_fullpath))
14501437
return 0;
1451-
} else if (strcasecmp(server->hostname, ctx->server_hostname) ||
1452-
!match_server_address(server, addr) ||
1453-
!match_port(server, addr)) {
1438+
} else if (ctx->leaf_fullpath) {
14541439
return 0;
14551440
}
1441+
} else if (server->origin_fullpath || server->leaf_fullpath) {
1442+
return 0;
14561443
}
14571444

1445+
/*
1446+
* Match for a regular connection (address/hostname/port) which has no
1447+
* DFS referrals set.
1448+
*/
1449+
if (!server->origin_fullpath && !server->leaf_fullpath &&
1450+
(strcasecmp(server->hostname, ctx->server_hostname) ||
1451+
!match_server_address(server, addr) ||
1452+
!match_port(server, addr)))
1453+
return 0;
1454+
14581455
if (!match_security(server, ctx))
14591456
return 0;
14601457

@@ -1485,7 +1482,7 @@ cifs_find_tcp_session(struct smb3_fs_context *ctx)
14851482
* Skip ses channels since they're only handled in lower layers
14861483
* (e.g. cifs_send_recv).
14871484
*/
1488-
if (CIFS_SERVER_IS_CHAN(server) || !match_server(server, ctx, false)) {
1485+
if (CIFS_SERVER_IS_CHAN(server) || !match_server(server, ctx)) {
14891486
spin_unlock(&server->srv_lock);
14901487
continue;
14911488
}
@@ -1869,7 +1866,7 @@ cifs_free_ipc(struct cifs_ses *ses)
18691866
static struct cifs_ses *
18701867
cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
18711868
{
1872-
struct cifs_ses *ses;
1869+
struct cifs_ses *ses, *ret = NULL;
18731870

18741871
spin_lock(&cifs_tcp_ses_lock);
18751872
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
@@ -1879,23 +1876,22 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
18791876
continue;
18801877
}
18811878
spin_lock(&ses->chan_lock);
1882-
if (!match_session(ses, ctx)) {
1879+
if (match_session(ses, ctx)) {
18831880
spin_unlock(&ses->chan_lock);
18841881
spin_unlock(&ses->ses_lock);
1885-
continue;
1882+
ret = ses;
1883+
break;
18861884
}
18871885
spin_unlock(&ses->chan_lock);
18881886
spin_unlock(&ses->ses_lock);
1889-
1890-
++ses->ses_count;
1891-
spin_unlock(&cifs_tcp_ses_lock);
1892-
return ses;
18931887
}
1888+
if (ret)
1889+
cifs_smb_ses_inc_refcount(ret);
18941890
spin_unlock(&cifs_tcp_ses_lock);
1895-
return NULL;
1891+
return ret;
18961892
}
18971893

1898-
void cifs_put_smb_ses(struct cifs_ses *ses)
1894+
void __cifs_put_smb_ses(struct cifs_ses *ses)
18991895
{
19001896
unsigned int rc, xid;
19011897
unsigned int chan_count;
@@ -2250,6 +2246,8 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
22502246
*/
22512247
spin_lock(&cifs_tcp_ses_lock);
22522248
ses->dfs_root_ses = ctx->dfs_root_ses;
2249+
if (ses->dfs_root_ses)
2250+
ses->dfs_root_ses->ses_count++;
22532251
list_add(&ses->smb_ses_list, &server->smb_ses_list);
22542252
spin_unlock(&cifs_tcp_ses_lock);
22552253

@@ -2266,12 +2264,15 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
22662264
}
22672265

22682266
/* this function must be called with tc_lock held */
2269-
static int match_tcon(struct cifs_tcon *tcon, struct smb3_fs_context *ctx, bool dfs_super_cmp)
2267+
static int match_tcon(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
22702268
{
2269+
struct TCP_Server_Info *server = tcon->ses->server;
2270+
22712271
if (tcon->status == TID_EXITING)
22722272
return 0;
2273-
/* Skip UNC validation when matching DFS superblocks */
2274-
if (!dfs_super_cmp && strncmp(tcon->tree_name, ctx->UNC, MAX_TREE_SIZE))
2273+
/* Skip UNC validation when matching DFS connections or superblocks */
2274+
if (!server->origin_fullpath && !server->leaf_fullpath &&
2275+
strncmp(tcon->tree_name, ctx->UNC, MAX_TREE_SIZE))
22752276
return 0;
22762277
if (tcon->seal != ctx->seal)
22772278
return 0;
@@ -2294,7 +2295,7 @@ cifs_find_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
22942295
spin_lock(&cifs_tcp_ses_lock);
22952296
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
22962297
spin_lock(&tcon->tc_lock);
2297-
if (!match_tcon(tcon, ctx, false)) {
2298+
if (!match_tcon(tcon, ctx)) {
22982299
spin_unlock(&tcon->tc_lock);
22992300
continue;
23002301
}
@@ -2670,16 +2671,22 @@ compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data)
26702671
return 1;
26712672
}
26722673

2673-
static int
2674-
match_prepath(struct super_block *sb, struct cifs_mnt_data *mnt_data)
2674+
static int match_prepath(struct super_block *sb,
2675+
struct TCP_Server_Info *server,
2676+
struct cifs_mnt_data *mnt_data)
26752677
{
2678+
struct smb3_fs_context *ctx = mnt_data->ctx;
26762679
struct cifs_sb_info *old = CIFS_SB(sb);
26772680
struct cifs_sb_info *new = mnt_data->cifs_sb;
26782681
bool old_set = (old->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) &&
26792682
old->prepath;
26802683
bool new_set = (new->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) &&
26812684
new->prepath;
26822685

2686+
if (server->origin_fullpath &&
2687+
dfs_src_pathname_equal(server->origin_fullpath, ctx->source))
2688+
return 1;
2689+
26832690
if (old_set && new_set && !strcmp(new->prepath, old->prepath))
26842691
return 1;
26852692
else if (!old_set && !new_set)
@@ -2698,7 +2705,6 @@ cifs_match_super(struct super_block *sb, void *data)
26982705
struct cifs_ses *ses;
26992706
struct cifs_tcon *tcon;
27002707
struct tcon_link *tlink;
2701-
bool dfs_super_cmp;
27022708
int rc = 0;
27032709

27042710
spin_lock(&cifs_tcp_ses_lock);
@@ -2713,18 +2719,16 @@ cifs_match_super(struct super_block *sb, void *data)
27132719
ses = tcon->ses;
27142720
tcp_srv = ses->server;
27152721

2716-
dfs_super_cmp = IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && tcp_srv->origin_fullpath;
2717-
27182722
ctx = mnt_data->ctx;
27192723

27202724
spin_lock(&tcp_srv->srv_lock);
27212725
spin_lock(&ses->ses_lock);
27222726
spin_lock(&ses->chan_lock);
27232727
spin_lock(&tcon->tc_lock);
2724-
if (!match_server(tcp_srv, ctx, dfs_super_cmp) ||
2728+
if (!match_server(tcp_srv, ctx) ||
27252729
!match_session(ses, ctx) ||
2726-
!match_tcon(tcon, ctx, dfs_super_cmp) ||
2727-
!match_prepath(sb, mnt_data)) {
2730+
!match_tcon(tcon, ctx) ||
2731+
!match_prepath(sb, tcp_srv, mnt_data)) {
27282732
rc = 0;
27292733
goto out;
27302734
}
@@ -3469,8 +3473,6 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
34693473

34703474
error:
34713475
dfs_put_root_smb_sessions(&mnt_ctx.dfs_ses_list);
3472-
kfree(mnt_ctx.origin_fullpath);
3473-
kfree(mnt_ctx.leaf_fullpath);
34743476
cifs_mount_put_conns(&mnt_ctx);
34753477
return rc;
34763478
}

0 commit comments

Comments
 (0)