]> git.itanic.dy.fi Git - linux-stable/commitdiff
SMB3: drop reference to cfile before sending oplock break
authorBharath SM <bharathsm@microsoft.com>
Mon, 15 May 2023 21:25:12 +0000 (21:25 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 24 May 2023 16:36:54 +0000 (17:36 +0100)
commit 59a556aebc43dded08535fe97d94ca3f657915e4 upstream.

In cifs_oplock_break function we drop reference to a cfile at
the end of function, due to which close command goes on wire
after lease break acknowledgment even if file is already closed
by application but we had deferred the handle close.
If other client with limited file shareaccess waiting on lease
break ack proceeds operation on that file as soon as first client
sends ack, then we may encounter status sharing violation error
because of open handle.
Solution is to put reference to cfile(send close on wire if last ref)
and then send oplock acknowledgment to server.

Fixes: 9e31678fb403 ("SMB3: fix lease break timeout when multiple deferred close handles for the same file.")
Cc: stable@kernel.org
Signed-off-by: Bharath SM <bharathsm@microsoft.com>
Reviewed-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/cifs/cifsglob.h
fs/cifs/file.c
fs/cifs/smb1ops.c
fs/cifs/smb2ops.c

index 0f1b9c48838cc9bad82ba9da9b8c1dd7d6dba947..2ee67a27020d9e5abc47a99a5235fa77738a90e5 100644 (file)
@@ -394,8 +394,8 @@ struct smb_version_operations {
        /* check for STATUS_NETWORK_SESSION_EXPIRED */
        bool (*is_session_expired)(char *);
        /* send oplock break response */
-       int (*oplock_response)(struct cifs_tcon *, struct cifs_fid *,
-                              struct cifsInodeInfo *);
+       int (*oplock_response)(struct cifs_tcon *tcon, __u64 persistent_fid, __u64 volatile_fid,
+                       __u16 net_fid, struct cifsInodeInfo *cifs_inode);
        /* query remote filesystem */
        int (*queryfs)(const unsigned int, struct cifs_tcon *,
                       struct cifs_sb_info *, struct kstatfs *);
index 0e9cbd1da0297f1177283a275ef8b35a4a4b3434..872aebac9f6860f7ff4b5aa40557a8d3eb00b7a8 100644 (file)
@@ -4869,7 +4869,9 @@ void cifs_oplock_break(struct work_struct *work)
        struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
        struct TCP_Server_Info *server = tcon->ses->server;
        int rc = 0;
-       bool purge_cache = false;
+       bool purge_cache = false, oplock_break_cancelled;
+       __u64 persistent_fid, volatile_fid;
+       __u16 net_fid;
 
        wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS,
                        TASK_UNINTERRUPTIBLE);
@@ -4914,19 +4916,24 @@ void cifs_oplock_break(struct work_struct *work)
        if (!CIFS_CACHE_HANDLE(cinode) && !list_empty(&cinode->deferred_closes))
                cifs_close_deferred_file(cinode);
 
+       persistent_fid = cfile->fid.persistent_fid;
+       volatile_fid = cfile->fid.volatile_fid;
+       net_fid = cfile->fid.netfid;
+       oplock_break_cancelled = cfile->oplock_break_cancelled;
+
+       _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
        /*
         * releasing stale oplock after recent reconnect of smb session using
         * a now incorrect file handle is not a data integrity issue but do
         * not bother sending an oplock release if session to server still is
         * disconnected since oplock already released by the server
         */
-       if (!cfile->oplock_break_cancelled) {
-               rc = tcon->ses->server->ops->oplock_response(tcon, &cfile->fid,
-                                                            cinode);
+       if (!oplock_break_cancelled) {
+               rc = tcon->ses->server->ops->oplock_response(tcon, persistent_fid,
+                               volatile_fid, net_fid, cinode);
                cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
        }
 
-       _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
        cifs_done_oplock_break(cinode);
 }
 
index 3b83839fc2c276d02a5e9e52fdf5f839c2b9465f..41e468d06b5b0890bb9c7a8f7023b60e22fcea00 100644 (file)
@@ -864,12 +864,11 @@ cifs_close_dir(const unsigned int xid, struct cifs_tcon *tcon,
 }
 
 static int
-cifs_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
-                    struct cifsInodeInfo *cinode)
+cifs_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid,
+               __u64 volatile_fid, __u16 net_fid, struct cifsInodeInfo *cinode)
 {
-       return CIFSSMBLock(0, tcon, fid->netfid, current->tgid, 0, 0, 0, 0,
-                          LOCKING_ANDX_OPLOCK_RELEASE, false,
-                          CIFS_CACHE_READ(cinode) ? 1 : 0);
+       return CIFSSMBLock(0, tcon, net_fid, current->tgid, 0, 0, 0, 0,
+                          LOCKING_ANDX_OPLOCK_RELEASE, false, CIFS_CACHE_READ(cinode) ? 1 : 0);
 }
 
 static int
index 54058a9ada7406f0c2d802fc81da976659a05efe..560c4ababfe1a8912e6f6fda6045cf25990edee7 100644 (file)
@@ -2570,15 +2570,14 @@ smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server)
 }
 
 static int
-smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
-                    struct cifsInodeInfo *cinode)
+smb2_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid,
+               __u64 volatile_fid, __u16 net_fid, struct cifsInodeInfo *cinode)
 {
        if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
                return SMB2_lease_break(0, tcon, cinode->lease_key,
                                        smb2_get_lease_state(cinode));
 
-       return SMB2_oplock_break(0, tcon, fid->persistent_fid,
-                                fid->volatile_fid,
+       return SMB2_oplock_break(0, tcon, persistent_fid, volatile_fid,
                                 CIFS_CACHE_READ(cinode) ? 1 : 0);
 }