s4: torture: Add test_smb2_close_full_information() test to smb2.rename.
authorJeremy Allison <jra@samba.org>
Tue, 29 Mar 2022 01:25:54 +0000 (18:25 -0700)
committerJule Anger <janger@samba.org>
Mon, 11 Apr 2022 07:49:13 +0000 (07:49 +0000)
Creates a file, opens it again on two different connections
and then renames it. When we close and ask for SMB2_CLOSE_FLAGS_FULL_INFORMATION
we expect this to succeed and return valid data on the handles that did not do
the rename request.

This currently succeeds by accident on master, so we are not
adding a knownfail.d/ file here. When we back-port this test
to 4.16.next, 4.15.next we will add a knownfail.d file.

The rename request zeros out the fsp->fsp_name->st field on the handles
that are open but are not being renamed, marking them as INVALID_STAT.

This should not happen on any open handle. Fix to follow will
preserve the field on rename in both the local connection and
different connection case.

Master gets away with this as in this branch, openat_pathref_fsp(),
which we use in the setup_close_full_information() call to fetch
the SMB2_CLOSE_FLAGS_FULL_INFORMATION data doesn't require an
existing VALID_STAT struct in order to open the file. This
hides the fact the rename zeroed out fsp->fsp_name->st.

4.16.x and 4.15.x don't have this fix, so expose the bug.
Regardless, even in master we should not zero out any
fsp->fsp_name->st values on rename.

Add knownfail.d/rename-full-info for 4.16.x, 4.15.x.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=15038

Signed-off-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
(Back-ported from commit 1301e6461393601a4d43cfc465a05114e6ae4662)

selftest/knownfail.d/rename-full-info [new file with mode: 0644]
source4/torture/smb2/rename.c

diff --git a/selftest/knownfail.d/rename-full-info b/selftest/knownfail.d/rename-full-info
new file mode 100644 (file)
index 0000000..86ac19f
--- /dev/null
@@ -0,0 +1 @@
+^samba3.smb2.rename.close-full-information\(fileserver\)
index c8d3c95d166393ba2d20015ddbadce16347bf741..1b3dca8bcbaff3fadef89d7b1876094567a1cc43 100644 (file)
@@ -1437,6 +1437,127 @@ static bool torture_smb2_rename_dir_bench(struct torture_context *tctx,
        return true;
 }
 
+static bool test_smb2_close_full_information(struct torture_context *torture,
+                                       struct smb2_tree *tree1,
+                                       struct smb2_tree *tree2)
+{
+       union smb_close cl;
+       struct smb2_create io = {0};
+       struct smb2_handle h1 = {{0}};
+       struct smb2_handle h2 = {{0}};
+       struct smb2_handle h3 = {{0}};
+       union smb_setfileinfo sinfo;
+       NTSTATUS status;
+       const char *fname_src = "request.dat";
+       const char *fname_dst = "renamed.dat";
+       bool ret = true;
+
+       /* Start with a tidy share. */
+       smb2_util_unlink(tree1, fname_src);
+       smb2_util_unlink(tree1, fname_dst);
+
+       /* Create the test file, and leave it open. */
+       io.in.fname = fname_src;
+       io.in.desired_access = SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE;
+       io.in.create_disposition = NTCREATEX_DISP_CREATE;
+       io.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+                               NTCREATEX_SHARE_ACCESS_WRITE |
+                               NTCREATEX_SHARE_ACCESS_DELETE;
+       status = smb2_create(tree1, tree1, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       h1 = io.out.file.handle;
+       CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+
+       /* Open the test file on the second connection. */
+       ZERO_STRUCT(io);
+       io.in.fname = fname_src;
+       io.in.desired_access = SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE;
+       io.in.create_disposition = NTCREATEX_DISP_OPEN;
+       io.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+                               NTCREATEX_SHARE_ACCESS_WRITE |
+                               NTCREATEX_SHARE_ACCESS_DELETE;
+       status = smb2_create(tree2, tree2, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       h2 = io.out.file.handle;
+
+       /* Now open for rename on the first connection. */
+       ZERO_STRUCT(io);
+       io.in.fname = fname_src;
+       io.in.desired_access = SEC_STD_DELETE | SEC_FILE_READ_ATTRIBUTE;
+       io.in.create_disposition = NTCREATEX_DISP_OPEN;
+       io.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+                               NTCREATEX_SHARE_ACCESS_WRITE |
+                               NTCREATEX_SHARE_ACCESS_DELETE;
+       status = smb2_create(tree1, tree1, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       h3 = io.out.file.handle;
+
+       /* Do the rename. */
+       ZERO_STRUCT(sinfo);
+       sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
+       sinfo.rename_information.in.file.handle = h3;
+       sinfo.rename_information.in.new_name = fname_dst;
+       status = smb2_setinfo_file(tree1, &sinfo);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* And close h3. */
+       ZERO_STRUCT(cl.smb2);
+       cl.smb2.level = RAW_CLOSE_SMB2;
+       cl.smb2.in.file.handle = h3;
+       status = smb2_close(tree1, &cl.smb2);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       ZERO_STRUCT(h3);
+
+       /*
+        * Close h1 with SMB2_CLOSE_FLAGS_FULL_INFORMATION.
+        * Ensure we get data.
+        */
+       ZERO_STRUCT(cl.smb2);
+       cl.smb2.level = RAW_CLOSE_SMB2;
+       cl.smb2.in.file.handle = h1;
+       cl.smb2.in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION;
+       status = smb2_close(tree1, &cl.smb2);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       ZERO_STRUCT(h1);
+       CHECK_VAL(cl.smb2.out.file_attr, 0x20);
+
+       /*
+        * Wait 3 seconds for name change to propagate
+        * to the other connection.
+        */
+       sleep(3);
+
+       /*
+        * Close h2 with SMB2_CLOSE_FLAGS_FULL_INFORMATION.
+        * This is on connection2.
+        * Ensure we get data.
+        */
+       ZERO_STRUCT(cl.smb2);
+       cl.smb2.level = RAW_CLOSE_SMB2;
+       cl.smb2.in.file.handle = h2;
+       cl.smb2.in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION;
+       status = smb2_close(tree2, &cl.smb2);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       ZERO_STRUCT(h2);
+       CHECK_VAL(cl.smb2.out.file_attr, 0x20);
+
+  done:
+
+       if (h1.data[0] != 0 || h1.data[1] != 0) {
+               smb2_util_close(tree1, h1);
+       }
+       if (h2.data[0] != 0 || h2.data[1] != 0) {
+               smb2_util_close(tree2, h2);
+       }
+       if (h3.data[0] != 0 || h3.data[1] != 0) {
+               smb2_util_close(tree1, h3);
+       }
+
+       smb2_util_unlink(tree1, fname_src);
+       smb2_util_unlink(tree1, fname_dst);
+
+       return ret;
+}
 
 /*
    basic testing of SMB2 rename
@@ -1483,6 +1604,10 @@ struct torture_suite *torture_smb2_rename_init(TALLOC_CTX *ctx)
                "rename_dir_bench",
                torture_smb2_rename_dir_bench);
 
+       torture_suite_add_2smb2_test(suite,
+               "close-full-information",
+               test_smb2_close_full_information);
+
        suite->description = talloc_strdup(suite, "smb2.rename tests");
 
        return suite;