]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
torture/smb2: Windows 2019 15 ms timestamp resolution
authorRalph Boehme <slow@samba.org>
Fri, 6 Mar 2020 15:21:47 +0000 (16:21 +0100)
committerJeremy Allison <jra@samba.org>
Thu, 19 Mar 2020 01:20:34 +0000 (01:20 +0000)
This test demonstrates that Windows has a timestamp resolution of ~15ms.

When a smaller amount of time than that has passed between modifying operations
on a file, it's not necessarily detectable on a Windows 2019 server that
implements immediate timestamp updates (no delayed magic).

Note that this test relies on a low latency SMB connection. Even with a low
latency connection of eg 1m there's a chance of 1/15 that the first part of the
test expecting no timestamp change fails as the writetime is updated.

Due to this timing dependency this test is skipped in Samba CI, but it is
preserved here for future SMB2 timestamps behaviour archealogists.

See also: https://lists.samba.org/archive/cifs-protocol/2019-December/003358.html

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
selftest/skip
source4/torture/smb2/smb2.c
source4/torture/smb2/timestamps.c

index 11bf29599fab86715d4025b097da62f05da776c5..549ba202021c3d94c21e46602e951d3035d21aa9 100644 (file)
@@ -75,6 +75,8 @@
 ^samba3.smb2.durable-open-disconnect    # Not a test, but a way to create a disconnected durable
 ^samba3.smb2.scan                       # No tests
 ^samba3.smb2.oplock.levelii501         # No test yet
+^samba3.smb2.timestamp_resolution       # See the comment on the test
+^samba4.smb2.timestamp_resolution
 ^samba3.rpc.samr.passwords.lockout\(ad_dc\) # No point running this version, it just waits 12 times longer the samba4 version of this test, covering the same code
 ^samba4.base.iometer
 ^samba4.base.casetable
index b9a6607e2bf260c0f8b9dbdd5e2f465acc0a947c..c860d03102e4c545e237cf9de3c95925988741d6 100644 (file)
@@ -198,6 +198,7 @@ NTSTATUS torture_smb2_init(TALLOC_CTX *ctx)
        torture_suite_add_suite(suite, torture_smb2_multichannel_init(suite));
        torture_suite_add_suite(suite, torture_smb2_samba3misc_init(suite));
        torture_suite_add_suite(suite, torture_smb2_timestamps_init(suite));
+       torture_suite_add_suite(suite, torture_smb2_timestamp_resolution_init(suite));
        torture_suite_add_1smb2_test(suite, "openattr", torture_smb2_openattrtest);
        torture_suite_add_1smb2_test(suite, "winattr", torture_smb2_winattrtest);
        torture_suite_add_suite(suite, torture_smb2_readwrite_init(suite));
index afa0fe241062a4188360d0d879a7629b17b49b0f..7f1a54c8ad0a5d65cbb96296e6a6ba578bbbeb0a 100644 (file)
@@ -727,3 +727,148 @@ struct torture_suite *torture_smb2_timestamps_init(TALLOC_CTX *ctx)
 
        return suite;
 }
+
+/*
+ * This test shows that Windows has a timestamp resolution of ~15ms. When so
+ * when a smaller amount of time than that has passed it's not necessarily
+ * detectable on a Windows 2019 and newer who implement immediate timestamp
+ * updates.
+ *
+ * Note that this test relies on a low latency SMB connection. Even with a low
+ * latency connection of eg 1m there's a chance of 1/15 that the first part of
+ * the test expecting no timestamp change fails as the writetime is updated.
+ *
+ * Due to this timing dependency this test is skipped in Samba CI, but it is
+ * preserved here for future SMB2 timestamps behaviour archealogists.
+ *
+ * See also: https://lists.samba.org/archive/cifs-protocol/2019-December/003358.html
+ */
+static bool test_timestamp_resolution1(struct torture_context *tctx,
+                                      struct smb2_tree *tree)
+{
+       union smb_fileinfo finfo1;
+       const char *fname = BASEDIR "\\" FNAME;
+       struct smb2_create cr;
+       struct smb2_handle h = {{0}};
+       struct smb2_close cl;
+       NTSTATUS status;
+       bool ret = true;
+
+       smb2_deltree(tree, BASEDIR);
+       status = torture_smb2_testdir(tree, BASEDIR, &h);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "create failed\n");
+       status = smb2_util_close(tree, h );
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "close failed\n");
+
+       torture_comment(tctx, "Write without delay, expect no "
+                       "write-time change\n");
+
+       smb2_generic_create(&cr, NULL, false, fname,
+                           NTCREATEX_DISP_CREATE,
+                           smb2_util_oplock_level(""), 0, 0);
+       status = smb2_create(tree, tree, &cr);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "create failed\n");
+       h = cr.out.file.handle;
+
+       finfo1 = (union smb_fileinfo) {
+               .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+               .generic.in.file.handle = h,
+       };
+       status = smb2_getinfo_file(tree, tree, &finfo1);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "getinfo failed\n");
+
+       status = smb2_util_write(tree, h, "123456789", 0, 9);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "write failed\n");
+
+       cl = (struct smb2_close) {
+               .in.file.handle = h,
+               .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
+       };
+
+       status = smb2_close(tree, &cl);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "close failed\n");
+       ZERO_STRUCT(h);
+
+       torture_comment(tctx, "Initial: %s\nClose: %s\n",
+                       nt_time_string(tctx, finfo1.basic_info.out.write_time),
+                       nt_time_string(tctx, cl.out.write_time));
+
+       torture_assert_u64_equal_goto(tctx,
+                                     finfo1.basic_info.out.write_time,
+                                     cl.out.write_time,
+                                     ret, done,
+                                     "Write time changed (wrong!)\n");
+
+       torture_comment(tctx, "Write with 20 ms delay, expect "
+                       "write-time change\n");
+
+       smb2_generic_create(&cr, NULL, false, fname,
+                           NTCREATEX_DISP_OPEN,
+                           smb2_util_oplock_level(""), 0, 0);
+       status = smb2_create(tree, tree, &cr);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "create failed\n");
+       h = cr.out.file.handle;
+
+       finfo1 = (union smb_fileinfo) {
+               .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+               .generic.in.file.handle = h,
+       };
+       status = smb2_getinfo_file(tree, tree, &finfo1);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "getinfo failed\n");
+
+       smb_msleep(20);
+
+       status = smb2_util_write(tree, h, "123456789", 0, 9);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "write failed\n");
+
+       cl = (struct smb2_close) {
+               .in.file.handle = h,
+               .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
+       };
+
+       status = smb2_close(tree, &cl);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "close failed\n");
+       ZERO_STRUCT(h);
+
+       torture_comment(tctx, "Initial: %s\nClose: %s\n",
+                       nt_time_string(tctx, finfo1.basic_info.out.write_time),
+                       nt_time_string(tctx, cl.out.write_time));
+
+       torture_assert_u64_not_equal_goto(
+               tctx,
+               finfo1.basic_info.out.write_time,
+               cl.out.write_time,
+               ret, done,
+               "Write time did not change (wrong!)\n");
+
+done:
+       if (!smb2_util_handle_empty(h)) {
+               smb2_util_close(tree, h);
+       }
+       smb2_deltree(tree, BASEDIR);
+       return ret;
+}
+
+/*
+   basic testing of SMB2 timestamps
+*/
+struct torture_suite *torture_smb2_timestamp_resolution_init(TALLOC_CTX *ctx)
+{
+       struct torture_suite *suite = torture_suite_create(ctx, "timestamp_resolution");
+
+       torture_suite_add_1smb2_test(suite, "resolution1", test_timestamp_resolution1);
+
+       suite->description = talloc_strdup(suite, "SMB2 timestamp tests");
+
+       return suite;
+}