From 7ca9125be5522679c777a8e9a27a0af22a3d273d Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu@redhat.com>
Date: Tue, 24 Jan 2017 12:43:03 +0530
Subject: [PATCH] cifs: Cache Access denied errors when reconnecting
If he account credentials on a mounted share is changed while still
mounted, remounts will fail with -EACCES. Since a new session setup call
is made every time an attempt is made to access this share, a large
number of failed session setup calls are made. This causes problems with
certain server setups which consider it as an attack on the account and
block further access from the account. To avoid this, cache all -EACCES
errors and avoid this problem.
Signed-off-by: Sachin Prabhu <sprabhu@redhat.com>
---
fs/cifs/cifsglob.h | 4 ++++
fs/cifs/cifssmb.c | 20 ++++++++++++++++++--
fs/cifs/connect.c | 14 ++++++++++++++
3 files changed, 36 insertions(+), 2 deletions(-)
@@ -75,6 +75,8 @@
#define SMB_ECHO_INTERVAL_MAX 600
#define SMB_ECHO_INTERVAL_DEFAULT 60
+#define SMB_NEGATIVE_CACHE_INTERVAL 10
+
/*
* Default number of credits to keep available for SMB3.
* This value is chosen somewhat arbitrarily. The Windows client
@@ -832,6 +834,8 @@ struct cifs_ses {
bool sign; /* is signing required? */
bool need_reconnect:1; /* connection reset, uid now invalid */
bool domainAuto:1;
+ bool cached_rc;
+ struct delayed_work clear_cached_rc;
#ifdef CONFIG_CIFS_SMB2
__u16 session_flags;
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
@@ -179,8 +179,24 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
*/
mutex_lock(&ses->session_mutex);
rc = cifs_negotiate_protocol(0, ses);
- if (rc == 0 && ses->need_reconnect)
- rc = cifs_setup_session(0, ses, nls_codepage);
+ if (rc) {
+ mutex_unlock(&ses->session_mutex);
+ goto out;
+ }
+
+ if (ses->need_reconnect) {
+ if (ses->cached_rc) {
+ rc = ses->cached_rc;
+ } else {
+ rc = cifs_setup_session(0, ses, nls_codepage);
+ if (rc == -EACCES) {
+ queue_delayed_work(cifsiod_wq,
+ &ses->clear_cached_rc,
+ SMB_NEGATIVE_CACHE_INTERVAL * HZ);
+ ses->cached_rc = rc;
+ }
+ }
+ }
/* do we need to reconnect tcon? */
if (rc || !tcon->need_reconnect) {
@@ -2375,6 +2375,7 @@ cifs_put_smb_ses(struct cifs_ses *ses)
list_del_init(&ses->smb_ses_list);
spin_unlock(&cifs_tcp_ses_lock);
+ cancel_delayed_work_sync(&ses->clear_cached_rc);
sesInfoFree(ses);
cifs_put_tcp_session(server, 0);
}
@@ -2510,6 +2511,16 @@ cifs_set_cifscreds(struct smb_vol *vol __attribute__((unused)),
}
#endif /* CONFIG_KEYS */
+static void clear_cached_rc(struct work_struct *work)
+{
+ struct cifs_ses *ses = container_of(work, struct cifs_ses,
+ clear_cached_rc.work);
+
+ mutex_lock(&ses->session_mutex);
+ ses->cached_rc = 0;
+ mutex_unlock(&ses->session_mutex);
+}
+
static struct cifs_ses *
cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
{
@@ -2592,6 +2603,9 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
ses->sectype = volume_info->sectype;
ses->sign = volume_info->sign;
+ ses->cached_rc = 0;
+ INIT_DELAYED_WORK(&ses->clear_cached_rc, clear_cached_rc);
+
mutex_lock(&ses->session_mutex);
rc = cifs_negotiate_protocol(xid, ses);
if (!rc)
--
2.9.3