From 728dcb36ef11adbf915256699002082458ab7e39 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 15 Aug 2018 08:40:54 +0200 Subject: [PATCH] test_delaywrite_delaywrite1 SMB1 --- source4/torture/basic/delaywrite.c | 438 +++++++++++++++++++++++++++++ 1 file changed, 438 insertions(+) diff --git a/source4/torture/basic/delaywrite.c b/source4/torture/basic/delaywrite.c index dd261d86d096..da69a5a6d126 100644 --- a/source4/torture/basic/delaywrite.c +++ b/source4/torture/basic/delaywrite.c @@ -3074,6 +3074,443 @@ static bool test_directory_update8(struct torture_context *tctx, struct smbcli_s return ret; } +#undef COMPARE_WRITE_TIME_CMP +#undef COMPARE_ACCESS_TIME_CMP + +#define COMPARE_TIME_CMP(given, gelem, correct, celem, cmp) do { \ + const uint64_t _r = 10*1000*1000; \ + NTTIME _g = (given).basic_info.out.gelem; \ + NTTIME _gr = (_g / _r) * _r; \ + NTTIME _c = (correct).basic_info.out.celem; \ + NTTIME _cr = (_c / _r) * _r; \ + bool _strict = torture_setting_bool(tctx, "strict mode", false); \ + const char *_err = NULL; \ + if (_strict && (_g cmp _c)) { \ + _err = "strict"; \ + } else if ((_g cmp _c) && (_gr cmp _cr)) { \ + /* handle filesystem without high resolution timestamps */ \ + _err = "rounded"; \ + } \ + if (_err != NULL) { \ + struct timeval _gtv; \ + struct timeval _ctv; \ + struct timeval_buf _gtvb; \ + struct timeval_buf _ctvb; \ + nttime_to_timeval(&_gtv, _g); \ + nttime_to_timeval(&_ctv, _c); \ + torture_result(tctx, TORTURE_FAIL, \ + __location__": %s wrong (%s.%s)%s %s (%s.%s)%s", \ + _err, \ + #given, #gelem, \ + timeval_str_buf(&_gtv, false, true, &_gtvb), \ + #cmp, \ + #correct, #celem, \ + timeval_str_buf(&_ctv, false, true, &_ctvb)); \ + ret = false; \ + goto done; \ + } \ +} while (0) +#define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \ + COMPARE_TIME_CMP(given, write_time, correct, write_time, cmp); \ +} while (0) +#define COMPARE_WRITE_TIME_EQUAL(given,correct) \ + COMPARE_WRITE_TIME_CMP(given,correct,!=) +#define COMPARE_WRITE_TIME_GREATER(given,correct) \ + COMPARE_WRITE_TIME_CMP(given,correct,<=) + +#define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \ + COMPARE_TIME_CMP(given, access_time, correct, access_time, cmp); \ +} while (0) +#define COMPARE_ACCESS_TIME_EQUAL(given,correct) \ + COMPARE_ACCESS_TIME_CMP(given,correct,!=) +#define COMPARE_ACCESS_TIME_GREATER(given,correct) \ + COMPARE_ACCESS_TIME_CMP(given,correct,<=) + +#define COMPARE_CHANGE_TIME_CMP(given, correct, cmp) do { \ + COMPARE_TIME_CMP(given, change_time, correct, change_time, cmp); \ +} while (0) +#define COMPARE_CHANGE_TIME_EQUAL(given,correct) \ + COMPARE_CHANGE_TIME_CMP(given,correct,!=) +#define COMPARE_CHANGE_TIME_GREATER(given,correct) \ + COMPARE_CHANGE_TIME_CMP(given,correct,<=) + +#define COMPARE_CREATE_TIME_CMP(given, correct, cmp) do { \ + COMPARE_TIME_CMP(given, create_time, correct, create_time, cmp); \ +} while (0) +#define COMPARE_CREATE_TIME_EQUAL(given,correct) \ + COMPARE_CREATE_TIME_CMP(given,correct,!=) + +#define COMPARE_ALL_TIMES_EQUAL(given,correct) do { \ + COMPARE_WRITE_TIME_EQUAL(given,correct); \ + COMPARE_CHANGE_TIME_EQUAL(given,correct); \ + COMPARE_ACCESS_TIME_EQUAL(given,correct); \ + COMPARE_CREATE_TIME_EQUAL(given,correct); \ +} while (0) + +#define COMPARE_TIMES_AFTER_WRITE(given,correct) do { \ + COMPARE_WRITE_TIME_GREATER(given,correct); \ + COMPARE_CHANGE_TIME_GREATER(given,correct); \ + COMPARE_ACCESS_TIME_EQUAL(given,correct); \ + COMPARE_CREATE_TIME_EQUAL(given,correct); \ + COMPARE_TIME_CMP(given, change_time, given, write_time, !=); \ +} while (0) + +struct test_delaywrite_delaywrite1_state { + struct torture_context *tctx; + struct smbcli_state *cli1; + struct smbcli_state *cli2; + int fnum1; + int fnum2; + const char *fname; +}; + +static bool test_delaywrite_delaywrite1_get_info(void *private_data, + union smb_fileinfo *finfo) +{ + struct test_delaywrite_delaywrite1_state *state = + (struct test_delaywrite_delaywrite1_state *)private_data; + struct torture_context *tctx = state->tctx; + struct smbcli_state *cli = state->cli1; + struct smbcli_state *cli2 = state->cli2; + union smb_fileinfo t1finfo; + union smb_fileinfo t2finfo; + union smb_fileinfo t2pinfo; + bool ret = true; + + ZERO_STRUCTP(finfo); + + ZERO_STRUCT(t1finfo); + t1finfo.basic_info.level = RAW_FILEINFO_BASIC_INFORMATION; + t1finfo.basic_info.in.file.fnum = state->fnum1; + + ZERO_STRUCT(t2finfo); + t2finfo.basic_info.level = RAW_FILEINFO_BASIC_INFORMATION; + t2finfo.basic_info.in.file.fnum = state->fnum2; + ZERO_STRUCT(t2pinfo); + t2pinfo.basic_info.level = RAW_FILEINFO_BASIC_INFORMATION; + t2pinfo.basic_info.in.file.path = state->fname; + + GET_INFO_FILE2(t2finfo); + GET_INFO_PATH(t2pinfo); + GET_INFO_FILE(t1finfo); + if (t1finfo.basic_info.out.write_time != t2finfo.basic_info.out.write_time) { + /* + * There was a race, get it again on handle 2, + * but then they have to match. + */ + GET_INFO_FILE2(t2finfo); + GET_INFO_PATH(t2pinfo); + } + COMPARE_ALL_TIMES_EQUAL(t1finfo, t2finfo); + COMPARE_ALL_TIMES_EQUAL(t1finfo, t2pinfo); + + finfo->basic_info.out = t1finfo.basic_info.out; +done: + return ret; +} + +static bool test_delaywrite_delaywrite1_write_data(void *private_data) +{ + struct test_delaywrite_delaywrite1_state *state = + (struct test_delaywrite_delaywrite1_state *)private_data; + struct torture_context *tctx = state->tctx; + bool ret = true; + ssize_t nwritten; + + nwritten = smbcli_write(state->cli1->tree, state->fnum1, 0, "x", 0, 1); + torture_assert_int_equal_goto(tctx, nwritten, 1, + ret, done, "smbcli_write"); + +done: + return ret; +} + +static bool test_delaywrite_delaywrite1(struct torture_context *tctx, + struct smbcli_state *cli, + struct smbcli_state *cli2) +{ + struct test_delaywrite_delaywrite1_state state = { + .tctx = tctx, + .cli1 = cli, + .cli2 = cli2, + }; + union smb_fileinfo c1finfo0, c1finfo1, c1finfo2; + union smb_fileinfo c2pinfo0, c2pinfo1, c2pinfo2, c2pinfo3; + const char *fname = BASEDIR "\\torture_file3.txt"; + int fnum1 = -1; + int fnum2 = -1; + bool ret = true; + //double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + //double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 1000000); + double normal_delay = 1000000; + double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", normal_delay); + //double normal_delay = 1000000; + //int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + bool ok; + + torture_comment(tctx, "\nRunning test_delayed_write_update3\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + torture_comment(tctx, "Open the file handle\n"); + fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum1 == -1) { + ret = false; + torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); + goto done; + } + fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum2 == -1) { + ret = false; + torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname); + goto done; + } + + c1finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; + c1finfo0.basic_info.in.file.fnum = fnum1; + c1finfo1 = c1finfo0; + c1finfo2 = c1finfo0; + c2pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; + c2pinfo0.basic_info.in.file.path = fname; + c2pinfo1 = c2pinfo0; + c2pinfo2 = c2pinfo0; + c2pinfo3 = c2pinfo0; + + /* get the initial times */ + GET_INFO_BOTH(c1finfo0,c2pinfo0); + + state.fname = fname; + state.fnum1 = fnum1; + state.fnum2 = fnum2; + + ok = test_delay_writetime1(tctx, used_delay, normal_delay, + "run1", + test_delaywrite_delaywrite1_get_info, + test_delaywrite_delaywrite1_write_data, + &state); + torture_assert(tctx, ok, "test_delay_writetime1(1)"); + ok = test_delay_writetime1(tctx, used_delay, normal_delay, + "run2", + test_delaywrite_delaywrite1_get_info, + test_delaywrite_delaywrite1_write_data, + &state); + torture_assert(tctx, ok, "test_delay_writetime2(2)"); + + GET_INFO_BOTH(c1finfo1, c2pinfo1); + COMPARE_TIMES_AFTER_WRITE(c1finfo1, c1finfo0); + + /* sleep */ + torture_comment(tctx, "Sleep ...\n"); + smb_msleep(5 * msec); + torture_comment(tctx, "... continue\n"); + + GET_INFO_BOTH(c1finfo2, c2pinfo2); + COMPARE_ALL_TIMES_EQUAL(c1finfo2, c1finfo1); + + /* + * the close updates the write time to the time of the close + * and not to the time of the last write! + */ + torture_comment(tctx, "Close the file handle\n"); + smbcli_close(cli->tree, fnum1); + fnum1 = -1; + + GET_INFO_PATH(c2pinfo3); + COMPARE_WRITE_TIME_GREATER(c2pinfo3, c2pinfo2); + + if (c2pinfo3.basic_info.out.write_time > c2pinfo2.basic_info.out.write_time) { + torture_comment(tctx, "Server updated the write_time on close (correct)\n"); + } + + done: + if (fnum1 != -1) { + smbcli_close(cli->tree, fnum1); + } + if (fnum2 != -1) { + smbcli_close(cli2->tree, fnum2); + } + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} +#if 0 + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_handle _h2; + struct smb2_handle *h2 = NULL; + struct smb2_create cr1; + struct smb2_create cr2; + union smb_fileinfo c1finfoCR, c1finfo0, c1finfo1, c1finfo2, c1finfoCL; + union smb_fileinfo c2finfoCR, c2finfo0, c2finfo1, c2finfo2, c2finfo3, c2finfoCL; + struct smb2_close cl1; + struct smb2_close cl2; + //double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + //double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 1000000); + double normal_delay = 1000000; + double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", normal_delay); + //double normal_delay = 1000000; + //int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + bool ret = true; + bool ok; + + /* Choose a random name in case the state is left a little funky. */ + snprintf(fname, 256, "durable_open_delaywrite1_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree1, fname); + + smb2_oplock_create_share(&cr1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + cr1.in.durable_open = true; + + status = smb2_create(tree1, mem_ctx, &cr1); + CHECK_STATUS(status, NT_STATUS_OK); + _h1 = cr1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(&cr1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(cr1.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(cr1.out.durable_open, true); + CHECK_VAL(cr1.out.durable_open_v2, false); + CHECK_VAL(cr1.out.persistent_open, false); + + cr2 = cr1; + cr2.in.desired_access = SEC_FILE_READ_ATTRIBUTE; + cr2.in.durable_open = false; + cr2.in.oplock_level = 0; + cr2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree2, mem_ctx, &cr2); + CHECK_STATUS(status, NT_STATUS_OK); + _h2 = cr2.out.file.handle; + h2 = &_h2; + CHECK_CREATED(&cr2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(cr2.out.oplock_level, 0); + CHECK_VAL(cr2.out.durable_open, false); + CHECK_VAL(cr2.out.durable_open_v2, false); + CHECK_VAL(cr2.out.persistent_open, false); + + ZERO_STRUCT(c1finfoCR); + c1finfoCR.basic_info.out.create_time = cr1.out.create_time; + c1finfoCR.basic_info.out.access_time = cr1.out.access_time; + c1finfoCR.basic_info.out.write_time = cr1.out.write_time; + c1finfoCR.basic_info.out.change_time = cr1.out.change_time; + c1finfoCR.basic_info.out.attrib = cr1.out.file_attr; + + ZERO_STRUCT(c2finfoCR); + c2finfoCR.basic_info.out.create_time = cr2.out.create_time; + c2finfoCR.basic_info.out.access_time = cr2.out.access_time; + c2finfoCR.basic_info.out.write_time = cr2.out.write_time; + c2finfoCR.basic_info.out.change_time = cr2.out.change_time; + c2finfoCR.basic_info.out.attrib = cr2.out.file_attr; + + COMPARE_ALL_TIMES_EQUAL(c1finfoCR, c2finfoCR); + + ZERO_STRUCT(c1finfo0); + c1finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFORMATION; + c1finfo0.basic_info.in.file.handle = *h1; + c1finfo1 = c1finfo0; + c1finfo2 = c1finfo0; + + ZERO_STRUCT(c2finfo0); + c2finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFORMATION; + c2finfo0.basic_info.in.file.handle = *h2; + c2finfo1 = c2finfo0; + c2finfo2 = c2finfo0; + c2finfo3 = c2finfo0; + + GET_INFO_BOTH(c1finfo0, c2finfo0); + COMPARE_ALL_TIMES_EQUAL(c1finfo0, c1finfoCR); + + state.h1 = h1; + state.h2 = h2; + + ok = test_delay_writetime1(tctx, used_delay, normal_delay, + "run1", + test_delaywrite_delaywrite1_get_info, + test_delaywrite_delaywrite1_write_data, + &state); + torture_assert(tctx, ok, "test_delay_writetime1(1)"); + ok = test_delay_writetime1(tctx, used_delay, normal_delay, + "run2", + test_delaywrite_delaywrite1_get_info, + test_delaywrite_delaywrite1_write_data, + &state); + torture_assert(tctx, ok, "test_delay_writetime2(2)"); + + GET_INFO_BOTH(c1finfo1, c2finfo1); + COMPARE_TIMES_AFTER_WRITE(c1finfo1, c1finfo0); + + /* sleep */ + torture_comment(tctx, "Sleep ...\n"); + smb_msleep(5 * msec); + torture_comment(tctx, "... continue\n"); + + GET_INFO_BOTH(c1finfo2, c2finfo2); + COMPARE_ALL_TIMES_EQUAL(c1finfo2, c1finfo1); + + /* + * the close updates the write time to the time of the close + * and not to the time of the last write! + */ + torture_comment(tctx, "Close the file handle\n"); + + ZERO_STRUCT(cl1); + cl1.in.file.handle = *h1; + cl1.in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION; + status = smb2_close(tree1, &cl1); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = NULL; + ZERO_STRUCT(c1finfoCL); + c1finfoCL.basic_info.out.create_time = cl1.out.create_time; + c1finfoCL.basic_info.out.access_time = cl1.out.access_time; + c1finfoCL.basic_info.out.write_time = cl1.out.write_time; + c1finfoCL.basic_info.out.change_time = cl1.out.change_time; + c1finfoCL.basic_info.out.attrib = cl1.out.file_attr; + COMPARE_ALL_TIMES_EQUAL(c1finfoCL, c1finfo2); + + GET_INFO_FILE(tree2, c2finfo3); + COMPARE_ALL_TIMES_EQUAL(c2finfo3, c1finfoCL); + + ZERO_STRUCT(cl2); + cl2.in.file.handle = *h2; + cl2.in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION; + status = smb2_close(tree2, &cl2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = NULL; + ZERO_STRUCT(c2finfoCL); + c2finfoCL.basic_info.out.create_time = cl2.out.create_time; + c2finfoCL.basic_info.out.access_time = cl2.out.access_time; + c2finfoCL.basic_info.out.write_time = cl2.out.write_time; + c2finfoCL.basic_info.out.change_time = cl2.out.change_time; + c2finfoCL.basic_info.out.attrib = cl2.out.file_attr; + COMPARE_ALL_TIMES_EQUAL(c2finfoCL, c1finfoCL); + +done: + if (h1 != NULL) { + smb2_util_close(tree1, *h1); + } + if (h2 != NULL) { + smb2_util_close(tree2, *h2); + } + + smb2_util_unlink(tree1, fname); + + talloc_free(tree1); + talloc_free(tree2); + + talloc_free(mem_ctx); + + return ret; +} +#endif /* testing of delayed update of write_time */ @@ -3098,6 +3535,7 @@ struct torture_suite *torture_delay_write(TALLOC_CTX *ctx) torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6); torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7); torture_suite_add_1smb_test(suite, "directory timestamp update test", test_directory_update8); + torture_suite_add_2smb_test(suite, "delaywrite1", test_delaywrite_delaywrite1); return suite; } -- 2.34.1