cifs: update the same create_guid on replay
authorSteve French <stfrench@microsoft.com>
Sun, 28 Apr 2024 06:32:09 +0000 (01:32 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 25 May 2024 14:22:46 +0000 (16:22 +0200)
[ Upstream commit 79520587fe42cd4988aff8695d60621e689109cb ]

File open requests made to the server contain a
CreateGuid, which is used by the server to identify
the open request. If the same request needs to be
replayed, it needs to be sent with the same CreateGuid
in the durable handle v2 context.

Without doing so, we could end up leaking handles on
the server when:
1. multichannel is used AND
2. connection goes down, but not for all channels

This is because the replayed open request would have a
new CreateGuid and the server will treat this as a new
request and open a new handle.

This change fixes this by reusing the existing create_guid
stored in the cached fid struct.

REF: MS-SMB2 4.9 Replay Create Request on an Alternate Channel

Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set")
Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/smb/client/cached_dir.c
fs/smb/client/cifsglob.h
fs/smb/client/smb2ops.c
fs/smb/client/smb2pdu.c

index ca0fd25236ef4b5fa6affbf77897ba5021c5f167..0ff2491c311d8a669c709fb94eb4a16a54515c68 100644 (file)
@@ -243,6 +243,7 @@ replay_again:
                                   FILE_READ_EA,
                .disposition = FILE_OPEN,
                .fid = pfid,
+               .replay = !!(retries),
        };
 
        rc = SMB2_open_init(tcon, server,
index 8fbdb781d70a6734e6ffbe074f202213856d4f54..181e9d5b10f926d457744180aa398041639b8f58 100644 (file)
@@ -1382,6 +1382,7 @@ struct cifs_open_parms {
        struct cifs_fid *fid;
        umode_t mode;
        bool reconnect:1;
+       bool replay:1; /* indicates that this open is for a replay */
 };
 
 struct cifs_fid {
index 06735c5685bf6cf2df9c168d4705c314de545bdc..23cf6e92fd54ccf1041a39eb1eafcd7d08ca419c 100644 (file)
@@ -1204,6 +1204,7 @@ replay_again:
                .disposition = FILE_OPEN,
                .create_options = cifs_create_options(cifs_sb, 0),
                .fid = &fid,
+               .replay = !!(retries),
        };
 
        rc = SMB2_open_init(tcon, server,
@@ -1570,6 +1571,7 @@ replay_again:
                .disposition = FILE_OPEN,
                .create_options = cifs_create_options(cifs_sb, create_options),
                .fid = &fid,
+               .replay = !!(retries),
        };
 
        if (qi.flags & PASSTHRU_FSCTL) {
@@ -2296,6 +2298,7 @@ replay_again:
                .disposition = FILE_OPEN,
                .create_options = cifs_create_options(cifs_sb, 0),
                .fid = fid,
+               .replay = !!(retries),
        };
 
        rc = SMB2_open_init(tcon, server,
@@ -2684,6 +2687,7 @@ replay_again:
                .disposition = FILE_OPEN,
                .create_options = cifs_create_options(cifs_sb, 0),
                .fid = &fid,
+               .replay = !!(retries),
        };
 
        rc = SMB2_open_init(tcon, server,
index c73a621a8b83ebc4eaf22df42a089c331303540e..60793143e24c630009ae196e671860d8deb6dc5b 100644 (file)
@@ -2421,8 +2421,13 @@ create_durable_v2_buf(struct cifs_open_parms *oparms)
         */
        buf->dcontext.Timeout = cpu_to_le32(oparms->tcon->handle_timeout);
        buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
-       generate_random_uuid(buf->dcontext.CreateGuid);
-       memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16);
+
+       /* for replay, we should not overwrite the existing create guid */
+       if (!oparms->replay) {
+               generate_random_uuid(buf->dcontext.CreateGuid);
+               memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16);
+       } else
+               memcpy(buf->dcontext.CreateGuid, pfid->create_guid, 16);
 
        /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */
        buf->Name[0] = 'D';
@@ -3159,6 +3164,7 @@ replay_again:
        /* reinitialize for possible replay */
        flags = 0;
        server = cifs_pick_channel(ses);
+       oparms->replay = !!(retries);
 
        cifs_dbg(FYI, "create/open\n");
        if (!ses || !server)