From: Ralph Boehme Date: Fri, 6 Mar 2020 15:21:47 +0000 (+0100) Subject: torture/smb2: Windows 2019 15 ms timestamp resolution X-Git-Tag: ldb-2.2.0~1396 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6f7d1d8a37bfb877b3f07423cbcffd15710e8d08;p=thirdparty%2Fsamba.git torture/smb2: Windows 2019 15 ms timestamp resolution 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 Reviewed-by: Jeremy Allison --- diff --git a/selftest/skip b/selftest/skip index 11bf29599fa..549ba202021 100644 --- a/selftest/skip +++ b/selftest/skip @@ -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 diff --git a/source4/torture/smb2/smb2.c b/source4/torture/smb2/smb2.c index b9a6607e2bf..c860d03102e 100644 --- a/source4/torture/smb2/smb2.c +++ b/source4/torture/smb2/smb2.c @@ -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)); diff --git a/source4/torture/smb2/timestamps.c b/source4/torture/smb2/timestamps.c index afa0fe24106..7f1a54c8ad0 100644 --- a/source4/torture/smb2/timestamps.c +++ b/source4/torture/smb2/timestamps.c @@ -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; +}