Commit fe756ad0 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '6.5-rc1-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client fixes from Steve French:

 - Two reconnect fixes: important fix to address inFlight count to leak
   (which can leak credits), and fix for better handling a deleted share

 - DFS fix

 - SMB1 cleanup fix

 - deferred close fix

* tag '6.5-rc1-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: fix mid leak during reconnection after timeout threshold
  cifs: is_network_name_deleted should return a bool
  smb: client: fix missed ses refcounting
  smb: client: Fix -Wstringop-overflow issues
  cifs: if deferred close is disabled then close files immediately
parents 20edcec2 69cba9d3
...@@ -532,7 +532,7 @@ struct smb_version_operations { ...@@ -532,7 +532,7 @@ struct smb_version_operations {
/* Check for STATUS_IO_TIMEOUT */ /* Check for STATUS_IO_TIMEOUT */
bool (*is_status_io_timeout)(char *buf); bool (*is_status_io_timeout)(char *buf);
/* Check for STATUS_NETWORK_NAME_DELETED */ /* Check for STATUS_NETWORK_NAME_DELETED */
void (*is_network_name_deleted)(char *buf, struct TCP_Server_Info *srv); bool (*is_network_name_deleted)(char *buf, struct TCP_Server_Info *srv);
}; };
struct smb_version_values { struct smb_version_values {
......
...@@ -3184,7 +3184,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -3184,7 +3184,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
param_offset = offsetof(struct smb_com_transaction2_spi_req, param_offset = offsetof(struct smb_com_transaction2_spi_req,
InformationLevel) - 4; InformationLevel) - 4;
offset = param_offset + params; offset = param_offset + params;
parm_data = ((char *) &pSMB->hdr.Protocol) + offset; parm_data = ((char *)pSMB) + sizeof(pSMB->hdr.smb_buf_length) + offset;
pSMB->ParameterOffset = cpu_to_le16(param_offset); pSMB->ParameterOffset = cpu_to_le16(param_offset);
/* convert to on the wire format for POSIX ACL */ /* convert to on the wire format for POSIX ACL */
......
...@@ -60,7 +60,7 @@ extern bool disable_legacy_dialects; ...@@ -60,7 +60,7 @@ extern bool disable_legacy_dialects;
#define TLINK_IDLE_EXPIRE (600 * HZ) #define TLINK_IDLE_EXPIRE (600 * HZ)
/* Drop the connection to not overload the server */ /* Drop the connection to not overload the server */
#define NUM_STATUS_IO_TIMEOUT 5 #define MAX_STATUS_IO_TIMEOUT 5
static int ip_connect(struct TCP_Server_Info *server); static int ip_connect(struct TCP_Server_Info *server);
static int generic_ip_connect(struct TCP_Server_Info *server); static int generic_ip_connect(struct TCP_Server_Info *server);
...@@ -1117,6 +1117,7 @@ cifs_demultiplex_thread(void *p) ...@@ -1117,6 +1117,7 @@ cifs_demultiplex_thread(void *p)
struct mid_q_entry *mids[MAX_COMPOUND]; struct mid_q_entry *mids[MAX_COMPOUND];
char *bufs[MAX_COMPOUND]; char *bufs[MAX_COMPOUND];
unsigned int noreclaim_flag, num_io_timeout = 0; unsigned int noreclaim_flag, num_io_timeout = 0;
bool pending_reconnect = false;
noreclaim_flag = memalloc_noreclaim_save(); noreclaim_flag = memalloc_noreclaim_save();
cifs_dbg(FYI, "Demultiplex PID: %d\n", task_pid_nr(current)); cifs_dbg(FYI, "Demultiplex PID: %d\n", task_pid_nr(current));
...@@ -1156,6 +1157,8 @@ cifs_demultiplex_thread(void *p) ...@@ -1156,6 +1157,8 @@ cifs_demultiplex_thread(void *p)
cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length); cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length);
if (!is_smb_response(server, buf[0])) if (!is_smb_response(server, buf[0]))
continue; continue;
pending_reconnect = false;
next_pdu: next_pdu:
server->pdu_size = pdu_length; server->pdu_size = pdu_length;
...@@ -1213,10 +1216,13 @@ cifs_demultiplex_thread(void *p) ...@@ -1213,10 +1216,13 @@ cifs_demultiplex_thread(void *p)
if (server->ops->is_status_io_timeout && if (server->ops->is_status_io_timeout &&
server->ops->is_status_io_timeout(buf)) { server->ops->is_status_io_timeout(buf)) {
num_io_timeout++; num_io_timeout++;
if (num_io_timeout > NUM_STATUS_IO_TIMEOUT) { if (num_io_timeout > MAX_STATUS_IO_TIMEOUT) {
cifs_reconnect(server, false); cifs_server_dbg(VFS,
"Number of request timeouts exceeded %d. Reconnecting",
MAX_STATUS_IO_TIMEOUT);
pending_reconnect = true;
num_io_timeout = 0; num_io_timeout = 0;
continue;
} }
} }
...@@ -1226,9 +1232,14 @@ cifs_demultiplex_thread(void *p) ...@@ -1226,9 +1232,14 @@ cifs_demultiplex_thread(void *p)
if (mids[i] != NULL) { if (mids[i] != NULL) {
mids[i]->resp_buf_size = server->pdu_size; mids[i]->resp_buf_size = server->pdu_size;
if (bufs[i] && server->ops->is_network_name_deleted) if (bufs[i] != NULL) {
if (server->ops->is_network_name_deleted &&
server->ops->is_network_name_deleted(bufs[i], server->ops->is_network_name_deleted(bufs[i],
server); server)) {
cifs_server_dbg(FYI,
"Share deleted. Reconnect needed");
}
}
if (!mids[i]->multiRsp || mids[i]->multiEnd) if (!mids[i]->multiRsp || mids[i]->multiEnd)
mids[i]->callback(mids[i]); mids[i]->callback(mids[i]);
...@@ -1263,6 +1274,11 @@ cifs_demultiplex_thread(void *p) ...@@ -1263,6 +1274,11 @@ cifs_demultiplex_thread(void *p)
buf = server->smallbuf; buf = server->smallbuf;
goto next_pdu; goto next_pdu;
} }
/* do this reconnect at the very end after processing all MIDs */
if (pending_reconnect)
cifs_reconnect(server, true);
} /* end while !EXITING */ } /* end while !EXITING */
/* buffer usually freed in free_mid - need to free it here on exit */ /* buffer usually freed in free_mid - need to free it here on exit */
......
...@@ -66,6 +66,12 @@ static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path) ...@@ -66,6 +66,12 @@ static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path)
return rc; return rc;
} }
/*
* Track individual DFS referral servers used by new DFS mount.
*
* On success, their lifetime will be shared by final tcon (dfs_ses_list).
* Otherwise, they will be put by dfs_put_root_smb_sessions() in cifs_mount().
*/
static int add_root_smb_session(struct cifs_mount_ctx *mnt_ctx) static int add_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
{ {
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
...@@ -80,11 +86,12 @@ static int add_root_smb_session(struct cifs_mount_ctx *mnt_ctx) ...@@ -80,11 +86,12 @@ static int add_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
INIT_LIST_HEAD(&root_ses->list); INIT_LIST_HEAD(&root_ses->list);
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
ses->ses_count++; cifs_smb_ses_inc_refcount(ses);
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
root_ses->ses = ses; root_ses->ses = ses;
list_add_tail(&root_ses->list, &mnt_ctx->dfs_ses_list); list_add_tail(&root_ses->list, &mnt_ctx->dfs_ses_list);
} }
/* Select new DFS referral server so that new referrals go through it */
ctx->dfs_root_ses = ses; ctx->dfs_root_ses = ses;
return 0; return 0;
} }
...@@ -242,7 +249,6 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) ...@@ -242,7 +249,6 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs) int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
{ {
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
struct cifs_ses *ses;
bool nodfs = ctx->nodfs; bool nodfs = ctx->nodfs;
int rc; int rc;
...@@ -276,20 +282,8 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs) ...@@ -276,20 +282,8 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
} }
*isdfs = true; *isdfs = true;
/* add_root_smb_session(mnt_ctx);
* Prevent DFS root session of being put in the first call to return __dfs_mount_share(mnt_ctx);
* cifs_mount_put_conns(). If another DFS root server was not found
* while chasing the referrals (@ctx->dfs_root_ses == @ses), then we
* can safely put extra refcount of @ses.
*/
ses = mnt_ctx->ses;
mnt_ctx->ses = NULL;
mnt_ctx->server = NULL;
rc = __dfs_mount_share(mnt_ctx);
if (ses == ctx->dfs_root_ses)
cifs_put_smb_ses(ses);
return rc;
} }
/* Update dfs referral path of superblock */ /* Update dfs referral path of superblock */
......
...@@ -1080,8 +1080,8 @@ int cifs_close(struct inode *inode, struct file *file) ...@@ -1080,8 +1080,8 @@ int cifs_close(struct inode *inode, struct file *file)
cfile = file->private_data; cfile = file->private_data;
file->private_data = NULL; file->private_data = NULL;
dclose = kmalloc(sizeof(struct cifs_deferred_close), GFP_KERNEL); dclose = kmalloc(sizeof(struct cifs_deferred_close), GFP_KERNEL);
if ((cinode->oplock == CIFS_CACHE_RHW_FLG) && if ((cifs_sb->ctx->closetimeo && cinode->oplock == CIFS_CACHE_RHW_FLG)
cinode->lease_granted && && cinode->lease_granted &&
!test_bit(CIFS_INO_CLOSE_ON_LOCK, &cinode->flags) && !test_bit(CIFS_INO_CLOSE_ON_LOCK, &cinode->flags) &&
dclose) { dclose) {
if (test_and_clear_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags)) { if (test_and_clear_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags)) {
......
...@@ -2395,7 +2395,7 @@ smb2_is_status_io_timeout(char *buf) ...@@ -2395,7 +2395,7 @@ smb2_is_status_io_timeout(char *buf)
return false; return false;
} }
static void static bool
smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server) smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server)
{ {
struct smb2_hdr *shdr = (struct smb2_hdr *)buf; struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
...@@ -2404,7 +2404,7 @@ smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server) ...@@ -2404,7 +2404,7 @@ smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server)
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
if (shdr->Status != STATUS_NETWORK_NAME_DELETED) if (shdr->Status != STATUS_NETWORK_NAME_DELETED)
return; return false;
/* If server is a channel, select the primary channel */ /* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;
...@@ -2419,11 +2419,13 @@ smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server) ...@@ -2419,11 +2419,13 @@ smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server)
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
pr_warn_once("Server share %s deleted.\n", pr_warn_once("Server share %s deleted.\n",
tcon->tree_name); tcon->tree_name);
return; return true;
} }
} }
} }
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return false;
} }
static int static int
......
...@@ -160,7 +160,7 @@ smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id) ...@@ -160,7 +160,7 @@ smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
spin_unlock(&ses->ses_lock); spin_unlock(&ses->ses_lock);
continue; continue;
} }
++ses->ses_count; cifs_smb_ses_inc_refcount(ses);
spin_unlock(&ses->ses_lock); spin_unlock(&ses->ses_lock);
return ses; return ses;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment