Skip to content

Commit

Permalink
CIFS: Close open handle after interrupted close
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
piastry authored and Steve French committed Nov 25, 2019
1 parent 44805b0 commit 9150c3a
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 15 deletions.
59 changes: 45 additions & 14 deletions fs/cifs/smb2misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -738,36 +738,67 @@ smb2_cancelled_close_fid(struct work_struct *work)
kfree(cancelled);
}

/* Caller should already has an extra reference to @tcon */
static int
__smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid,
__u64 volatile_fid)
{
struct close_cancelled_open *cancelled;

cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL);
if (!cancelled)
return -ENOMEM;

cancelled->fid.persistent_fid = persistent_fid;
cancelled->fid.volatile_fid = volatile_fid;
cancelled->tcon = tcon;
INIT_WORK(&cancelled->work, smb2_cancelled_close_fid);
WARN_ON(queue_work(cifsiod_wq, &cancelled->work) == false);

return 0;
}

int
smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid,
__u64 volatile_fid)
{
int rc;

cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count);
spin_lock(&cifs_tcp_ses_lock);
tcon->tc_count++;
spin_unlock(&cifs_tcp_ses_lock);

rc = __smb2_handle_cancelled_close(tcon, persistent_fid, volatile_fid);
if (rc)
cifs_put_tcon(tcon);

return rc;
}

int
smb2_handle_cancelled_mid(char *buffer, struct TCP_Server_Info *server)
{
struct smb2_sync_hdr *sync_hdr = (struct smb2_sync_hdr *)buffer;
struct smb2_create_rsp *rsp = (struct smb2_create_rsp *)buffer;
struct cifs_tcon *tcon;
struct close_cancelled_open *cancelled;
int rc;

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

cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL);
if (!cancelled)
return -ENOMEM;

tcon = smb2_find_smb_tcon(server, sync_hdr->SessionId,
sync_hdr->TreeId);
if (!tcon) {
kfree(cancelled);
if (!tcon)
return -ENOENT;
}

cancelled->fid.persistent_fid = rsp->PersistentFileId;
cancelled->fid.volatile_fid = rsp->VolatileFileId;
cancelled->tcon = tcon;
INIT_WORK(&cancelled->work, smb2_cancelled_close_fid);
queue_work(cifsiod_wq, &cancelled->work);
rc = __smb2_handle_cancelled_close(tcon, rsp->PersistentFileId,
rsp->VolatileFileId);
if (rc)
cifs_put_tcon(tcon);

return 0;
return rc;
}

/**
Expand Down
16 changes: 15 additions & 1 deletion fs/cifs/smb2pdu.c
Original file line number Diff line number Diff line change
Expand Up @@ -2974,7 +2974,21 @@ int
SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid)
{
return SMB2_close_flags(xid, tcon, persistent_fid, volatile_fid, 0);
int rc;
int tmp_rc;

rc = SMB2_close_flags(xid, tcon, persistent_fid, volatile_fid, 0);

/* retry close in a worker thread if this one is interrupted */
if (rc == -EINTR) {
tmp_rc = smb2_handle_cancelled_close(tcon, persistent_fid,
volatile_fid);
if (tmp_rc)
cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n",
persistent_fid, tmp_rc);
}

return rc;
}

int
Expand Down
3 changes: 3 additions & 0 deletions fs/cifs/smb2proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
const u64 persistent_fid, const u64 volatile_fid,
const __u8 oplock_level);
extern int smb2_handle_cancelled_close(struct cifs_tcon *tcon,
__u64 persistent_fid,
__u64 volatile_fid);
extern int smb2_handle_cancelled_mid(char *buffer,
struct TCP_Server_Info *server);
void smb2_cancelled_close_fid(struct work_struct *work);
Expand Down

0 comments on commit 9150c3a

Please sign in to comment.