* [PATCH 1/2] cifs: improve checking of DFS links over STATUS_OBJECT_NAME_INVALID
@ 2023-02-27 13:53 Paulo Alcantara
2023-02-27 13:53 ` [PATCH 2/2] cifs: prevent data race in cifs_reconnect_tcon() Paulo Alcantara
2023-02-28 22:01 ` [PATCH v2 1/2] cifs: improve checking of DFS links over STATUS_OBJECT_NAME_INVALID Paulo Alcantara
0 siblings, 2 replies; 9+ messages in thread
From: Paulo Alcantara @ 2023-02-27 13:53 UTC (permalink / raw)
To: smfrench; +Cc: linux-cifs, Paulo Alcantara
Do not map STATUS_OBJECT_NAME_INVALID to -EREMOTE under non-DFS
shares, or 'nodfs' mounts or CONFIG_CIFS_DFS_UPCALL=n builds.
Otherwise, in the slow path, get a referral to figure out whether it
is an actual DFS link.
This could be simply reproduced under a non-DFS share by running the
following
$ mount.cifs //srv/share /mnt -o ...
$ cat /mnt/$(printf '\U110000')
cat: '/mnt/'$'\364\220\200\200': Object is remote
Fixes: c877ce47e137 ("cifs: reduce roundtrips on create/qinfo requests")
Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
---
fs/cifs/cifsproto.h | 20 ++++++++++----
fs/cifs/misc.c | 66 +++++++++++++++++++++++++++++++++++++++++++++
fs/cifs/smb2inode.c | 21 ++++++++-------
fs/cifs/smb2ops.c | 23 +++++++++-------
4 files changed, 105 insertions(+), 25 deletions(-)
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index b7a36ebd0f2f..20a2f0f3f682 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -667,11 +667,21 @@ static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
int match_target_ip(struct TCP_Server_Info *server,
const char *share, size_t share_len,
bool *result);
-
-int cifs_dfs_query_info_nonascii_quirk(const unsigned int xid,
- struct cifs_tcon *tcon,
- struct cifs_sb_info *cifs_sb,
- const char *dfs_link_path);
+int cifs_inval_name_dfs_link_error(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+ const char *full_path,
+ bool *islink);
+#else
+static inline int cifs_inval_name_dfs_link_error(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+ const char *full_path,
+ bool *islink)
+{
+ *islink = false;
+ return 0;
+}
#endif
static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 2905734eb289..003b0a522fea 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -21,6 +21,7 @@
#include "cifsfs.h"
#ifdef CONFIG_CIFS_DFS_UPCALL
#include "dns_resolve.h"
+#include "dfs_cache.h"
#endif
#include "fs_context.h"
#include "cached_dir.h"
@@ -1198,4 +1199,69 @@ int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
return 0;
}
+
+/*
+ * Handle weird Windows SMB server behaviour. It responds with
+ * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request for
+ * "\<server>\<dfsname>\<linkpath>" DFS reference, where <dfsname> contains
+ * non-ASCII unicode symbols.
+ */
+int cifs_inval_name_dfs_link_error(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+ const char *full_path,
+ bool *islink)
+{
+ struct cifs_ses *ses = tcon->ses;
+ size_t len;
+ char *path;
+ char *ref_path;
+
+ *islink = false;
+
+ /*
+ * Fast path - skip check when @full_path doesn't have a prefix path to
+ * look up or tcon is not DFS.
+ */
+ if (strlen(full_path) < 2 || !cifs_sb ||
+ (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) ||
+ !is_tcon_dfs(tcon) || !ses->server->origin_fullpath)
+ return 0;
+
+ /*
+ * Slow path - tcon is DFS and @full_path has prefix path, so attempt
+ * to get a referral to figure out whether it is an DFS link.
+ */
+ len = strnlen(tcon->tree_name, MAX_TREE_SIZE + 1) + strlen(full_path) + 1;
+ path = kmalloc(len, GFP_KERNEL);
+ if (!path)
+ return -ENOMEM;
+
+ scnprintf(path, len, "%s%s", tcon->tree_name, full_path);
+ ref_path = dfs_cache_canonical_path(path + 1, cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+ if (IS_ERR(ref_path)) {
+ if (PTR_ERR(ref_path) != -EINVAL)
+ return PTR_ERR(ref_path);
+ } else if (ses->server->ops->get_dfs_refer) {
+ struct dfs_info3_param *refs = NULL;
+ int num_refs = 0;
+
+ /*
+ * XXX: we are not using dfs_cache_find() here because we might
+ * end filling all the DFS cache and thus potentially
+ * removing cached DFS targets that the client would eventually
+ * need during failover.
+ */
+ if (!ses->server->ops->get_dfs_refer(xid, ses, ref_path, &refs,
+ &num_refs, cifs_sb->local_nls,
+ cifs_remap(cifs_sb)))
+ *islink = refs[0].server_type == DFS_TYPE_LINK;
+ free_dfs_info_array(refs, num_refs);
+ kfree(ref_path);
+ }
+
+ kfree(path);
+ return 0;
+}
#endif
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index 37b4cd59245d..9b956294e864 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -527,12 +527,13 @@ int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path,
struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse)
{
- int rc;
__u32 create_options = 0;
struct cifsFileInfo *cfile;
struct cached_fid *cfid = NULL;
struct kvec err_iov[3] = {};
int err_buftype[3] = {};
+ bool islink;
+ int rc, rc2;
*adjust_tz = false;
*reparse = false;
@@ -580,15 +581,15 @@ int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
SMB2_OP_QUERY_INFO, cfile, NULL, NULL,
NULL, NULL);
goto out;
- } else if (rc != -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) &&
- hdr->Status == STATUS_OBJECT_NAME_INVALID) {
- /*
- * Handle weird Windows SMB server behaviour. It responds with
- * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request
- * for "\<server>\<dfsname>\<linkpath>" DFS reference,
- * where <dfsname> contains non-ASCII unicode symbols.
- */
- rc = -EREMOTE;
+ } else if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) {
+ rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,
+ full_path, &islink);
+ if (rc2) {
+ rc = rc2;
+ goto out;
+ }
+ if (islink)
+ rc = -EREMOTE;
}
if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb &&
(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS))
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index f79b075f2992..6dfb865ee9d7 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -796,7 +796,6 @@ static int
smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path)
{
- int rc;
__le16 *utf16_path;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
int err_buftype = CIFS_NO_BUFFER;
@@ -804,6 +803,8 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
struct kvec err_iov = {};
struct cifs_fid fid;
struct cached_fid *cfid;
+ bool islink;
+ int rc, rc2;
rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid);
if (!rc) {
@@ -833,15 +834,17 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
if (unlikely(!hdr || err_buftype == CIFS_NO_BUFFER))
goto out;
- /*
- * Handle weird Windows SMB server behaviour. It responds with
- * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request
- * for "\<server>\<dfsname>\<linkpath>" DFS reference,
- * where <dfsname> contains non-ASCII unicode symbols.
- */
- if (rc != -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) &&
- hdr->Status == STATUS_OBJECT_NAME_INVALID)
- rc = -EREMOTE;
+
+ if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) {
+ rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,
+ full_path, &islink);
+ if (rc2) {
+ rc = rc2;
+ goto out;
+ }
+ if (islink)
+ rc = -EREMOTE;
+ }
if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb &&
(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS))
rc = -EOPNOTSUPP;
--
2.39.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/2] cifs: prevent data race in cifs_reconnect_tcon()
2023-02-27 13:53 [PATCH 1/2] cifs: improve checking of DFS links over STATUS_OBJECT_NAME_INVALID Paulo Alcantara
@ 2023-02-27 13:53 ` Paulo Alcantara
2023-02-27 22:40 ` Steve French
2023-02-28 22:01 ` [PATCH v2 1/2] cifs: improve checking of DFS links over STATUS_OBJECT_NAME_INVALID Paulo Alcantara
1 sibling, 1 reply; 9+ messages in thread
From: Paulo Alcantara @ 2023-02-27 13:53 UTC (permalink / raw)
To: smfrench; +Cc: linux-cifs, Paulo Alcantara
Make sure to get an up-to-date TCP_Server_Info::nr_targets value prior
to waiting the server to be reconnected in cifs_reconnect_tcon(). It
is set in cifs_tcp_ses_needs_reconnect() and protected by
TCP_Server_Info::srv_lock.
Create a new cifs_wait_for_server_reconnect() helper that can be used
by both SMB2+ and CIFS reconnect code.
Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
---
fs/cifs/cifsproto.h | 1 +
fs/cifs/cifssmb.c | 43 ++----------------------
fs/cifs/misc.c | 44 ++++++++++++++++++++++++
fs/cifs/smb2pdu.c | 82 ++++++++++++---------------------------------
4 files changed, 69 insertions(+), 101 deletions(-)
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 20a2f0f3f682..e2eff66eefab 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -694,5 +694,6 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
void cifs_put_tcon_super(struct super_block *sb);
+int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry);
#endif /* _CIFSPROTO_H */
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index a24e4ddf8043..a43c78396dd8 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -72,7 +72,6 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
struct cifs_ses *ses;
struct TCP_Server_Info *server;
struct nls_table *nls_codepage;
- int retries;
/*
* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for
@@ -102,45 +101,9 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
}
spin_unlock(&tcon->tc_lock);
- retries = server->nr_targets;
-
- /*
- * Give demultiplex thread up to 10 seconds to each target available for
- * reconnect -- should be greater than cifs socket timeout which is 7
- * seconds.
- */
- while (server->tcpStatus == CifsNeedReconnect) {
- rc = wait_event_interruptible_timeout(server->response_q,
- (server->tcpStatus != CifsNeedReconnect),
- 10 * HZ);
- if (rc < 0) {
- cifs_dbg(FYI, "%s: aborting reconnect due to a received signal by the process\n",
- __func__);
- return -ERESTARTSYS;
- }
-
- /* are we still trying to reconnect? */
- spin_lock(&server->srv_lock);
- if (server->tcpStatus != CifsNeedReconnect) {
- spin_unlock(&server->srv_lock);
- break;
- }
- spin_unlock(&server->srv_lock);
-
- if (retries && --retries)
- continue;
-
- /*
- * on "soft" mounts we wait once. Hard mounts keep
- * retrying until process is killed or server comes
- * back on-line
- */
- if (!tcon->retry) {
- cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n");
- return -EHOSTDOWN;
- }
- retries = server->nr_targets;
- }
+ rc = cifs_wait_for_server_reconnect(server, tcon->retry);
+ if (rc)
+ return rc;
spin_lock(&ses->chan_lock);
if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) {
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 003b0a522fea..5cd7db817bee 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -1265,3 +1265,47 @@ int cifs_inval_name_dfs_link_error(const unsigned int xid,
return 0;
}
#endif
+
+int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry)
+{
+ int timeout = 10;
+ int rc;
+
+ spin_lock(&server->srv_lock);
+ if (server->tcpStatus != CifsNeedReconnect) {
+ spin_unlock(&server->srv_lock);
+ return 0;
+ }
+ timeout *= server->nr_targets;
+ spin_unlock(&server->srv_lock);
+
+ /*
+ * Give demultiplex thread up to 10 seconds to each target available for
+ * reconnect -- should be greater than cifs socket timeout which is 7
+ * seconds.
+ *
+ * On "soft" mounts we wait once. Hard mounts keep retrying until
+ * process is killed or server comes back on-line.
+ */
+ do {
+ rc = wait_event_interruptible_timeout(server->response_q,
+ (server->tcpStatus != CifsNeedReconnect),
+ timeout * HZ);
+ if (rc < 0) {
+ cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n",
+ __func__);
+ return -ERESTARTSYS;
+ }
+
+ /* are we still trying to reconnect? */
+ spin_lock(&server->srv_lock);
+ if (server->tcpStatus != CifsNeedReconnect) {
+ spin_unlock(&server->srv_lock);
+ return 0;
+ }
+ spin_unlock(&server->srv_lock);
+ } while (retry);
+
+ cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__);
+ return -EHOSTDOWN;
+}
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index ca9d7110ddcb..62c125e73b73 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -139,66 +139,6 @@ smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd,
return;
}
-static int wait_for_server_reconnect(struct TCP_Server_Info *server,
- __le16 smb2_command, bool retry)
-{
- int timeout = 10;
- int rc;
-
- spin_lock(&server->srv_lock);
- if (server->tcpStatus != CifsNeedReconnect) {
- spin_unlock(&server->srv_lock);
- return 0;
- }
- timeout *= server->nr_targets;
- spin_unlock(&server->srv_lock);
-
- /*
- * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
- * here since they are implicitly done when session drops.
- */
- switch (smb2_command) {
- /*
- * BB Should we keep oplock break and add flush to exceptions?
- */
- case SMB2_TREE_DISCONNECT:
- case SMB2_CANCEL:
- case SMB2_CLOSE:
- case SMB2_OPLOCK_BREAK:
- return -EAGAIN;
- }
-
- /*
- * Give demultiplex thread up to 10 seconds to each target available for
- * reconnect -- should be greater than cifs socket timeout which is 7
- * seconds.
- *
- * On "soft" mounts we wait once. Hard mounts keep retrying until
- * process is killed or server comes back on-line.
- */
- do {
- rc = wait_event_interruptible_timeout(server->response_q,
- (server->tcpStatus != CifsNeedReconnect),
- timeout * HZ);
- if (rc < 0) {
- cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n",
- __func__);
- return -ERESTARTSYS;
- }
-
- /* are we still trying to reconnect? */
- spin_lock(&server->srv_lock);
- if (server->tcpStatus != CifsNeedReconnect) {
- spin_unlock(&server->srv_lock);
- return 0;
- }
- spin_unlock(&server->srv_lock);
- } while (retry);
-
- cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__);
- return -EHOSTDOWN;
-}
-
static int
smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
struct TCP_Server_Info *server)
@@ -243,7 +183,27 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
(!tcon->ses->server) || !server)
return -EIO;
- rc = wait_for_server_reconnect(server, smb2_command, tcon->retry);
+ spin_lock(&server->srv_lock);
+ if (server->tcpStatus == CifsNeedReconnect) {
+ spin_unlock(&server->srv_lock);
+ /*
+ * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
+ * here since they are implicitly done when session drops.
+ */
+ switch (smb2_command) {
+ /*
+ * BB Should we keep oplock break and add flush to exceptions?
+ */
+ case SMB2_TREE_DISCONNECT:
+ case SMB2_CANCEL:
+ case SMB2_CLOSE:
+ case SMB2_OPLOCK_BREAK:
+ return -EAGAIN;
+ }
+ }
+ spin_unlock(&server->srv_lock);
+
+ rc = cifs_wait_for_server_reconnect(server, tcon->retry);
if (rc)
return rc;
--
2.39.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] cifs: prevent data race in cifs_reconnect_tcon()
2023-02-27 13:53 ` [PATCH 2/2] cifs: prevent data race in cifs_reconnect_tcon() Paulo Alcantara
@ 2023-02-27 22:40 ` Steve French
2023-02-28 0:09 ` Paulo Alcantara
0 siblings, 1 reply; 9+ messages in thread
From: Steve French @ 2023-02-27 22:40 UTC (permalink / raw)
To: Paulo Alcantara; +Cc: linux-cifs
CHECK /home/smfrench/cifs-2.6/fs/cifs/smb2pdu.c
/home/smfrench/cifs-2.6/fs/cifs/smb2pdu.c:204:20: warning: context
imbalance in 'smb2_reconnect' - unexpected unlock
There is a double unlock here:
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 186)
spin_lock(&server->srv_lock);
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 187) if
(server->tcpStatus == CifsNeedReconnect) {
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 188)
spin_unlock(&server->srv_lock);
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 189) /*
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 190)
* Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 191)
* here since they are implicitly done when session drops.
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 192) */
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 193)
switch (smb2_command) {
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 194) /*
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 195)
* BB Should we keep oplock break and add flush to exceptions?
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 196) */
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 197)
case SMB2_TREE_DISCONNECT:
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 198)
case SMB2_CANCEL:
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 199)
case SMB2_CLOSE:
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 200)
case SMB2_OPLOCK_BREAK:
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 201)
return -EAGAIN;
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 202) }
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 203) }
dc9dc86eabc46 (Paulo Alcantara 2023-02-27 10:53:23 -0300 204)
spin_unlock(&server->srv_lock);
On Mon, Feb 27, 2023 at 7:53 AM Paulo Alcantara <pc@manguebit.com> wrote:
>
> Make sure to get an up-to-date TCP_Server_Info::nr_targets value prior
> to waiting the server to be reconnected in cifs_reconnect_tcon(). It
> is set in cifs_tcp_ses_needs_reconnect() and protected by
> TCP_Server_Info::srv_lock.
>
> Create a new cifs_wait_for_server_reconnect() helper that can be used
> by both SMB2+ and CIFS reconnect code.
>
> Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
> ---
> fs/cifs/cifsproto.h | 1 +
> fs/cifs/cifssmb.c | 43 ++----------------------
> fs/cifs/misc.c | 44 ++++++++++++++++++++++++
> fs/cifs/smb2pdu.c | 82 ++++++++++++---------------------------------
> 4 files changed, 69 insertions(+), 101 deletions(-)
>
> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> index 20a2f0f3f682..e2eff66eefab 100644
> --- a/fs/cifs/cifsproto.h
> +++ b/fs/cifs/cifsproto.h
> @@ -694,5 +694,6 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
>
> struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
> void cifs_put_tcon_super(struct super_block *sb);
> +int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry);
>
> #endif /* _CIFSPROTO_H */
> diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
> index a24e4ddf8043..a43c78396dd8 100644
> --- a/fs/cifs/cifssmb.c
> +++ b/fs/cifs/cifssmb.c
> @@ -72,7 +72,6 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
> struct cifs_ses *ses;
> struct TCP_Server_Info *server;
> struct nls_table *nls_codepage;
> - int retries;
>
> /*
> * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for
> @@ -102,45 +101,9 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
> }
> spin_unlock(&tcon->tc_lock);
>
> - retries = server->nr_targets;
> -
> - /*
> - * Give demultiplex thread up to 10 seconds to each target available for
> - * reconnect -- should be greater than cifs socket timeout which is 7
> - * seconds.
> - */
> - while (server->tcpStatus == CifsNeedReconnect) {
> - rc = wait_event_interruptible_timeout(server->response_q,
> - (server->tcpStatus != CifsNeedReconnect),
> - 10 * HZ);
> - if (rc < 0) {
> - cifs_dbg(FYI, "%s: aborting reconnect due to a received signal by the process\n",
> - __func__);
> - return -ERESTARTSYS;
> - }
> -
> - /* are we still trying to reconnect? */
> - spin_lock(&server->srv_lock);
> - if (server->tcpStatus != CifsNeedReconnect) {
> - spin_unlock(&server->srv_lock);
> - break;
> - }
> - spin_unlock(&server->srv_lock);
> -
> - if (retries && --retries)
> - continue;
> -
> - /*
> - * on "soft" mounts we wait once. Hard mounts keep
> - * retrying until process is killed or server comes
> - * back on-line
> - */
> - if (!tcon->retry) {
> - cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n");
> - return -EHOSTDOWN;
> - }
> - retries = server->nr_targets;
> - }
> + rc = cifs_wait_for_server_reconnect(server, tcon->retry);
> + if (rc)
> + return rc;
>
> spin_lock(&ses->chan_lock);
> if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) {
> diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
> index 003b0a522fea..5cd7db817bee 100644
> --- a/fs/cifs/misc.c
> +++ b/fs/cifs/misc.c
> @@ -1265,3 +1265,47 @@ int cifs_inval_name_dfs_link_error(const unsigned int xid,
> return 0;
> }
> #endif
> +
> +int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry)
> +{
> + int timeout = 10;
> + int rc;
> +
> + spin_lock(&server->srv_lock);
> + if (server->tcpStatus != CifsNeedReconnect) {
> + spin_unlock(&server->srv_lock);
> + return 0;
> + }
> + timeout *= server->nr_targets;
> + spin_unlock(&server->srv_lock);
> +
> + /*
> + * Give demultiplex thread up to 10 seconds to each target available for
> + * reconnect -- should be greater than cifs socket timeout which is 7
> + * seconds.
> + *
> + * On "soft" mounts we wait once. Hard mounts keep retrying until
> + * process is killed or server comes back on-line.
> + */
> + do {
> + rc = wait_event_interruptible_timeout(server->response_q,
> + (server->tcpStatus != CifsNeedReconnect),
> + timeout * HZ);
> + if (rc < 0) {
> + cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n",
> + __func__);
> + return -ERESTARTSYS;
> + }
> +
> + /* are we still trying to reconnect? */
> + spin_lock(&server->srv_lock);
> + if (server->tcpStatus != CifsNeedReconnect) {
> + spin_unlock(&server->srv_lock);
> + return 0;
> + }
> + spin_unlock(&server->srv_lock);
> + } while (retry);
> +
> + cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__);
> + return -EHOSTDOWN;
> +}
> diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> index ca9d7110ddcb..62c125e73b73 100644
> --- a/fs/cifs/smb2pdu.c
> +++ b/fs/cifs/smb2pdu.c
> @@ -139,66 +139,6 @@ smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd,
> return;
> }
>
> -static int wait_for_server_reconnect(struct TCP_Server_Info *server,
> - __le16 smb2_command, bool retry)
> -{
> - int timeout = 10;
> - int rc;
> -
> - spin_lock(&server->srv_lock);
> - if (server->tcpStatus != CifsNeedReconnect) {
> - spin_unlock(&server->srv_lock);
> - return 0;
> - }
> - timeout *= server->nr_targets;
> - spin_unlock(&server->srv_lock);
> -
> - /*
> - * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
> - * here since they are implicitly done when session drops.
> - */
> - switch (smb2_command) {
> - /*
> - * BB Should we keep oplock break and add flush to exceptions?
> - */
> - case SMB2_TREE_DISCONNECT:
> - case SMB2_CANCEL:
> - case SMB2_CLOSE:
> - case SMB2_OPLOCK_BREAK:
> - return -EAGAIN;
> - }
> -
> - /*
> - * Give demultiplex thread up to 10 seconds to each target available for
> - * reconnect -- should be greater than cifs socket timeout which is 7
> - * seconds.
> - *
> - * On "soft" mounts we wait once. Hard mounts keep retrying until
> - * process is killed or server comes back on-line.
> - */
> - do {
> - rc = wait_event_interruptible_timeout(server->response_q,
> - (server->tcpStatus != CifsNeedReconnect),
> - timeout * HZ);
> - if (rc < 0) {
> - cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n",
> - __func__);
> - return -ERESTARTSYS;
> - }
> -
> - /* are we still trying to reconnect? */
> - spin_lock(&server->srv_lock);
> - if (server->tcpStatus != CifsNeedReconnect) {
> - spin_unlock(&server->srv_lock);
> - return 0;
> - }
> - spin_unlock(&server->srv_lock);
> - } while (retry);
> -
> - cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__);
> - return -EHOSTDOWN;
> -}
> -
> static int
> smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
> struct TCP_Server_Info *server)
> @@ -243,7 +183,27 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
> (!tcon->ses->server) || !server)
> return -EIO;
>
> - rc = wait_for_server_reconnect(server, smb2_command, tcon->retry);
> + spin_lock(&server->srv_lock);
> + if (server->tcpStatus == CifsNeedReconnect) {
> + spin_unlock(&server->srv_lock);
> + /*
> + * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
> + * here since they are implicitly done when session drops.
> + */
> + switch (smb2_command) {
> + /*
> + * BB Should we keep oplock break and add flush to exceptions?
> + */
> + case SMB2_TREE_DISCONNECT:
> + case SMB2_CANCEL:
> + case SMB2_CLOSE:
> + case SMB2_OPLOCK_BREAK:
> + return -EAGAIN;
> + }
> + }
> + spin_unlock(&server->srv_lock);
> +
> + rc = cifs_wait_for_server_reconnect(server, tcon->retry);
> if (rc)
> return rc;
>
> --
> 2.39.2
>
--
Thanks,
Steve
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] cifs: prevent data race in cifs_reconnect_tcon()
2023-02-27 22:40 ` Steve French
@ 2023-02-28 0:09 ` Paulo Alcantara
0 siblings, 0 replies; 9+ messages in thread
From: Paulo Alcantara @ 2023-02-28 0:09 UTC (permalink / raw)
To: Steve French; +Cc: linux-cifs
Steve French <smfrench@gmail.com> writes:
> CHECK /home/smfrench/cifs-2.6/fs/cifs/smb2pdu.c
> /home/smfrench/cifs-2.6/fs/cifs/smb2pdu.c:204:20: warning: context
> imbalance in 'smb2_reconnect' - unexpected unlock
Thanks.
Please fold this change in
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 62c125e73b73..0e53265e1462 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -185,7 +185,6 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
spin_lock(&server->srv_lock);
if (server->tcpStatus == CifsNeedReconnect) {
- spin_unlock(&server->srv_lock);
/*
* Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
* here since they are implicitly done when session drops.
@@ -198,6 +197,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
case SMB2_CANCEL:
case SMB2_CLOSE:
case SMB2_OPLOCK_BREAK:
+ spin_unlock(&server->srv_lock);
return -EAGAIN;
}
}
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 1/2] cifs: improve checking of DFS links over STATUS_OBJECT_NAME_INVALID
2023-02-27 13:53 [PATCH 1/2] cifs: improve checking of DFS links over STATUS_OBJECT_NAME_INVALID Paulo Alcantara
2023-02-27 13:53 ` [PATCH 2/2] cifs: prevent data race in cifs_reconnect_tcon() Paulo Alcantara
@ 2023-02-28 22:01 ` Paulo Alcantara
2023-02-28 22:01 ` [PATCH v2 2/2] cifs: prevent data race in cifs_reconnect_tcon() Paulo Alcantara
2023-02-28 22:20 ` [PATCH v2 1/2] cifs: improve checking of DFS links over STATUS_OBJECT_NAME_INVALID ronnie sahlberg
1 sibling, 2 replies; 9+ messages in thread
From: Paulo Alcantara @ 2023-02-28 22:01 UTC (permalink / raw)
To: smfrench; +Cc: linux-cifs, Paulo Alcantara
Do not map STATUS_OBJECT_NAME_INVALID to -EREMOTE under non-DFS
shares, or 'nodfs' mounts or CONFIG_CIFS_DFS_UPCALL=n builds.
Otherwise, in the slow path, get a referral to figure out whether it
is an actual DFS link.
This could be simply reproduced under a non-DFS share by running the
following
$ mount.cifs //srv/share /mnt -o ...
$ cat /mnt/$(printf '\U110000')
cat: '/mnt/'$'\364\220\200\200': Object is remote
Fixes: c877ce47e137 ("cifs: reduce roundtrips on create/qinfo requests")
Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
---
v1 -> v2: fixed mem leak on PTR_ERR(ref_path) != -EINVAL
fs/cifs/cifsproto.h | 20 ++++++++++----
fs/cifs/misc.c | 67 +++++++++++++++++++++++++++++++++++++++++++++
fs/cifs/smb2inode.c | 21 +++++++-------
fs/cifs/smb2ops.c | 23 +++++++++-------
4 files changed, 106 insertions(+), 25 deletions(-)
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index b7a36ebd0f2f..20a2f0f3f682 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -667,11 +667,21 @@ static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
int match_target_ip(struct TCP_Server_Info *server,
const char *share, size_t share_len,
bool *result);
-
-int cifs_dfs_query_info_nonascii_quirk(const unsigned int xid,
- struct cifs_tcon *tcon,
- struct cifs_sb_info *cifs_sb,
- const char *dfs_link_path);
+int cifs_inval_name_dfs_link_error(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+ const char *full_path,
+ bool *islink);
+#else
+static inline int cifs_inval_name_dfs_link_error(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+ const char *full_path,
+ bool *islink)
+{
+ *islink = false;
+ return 0;
+}
#endif
static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 2905734eb289..0c6c1fc8dae9 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -21,6 +21,7 @@
#include "cifsfs.h"
#ifdef CONFIG_CIFS_DFS_UPCALL
#include "dns_resolve.h"
+#include "dfs_cache.h"
#endif
#include "fs_context.h"
#include "cached_dir.h"
@@ -1198,4 +1199,70 @@ int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
return 0;
}
+
+/*
+ * Handle weird Windows SMB server behaviour. It responds with
+ * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request for
+ * "\<server>\<dfsname>\<linkpath>" DFS reference, where <dfsname> contains
+ * non-ASCII unicode symbols.
+ */
+int cifs_inval_name_dfs_link_error(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+ const char *full_path,
+ bool *islink)
+{
+ struct cifs_ses *ses = tcon->ses;
+ size_t len;
+ char *path;
+ char *ref_path;
+
+ *islink = false;
+
+ /*
+ * Fast path - skip check when @full_path doesn't have a prefix path to
+ * look up or tcon is not DFS.
+ */
+ if (strlen(full_path) < 2 || !cifs_sb ||
+ (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) ||
+ !is_tcon_dfs(tcon) || !ses->server->origin_fullpath)
+ return 0;
+
+ /*
+ * Slow path - tcon is DFS and @full_path has prefix path, so attempt
+ * to get a referral to figure out whether it is an DFS link.
+ */
+ len = strnlen(tcon->tree_name, MAX_TREE_SIZE + 1) + strlen(full_path) + 1;
+ path = kmalloc(len, GFP_KERNEL);
+ if (!path)
+ return -ENOMEM;
+
+ scnprintf(path, len, "%s%s", tcon->tree_name, full_path);
+ ref_path = dfs_cache_canonical_path(path + 1, cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+ kfree(path);
+
+ if (IS_ERR(ref_path)) {
+ if (PTR_ERR(ref_path) != -EINVAL)
+ return PTR_ERR(ref_path);
+ } else {
+ struct dfs_info3_param *refs = NULL;
+ int num_refs = 0;
+
+ /*
+ * XXX: we are not using dfs_cache_find() here because we might
+ * end filling all the DFS cache and thus potentially
+ * removing cached DFS targets that the client would eventually
+ * need during failover.
+ */
+ if (ses->server->ops->get_dfs_refer &&
+ !ses->server->ops->get_dfs_refer(xid, ses, ref_path, &refs,
+ &num_refs, cifs_sb->local_nls,
+ cifs_remap(cifs_sb)))
+ *islink = refs[0].server_type == DFS_TYPE_LINK;
+ free_dfs_info_array(refs, num_refs);
+ kfree(ref_path);
+ }
+ return 0;
+}
#endif
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index 37b4cd59245d..9b956294e864 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -527,12 +527,13 @@ int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path,
struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse)
{
- int rc;
__u32 create_options = 0;
struct cifsFileInfo *cfile;
struct cached_fid *cfid = NULL;
struct kvec err_iov[3] = {};
int err_buftype[3] = {};
+ bool islink;
+ int rc, rc2;
*adjust_tz = false;
*reparse = false;
@@ -580,15 +581,15 @@ int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
SMB2_OP_QUERY_INFO, cfile, NULL, NULL,
NULL, NULL);
goto out;
- } else if (rc != -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) &&
- hdr->Status == STATUS_OBJECT_NAME_INVALID) {
- /*
- * Handle weird Windows SMB server behaviour. It responds with
- * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request
- * for "\<server>\<dfsname>\<linkpath>" DFS reference,
- * where <dfsname> contains non-ASCII unicode symbols.
- */
- rc = -EREMOTE;
+ } else if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) {
+ rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,
+ full_path, &islink);
+ if (rc2) {
+ rc = rc2;
+ goto out;
+ }
+ if (islink)
+ rc = -EREMOTE;
}
if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb &&
(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS))
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index f79b075f2992..6dfb865ee9d7 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -796,7 +796,6 @@ static int
smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path)
{
- int rc;
__le16 *utf16_path;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
int err_buftype = CIFS_NO_BUFFER;
@@ -804,6 +803,8 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
struct kvec err_iov = {};
struct cifs_fid fid;
struct cached_fid *cfid;
+ bool islink;
+ int rc, rc2;
rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid);
if (!rc) {
@@ -833,15 +834,17 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
if (unlikely(!hdr || err_buftype == CIFS_NO_BUFFER))
goto out;
- /*
- * Handle weird Windows SMB server behaviour. It responds with
- * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request
- * for "\<server>\<dfsname>\<linkpath>" DFS reference,
- * where <dfsname> contains non-ASCII unicode symbols.
- */
- if (rc != -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) &&
- hdr->Status == STATUS_OBJECT_NAME_INVALID)
- rc = -EREMOTE;
+
+ if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) {
+ rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,
+ full_path, &islink);
+ if (rc2) {
+ rc = rc2;
+ goto out;
+ }
+ if (islink)
+ rc = -EREMOTE;
+ }
if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb &&
(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS))
rc = -EOPNOTSUPP;
--
2.39.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 2/2] cifs: prevent data race in cifs_reconnect_tcon()
2023-02-28 22:01 ` [PATCH v2 1/2] cifs: improve checking of DFS links over STATUS_OBJECT_NAME_INVALID Paulo Alcantara
@ 2023-02-28 22:01 ` Paulo Alcantara
2023-02-28 22:16 ` Steve French
2023-02-28 22:20 ` [PATCH v2 1/2] cifs: improve checking of DFS links over STATUS_OBJECT_NAME_INVALID ronnie sahlberg
1 sibling, 1 reply; 9+ messages in thread
From: Paulo Alcantara @ 2023-02-28 22:01 UTC (permalink / raw)
To: smfrench; +Cc: linux-cifs, Paulo Alcantara
Make sure to get an up-to-date TCP_Server_Info::nr_targets value prior
to waiting the server to be reconnected in cifs_reconnect_tcon(). It
is set in cifs_tcp_ses_needs_reconnect() and protected by
TCP_Server_Info::srv_lock.
Create a new cifs_wait_for_server_reconnect() helper that can be used
by both SMB2+ and CIFS reconnect code.
Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
---
v1 -> v2: fixed double unlock pointed out by Steve
fs/cifs/cifsproto.h | 1 +
fs/cifs/cifssmb.c | 43 ++----------------------
fs/cifs/misc.c | 44 ++++++++++++++++++++++++
fs/cifs/smb2pdu.c | 82 ++++++++++++---------------------------------
4 files changed, 69 insertions(+), 101 deletions(-)
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 20a2f0f3f682..e2eff66eefab 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -694,5 +694,6 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
void cifs_put_tcon_super(struct super_block *sb);
+int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry);
#endif /* _CIFSPROTO_H */
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index a24e4ddf8043..a43c78396dd8 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -72,7 +72,6 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
struct cifs_ses *ses;
struct TCP_Server_Info *server;
struct nls_table *nls_codepage;
- int retries;
/*
* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for
@@ -102,45 +101,9 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
}
spin_unlock(&tcon->tc_lock);
- retries = server->nr_targets;
-
- /*
- * Give demultiplex thread up to 10 seconds to each target available for
- * reconnect -- should be greater than cifs socket timeout which is 7
- * seconds.
- */
- while (server->tcpStatus == CifsNeedReconnect) {
- rc = wait_event_interruptible_timeout(server->response_q,
- (server->tcpStatus != CifsNeedReconnect),
- 10 * HZ);
- if (rc < 0) {
- cifs_dbg(FYI, "%s: aborting reconnect due to a received signal by the process\n",
- __func__);
- return -ERESTARTSYS;
- }
-
- /* are we still trying to reconnect? */
- spin_lock(&server->srv_lock);
- if (server->tcpStatus != CifsNeedReconnect) {
- spin_unlock(&server->srv_lock);
- break;
- }
- spin_unlock(&server->srv_lock);
-
- if (retries && --retries)
- continue;
-
- /*
- * on "soft" mounts we wait once. Hard mounts keep
- * retrying until process is killed or server comes
- * back on-line
- */
- if (!tcon->retry) {
- cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n");
- return -EHOSTDOWN;
- }
- retries = server->nr_targets;
- }
+ rc = cifs_wait_for_server_reconnect(server, tcon->retry);
+ if (rc)
+ return rc;
spin_lock(&ses->chan_lock);
if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) {
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 0c6c1fc8dae9..a0d286ee723d 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -1266,3 +1266,47 @@ int cifs_inval_name_dfs_link_error(const unsigned int xid,
return 0;
}
#endif
+
+int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry)
+{
+ int timeout = 10;
+ int rc;
+
+ spin_lock(&server->srv_lock);
+ if (server->tcpStatus != CifsNeedReconnect) {
+ spin_unlock(&server->srv_lock);
+ return 0;
+ }
+ timeout *= server->nr_targets;
+ spin_unlock(&server->srv_lock);
+
+ /*
+ * Give demultiplex thread up to 10 seconds to each target available for
+ * reconnect -- should be greater than cifs socket timeout which is 7
+ * seconds.
+ *
+ * On "soft" mounts we wait once. Hard mounts keep retrying until
+ * process is killed or server comes back on-line.
+ */
+ do {
+ rc = wait_event_interruptible_timeout(server->response_q,
+ (server->tcpStatus != CifsNeedReconnect),
+ timeout * HZ);
+ if (rc < 0) {
+ cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n",
+ __func__);
+ return -ERESTARTSYS;
+ }
+
+ /* are we still trying to reconnect? */
+ spin_lock(&server->srv_lock);
+ if (server->tcpStatus != CifsNeedReconnect) {
+ spin_unlock(&server->srv_lock);
+ return 0;
+ }
+ spin_unlock(&server->srv_lock);
+ } while (retry);
+
+ cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__);
+ return -EHOSTDOWN;
+}
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index ca9d7110ddcb..0e53265e1462 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -139,66 +139,6 @@ smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd,
return;
}
-static int wait_for_server_reconnect(struct TCP_Server_Info *server,
- __le16 smb2_command, bool retry)
-{
- int timeout = 10;
- int rc;
-
- spin_lock(&server->srv_lock);
- if (server->tcpStatus != CifsNeedReconnect) {
- spin_unlock(&server->srv_lock);
- return 0;
- }
- timeout *= server->nr_targets;
- spin_unlock(&server->srv_lock);
-
- /*
- * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
- * here since they are implicitly done when session drops.
- */
- switch (smb2_command) {
- /*
- * BB Should we keep oplock break and add flush to exceptions?
- */
- case SMB2_TREE_DISCONNECT:
- case SMB2_CANCEL:
- case SMB2_CLOSE:
- case SMB2_OPLOCK_BREAK:
- return -EAGAIN;
- }
-
- /*
- * Give demultiplex thread up to 10 seconds to each target available for
- * reconnect -- should be greater than cifs socket timeout which is 7
- * seconds.
- *
- * On "soft" mounts we wait once. Hard mounts keep retrying until
- * process is killed or server comes back on-line.
- */
- do {
- rc = wait_event_interruptible_timeout(server->response_q,
- (server->tcpStatus != CifsNeedReconnect),
- timeout * HZ);
- if (rc < 0) {
- cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n",
- __func__);
- return -ERESTARTSYS;
- }
-
- /* are we still trying to reconnect? */
- spin_lock(&server->srv_lock);
- if (server->tcpStatus != CifsNeedReconnect) {
- spin_unlock(&server->srv_lock);
- return 0;
- }
- spin_unlock(&server->srv_lock);
- } while (retry);
-
- cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__);
- return -EHOSTDOWN;
-}
-
static int
smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
struct TCP_Server_Info *server)
@@ -243,7 +183,27 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
(!tcon->ses->server) || !server)
return -EIO;
- rc = wait_for_server_reconnect(server, smb2_command, tcon->retry);
+ spin_lock(&server->srv_lock);
+ if (server->tcpStatus == CifsNeedReconnect) {
+ /*
+ * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
+ * here since they are implicitly done when session drops.
+ */
+ switch (smb2_command) {
+ /*
+ * BB Should we keep oplock break and add flush to exceptions?
+ */
+ case SMB2_TREE_DISCONNECT:
+ case SMB2_CANCEL:
+ case SMB2_CLOSE:
+ case SMB2_OPLOCK_BREAK:
+ spin_unlock(&server->srv_lock);
+ return -EAGAIN;
+ }
+ }
+ spin_unlock(&server->srv_lock);
+
+ rc = cifs_wait_for_server_reconnect(server, tcon->retry);
if (rc)
return rc;
--
2.39.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2 2/2] cifs: prevent data race in cifs_reconnect_tcon()
2023-02-28 22:01 ` [PATCH v2 2/2] cifs: prevent data race in cifs_reconnect_tcon() Paulo Alcantara
@ 2023-02-28 22:16 ` Steve French
0 siblings, 0 replies; 9+ messages in thread
From: Steve French @ 2023-02-28 22:16 UTC (permalink / raw)
To: Paulo Alcantara; +Cc: linux-cifs
updated cifs-2.6.git for-next with these two patches
On Tue, Feb 28, 2023 at 4:02 PM Paulo Alcantara <pc@manguebit.com> wrote:
>
> Make sure to get an up-to-date TCP_Server_Info::nr_targets value prior
> to waiting the server to be reconnected in cifs_reconnect_tcon(). It
> is set in cifs_tcp_ses_needs_reconnect() and protected by
> TCP_Server_Info::srv_lock.
>
> Create a new cifs_wait_for_server_reconnect() helper that can be used
> by both SMB2+ and CIFS reconnect code.
>
> Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
> ---
> v1 -> v2: fixed double unlock pointed out by Steve
>
> fs/cifs/cifsproto.h | 1 +
> fs/cifs/cifssmb.c | 43 ++----------------------
> fs/cifs/misc.c | 44 ++++++++++++++++++++++++
> fs/cifs/smb2pdu.c | 82 ++++++++++++---------------------------------
> 4 files changed, 69 insertions(+), 101 deletions(-)
>
> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> index 20a2f0f3f682..e2eff66eefab 100644
> --- a/fs/cifs/cifsproto.h
> +++ b/fs/cifs/cifsproto.h
> @@ -694,5 +694,6 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
>
> struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
> void cifs_put_tcon_super(struct super_block *sb);
> +int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry);
>
> #endif /* _CIFSPROTO_H */
> diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
> index a24e4ddf8043..a43c78396dd8 100644
> --- a/fs/cifs/cifssmb.c
> +++ b/fs/cifs/cifssmb.c
> @@ -72,7 +72,6 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
> struct cifs_ses *ses;
> struct TCP_Server_Info *server;
> struct nls_table *nls_codepage;
> - int retries;
>
> /*
> * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for
> @@ -102,45 +101,9 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
> }
> spin_unlock(&tcon->tc_lock);
>
> - retries = server->nr_targets;
> -
> - /*
> - * Give demultiplex thread up to 10 seconds to each target available for
> - * reconnect -- should be greater than cifs socket timeout which is 7
> - * seconds.
> - */
> - while (server->tcpStatus == CifsNeedReconnect) {
> - rc = wait_event_interruptible_timeout(server->response_q,
> - (server->tcpStatus != CifsNeedReconnect),
> - 10 * HZ);
> - if (rc < 0) {
> - cifs_dbg(FYI, "%s: aborting reconnect due to a received signal by the process\n",
> - __func__);
> - return -ERESTARTSYS;
> - }
> -
> - /* are we still trying to reconnect? */
> - spin_lock(&server->srv_lock);
> - if (server->tcpStatus != CifsNeedReconnect) {
> - spin_unlock(&server->srv_lock);
> - break;
> - }
> - spin_unlock(&server->srv_lock);
> -
> - if (retries && --retries)
> - continue;
> -
> - /*
> - * on "soft" mounts we wait once. Hard mounts keep
> - * retrying until process is killed or server comes
> - * back on-line
> - */
> - if (!tcon->retry) {
> - cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n");
> - return -EHOSTDOWN;
> - }
> - retries = server->nr_targets;
> - }
> + rc = cifs_wait_for_server_reconnect(server, tcon->retry);
> + if (rc)
> + return rc;
>
> spin_lock(&ses->chan_lock);
> if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) {
> diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
> index 0c6c1fc8dae9..a0d286ee723d 100644
> --- a/fs/cifs/misc.c
> +++ b/fs/cifs/misc.c
> @@ -1266,3 +1266,47 @@ int cifs_inval_name_dfs_link_error(const unsigned int xid,
> return 0;
> }
> #endif
> +
> +int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry)
> +{
> + int timeout = 10;
> + int rc;
> +
> + spin_lock(&server->srv_lock);
> + if (server->tcpStatus != CifsNeedReconnect) {
> + spin_unlock(&server->srv_lock);
> + return 0;
> + }
> + timeout *= server->nr_targets;
> + spin_unlock(&server->srv_lock);
> +
> + /*
> + * Give demultiplex thread up to 10 seconds to each target available for
> + * reconnect -- should be greater than cifs socket timeout which is 7
> + * seconds.
> + *
> + * On "soft" mounts we wait once. Hard mounts keep retrying until
> + * process is killed or server comes back on-line.
> + */
> + do {
> + rc = wait_event_interruptible_timeout(server->response_q,
> + (server->tcpStatus != CifsNeedReconnect),
> + timeout * HZ);
> + if (rc < 0) {
> + cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n",
> + __func__);
> + return -ERESTARTSYS;
> + }
> +
> + /* are we still trying to reconnect? */
> + spin_lock(&server->srv_lock);
> + if (server->tcpStatus != CifsNeedReconnect) {
> + spin_unlock(&server->srv_lock);
> + return 0;
> + }
> + spin_unlock(&server->srv_lock);
> + } while (retry);
> +
> + cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__);
> + return -EHOSTDOWN;
> +}
> diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> index ca9d7110ddcb..0e53265e1462 100644
> --- a/fs/cifs/smb2pdu.c
> +++ b/fs/cifs/smb2pdu.c
> @@ -139,66 +139,6 @@ smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd,
> return;
> }
>
> -static int wait_for_server_reconnect(struct TCP_Server_Info *server,
> - __le16 smb2_command, bool retry)
> -{
> - int timeout = 10;
> - int rc;
> -
> - spin_lock(&server->srv_lock);
> - if (server->tcpStatus != CifsNeedReconnect) {
> - spin_unlock(&server->srv_lock);
> - return 0;
> - }
> - timeout *= server->nr_targets;
> - spin_unlock(&server->srv_lock);
> -
> - /*
> - * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
> - * here since they are implicitly done when session drops.
> - */
> - switch (smb2_command) {
> - /*
> - * BB Should we keep oplock break and add flush to exceptions?
> - */
> - case SMB2_TREE_DISCONNECT:
> - case SMB2_CANCEL:
> - case SMB2_CLOSE:
> - case SMB2_OPLOCK_BREAK:
> - return -EAGAIN;
> - }
> -
> - /*
> - * Give demultiplex thread up to 10 seconds to each target available for
> - * reconnect -- should be greater than cifs socket timeout which is 7
> - * seconds.
> - *
> - * On "soft" mounts we wait once. Hard mounts keep retrying until
> - * process is killed or server comes back on-line.
> - */
> - do {
> - rc = wait_event_interruptible_timeout(server->response_q,
> - (server->tcpStatus != CifsNeedReconnect),
> - timeout * HZ);
> - if (rc < 0) {
> - cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n",
> - __func__);
> - return -ERESTARTSYS;
> - }
> -
> - /* are we still trying to reconnect? */
> - spin_lock(&server->srv_lock);
> - if (server->tcpStatus != CifsNeedReconnect) {
> - spin_unlock(&server->srv_lock);
> - return 0;
> - }
> - spin_unlock(&server->srv_lock);
> - } while (retry);
> -
> - cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__);
> - return -EHOSTDOWN;
> -}
> -
> static int
> smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
> struct TCP_Server_Info *server)
> @@ -243,7 +183,27 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
> (!tcon->ses->server) || !server)
> return -EIO;
>
> - rc = wait_for_server_reconnect(server, smb2_command, tcon->retry);
> + spin_lock(&server->srv_lock);
> + if (server->tcpStatus == CifsNeedReconnect) {
> + /*
> + * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
> + * here since they are implicitly done when session drops.
> + */
> + switch (smb2_command) {
> + /*
> + * BB Should we keep oplock break and add flush to exceptions?
> + */
> + case SMB2_TREE_DISCONNECT:
> + case SMB2_CANCEL:
> + case SMB2_CLOSE:
> + case SMB2_OPLOCK_BREAK:
> + spin_unlock(&server->srv_lock);
> + return -EAGAIN;
> + }
> + }
> + spin_unlock(&server->srv_lock);
> +
> + rc = cifs_wait_for_server_reconnect(server, tcon->retry);
> if (rc)
> return rc;
>
> --
> 2.39.2
>
--
Thanks,
Steve
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/2] cifs: improve checking of DFS links over STATUS_OBJECT_NAME_INVALID
2023-02-28 22:01 ` [PATCH v2 1/2] cifs: improve checking of DFS links over STATUS_OBJECT_NAME_INVALID Paulo Alcantara
2023-02-28 22:01 ` [PATCH v2 2/2] cifs: prevent data race in cifs_reconnect_tcon() Paulo Alcantara
@ 2023-02-28 22:20 ` ronnie sahlberg
2023-02-28 23:35 ` Steve French
1 sibling, 1 reply; 9+ messages in thread
From: ronnie sahlberg @ 2023-02-28 22:20 UTC (permalink / raw)
To: Paulo Alcantara; +Cc: smfrench, linux-cifs
Reviewed-by: Ronnie Sahlberg <lsahlber@redhat.com>
On Wed, 1 Mar 2023 at 08:15, Paulo Alcantara <pc@manguebit.com> wrote:
>
> Do not map STATUS_OBJECT_NAME_INVALID to -EREMOTE under non-DFS
> shares, or 'nodfs' mounts or CONFIG_CIFS_DFS_UPCALL=n builds.
> Otherwise, in the slow path, get a referral to figure out whether it
> is an actual DFS link.
>
> This could be simply reproduced under a non-DFS share by running the
> following
>
> $ mount.cifs //srv/share /mnt -o ...
> $ cat /mnt/$(printf '\U110000')
> cat: '/mnt/'$'\364\220\200\200': Object is remote
>
> Fixes: c877ce47e137 ("cifs: reduce roundtrips on create/qinfo requests")
> Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
> ---
> v1 -> v2: fixed mem leak on PTR_ERR(ref_path) != -EINVAL
>
> fs/cifs/cifsproto.h | 20 ++++++++++----
> fs/cifs/misc.c | 67 +++++++++++++++++++++++++++++++++++++++++++++
> fs/cifs/smb2inode.c | 21 +++++++-------
> fs/cifs/smb2ops.c | 23 +++++++++-------
> 4 files changed, 106 insertions(+), 25 deletions(-)
>
> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> index b7a36ebd0f2f..20a2f0f3f682 100644
> --- a/fs/cifs/cifsproto.h
> +++ b/fs/cifs/cifsproto.h
> @@ -667,11 +667,21 @@ static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
> int match_target_ip(struct TCP_Server_Info *server,
> const char *share, size_t share_len,
> bool *result);
> -
> -int cifs_dfs_query_info_nonascii_quirk(const unsigned int xid,
> - struct cifs_tcon *tcon,
> - struct cifs_sb_info *cifs_sb,
> - const char *dfs_link_path);
> +int cifs_inval_name_dfs_link_error(const unsigned int xid,
> + struct cifs_tcon *tcon,
> + struct cifs_sb_info *cifs_sb,
> + const char *full_path,
> + bool *islink);
> +#else
> +static inline int cifs_inval_name_dfs_link_error(const unsigned int xid,
> + struct cifs_tcon *tcon,
> + struct cifs_sb_info *cifs_sb,
> + const char *full_path,
> + bool *islink)
> +{
> + *islink = false;
> + return 0;
> +}
> #endif
>
> static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
> diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
> index 2905734eb289..0c6c1fc8dae9 100644
> --- a/fs/cifs/misc.c
> +++ b/fs/cifs/misc.c
> @@ -21,6 +21,7 @@
> #include "cifsfs.h"
> #ifdef CONFIG_CIFS_DFS_UPCALL
> #include "dns_resolve.h"
> +#include "dfs_cache.h"
> #endif
> #include "fs_context.h"
> #include "cached_dir.h"
> @@ -1198,4 +1199,70 @@ int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix)
> cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
> return 0;
> }
> +
> +/*
> + * Handle weird Windows SMB server behaviour. It responds with
> + * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request for
> + * "\<server>\<dfsname>\<linkpath>" DFS reference, where <dfsname> contains
> + * non-ASCII unicode symbols.
> + */
> +int cifs_inval_name_dfs_link_error(const unsigned int xid,
> + struct cifs_tcon *tcon,
> + struct cifs_sb_info *cifs_sb,
> + const char *full_path,
> + bool *islink)
> +{
> + struct cifs_ses *ses = tcon->ses;
> + size_t len;
> + char *path;
> + char *ref_path;
> +
> + *islink = false;
> +
> + /*
> + * Fast path - skip check when @full_path doesn't have a prefix path to
> + * look up or tcon is not DFS.
> + */
> + if (strlen(full_path) < 2 || !cifs_sb ||
> + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) ||
> + !is_tcon_dfs(tcon) || !ses->server->origin_fullpath)
> + return 0;
> +
> + /*
> + * Slow path - tcon is DFS and @full_path has prefix path, so attempt
> + * to get a referral to figure out whether it is an DFS link.
> + */
> + len = strnlen(tcon->tree_name, MAX_TREE_SIZE + 1) + strlen(full_path) + 1;
> + path = kmalloc(len, GFP_KERNEL);
> + if (!path)
> + return -ENOMEM;
> +
> + scnprintf(path, len, "%s%s", tcon->tree_name, full_path);
> + ref_path = dfs_cache_canonical_path(path + 1, cifs_sb->local_nls,
> + cifs_remap(cifs_sb));
> + kfree(path);
> +
> + if (IS_ERR(ref_path)) {
> + if (PTR_ERR(ref_path) != -EINVAL)
> + return PTR_ERR(ref_path);
> + } else {
> + struct dfs_info3_param *refs = NULL;
> + int num_refs = 0;
> +
> + /*
> + * XXX: we are not using dfs_cache_find() here because we might
> + * end filling all the DFS cache and thus potentially
> + * removing cached DFS targets that the client would eventually
> + * need during failover.
> + */
> + if (ses->server->ops->get_dfs_refer &&
> + !ses->server->ops->get_dfs_refer(xid, ses, ref_path, &refs,
> + &num_refs, cifs_sb->local_nls,
> + cifs_remap(cifs_sb)))
> + *islink = refs[0].server_type == DFS_TYPE_LINK;
> + free_dfs_info_array(refs, num_refs);
> + kfree(ref_path);
> + }
> + return 0;
> +}
> #endif
> diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
> index 37b4cd59245d..9b956294e864 100644
> --- a/fs/cifs/smb2inode.c
> +++ b/fs/cifs/smb2inode.c
> @@ -527,12 +527,13 @@ int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
> struct cifs_sb_info *cifs_sb, const char *full_path,
> struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse)
> {
> - int rc;
> __u32 create_options = 0;
> struct cifsFileInfo *cfile;
> struct cached_fid *cfid = NULL;
> struct kvec err_iov[3] = {};
> int err_buftype[3] = {};
> + bool islink;
> + int rc, rc2;
>
> *adjust_tz = false;
> *reparse = false;
> @@ -580,15 +581,15 @@ int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
> SMB2_OP_QUERY_INFO, cfile, NULL, NULL,
> NULL, NULL);
> goto out;
> - } else if (rc != -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) &&
> - hdr->Status == STATUS_OBJECT_NAME_INVALID) {
> - /*
> - * Handle weird Windows SMB server behaviour. It responds with
> - * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request
> - * for "\<server>\<dfsname>\<linkpath>" DFS reference,
> - * where <dfsname> contains non-ASCII unicode symbols.
> - */
> - rc = -EREMOTE;
> + } else if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) {
> + rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,
> + full_path, &islink);
> + if (rc2) {
> + rc = rc2;
> + goto out;
> + }
> + if (islink)
> + rc = -EREMOTE;
> }
> if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb &&
> (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS))
> diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
> index f79b075f2992..6dfb865ee9d7 100644
> --- a/fs/cifs/smb2ops.c
> +++ b/fs/cifs/smb2ops.c
> @@ -796,7 +796,6 @@ static int
> smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
> struct cifs_sb_info *cifs_sb, const char *full_path)
> {
> - int rc;
> __le16 *utf16_path;
> __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
> int err_buftype = CIFS_NO_BUFFER;
> @@ -804,6 +803,8 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
> struct kvec err_iov = {};
> struct cifs_fid fid;
> struct cached_fid *cfid;
> + bool islink;
> + int rc, rc2;
>
> rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid);
> if (!rc) {
> @@ -833,15 +834,17 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
>
> if (unlikely(!hdr || err_buftype == CIFS_NO_BUFFER))
> goto out;
> - /*
> - * Handle weird Windows SMB server behaviour. It responds with
> - * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request
> - * for "\<server>\<dfsname>\<linkpath>" DFS reference,
> - * where <dfsname> contains non-ASCII unicode symbols.
> - */
> - if (rc != -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) &&
> - hdr->Status == STATUS_OBJECT_NAME_INVALID)
> - rc = -EREMOTE;
> +
> + if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) {
> + rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,
> + full_path, &islink);
> + if (rc2) {
> + rc = rc2;
> + goto out;
> + }
> + if (islink)
> + rc = -EREMOTE;
> + }
> if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb &&
> (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS))
> rc = -EOPNOTSUPP;
> --
> 2.39.2
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/2] cifs: improve checking of DFS links over STATUS_OBJECT_NAME_INVALID
2023-02-28 22:20 ` [PATCH v2 1/2] cifs: improve checking of DFS links over STATUS_OBJECT_NAME_INVALID ronnie sahlberg
@ 2023-02-28 23:35 ` Steve French
0 siblings, 0 replies; 9+ messages in thread
From: Steve French @ 2023-02-28 23:35 UTC (permalink / raw)
To: ronnie sahlberg; +Cc: Paulo Alcantara, linux-cifs
updated to add RB tag and added:
CC: stable@vger.kernel.org # 6.2
On Tue, Feb 28, 2023 at 4:21 PM ronnie sahlberg
<ronniesahlberg@gmail.com> wrote:
>
> Reviewed-by: Ronnie Sahlberg <lsahlber@redhat.com>
>
> On Wed, 1 Mar 2023 at 08:15, Paulo Alcantara <pc@manguebit.com> wrote:
> >
> > Do not map STATUS_OBJECT_NAME_INVALID to -EREMOTE under non-DFS
> > shares, or 'nodfs' mounts or CONFIG_CIFS_DFS_UPCALL=n builds.
> > Otherwise, in the slow path, get a referral to figure out whether it
> > is an actual DFS link.
> >
> > This could be simply reproduced under a non-DFS share by running the
> > following
> >
> > $ mount.cifs //srv/share /mnt -o ...
> > $ cat /mnt/$(printf '\U110000')
> > cat: '/mnt/'$'\364\220\200\200': Object is remote
> >
> > Fixes: c877ce47e137 ("cifs: reduce roundtrips on create/qinfo requests")
> > Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
> > ---
> > v1 -> v2: fixed mem leak on PTR_ERR(ref_path) != -EINVAL
> >
> > fs/cifs/cifsproto.h | 20 ++++++++++----
> > fs/cifs/misc.c | 67 +++++++++++++++++++++++++++++++++++++++++++++
> > fs/cifs/smb2inode.c | 21 +++++++-------
> > fs/cifs/smb2ops.c | 23 +++++++++-------
> > 4 files changed, 106 insertions(+), 25 deletions(-)
> >
> > diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> > index b7a36ebd0f2f..20a2f0f3f682 100644
> > --- a/fs/cifs/cifsproto.h
> > +++ b/fs/cifs/cifsproto.h
> > @@ -667,11 +667,21 @@ static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
> > int match_target_ip(struct TCP_Server_Info *server,
> > const char *share, size_t share_len,
> > bool *result);
> > -
> > -int cifs_dfs_query_info_nonascii_quirk(const unsigned int xid,
> > - struct cifs_tcon *tcon,
> > - struct cifs_sb_info *cifs_sb,
> > - const char *dfs_link_path);
> > +int cifs_inval_name_dfs_link_error(const unsigned int xid,
> > + struct cifs_tcon *tcon,
> > + struct cifs_sb_info *cifs_sb,
> > + const char *full_path,
> > + bool *islink);
> > +#else
> > +static inline int cifs_inval_name_dfs_link_error(const unsigned int xid,
> > + struct cifs_tcon *tcon,
> > + struct cifs_sb_info *cifs_sb,
> > + const char *full_path,
> > + bool *islink)
> > +{
> > + *islink = false;
> > + return 0;
> > +}
> > #endif
> >
> > static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
> > diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
> > index 2905734eb289..0c6c1fc8dae9 100644
> > --- a/fs/cifs/misc.c
> > +++ b/fs/cifs/misc.c
> > @@ -21,6 +21,7 @@
> > #include "cifsfs.h"
> > #ifdef CONFIG_CIFS_DFS_UPCALL
> > #include "dns_resolve.h"
> > +#include "dfs_cache.h"
> > #endif
> > #include "fs_context.h"
> > #include "cached_dir.h"
> > @@ -1198,4 +1199,70 @@ int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix)
> > cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
> > return 0;
> > }
> > +
> > +/*
> > + * Handle weird Windows SMB server behaviour. It responds with
> > + * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request for
> > + * "\<server>\<dfsname>\<linkpath>" DFS reference, where <dfsname> contains
> > + * non-ASCII unicode symbols.
> > + */
> > +int cifs_inval_name_dfs_link_error(const unsigned int xid,
> > + struct cifs_tcon *tcon,
> > + struct cifs_sb_info *cifs_sb,
> > + const char *full_path,
> > + bool *islink)
> > +{
> > + struct cifs_ses *ses = tcon->ses;
> > + size_t len;
> > + char *path;
> > + char *ref_path;
> > +
> > + *islink = false;
> > +
> > + /*
> > + * Fast path - skip check when @full_path doesn't have a prefix path to
> > + * look up or tcon is not DFS.
> > + */
> > + if (strlen(full_path) < 2 || !cifs_sb ||
> > + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) ||
> > + !is_tcon_dfs(tcon) || !ses->server->origin_fullpath)
> > + return 0;
> > +
> > + /*
> > + * Slow path - tcon is DFS and @full_path has prefix path, so attempt
> > + * to get a referral to figure out whether it is an DFS link.
> > + */
> > + len = strnlen(tcon->tree_name, MAX_TREE_SIZE + 1) + strlen(full_path) + 1;
> > + path = kmalloc(len, GFP_KERNEL);
> > + if (!path)
> > + return -ENOMEM;
> > +
> > + scnprintf(path, len, "%s%s", tcon->tree_name, full_path);
> > + ref_path = dfs_cache_canonical_path(path + 1, cifs_sb->local_nls,
> > + cifs_remap(cifs_sb));
> > + kfree(path);
> > +
> > + if (IS_ERR(ref_path)) {
> > + if (PTR_ERR(ref_path) != -EINVAL)
> > + return PTR_ERR(ref_path);
> > + } else {
> > + struct dfs_info3_param *refs = NULL;
> > + int num_refs = 0;
> > +
> > + /*
> > + * XXX: we are not using dfs_cache_find() here because we might
> > + * end filling all the DFS cache and thus potentially
> > + * removing cached DFS targets that the client would eventually
> > + * need during failover.
> > + */
> > + if (ses->server->ops->get_dfs_refer &&
> > + !ses->server->ops->get_dfs_refer(xid, ses, ref_path, &refs,
> > + &num_refs, cifs_sb->local_nls,
> > + cifs_remap(cifs_sb)))
> > + *islink = refs[0].server_type == DFS_TYPE_LINK;
> > + free_dfs_info_array(refs, num_refs);
> > + kfree(ref_path);
> > + }
> > + return 0;
> > +}
> > #endif
> > diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
> > index 37b4cd59245d..9b956294e864 100644
> > --- a/fs/cifs/smb2inode.c
> > +++ b/fs/cifs/smb2inode.c
> > @@ -527,12 +527,13 @@ int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
> > struct cifs_sb_info *cifs_sb, const char *full_path,
> > struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse)
> > {
> > - int rc;
> > __u32 create_options = 0;
> > struct cifsFileInfo *cfile;
> > struct cached_fid *cfid = NULL;
> > struct kvec err_iov[3] = {};
> > int err_buftype[3] = {};
> > + bool islink;
> > + int rc, rc2;
> >
> > *adjust_tz = false;
> > *reparse = false;
> > @@ -580,15 +581,15 @@ int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
> > SMB2_OP_QUERY_INFO, cfile, NULL, NULL,
> > NULL, NULL);
> > goto out;
> > - } else if (rc != -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) &&
> > - hdr->Status == STATUS_OBJECT_NAME_INVALID) {
> > - /*
> > - * Handle weird Windows SMB server behaviour. It responds with
> > - * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request
> > - * for "\<server>\<dfsname>\<linkpath>" DFS reference,
> > - * where <dfsname> contains non-ASCII unicode symbols.
> > - */
> > - rc = -EREMOTE;
> > + } else if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) {
> > + rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,
> > + full_path, &islink);
> > + if (rc2) {
> > + rc = rc2;
> > + goto out;
> > + }
> > + if (islink)
> > + rc = -EREMOTE;
> > }
> > if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb &&
> > (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS))
> > diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
> > index f79b075f2992..6dfb865ee9d7 100644
> > --- a/fs/cifs/smb2ops.c
> > +++ b/fs/cifs/smb2ops.c
> > @@ -796,7 +796,6 @@ static int
> > smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
> > struct cifs_sb_info *cifs_sb, const char *full_path)
> > {
> > - int rc;
> > __le16 *utf16_path;
> > __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
> > int err_buftype = CIFS_NO_BUFFER;
> > @@ -804,6 +803,8 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
> > struct kvec err_iov = {};
> > struct cifs_fid fid;
> > struct cached_fid *cfid;
> > + bool islink;
> > + int rc, rc2;
> >
> > rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid);
> > if (!rc) {
> > @@ -833,15 +834,17 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
> >
> > if (unlikely(!hdr || err_buftype == CIFS_NO_BUFFER))
> > goto out;
> > - /*
> > - * Handle weird Windows SMB server behaviour. It responds with
> > - * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request
> > - * for "\<server>\<dfsname>\<linkpath>" DFS reference,
> > - * where <dfsname> contains non-ASCII unicode symbols.
> > - */
> > - if (rc != -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) &&
> > - hdr->Status == STATUS_OBJECT_NAME_INVALID)
> > - rc = -EREMOTE;
> > +
> > + if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) {
> > + rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,
> > + full_path, &islink);
> > + if (rc2) {
> > + rc = rc2;
> > + goto out;
> > + }
> > + if (islink)
> > + rc = -EREMOTE;
> > + }
> > if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb &&
> > (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS))
> > rc = -EOPNOTSUPP;
> > --
> > 2.39.2
> >
--
Thanks,
Steve
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2023-02-28 23:35 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-27 13:53 [PATCH 1/2] cifs: improve checking of DFS links over STATUS_OBJECT_NAME_INVALID Paulo Alcantara
2023-02-27 13:53 ` [PATCH 2/2] cifs: prevent data race in cifs_reconnect_tcon() Paulo Alcantara
2023-02-27 22:40 ` Steve French
2023-02-28 0:09 ` Paulo Alcantara
2023-02-28 22:01 ` [PATCH v2 1/2] cifs: improve checking of DFS links over STATUS_OBJECT_NAME_INVALID Paulo Alcantara
2023-02-28 22:01 ` [PATCH v2 2/2] cifs: prevent data race in cifs_reconnect_tcon() Paulo Alcantara
2023-02-28 22:16 ` Steve French
2023-02-28 22:20 ` [PATCH v2 1/2] cifs: improve checking of DFS links over STATUS_OBJECT_NAME_INVALID ronnie sahlberg
2023-02-28 23:35 ` Steve French
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).