📄 delaywrite.c
字号:
/* One more test to do. We should read the filetime via findfirst on the second connection to ensure it's the same. This is very easy for a Windows server but a bastard to get right on a POSIX server. JRA. */ if (fnum1 != -1) smbcli_close(cli->tree, fnum1); smbcli_unlink(cli->tree, fname); smbcli_deltree(cli->tree, BASEDIR); return ret;}/* Windows does obviously not update the stat info during a write call. I * *think* this is the problem causing a spurious Excel 2003 on XP error * message when saving a file. Excel does a setfileinfo, writes, and then does * a getpath(!)info. Or so... For Samba sometimes it displays an error message * that the file might have been changed in between. What i've been able to * trace down is that this happens if the getpathinfo after the write shows a * different last write time than the setfileinfo showed. This is really * nasty.... */static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli, struct smbcli_state *cli2){ union smb_fileinfo finfo1, finfo2; const char *fname = BASEDIR "\\torture_file.txt"; NTSTATUS status; int fnum1 = -1; int fnum2; bool ret = true; ssize_t written; int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); int normal_delay = 2000000; double sec = ((double)used_delay) / ((double)normal_delay); int msec = 1000 * sec; if (!torture_setup_dir(cli, BASEDIR)) { return false; } 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; } finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO; finfo1.basic_info.in.file.fnum = fnum1; status = smb_raw_fileinfo(cli->tree, tctx, &finfo1); if (!NT_STATUS_IS_OK(status)) { ret = false; torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status)); goto done; } msleep(1 * msec); written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); if (written != 1) { torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); ret = false; goto done; } fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE); if (fnum2 == -1) { torture_result(tctx, TORTURE_FAIL, __location__": failed to open 2nd time - %s", smbcli_errstr(cli2->tree)); ret = false; goto done; } written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1); if (written != 1) { torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); ret = false; goto done; } finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO; finfo2.basic_info.in.file.path = fname; status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2); if (!NT_STATUS_IS_OK(status)) { torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status)); ret = false; goto done; } if (finfo1.basic_info.out.create_time != finfo2.basic_info.out.create_time) { torture_result(tctx, TORTURE_FAIL, __location__": create_time changed"); ret = false; goto done; } if (finfo1.basic_info.out.access_time != finfo2.basic_info.out.access_time) { torture_result(tctx, TORTURE_FAIL, __location__": access_time changed"); ret = false; goto done; } if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) { torture_result(tctx, TORTURE_FAIL, __location__": write_time changed:\n" "write time conn 1 = %s, conn 2 = %s", nt_time_string(tctx, finfo1.basic_info.out.write_time), nt_time_string(tctx, finfo2.basic_info.out.write_time)); ret = false; goto done; } if (finfo1.basic_info.out.change_time != finfo2.basic_info.out.change_time) { torture_result(tctx, TORTURE_FAIL, __location__": change_time changed"); ret = false; goto done; } /* One of the two following calls updates the qpathinfo. */ /* If you had skipped the smbcli_write on fnum2, it would * *not* have updated the stat on disk */ smbcli_close(cli2->tree, fnum2); cli2 = NULL; /* This call is only for the people looking at ethereal :-) */ finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO; finfo2.basic_info.in.file.path = fname; status = smb_raw_pathinfo(cli->tree, tctx, &finfo2); if (!NT_STATUS_IS_OK(status)) { torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status)); ret = false; goto done; } done: if (fnum1 != -1) smbcli_close(cli->tree, fnum1); smbcli_unlink(cli->tree, fname); smbcli_deltree(cli->tree, BASEDIR); return ret;}#define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \ uint64_t r = 10*1000*1000; \ NTTIME g = (given).basic_info.out.write_time; \ NTTIME gr = (g / r) * r; \ NTTIME c = (correct).basic_info.out.write_time; \ NTTIME cr = (c / r) * r; \ bool strict = torture_setting_bool(tctx, "strict mode", false); \ bool err = false; \ if (strict && (g cmp c)) { \ err = true; \ } else if (gr cmp cr) { \ /* handle filesystem without high resolution timestamps */ \ err = true; \ } \ if (err) { \ torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \ #given, nt_time_string(tctx, g), (unsigned long long)g, \ #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \ ret = false; \ goto done; \ } \} 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_WRITE_TIME_LESS(given,correct) \ COMPARE_WRITE_TIME_CMP(given,correct,>=)#define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \ NTTIME g = (given).basic_info.out.access_time; \ NTTIME c = (correct).basic_info.out.access_time; \ if (g cmp c) { \ torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \ #given, nt_time_string(tctx, g), \ #cmp, #correct, nt_time_string(tctx, c)); \ ret = false; \ goto done; \ } \} 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_ACCESS_TIME_LESS(given,correct) \ COMPARE_ACCESS_TIME_CMP(given,correct,>=)#define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \ COMPARE_ACCESS_TIME_EQUAL(given,correct); \ COMPARE_WRITE_TIME_EQUAL(given,correct); \} while (0)#define COMPARE_BOTH_TIMES_GEATER(given,correct) do { \ COMPARE_ACCESS_TIME_GREATER(given,correct); \ COMPARE_WRITE_TIME_GREATER(given,correct); \} while (0)#define COMPARE_BOTH_TIMES_LESS(given,correct) do { \ COMPARE_ACCESS_TIME_LESS(given,correct); \ COMPARE_WRITE_TIME_LESS(given,correct); \} while (0)#define GET_INFO_FILE(finfo) do { \ NTSTATUS _status; \ _status = smb_raw_fileinfo(cli->tree, tctx, &finfo); \ if (!NT_STATUS_IS_OK(_status)) { \ ret = false; \ torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \ nt_errstr(_status)); \ goto done; \ } \ torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \ nt_time_string(tctx, finfo.basic_info.out.access_time), \ nt_time_string(tctx, finfo.basic_info.out.write_time)); \} while (0)#define GET_INFO_PATH(pinfo) do { \ NTSTATUS _status; \ _status = smb_raw_pathinfo(cli2->tree, tctx, &pinfo); \ if (!NT_STATUS_IS_OK(_status)) { \ torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \ nt_errstr(_status)); \ ret = false; \ goto done; \ } \ torture_comment(tctx, "pathinfo: Access(%s) Write(%s)\n", \ nt_time_string(tctx, pinfo.basic_info.out.access_time), \ nt_time_string(tctx, pinfo.basic_info.out.write_time)); \} while (0)#define GET_INFO_BOTH(finfo,pinfo) do { \ GET_INFO_FILE(finfo); \ GET_INFO_PATH(pinfo); \ COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \} while (0)#define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \ NTSTATUS _status; \ union smb_setfileinfo sfinfo; \ sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \ sfinfo.basic_info.in.file.fnum = tfnum; \ sfinfo.basic_info.in.create_time = 0; \ sfinfo.basic_info.in.access_time = 0; \ unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \ sfinfo.basic_info.in.change_time = 0; \ sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \ _status = smb_raw_setfileinfo(tree, &sfinfo); \ if (!NT_STATUS_IS_OK(_status)) { \ torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \ nt_errstr(_status)); \ ret = false; \ goto done; \ } \} while (0)#define SET_INFO_FILE(finfo, wrtime) \ SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)static bool test_delayed_write_update3(struct torture_context *tctx, struct smbcli_state *cli, struct smbcli_state *cli2){ union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4; union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5; const char *fname = BASEDIR "\\torture_file.txt"; int fnum1 = -1; bool ret = true; ssize_t written; struct timeval start; struct timeval end; int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); int normal_delay = 2000000; double sec = ((double)used_delay) / ((double)normal_delay); int msec = 1000 * sec; if (!torture_setup_dir(cli, BASEDIR)) { return false; } 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; } finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; finfo0.basic_info.in.file.fnum = fnum1; finfo1 = finfo0; finfo2 = finfo0; finfo3 = finfo0; finfo4 = finfo0; pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; pinfo0.basic_info.in.file.path = fname; pinfo1 = pinfo0; pinfo2 = pinfo0; pinfo3 = pinfo0; pinfo4 = pinfo0; pinfo5 = pinfo0; /* get the initial times */ GET_INFO_BOTH(finfo0,pinfo0); /* * make sure the write time is updated 2 seconds later * calcuated from the first write * (but expect upto 5 seconds extra time for a busy server) */ start = timeval_current(); end = timeval_add(&start, 7 * sec, 0); while (!timeval_expired(&end)) { /* do a write */ torture_comment(tctx, "Do a write on the file handle\n"); written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); if (written != 1) { torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); ret = false; goto done; } /* get the times after the write */ GET_INFO_FILE(finfo1); if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) { double diff = timeval_elapsed(&start); if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */ torture_comment(tctx, "Server updated write_time after %.2f seconds " "(1sec == %.2f) (wrong!)\n", diff, sec); ret = false; break; } torture_comment(tctx, "Server updated write_time after %.2f seconds " "(1sec == %.2f) (correct)\n", diff, sec); break; } msleep(0.5 * msec); } GET_INFO_BOTH(finfo1,pinfo1); /* sure any further write doesn't update the write time */ start = timeval_current(); end = timeval_add(&start, 15 * sec, 0); while (!timeval_expired(&end)) { /* do a write */ torture_comment(tctx, "Do a write on the file handle\n"); written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1); if (written != 1) { torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written); ret = false; goto done; } /* get the times after the write */ GET_INFO_BOTH(finfo2,pinfo2); if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) { double diff = timeval_elapsed(&start); torture_comment(tctx, "Server updated write_time after %.2f seconds " "(1sec == %.2f) (wrong!)\n", diff, sec); ret = false; break; } msleep(2 * msec); } GET_INFO_BOTH(finfo2,pinfo2); COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1); if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) { torture_comment(tctx, "Server did not update write_time (correct)\n"); } /* sleep */ msleep(5 * msec); GET_INFO_BOTH(finfo3,pinfo3); COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2); /* * 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(pinfo4); COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3); if (pinfo4.basic_info.out.write_time > pinfo3.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); smbcli_unlink(cli->tree, fname); smbcli_deltree(cli->tree, BASEDIR); return ret;}static bool test_delayed_write_update4(struct torture_context *tctx, struct smbcli_state *cli, struct smbcli_state *cli2){ union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4; union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5; const char *fname = BASEDIR "\\torture_file.txt"; int fnum1 = -1; bool ret = true; ssize_t written; struct timeval start; struct timeval end; int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); int normal_delay = 2000000; double sec = ((double)used_delay) / ((double)normal_delay); int msec = 1000 * sec; if (!torture_setup_dir(cli, BASEDIR)) { return false; } 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; } finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; finfo0.basic_info.in.file.fnum = fnum1; finfo1 = finfo0; finfo2 = finfo0; finfo3 = finfo0; finfo4 = finfo0; pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO; pinfo0.basic_info.in.file.path = fname; pinfo1 = pinfo0; pinfo2 = pinfo0; pinfo3 = pinfo0; pinfo4 = pinfo0; pinfo5 = pinfo0; /* get the initial times */ GET_INFO_BOTH(finfo0,pinfo0); /* sleep a bit */ msleep(5 * msec); /* do a write */ torture_comment(tctx, "Do a write on the file handle\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -