2 Unix SMB/CIFS implementation.
4 test suite for delayed write update
6 Copyright (C) Volker Lendecke 2004
7 Copyright (C) Andrew Tridgell 2004
8 Copyright (C) Jeremy Allison 2004
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "torture/torture.h"
26 #include "libcli/raw/libcliraw.h"
27 #include "libcli/raw/raw_proto.h"
28 #include "system/time.h"
29 #include "system/filesys.h"
30 #include "libcli/libcli.h"
31 #include "torture/util.h"
33 #define BASEDIR "\\delaywrite"
35 static bool test_delayed_write_update(struct torture_context *tctx, struct smbcli_state *cli)
37 union smb_fileinfo finfo1, finfo2;
38 const char *fname = BASEDIR "\\torture_file.txt";
45 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
46 int normal_delay = 2000000;
47 double sec = ((double)used_delay) / ((double)normal_delay);
48 int msec = 1000 * sec;
50 if (!torture_setup_dir(cli, BASEDIR)) {
54 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
56 torture_comment(tctx, "Failed to open %s\n", fname);
60 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
61 finfo1.basic_info.in.file.fnum = fnum1;
64 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
66 if (!NT_STATUS_IS_OK(status)) {
67 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
71 torture_comment(tctx, "Initial write time %s\n",
72 nt_time_string(tctx, finfo1.basic_info.out.write_time));
74 /* 3 second delay to ensure we get past any 2 second time
75 granularity (older systems may have that) */
78 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
81 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
82 (int)written, __location__);
86 start = timeval_current();
87 end = timeval_add(&start, (120*sec), 0);
88 while (!timeval_expired(&end)) {
89 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
91 if (!NT_STATUS_IS_OK(status)) {
92 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
96 torture_comment(tctx, "write time %s\n",
97 nt_time_string(tctx, finfo2.basic_info.out.write_time));
98 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
99 double diff = timeval_elapsed(&start);
100 if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
101 torture_comment(tctx, "Server updated write_time after %.2f seconds"
102 "(1 sec == %.2f)(wrong!)\n",
108 torture_comment(tctx, "Server updated write_time after %.2f seconds"
109 "(1 sec == %.2f)(correct)\n",
117 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
118 torture_comment(tctx, "Server did not update write time (wrong!)\n");
124 smbcli_close(cli->tree, fnum1);
125 smbcli_unlink(cli->tree, fname);
126 smbcli_deltree(cli->tree, BASEDIR);
131 /* Updating with a SMBwrite of zero length
132 * changes the write time immediately - even on expand. */
134 static bool test_delayed_write_update1a(struct torture_context *tctx, struct smbcli_state *cli)
136 union smb_fileinfo finfo1, finfo2;
137 const char *fname = BASEDIR "\\torture_file1a.txt";
142 struct timeval start;
144 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
145 int normal_delay = 2000000;
146 double sec = ((double)used_delay) / ((double)normal_delay);
147 int msec = 1000 * sec;
150 if (!torture_setup_dir(cli, BASEDIR)) {
154 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
156 torture_comment(tctx, "Failed to open %s\n", fname);
160 memset(buf, 'x', 2048);
161 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
163 /* 3 second delay to ensure we get past any 2 second time
164 granularity (older systems may have that) */
167 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
168 finfo1.all_info.in.file.fnum = fnum1;
171 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
173 if (!NT_STATUS_IS_OK(status)) {
174 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
178 torture_comment(tctx, "Initial write time %s\n",
179 nt_time_string(tctx, finfo1.all_info.out.write_time));
181 /* Do a zero length SMBwrite call to truncate. */
182 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
185 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
186 (int)written, __location__);
190 start = timeval_current();
191 end = timeval_add(&start, (120*sec), 0);
192 while (!timeval_expired(&end)) {
193 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
195 if (!NT_STATUS_IS_OK(status)) {
196 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
201 if (finfo2.all_info.out.size != 10240) {
202 DEBUG(0, ("file not truncated\n"));
207 torture_comment(tctx, "write time %s\n",
208 nt_time_string(tctx, finfo2.all_info.out.write_time));
209 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
210 double diff = timeval_elapsed(&start);
211 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
212 torture_comment(tctx, "After SMBwrite truncate "
213 "server updated write_time after %.2f seconds"
214 "(1 sec == %.2f)(wrong!)\n",
220 torture_comment(tctx, "After SMBwrite truncate "
221 "server updated write_time after %.2f seconds"
222 "(1 sec == %.2f)(correct)\n",
230 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
231 torture_comment(tctx, "Server did not update write time (wrong!)\n");
237 smbcli_close(cli->tree, fnum1);
238 smbcli_unlink(cli->tree, fname);
239 smbcli_deltree(cli->tree, BASEDIR);
244 /* Updating with a SET_FILE_END_OF_FILE_INFO
245 * changes the write time immediately - even on expand. */
247 static bool test_delayed_write_update1b(struct torture_context *tctx, struct smbcli_state *cli)
249 union smb_fileinfo finfo1, finfo2;
250 const char *fname = BASEDIR "\\torture_file1b.txt";
255 struct timeval start;
257 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
258 int normal_delay = 2000000;
259 double sec = ((double)used_delay) / ((double)normal_delay);
260 int msec = 1000 * sec;
263 if (!torture_setup_dir(cli, BASEDIR)) {
267 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
269 torture_comment(tctx, "Failed to open %s\n", fname);
273 memset(buf, 'x', 2048);
274 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
276 /* 3 second delay to ensure we get past any 2 second time
277 granularity (older systems may have that) */
280 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
281 finfo1.all_info.in.file.fnum = fnum1;
284 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
286 if (!NT_STATUS_IS_OK(status)) {
287 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
291 torture_comment(tctx, "Initial write time %s\n",
292 nt_time_string(tctx, finfo1.all_info.out.write_time));
294 /* Do a SET_END_OF_FILE_INFO call to truncate. */
295 status = smbcli_ftruncate(cli->tree, fnum1, (uint64_t)10240);
297 if (!NT_STATUS_IS_OK(status)) {
298 torture_comment(tctx, "SET_END_OF_FILE failed (%s)\n",
303 start = timeval_current();
304 end = timeval_add(&start, (120*sec), 0);
305 while (!timeval_expired(&end)) {
306 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
308 if (!NT_STATUS_IS_OK(status)) {
309 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
314 if (finfo2.all_info.out.size != 10240) {
315 DEBUG(0, ("file not truncated\n"));
320 torture_comment(tctx, "write time %s\n",
321 nt_time_string(tctx, finfo2.all_info.out.write_time));
322 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
323 double diff = timeval_elapsed(&start);
324 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
325 torture_comment(tctx, "After SET_END_OF_FILE truncate "
326 "server updated write_time after %.2f seconds"
327 "(1 sec == %.2f)(wrong!)\n",
333 torture_comment(tctx, "After SET_END_OF_FILE truncate "
334 "server updated write_time after %.2f seconds"
335 "(1 sec == %.2f)(correct)\n",
343 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
344 torture_comment(tctx, "Server did not update write time (wrong!)\n");
350 smbcli_close(cli->tree, fnum1);
351 smbcli_unlink(cli->tree, fname);
352 smbcli_deltree(cli->tree, BASEDIR);
357 /* Updating with a SET_ALLOCATION_INFO (truncate) does so immediately. */
359 static bool test_delayed_write_update1c(struct torture_context *tctx, struct smbcli_state *cli)
361 union smb_setfileinfo parms;
362 union smb_fileinfo finfo1, finfo2;
363 const char *fname = BASEDIR "\\torture_file1c.txt";
368 struct timeval start;
370 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
371 int normal_delay = 2000000;
372 double sec = ((double)used_delay) / ((double)normal_delay);
373 int msec = 1000 * sec;
376 if (!torture_setup_dir(cli, BASEDIR)) {
380 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
382 torture_comment(tctx, "Failed to open %s\n", fname);
386 memset(buf, 'x', 2048);
387 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
389 /* 3 second delay to ensure we get past any 2 second time
390 granularity (older systems may have that) */
393 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
394 finfo1.all_info.in.file.fnum = fnum1;
397 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
399 if (!NT_STATUS_IS_OK(status)) {
400 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
404 torture_comment(tctx, "Initial write time %s\n",
405 nt_time_string(tctx, finfo1.all_info.out.write_time));
407 /* Do a SET_ALLOCATION_SIZE call to truncate. */
408 parms.allocation_info.level = RAW_SFILEINFO_ALLOCATION_INFO;
409 parms.allocation_info.in.file.fnum = fnum1;
410 parms.allocation_info.in.alloc_size = 0;
412 status = smb_raw_setfileinfo(cli->tree, &parms);
414 if (!NT_STATUS_IS_OK(status)) {
415 torture_comment(tctx, "RAW_SFILEINFO_ALLOCATION_INFO failed (%s)\n",
420 start = timeval_current();
421 end = timeval_add(&start, (120*sec), 0);
422 while (!timeval_expired(&end)) {
423 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
425 if (!NT_STATUS_IS_OK(status)) {
426 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
431 if (finfo2.all_info.out.size != 0) {
432 DEBUG(0, ("file not truncated\n"));
437 torture_comment(tctx, "write time %s\n",
438 nt_time_string(tctx, finfo2.all_info.out.write_time));
439 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
440 double diff = timeval_elapsed(&start);
441 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
442 torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
443 "server updated write_time after %.2f seconds"
444 "(1 sec == %.2f)(wrong!)\n",
450 torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
451 "server updated write_time after %.2f seconds"
452 "(1 sec == %.2f)(correct)\n",
460 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
461 torture_comment(tctx, "Server did not update write time (wrong!)\n");
467 smbcli_close(cli->tree, fnum1);
468 smbcli_unlink(cli->tree, fname);
469 smbcli_deltree(cli->tree, BASEDIR);
475 * Do as above, but using 2 connections.
478 static bool test_delayed_write_update2(struct torture_context *tctx, struct smbcli_state *cli,
479 struct smbcli_state *cli2)
481 union smb_fileinfo finfo1, finfo2;
482 const char *fname = BASEDIR "\\torture_file.txt";
488 struct timeval start;
490 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
491 int normal_delay = 2000000;
492 double sec = ((double)used_delay) / ((double)normal_delay);
493 int msec = 1000 * sec;
494 union smb_flush flsh;
496 if (!torture_setup_dir(cli, BASEDIR)) {
500 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
502 torture_comment(tctx, "Failed to open %s\n", fname);
506 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
507 finfo1.basic_info.in.file.fnum = fnum1;
510 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
512 if (!NT_STATUS_IS_OK(status)) {
513 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
517 torture_comment(tctx, "Initial write time %s\n",
518 nt_time_string(tctx, finfo1.basic_info.out.write_time));
520 /* 3 second delay to ensure we get past any 2 second time
521 granularity (older systems may have that) */
525 /* Try using setfileinfo instead of write to update write time. */
526 union smb_setfileinfo sfinfo;
527 time_t t_set = time(NULL);
528 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
529 sfinfo.basic_info.in.file.fnum = fnum1;
530 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
531 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
533 /* I tried this with both + and - ve to see if it makes a different.
534 It doesn't - once the filetime is set via setfileinfo it stays that way. */
536 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
538 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
540 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
541 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
543 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
545 if (!NT_STATUS_IS_OK(status)) {
546 DEBUG(0, ("sfileinfo failed: %s\n", nt_errstr(status)));
551 finfo2.basic_info.in.file.path = fname;
553 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
555 if (!NT_STATUS_IS_OK(status)) {
556 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
559 torture_comment(tctx, "write time %s\n",
560 nt_time_string(tctx, finfo2.basic_info.out.write_time));
562 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
563 torture_comment(tctx, "Server updated write_time (correct)\n");
565 torture_comment(tctx, "Server did not update write time (wrong!)\n");
569 /* Now try a write to see if the write time gets reset. */
571 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
572 finfo1.basic_info.in.file.fnum = fnum1;
575 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
577 if (!NT_STATUS_IS_OK(status)) {
578 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
582 torture_comment(tctx, "Modified write time %s\n",
583 nt_time_string(tctx, finfo1.basic_info.out.write_time));
586 torture_comment(tctx, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
588 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
591 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
592 (int)written, __location__);
596 /* Just to prove to tridge that the an smbflush has no effect on
597 the write time :-). The setfileinfo IS STICKY. JRA. */
599 torture_comment(tctx, "Doing flush after write\n");
601 flsh.flush.level = RAW_FLUSH_FLUSH;
602 flsh.flush.in.file.fnum = fnum1;
603 status = smb_raw_flush(cli->tree, &flsh);
604 if (!NT_STATUS_IS_OK(status)) {
605 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
609 /* Once the time was set using setfileinfo then it stays set - writes
610 don't have any effect. But make sure. */
611 start = timeval_current();
612 end = timeval_add(&start, (15*sec), 0);
613 while (!timeval_expired(&end)) {
614 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
616 if (!NT_STATUS_IS_OK(status)) {
617 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
621 torture_comment(tctx, "write time %s\n",
622 nt_time_string(tctx, finfo2.basic_info.out.write_time));
623 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
624 double diff = timeval_elapsed(&start);
625 torture_comment(tctx, "Server updated write_time after %.2f seconds"
626 "(1sec == %.2f) (wrong!)\n",
635 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
636 torture_comment(tctx, "Server did not update write time (correct)\n");
639 fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
641 torture_comment(tctx, "Failed to open %s\n", fname);
645 torture_comment(tctx, "Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n");
647 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
650 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
651 (int)written, __location__);
655 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
657 if (!NT_STATUS_IS_OK(status)) {
658 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
661 torture_comment(tctx, "write time %s\n",
662 nt_time_string(tctx, finfo2.basic_info.out.write_time));
663 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
664 torture_comment(tctx, "Server updated write_time (wrong!)\n");
668 torture_comment(tctx, "Closing the first fd to see if write time updated.\n");
669 smbcli_close(cli->tree, fnum1);
672 torture_comment(tctx, "Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n");
674 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
677 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
678 (int)written, __location__);
682 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
683 finfo1.basic_info.in.file.fnum = fnum2;
685 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
687 if (!NT_STATUS_IS_OK(status)) {
688 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
691 torture_comment(tctx, "write time %s\n",
692 nt_time_string(tctx, finfo2.basic_info.out.write_time));
693 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
694 torture_comment(tctx, "Server updated write_time (wrong!)\n");
698 /* Once the time was set using setfileinfo then it stays set - writes
699 don't have any effect. But make sure. */
700 start = timeval_current();
701 end = timeval_add(&start, (15*sec), 0);
702 while (!timeval_expired(&end)) {
703 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
705 if (!NT_STATUS_IS_OK(status)) {
706 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
710 torture_comment(tctx, "write time %s\n",
711 nt_time_string(tctx, finfo2.basic_info.out.write_time));
712 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
713 double diff = timeval_elapsed(&start);
714 torture_comment(tctx, "Server updated write_time after %.2f seconds "
715 "(1sec == %.2f) (wrong!)\n",
724 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
725 torture_comment(tctx, "Server did not update write time (correct)\n");
728 torture_comment(tctx, "Closing second fd to see if write time updated.\n");
730 smbcli_close(cli->tree, fnum2);
733 fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
735 torture_comment(tctx, "Failed to open %s\n", fname);
739 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
740 finfo1.basic_info.in.file.fnum = fnum1;
743 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
745 if (!NT_STATUS_IS_OK(status)) {
746 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
750 torture_comment(tctx, "Second open initial write time %s\n",
751 nt_time_string(tctx, finfo1.basic_info.out.write_time));
754 torture_comment(tctx, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
756 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
759 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
760 (int)written, __location__);
764 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
765 finfo1.basic_info.in.file.fnum = fnum1;
767 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
769 if (!NT_STATUS_IS_OK(status)) {
770 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
773 torture_comment(tctx, "write time %s\n",
774 nt_time_string(tctx, finfo2.basic_info.out.write_time));
775 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
776 torture_comment(tctx, "Server updated write_time (wrong!)\n");
780 /* Now the write time should be updated again */
781 start = timeval_current();
782 end = timeval_add(&start, (15*sec), 0);
783 while (!timeval_expired(&end)) {
784 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
786 if (!NT_STATUS_IS_OK(status)) {
787 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
791 torture_comment(tctx, "write time %s\n",
792 nt_time_string(tctx, finfo2.basic_info.out.write_time));
793 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
794 double diff = timeval_elapsed(&start);
795 if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
796 torture_comment(tctx, "Server updated write_time after %.2f seconds"
797 "(1sec == %.2f) (wrong!)\n",
803 torture_comment(tctx, "Server updated write_time after %.2f seconds"
804 "(1sec == %.2f) (correct)\n",
812 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
813 torture_comment(tctx, "Server did not update write time (wrong!)\n");
818 /* One more test to do. We should read the filetime via findfirst on the
819 second connection to ensure it's the same. This is very easy for a Windows
820 server but a bastard to get right on a POSIX server. JRA. */
823 smbcli_close(cli->tree, fnum1);
824 smbcli_unlink(cli->tree, fname);
825 smbcli_deltree(cli->tree, BASEDIR);
831 /* Windows does obviously not update the stat info during a write call. I
832 * *think* this is the problem causing a spurious Excel 2003 on XP error
833 * message when saving a file. Excel does a setfileinfo, writes, and then does
834 * a getpath(!)info. Or so... For Samba sometimes it displays an error message
835 * that the file might have been changed in between. What i've been able to
836 * trace down is that this happens if the getpathinfo after the write shows a
837 * different last write time than the setfileinfo showed. This is really
841 static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli,
842 struct smbcli_state *cli2)
844 union smb_fileinfo finfo1, finfo2;
845 const char *fname = BASEDIR "\\torture_file.txt";
851 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
852 int normal_delay = 2000000;
853 double sec = ((double)used_delay) / ((double)normal_delay);
854 int msec = 1000 * sec;
856 if (!torture_setup_dir(cli, BASEDIR)) {
860 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
863 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
867 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
868 finfo1.basic_info.in.file.fnum = fnum1;
870 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
872 if (!NT_STATUS_IS_OK(status)) {
874 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
880 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
883 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
888 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
890 torture_result(tctx, TORTURE_FAIL, __location__": failed to open 2nd time - %s",
891 smbcli_errstr(cli2->tree));
896 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
899 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1",
905 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
906 finfo2.basic_info.in.file.path = fname;
908 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
910 if (!NT_STATUS_IS_OK(status)) {
911 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s",
917 if (finfo1.basic_info.out.create_time !=
918 finfo2.basic_info.out.create_time) {
919 torture_result(tctx, TORTURE_FAIL, __location__": create_time changed");
924 if (finfo1.basic_info.out.access_time !=
925 finfo2.basic_info.out.access_time) {
926 torture_result(tctx, TORTURE_FAIL, __location__": access_time changed");
931 if (finfo1.basic_info.out.write_time !=
932 finfo2.basic_info.out.write_time) {
933 torture_result(tctx, TORTURE_FAIL, __location__": write_time changed:\n"
934 "write time conn 1 = %s, conn 2 = %s",
935 nt_time_string(tctx, finfo1.basic_info.out.write_time),
936 nt_time_string(tctx, finfo2.basic_info.out.write_time));
941 if (finfo1.basic_info.out.change_time !=
942 finfo2.basic_info.out.change_time) {
943 torture_result(tctx, TORTURE_FAIL, __location__": change_time changed");
948 /* One of the two following calls updates the qpathinfo. */
950 /* If you had skipped the smbcli_write on fnum2, it would
951 * *not* have updated the stat on disk */
953 smbcli_close(cli2->tree, fnum2);
956 /* This call is only for the people looking at ethereal :-) */
957 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
958 finfo2.basic_info.in.file.path = fname;
960 status = smb_raw_pathinfo(cli->tree, tctx, &finfo2);
962 if (!NT_STATUS_IS_OK(status)) {
963 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
970 smbcli_close(cli->tree, fnum1);
971 smbcli_unlink(cli->tree, fname);
972 smbcli_deltree(cli->tree, BASEDIR);
977 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
978 uint64_t r = 10*1000*1000; \
979 NTTIME g = (given).basic_info.out.write_time; \
980 NTTIME gr = (g / r) * r; \
981 NTTIME c = (correct).basic_info.out.write_time; \
982 NTTIME cr = (c / r) * r; \
983 bool strict = torture_setting_bool(tctx, "strict mode", false); \
985 if (strict && (g cmp c)) { \
987 } else if ((g cmp c) && (gr cmp cr)) { \
988 /* handle filesystem without high resolution timestamps */ \
992 torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \
993 #given, nt_time_string(tctx, g), (unsigned long long)g, \
994 #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \
999 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
1000 COMPARE_WRITE_TIME_CMP(given,correct,!=)
1001 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
1002 COMPARE_WRITE_TIME_CMP(given,correct,<=)
1003 #define COMPARE_WRITE_TIME_LESS(given,correct) \
1004 COMPARE_WRITE_TIME_CMP(given,correct,>=)
1006 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
1007 NTTIME g = (given).basic_info.out.access_time; \
1008 NTTIME c = (correct).basic_info.out.access_time; \
1010 torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \
1011 #given, nt_time_string(tctx, g), \
1012 #cmp, #correct, nt_time_string(tctx, c)); \
1017 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
1018 COMPARE_ACCESS_TIME_CMP(given,correct,!=)
1020 #define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \
1021 COMPARE_ACCESS_TIME_EQUAL(given,correct); \
1022 COMPARE_WRITE_TIME_EQUAL(given,correct); \
1025 #define GET_INFO_FILE(finfo) do { \
1027 _status = smb_raw_fileinfo(cli->tree, tctx, &finfo); \
1028 if (!NT_STATUS_IS_OK(_status)) { \
1030 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1031 nt_errstr(_status)); \
1034 torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1035 nt_time_string(tctx, finfo.basic_info.out.access_time), \
1036 nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1038 #define GET_INFO_PATH(pinfo) do { \
1040 _status = smb_raw_pathinfo(cli2->tree, tctx, &pinfo); \
1041 if (!NT_STATUS_IS_OK(_status)) { \
1042 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \
1043 nt_errstr(_status)); \
1047 torture_comment(tctx, "pathinfo: Access(%s) Write(%s)\n", \
1048 nt_time_string(tctx, pinfo.basic_info.out.access_time), \
1049 nt_time_string(tctx, pinfo.basic_info.out.write_time)); \
1051 #define GET_INFO_BOTH(finfo,pinfo) do { \
1052 GET_INFO_FILE(finfo); \
1053 GET_INFO_PATH(pinfo); \
1054 COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \
1057 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
1059 union smb_setfileinfo sfinfo; \
1060 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1061 sfinfo.basic_info.in.file.fnum = tfnum; \
1062 sfinfo.basic_info.in.create_time = 0; \
1063 sfinfo.basic_info.in.access_time = 0; \
1064 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1065 sfinfo.basic_info.in.change_time = 0; \
1066 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1067 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1068 if (!NT_STATUS_IS_OK(_status)) { \
1069 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1070 nt_errstr(_status)); \
1075 #define SET_INFO_FILE(finfo, wrtime) \
1076 SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
1078 static bool test_delayed_write_update3(struct torture_context *tctx,
1079 struct smbcli_state *cli,
1080 struct smbcli_state *cli2)
1082 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1083 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1084 const char *fname = BASEDIR "\\torture_file.txt";
1088 struct timeval start;
1090 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1091 int normal_delay = 2000000;
1092 double sec = ((double)used_delay) / ((double)normal_delay);
1093 int msec = 1000 * sec;
1095 if (!torture_setup_dir(cli, BASEDIR)) {
1099 torture_comment(tctx, "Open the file handle\n");
1100 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1103 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1107 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1108 finfo0.basic_info.in.file.fnum = fnum1;
1113 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1114 pinfo0.basic_info.in.file.path = fname;
1121 /* get the initial times */
1122 GET_INFO_BOTH(finfo0,pinfo0);
1125 * make sure the write time is updated 2 seconds later
1126 * calcuated from the first write
1127 * (but expect upto 5 seconds extra time for a busy server)
1129 start = timeval_current();
1130 end = timeval_add(&start, 7 * sec, 0);
1131 while (!timeval_expired(&end)) {
1133 torture_comment(tctx, "Do a write on the file handle\n");
1134 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1136 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1140 /* get the times after the write */
1141 GET_INFO_FILE(finfo1);
1143 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1144 double diff = timeval_elapsed(&start);
1145 if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
1146 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1147 "(1sec == %.2f) (wrong!)\n",
1153 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1154 "(1sec == %.2f) (correct)\n",
1161 GET_INFO_BOTH(finfo1,pinfo1);
1162 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1164 /* sure any further write doesn't update the write time */
1165 start = timeval_current();
1166 end = timeval_add(&start, 15 * sec, 0);
1167 while (!timeval_expired(&end)) {
1169 torture_comment(tctx, "Do a write on the file handle\n");
1170 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1172 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1176 /* get the times after the write */
1177 GET_INFO_BOTH(finfo2,pinfo2);
1179 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1180 double diff = timeval_elapsed(&start);
1181 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1182 "(1sec == %.2f) (wrong!)\n",
1190 GET_INFO_BOTH(finfo2,pinfo2);
1191 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1192 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1193 torture_comment(tctx, "Server did not update write_time (correct)\n");
1199 GET_INFO_BOTH(finfo3,pinfo3);
1200 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1203 * the close updates the write time to the time of the close
1204 * and not to the time of the last write!
1206 torture_comment(tctx, "Close the file handle\n");
1207 smbcli_close(cli->tree, fnum1);
1210 GET_INFO_PATH(pinfo4);
1211 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1213 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1214 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1219 smbcli_close(cli->tree, fnum1);
1220 smbcli_unlink(cli->tree, fname);
1221 smbcli_deltree(cli->tree, BASEDIR);
1226 static bool test_delayed_write_update4(struct torture_context *tctx,
1227 struct smbcli_state *cli,
1228 struct smbcli_state *cli2)
1230 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1231 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1232 const char *fname = BASEDIR "\\torture_file.txt";
1236 struct timeval start;
1238 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1239 int normal_delay = 2000000;
1240 double sec = ((double)used_delay) / ((double)normal_delay);
1241 int msec = 1000 * sec;
1243 if (!torture_setup_dir(cli, BASEDIR)) {
1247 torture_comment(tctx, "Open the file handle\n");
1248 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1251 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1255 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1256 finfo0.basic_info.in.file.fnum = fnum1;
1261 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1262 pinfo0.basic_info.in.file.path = fname;
1269 /* get the initial times */
1270 GET_INFO_BOTH(finfo0,pinfo0);
1276 torture_comment(tctx, "Do a write on the file handle\n");
1277 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1279 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1284 GET_INFO_BOTH(finfo1,pinfo1);
1285 COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
1288 * make sure the write time is updated 2 seconds later
1289 * calcuated from the first write
1290 * (but expect upto 3 seconds extra time for a busy server)
1292 start = timeval_current();
1293 end = timeval_add(&start, 5 * sec, 0);
1294 while (!timeval_expired(&end)) {
1295 /* get the times after the first write */
1296 GET_INFO_FILE(finfo1);
1298 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1299 double diff = timeval_elapsed(&start);
1300 if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
1301 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1302 "(1sec == %.2f) (wrong!)\n",
1308 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1309 "(1sec == %.2f) (correct)\n",
1316 GET_INFO_BOTH(finfo1,pinfo1);
1317 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1319 /* sure any further write doesn't update the write time */
1320 start = timeval_current();
1321 end = timeval_add(&start, 15 * sec, 0);
1322 while (!timeval_expired(&end)) {
1324 torture_comment(tctx, "Do a write on the file handle\n");
1325 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1327 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1331 /* get the times after the write */
1332 GET_INFO_BOTH(finfo2,pinfo2);
1334 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1335 double diff = timeval_elapsed(&start);
1336 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1337 "(1sec == %.2f) (wrong!)\n",
1345 GET_INFO_BOTH(finfo2,pinfo2);
1346 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1347 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1348 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
1354 GET_INFO_BOTH(finfo3,pinfo3);
1355 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1358 * the close updates the write time to the time of the close
1359 * and not to the time of the last write!
1361 torture_comment(tctx, "Close the file handle\n");
1362 smbcli_close(cli->tree, fnum1);
1365 GET_INFO_PATH(pinfo4);
1366 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1368 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1369 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1374 smbcli_close(cli->tree, fnum1);
1375 smbcli_unlink(cli->tree, fname);
1376 smbcli_deltree(cli->tree, BASEDIR);
1381 static bool test_delayed_write_update5(struct torture_context *tctx,
1382 struct smbcli_state *cli,
1383 struct smbcli_state *cli2)
1385 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
1386 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
1387 const char *fname = BASEDIR "\\torture_file.txt";
1391 struct timeval start;
1393 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1394 int normal_delay = 2000000;
1395 double sec = ((double)used_delay) / ((double)normal_delay);
1396 int msec = 1000 * sec;
1398 if (!torture_setup_dir(cli, BASEDIR)) {
1402 torture_comment(tctx, "Open the file handle\n");
1403 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1406 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1410 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1411 finfo0.basic_info.in.file.fnum = fnum1;
1417 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1418 pinfo0.basic_info.in.file.path = fname;
1426 /* get the initial times */
1427 GET_INFO_BOTH(finfo0,pinfo0);
1430 torture_comment(tctx, "Do a write on the file handle\n");
1431 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1433 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1438 GET_INFO_BOTH(finfo1,pinfo1);
1439 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1441 torture_comment(tctx, "Set write time in the future on the file handle\n");
1442 SET_INFO_FILE(finfo0, time(NULL) + 86400);
1443 GET_INFO_BOTH(finfo2,pinfo2);
1444 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1446 torture_comment(tctx, "Set write time in the past on the file handle\n");
1447 SET_INFO_FILE(finfo0, time(NULL) - 86400);
1448 GET_INFO_BOTH(finfo2,pinfo2);
1449 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
1451 /* make sure the 2 second delay from the first write are canceled */
1452 start = timeval_current();
1453 end = timeval_add(&start, 15 * sec, 0);
1454 while (!timeval_expired(&end)) {
1456 /* get the times after the first write */
1457 GET_INFO_BOTH(finfo3,pinfo3);
1459 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
1460 double diff = timeval_elapsed(&start);
1461 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1462 "(1sec == %.2f) (wrong!)\n",
1470 GET_INFO_BOTH(finfo3,pinfo3);
1471 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1472 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1473 torture_comment(tctx, "Server did not update write_time (correct)\n");
1476 /* sure any further write doesn't update the write time */
1477 start = timeval_current();
1478 end = timeval_add(&start, 15 * sec, 0);
1479 while (!timeval_expired(&end)) {
1481 torture_comment(tctx, "Do a write on the file handle\n");
1482 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1484 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1488 /* get the times after the write */
1489 GET_INFO_BOTH(finfo4,pinfo4);
1491 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
1492 double diff = timeval_elapsed(&start);
1493 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1494 "(1sec == %.2f) (wrong!)\n",
1502 GET_INFO_BOTH(finfo4,pinfo4);
1503 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
1504 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1505 torture_comment(tctx, "Server did not update write_time (correct)\n");
1511 GET_INFO_BOTH(finfo5,pinfo5);
1512 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
1515 * the close doesn't update the write time
1517 torture_comment(tctx, "Close the file handle\n");
1518 smbcli_close(cli->tree, fnum1);
1521 GET_INFO_PATH(pinfo6);
1522 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
1524 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
1525 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1530 smbcli_close(cli->tree, fnum1);
1531 smbcli_unlink(cli->tree, fname);
1532 smbcli_deltree(cli->tree, BASEDIR);
1537 static bool test_delayed_write_update6(struct torture_context *tctx,
1538 struct smbcli_state *cli,
1539 struct smbcli_state *cli2)
1541 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
1542 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
1543 const char *fname = BASEDIR "\\torture_file.txt";
1548 struct timeval start;
1550 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1551 int normal_delay = 2000000;
1552 double sec = ((double)used_delay) / ((double)normal_delay);
1553 int msec = 1000 * sec;
1556 if (!torture_setup_dir(cli, BASEDIR)) {
1560 torture_comment(tctx, "Open the file handle\n");
1561 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1564 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1569 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
1570 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1573 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1578 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1579 finfo0.basic_info.in.file.fnum = fnum1;
1585 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1586 pinfo0.basic_info.in.file.path = fname;
1595 /* get the initial times */
1596 GET_INFO_BOTH(finfo0,pinfo0);
1599 torture_comment(tctx, "Do a write on the file handle\n");
1600 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1602 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1607 GET_INFO_BOTH(finfo1,pinfo1);
1608 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1610 torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
1611 SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
1612 GET_INFO_BOTH(finfo2,pinfo2);
1613 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1615 torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
1616 SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
1617 GET_INFO_BOTH(finfo2,pinfo2);
1618 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
1620 /* make sure the 2 second delay from the first write are canceled */
1621 start = timeval_current();
1622 end = timeval_add(&start, 15 * sec, 0);
1623 while (!timeval_expired(&end)) {
1625 /* get the times after the first write */
1626 GET_INFO_BOTH(finfo3,pinfo3);
1628 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
1629 double diff = timeval_elapsed(&start);
1630 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1631 "(1sec == %.2f) (wrong!)\n",
1639 GET_INFO_BOTH(finfo3,pinfo3);
1640 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1641 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1642 torture_comment(tctx, "Server did not update write_time (correct)\n");
1645 /* sure any further write doesn't update the write time */
1646 start = timeval_current();
1647 end = timeval_add(&start, 15 * sec, 0);
1648 while (!timeval_expired(&end)) {
1650 torture_comment(tctx, "Do a write on the file handle\n");
1651 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1653 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1657 /* get the times after the write */
1658 GET_INFO_BOTH(finfo4,pinfo4);
1660 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
1661 double diff = timeval_elapsed(&start);
1662 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1663 "(1sec == %.2f) (wrong!)\n",
1671 GET_INFO_BOTH(finfo4,pinfo4);
1672 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
1673 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1674 torture_comment(tctx, "Server did not update write_time (correct)\n");
1680 GET_INFO_BOTH(finfo5,pinfo5);
1681 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
1684 * the close updates the write time to the time of the close
1685 * as the write time was set on the 2nd handle
1687 torture_comment(tctx, "Close the file handle\n");
1688 smbcli_close(cli->tree, fnum1);
1691 GET_INFO_PATH(pinfo6);
1692 COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
1694 if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
1695 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1698 /* keep the 2nd handle open and rerun tests */
1705 * closing the 2nd handle will cause no write time update
1706 * as the write time was explicit set on this handle
1708 torture_comment(tctx, "Close the 2nd file handle\n");
1709 smbcli_close(cli2->tree, fnum2);
1712 GET_INFO_PATH(pinfo7);
1713 COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
1715 if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
1716 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1721 smbcli_close(cli->tree, fnum1);
1723 smbcli_close(cli2->tree, fnum2);
1724 smbcli_unlink(cli->tree, fname);
1725 smbcli_deltree(cli->tree, BASEDIR);
1732 testing of delayed update of write_time
1734 struct torture_suite *torture_delay_write(void)
1736 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "DELAYWRITE");
1738 torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
1739 torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
1740 torture_suite_add_1smb_test(suite, "update of write time and SMBread truncate", test_delayed_write_update1a);
1741 torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b);
1742 torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c);
1743 torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
1744 torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
1745 torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
1746 torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
1747 torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);