Skip to content

Commit 9150c3a

Browse files
piastrySteve French
authored and
Steve French
committed
CIFS: Close open handle after interrupted close
If Close command is interrupted before sending a request to the server the client ends up leaking an open file handle. This wastes server resources and can potentially block applications that try to remove the file or any directory containing this file. Fix this by putting the close command into a worker queue, so another thread retries it later. Cc: Stable <[email protected]> Tested-by: Frank Sorenson <[email protected]> Reviewed-by: Ronnie Sahlberg <[email protected]> Signed-off-by: Pavel Shilovsky <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 44805b0 commit 9150c3a

File tree

3 files changed

+63
-15
lines changed

3 files changed

+63
-15
lines changed

fs/cifs/smb2misc.c

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -738,36 +738,67 @@ smb2_cancelled_close_fid(struct work_struct *work)
738738
kfree(cancelled);
739739
}
740740

741+
/* Caller should already has an extra reference to @tcon */
742+
static int
743+
__smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid,
744+
__u64 volatile_fid)
745+
{
746+
struct close_cancelled_open *cancelled;
747+
748+
cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL);
749+
if (!cancelled)
750+
return -ENOMEM;
751+
752+
cancelled->fid.persistent_fid = persistent_fid;
753+
cancelled->fid.volatile_fid = volatile_fid;
754+
cancelled->tcon = tcon;
755+
INIT_WORK(&cancelled->work, smb2_cancelled_close_fid);
756+
WARN_ON(queue_work(cifsiod_wq, &cancelled->work) == false);
757+
758+
return 0;
759+
}
760+
761+
int
762+
smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid,
763+
__u64 volatile_fid)
764+
{
765+
int rc;
766+
767+
cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count);
768+
spin_lock(&cifs_tcp_ses_lock);
769+
tcon->tc_count++;
770+
spin_unlock(&cifs_tcp_ses_lock);
771+
772+
rc = __smb2_handle_cancelled_close(tcon, persistent_fid, volatile_fid);
773+
if (rc)
774+
cifs_put_tcon(tcon);
775+
776+
return rc;
777+
}
778+
741779
int
742780
smb2_handle_cancelled_mid(char *buffer, struct TCP_Server_Info *server)
743781
{
744782
struct smb2_sync_hdr *sync_hdr = (struct smb2_sync_hdr *)buffer;
745783
struct smb2_create_rsp *rsp = (struct smb2_create_rsp *)buffer;
746784
struct cifs_tcon *tcon;
747-
struct close_cancelled_open *cancelled;
785+
int rc;
748786

749787
if (sync_hdr->Command != SMB2_CREATE ||
750788
sync_hdr->Status != STATUS_SUCCESS)
751789
return 0;
752790

753-
cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL);
754-
if (!cancelled)
755-
return -ENOMEM;
756-
757791
tcon = smb2_find_smb_tcon(server, sync_hdr->SessionId,
758792
sync_hdr->TreeId);
759-
if (!tcon) {
760-
kfree(cancelled);
793+
if (!tcon)
761794
return -ENOENT;
762-
}
763795

764-
cancelled->fid.persistent_fid = rsp->PersistentFileId;
765-
cancelled->fid.volatile_fid = rsp->VolatileFileId;
766-
cancelled->tcon = tcon;
767-
INIT_WORK(&cancelled->work, smb2_cancelled_close_fid);
768-
queue_work(cifsiod_wq, &cancelled->work);
796+
rc = __smb2_handle_cancelled_close(tcon, rsp->PersistentFileId,
797+
rsp->VolatileFileId);
798+
if (rc)
799+
cifs_put_tcon(tcon);
769800

770-
return 0;
801+
return rc;
771802
}
772803

773804
/**

fs/cifs/smb2pdu.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2974,7 +2974,21 @@ int
29742974
SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
29752975
u64 persistent_fid, u64 volatile_fid)
29762976
{
2977-
return SMB2_close_flags(xid, tcon, persistent_fid, volatile_fid, 0);
2977+
int rc;
2978+
int tmp_rc;
2979+
2980+
rc = SMB2_close_flags(xid, tcon, persistent_fid, volatile_fid, 0);
2981+
2982+
/* retry close in a worker thread if this one is interrupted */
2983+
if (rc == -EINTR) {
2984+
tmp_rc = smb2_handle_cancelled_close(tcon, persistent_fid,
2985+
volatile_fid);
2986+
if (tmp_rc)
2987+
cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n",
2988+
persistent_fid, tmp_rc);
2989+
}
2990+
2991+
return rc;
29782992
}
29792993

29802994
int

fs/cifs/smb2proto.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
212212
extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
213213
const u64 persistent_fid, const u64 volatile_fid,
214214
const __u8 oplock_level);
215+
extern int smb2_handle_cancelled_close(struct cifs_tcon *tcon,
216+
__u64 persistent_fid,
217+
__u64 volatile_fid);
215218
extern int smb2_handle_cancelled_mid(char *buffer,
216219
struct TCP_Server_Info *server);
217220
void smb2_cancelled_close_fid(struct work_struct *work);

0 commit comments

Comments
 (0)