cifs: update the same create_guid on replay
authorShyam Prasad N <sprasad@microsoft.com>
Fri, 9 Feb 2024 11:25:42 +0000 (11:25 +0000)
committerSteve French <stfrench@microsoft.com>
Mon, 12 Feb 2024 01:07:08 +0000 (19:07 -0600)
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>
fs/smb/client/cached_dir.c
fs/smb/client/cifsglob.h
fs/smb/client/smb2ops.c
fs/smb/client/smb2pdu.c

index 1daeb5714faad14c24c49a5efd5d118aaf04b54c..3de5047a7ff988c2049350e464771e912b12894e 100644 (file)
@@ -242,6 +242,7 @@ replay_again:
                .desired_access =  FILE_READ_DATA | FILE_READ_ATTRIBUTES,
                .disposition = FILE_OPEN,
                .fid = pfid,
+               .replay = !!(retries),
        };
 
        rc = SMB2_open_init(tcon, server,
index c86a72c9d9ecd4268481a4caa29e58004cb7d6b9..53c75cfb33ab9446740133e7f19da6229eeffd55 100644 (file)
@@ -1378,6 +1378,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 755f1c66b573aa15d1274b02323c8ccd5ca20099..6b3c384ead0d8ef270aa5682411fa16e8c6eef05 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,
@@ -1569,6 +1570,7 @@ replay_again:
                .disposition = FILE_OPEN,
                .create_options = cifs_create_options(cifs_sb, create_options),
                .fid = &fid,
+               .replay = !!(retries),
        };
 
        if (qi.flags & PASSTHRU_FSCTL) {
@@ -2295,6 +2297,7 @@ replay_again:
                .disposition = FILE_OPEN,
                .create_options = cifs_create_options(cifs_sb, 0),
                .fid = fid,
+               .replay = !!(retries),
        };
 
        rc = SMB2_open_init(tcon, server,
@@ -2681,6 +2684,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 4085ce27fd388c7eab9a8402e095e84551158cb1..608ee05491e262c5cf4555c6b51b364cdc60a03b 100644 (file)
@@ -2404,8 +2404,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';
@@ -3142,6 +3147,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)