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"
32 #include "torture/basic/proto.h"
33 #include "lib/util/time_basic.h"
35 #define BASEDIR "\\delaywrite"
37 static bool test_delayed_write_update(struct torture_context *tctx, struct smbcli_state *cli)
39 union smb_fileinfo finfo1, finfo2;
40 union smb_fileinfo pinfo1;
41 const char *fname = BASEDIR "\\torture_file.txt";
48 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
49 int normal_delay = 2000000;
50 double sec = ((double)used_delay) / ((double)normal_delay);
51 int msec = 1000 * sec;
53 torture_comment(tctx, "\nRunning test_delayed_write_update\n");
55 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
57 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
58 torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
59 "Failed to open %s", fname));
61 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
62 finfo1.basic_info.in.file.fnum = fnum1;
65 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
66 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
68 torture_comment(tctx, "Initial write time %s\n",
69 nt_time_string(tctx, finfo1.basic_info.out.write_time));
71 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
72 torture_assert_int_equal(tctx, written, 1,
73 "unexpected number of bytes written");
75 start = timeval_current();
76 //smb_msleep(5 * msec);
77 pinfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
78 pinfo1.basic_info.level = RAW_FILEINFO_MODE_INFORMATION;
79 pinfo1.basic_info.in.file.path = fname;
80 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo1);
81 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
83 //written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
84 //torture_assert_int_equal(tctx, written, 1,
85 // "unexpected number of bytes written");
87 end = timeval_add(&start, (30 * sec), 0);
88 while (!timeval_expired(&end)) {
89 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
91 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
93 torture_comment(tctx, "write time %s\n",
94 nt_time_string(tctx, finfo2.basic_info.out.write_time));
96 if (finfo1.basic_info.out.write_time !=
97 finfo2.basic_info.out.write_time)
99 double diff = timeval_elapsed(&start);
102 diff >= (used_delay / (double)1000000),
103 talloc_asprintf(tctx,
104 "Server updated write_time after %.2f "
105 "seconds (expected >= %.2f)\n",
106 diff, used_delay/(double)1000000));
108 torture_comment(tctx, "Server updated write_time after %.2f seconds (correct)\n",
113 smb_msleep(1 * msec);
116 //torture_assert_u64_not_equal(tctx,
117 // finfo2.basic_info.out.write_time,
118 // finfo1.basic_info.out.write_time,
119 // "Server did not update write time within "
123 smbcli_close(cli->tree, fnum1);
124 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo1);
125 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
126 smbcli_unlink(cli->tree, fname);
127 smbcli_deltree(cli->tree, BASEDIR);
132 static bool test_delayed_write_update1(struct torture_context *tctx, struct smbcli_state *cli)
134 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
135 const char *fname = BASEDIR "\\torture_file1.txt";
140 struct timeval start;
142 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
143 int normal_delay = 2000000;
144 double sec = ((double)used_delay) / ((double)normal_delay);
145 int msec = 1000 * sec;
150 torture_comment(tctx, "\nRunning test_delayed_write_update1\n");
152 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
154 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
155 torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
156 "Failed to open %s", fname));
158 memset(buf, 'x', 2048);
159 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
161 /* 3 second delay to ensure we get past any 2 second time
162 granularity (older systems may have that) */
163 smb_msleep(3 * msec);
165 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
166 finfo1.all_info.in.file.fnum = fnum1;
169 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
170 pinfo4.all_info.in.file.path = fname;
172 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
174 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
176 torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048,
177 "file size not as expected after write(2048)");
179 torture_comment(tctx, "Initial write time %s\n",
180 nt_time_string(tctx, finfo1.all_info.out.write_time));
182 /* 3 second delay to ensure we get past any 2 second time
183 granularity (older systems may have that) */
184 smb_msleep(3 * msec);
186 /* Do a zero length SMBwrite call to truncate. */
187 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
188 torture_assert_int_equal(tctx, written, 0,
189 "unexpected number of bytes written");
191 start = timeval_current();
192 end = timeval_add(&start, (120 * sec), 0);
195 while (!timeval_expired(&end)) {
196 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
198 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
200 torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 1024,
201 "file not truncated to expected size "
204 torture_comment(tctx, "write time %s\n",
205 nt_time_string(tctx, finfo2.all_info.out.write_time));
207 if (finfo1.all_info.out.write_time !=
208 finfo2.all_info.out.write_time)
215 smb_msleep(1 * msec);
219 torture_assert(tctx, updated,
220 "Server did not update write time within 120 seconds");
222 torture_assert(tctx, first, talloc_asprintf(tctx,
223 "Server did not update write time immediately but only "
224 "after %.2f seconds!", timeval_elapsed(&start)));
226 torture_comment(tctx, "Server updated write time immediately. Good!\n");
229 smb_msleep(2 * msec);
231 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
232 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
233 torture_assert_int_equal(tctx, written, 1,
234 "unexpected number of bytes written");
236 start = timeval_current();
237 end = timeval_add(&start, (10*sec), 0);
238 while (!timeval_expired(&end)) {
239 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
241 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
243 torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 1024,
244 "file not truncated to expected size "
247 torture_comment(tctx, "write time %s\n",
248 nt_time_string(tctx, finfo3.all_info.out.write_time));
250 torture_assert_u64_equal(tctx,
251 finfo3.all_info.out.write_time,
252 finfo2.all_info.out.write_time,
253 talloc_asprintf(tctx,
254 "Server updated write time "
255 "after %.2f seconds (wrong!)",
256 timeval_elapsed(&start)));
259 smb_msleep(1 * msec);
262 torture_comment(tctx, "Server did not update write time within 10 "
266 smb_msleep(2 * msec);
268 /* the close should trigger an write time update */
269 smbcli_close(cli->tree, fnum1);
272 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
273 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
275 torture_assert_u64_not_equal(tctx,
276 pinfo4.all_info.out.write_time,
277 finfo3.all_info.out.write_time,
278 "Server did not update write time on "
281 pinfo4.all_info.out.write_time > finfo3.all_info.out.write_time,
282 "Server updated write time on close, but to an earlier point "
285 torture_comment(tctx, "Server updated write time on close (correct)\n");
288 smbcli_close(cli->tree, fnum1);
289 smbcli_unlink(cli->tree, fname);
290 smbcli_deltree(cli->tree, BASEDIR);
295 /* Updating with a SMBwrite of zero length
296 * changes the write time immediately - even on expand. */
298 static bool test_delayed_write_update1a(struct torture_context *tctx, struct smbcli_state *cli)
300 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
301 const char *fname = BASEDIR "\\torture_file1a.txt";
306 struct timeval start;
308 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
309 int normal_delay = 2000000;
310 double sec = ((double)used_delay) / ((double)normal_delay);
311 int msec = 1000 * sec;
316 torture_comment(tctx, "\nRunning test_delayed_write_update1a\n");
318 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
320 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
321 torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
322 "Failed to open %s", fname));
324 memset(buf, 'x', 2048);
325 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
327 /* 3 second delay to ensure we get past any 2 second time
328 granularity (older systems may have that) */
329 smb_msleep(3 * msec);
331 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
332 finfo1.all_info.in.file.fnum = fnum1;
335 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
336 pinfo4.all_info.in.file.path = fname;
338 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
340 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
342 torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048,
343 "file size not as expected after write(2048)");
345 torture_comment(tctx, "Initial write time %s\n",
346 nt_time_string(tctx, finfo1.all_info.out.write_time));
348 /* Do a zero length SMBwrite call to truncate. */
349 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
351 torture_assert_int_equal(tctx, written, 0,
352 "unexpected number of bytes written");
354 start = timeval_current();
355 end = timeval_add(&start, (120*sec), 0);
358 while (!timeval_expired(&end)) {
359 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
361 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
363 torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 10240,
364 "file not truncated to expected size "
367 torture_comment(tctx, "write time %s\n",
368 nt_time_string(tctx, finfo2.all_info.out.write_time));
370 if (finfo1.all_info.out.write_time !=
371 finfo2.all_info.out.write_time)
378 smb_msleep(1 * msec);
382 torture_assert(tctx, updated,
383 "Server did not update write time within 120 seconds");
385 torture_assert(tctx, first, talloc_asprintf(tctx,
386 "Server did not update write time immediately but only "
387 "after %.2f seconds!", timeval_elapsed(&start)));
389 torture_comment(tctx, "Server updated write time immediately. Good!\n");
392 smb_msleep(2 * msec);
394 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
395 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
397 torture_assert_int_equal(tctx, written, 1,
398 "unexpected number of bytes written");
400 start = timeval_current();
401 end = timeval_add(&start, (10*sec), 0);
402 while (!timeval_expired(&end)) {
403 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
405 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
407 torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 10240,
408 "file not truncated to expected size "
411 torture_comment(tctx, "write time %s\n",
412 nt_time_string(tctx, finfo3.all_info.out.write_time));
414 torture_assert_u64_equal(tctx,
415 finfo3.all_info.out.write_time,
416 finfo2.all_info.out.write_time,
417 talloc_asprintf(tctx,
418 "Server updated write time "
419 "after %.2f seconds (wrong!)",
420 timeval_elapsed(&start)));
423 smb_msleep(1 * msec);
426 torture_comment(tctx, "Server did not update write time within 10 "
429 /* the close should trigger an write time update */
430 smbcli_close(cli->tree, fnum1);
433 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
434 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
436 torture_assert_u64_not_equal(tctx,
437 pinfo4.all_info.out.write_time,
438 finfo3.all_info.out.write_time,
439 "Server did not update write time on "
442 pinfo4.all_info.out.write_time > finfo3.all_info.out.write_time,
443 "Server updated write time on close, but to an earlier point "
446 torture_comment(tctx, "Server updated write time on close (correct)\n");
449 smbcli_close(cli->tree, fnum1);
450 smbcli_unlink(cli->tree, fname);
451 smbcli_deltree(cli->tree, BASEDIR);
456 /* Updating with a SET_FILE_END_OF_FILE_INFO
457 * changes the write time immediately - even on expand. */
459 static bool test_delayed_write_update1b(struct torture_context *tctx, struct smbcli_state *cli)
461 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
462 const char *fname = BASEDIR "\\torture_file1b.txt";
467 struct timeval start;
469 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
470 int normal_delay = 2000000;
471 double sec = ((double)used_delay) / ((double)normal_delay);
472 int msec = 1000 * sec;
477 torture_comment(tctx, "\nRunning test_delayed_write_update1b\n");
479 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
481 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
482 torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
483 "Failed to open %s", fname));
485 memset(buf, 'x', 2048);
486 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
488 /* 3 second delay to ensure we get past any 2 second time
489 granularity (older systems may have that) */
490 smb_msleep(3 * msec);
492 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
493 finfo1.all_info.in.file.fnum = fnum1;
496 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
497 pinfo4.all_info.in.file.path = fname;
499 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
501 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
503 torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048,
504 "file size not as expected after write(2048)");
506 torture_comment(tctx, "Initial write time %s\n",
507 nt_time_string(tctx, finfo1.all_info.out.write_time));
509 /* Do a SET_END_OF_FILE_INFO call to truncate. */
510 status = smbcli_ftruncate(cli->tree, fnum1, (uint64_t)10240);
512 torture_assert_ntstatus_ok(tctx, status, "SET_END_OF_FILE failed");
514 start = timeval_current();
515 end = timeval_add(&start, (120*sec), 0);
518 while (!timeval_expired(&end)) {
519 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
521 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
523 torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 10240,
524 "file not truncated to expected size "
527 torture_comment(tctx, "write time %s\n",
528 nt_time_string(tctx, finfo2.all_info.out.write_time));
530 if (finfo1.all_info.out.write_time !=
531 finfo2.all_info.out.write_time)
538 smb_msleep(1 * msec);
542 torture_assert(tctx, updated,
543 "Server did not update write time within 120 seconds");
545 torture_assert(tctx, first, talloc_asprintf(tctx,
546 "Server did not update write time immediately but only "
547 "after %.2f seconds!", timeval_elapsed(&start)));
549 torture_comment(tctx, "Server updated write time immediately. Good!\n");
552 smb_msleep(2 * msec);
554 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
555 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
557 torture_assert_int_equal(tctx, written, 1,
558 "unexpected number of bytes written");
560 start = timeval_current();
561 end = timeval_add(&start, (10*sec), 0);
562 while (!timeval_expired(&end)) {
563 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
565 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
567 torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 10240,
568 "file not truncated to expected size "
571 torture_comment(tctx, "write time %s\n",
572 nt_time_string(tctx, finfo3.all_info.out.write_time));
574 torture_assert_u64_equal(tctx,
575 finfo3.all_info.out.write_time,
576 finfo2.all_info.out.write_time,
577 talloc_asprintf(tctx,
578 "Server updated write time "
579 "after %.2f seconds (wrong!)",
580 timeval_elapsed(&start)));
583 smb_msleep(1 * msec);
586 torture_comment(tctx, "Server did not update write time within 10 "
589 /* the close should trigger an write time update */
590 smbcli_close(cli->tree, fnum1);
593 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
594 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
596 torture_assert_u64_not_equal(tctx,
597 pinfo4.all_info.out.write_time,
598 finfo3.all_info.out.write_time,
599 "Server did not update write time on "
602 pinfo4.all_info.out.write_time > finfo3.all_info.out.write_time,
603 "Server updated write time on close, but to an earlier point "
606 torture_comment(tctx, "Server updated write time on close (correct)\n");
609 smbcli_close(cli->tree, fnum1);
610 smbcli_unlink(cli->tree, fname);
611 smbcli_deltree(cli->tree, BASEDIR);
616 /* Updating with a SET_ALLOCATION_INFO (truncate) does so immediately. */
618 static bool test_delayed_write_update1c(struct torture_context *tctx, struct smbcli_state *cli)
620 union smb_setfileinfo parms;
621 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
622 const char *fname = BASEDIR "\\torture_file1c.txt";
627 struct timeval start;
629 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
630 int normal_delay = 2000000;
631 double sec = ((double)used_delay) / ((double)normal_delay);
632 int msec = 1000 * sec;
637 torture_comment(tctx, "\nRunning test_delayed_write_update1c\n");
639 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
641 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
642 torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
643 "Failed to open %s", fname));
645 memset(buf, 'x', 2048);
646 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
648 /* 3 second delay to ensure we get past any 2 second time
649 granularity (older systems may have that) */
650 smb_msleep(3 * msec);
652 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
653 finfo1.all_info.in.file.fnum = fnum1;
656 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
657 pinfo4.all_info.in.file.path = fname;
659 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
661 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
663 torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048,
664 "file size not as expected after write(2048)");
666 torture_comment(tctx, "Initial write time %s\n",
667 nt_time_string(tctx, finfo1.all_info.out.write_time));
669 /* Do a SET_ALLOCATION_SIZE call to truncate. */
670 parms.allocation_info.level = RAW_SFILEINFO_ALLOCATION_INFO;
671 parms.allocation_info.in.file.fnum = fnum1;
672 parms.allocation_info.in.alloc_size = 0;
674 status = smb_raw_setfileinfo(cli->tree, &parms);
676 torture_assert_ntstatus_ok(tctx, status,
677 "RAW_SFILEINFO_ALLOCATION_INFO failed");
679 start = timeval_current();
680 end = timeval_add(&start, (120*sec), 0);
683 while (!timeval_expired(&end)) {
684 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
686 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
688 torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 0,
689 "file not truncated to expected size "
692 torture_comment(tctx, "write time %s\n",
693 nt_time_string(tctx, finfo2.all_info.out.write_time));
695 if (finfo1.all_info.out.write_time !=
696 finfo2.all_info.out.write_time)
703 smb_msleep(1 * msec);
707 torture_assert(tctx, updated,
708 "Server did not update write time within 120 seconds");
710 torture_assert(tctx, first, talloc_asprintf(tctx,
711 "Server did not update write time immediately but only "
712 "after %.2f seconds!", timeval_elapsed(&start)));
714 torture_comment(tctx, "Server updated write time immediately. Good!\n");
717 smb_msleep(2 * msec);
719 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
720 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
721 torture_assert_int_equal(tctx, written, 1,
722 "Unexpected number of bytes written");
724 start = timeval_current();
725 end = timeval_add(&start, (10*sec), 0);
726 while (!timeval_expired(&end)) {
727 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
729 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
731 torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 1,
732 "file not expaneded");
734 torture_comment(tctx, "write time %s\n",
735 nt_time_string(tctx, finfo3.all_info.out.write_time));
737 torture_assert_u64_equal(tctx,
738 finfo3.all_info.out.write_time,
739 finfo2.all_info.out.write_time,
740 talloc_asprintf(tctx,
741 "Server updated write time "
742 "after %.2f seconds (wrong!)",
743 timeval_elapsed(&start)));
746 smb_msleep(1 * msec);
749 torture_comment(tctx, "Server did not update write time within 10 "
752 /* the close should trigger an write time update */
753 smbcli_close(cli->tree, fnum1);
756 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
757 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
759 torture_assert_u64_not_equal(tctx,
760 pinfo4.all_info.out.write_time,
761 finfo3.all_info.out.write_time,
762 "Server did not update write time on "
765 pinfo4.all_info.out.write_time > finfo3.all_info.out.write_time,
766 "Server updated write time on close, but to an earlier point "
770 smbcli_close(cli->tree, fnum1);
771 smbcli_unlink(cli->tree, fname);
772 smbcli_deltree(cli->tree, BASEDIR);
778 * Do as above, but using 2 connections.
781 static bool test_delayed_write_update2(struct torture_context *tctx, struct smbcli_state *cli,
782 struct smbcli_state *cli2)
784 union smb_fileinfo finfo1, finfo2;
785 const char *fname = BASEDIR "\\torture_file.txt";
791 struct timeval start;
793 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
794 int normal_delay = 2000000;
795 double sec = ((double)used_delay) / ((double)normal_delay);
796 int msec = 1000 * sec;
797 union smb_flush flsh;
799 torture_comment(tctx, "\nRunning test_delayed_write_update2\n");
801 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
803 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
805 torture_comment(tctx, "Failed to open %s\n", fname);
809 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
810 finfo1.basic_info.in.file.fnum = fnum1;
813 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
815 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
817 torture_comment(tctx, "Initial write time %s\n",
818 nt_time_string(tctx, finfo1.basic_info.out.write_time));
820 /* 3 second delay to ensure we get past any 2 second time
821 granularity (older systems may have that) */
822 smb_msleep(3 * msec);
825 /* Try using setfileinfo instead of write to update write time. */
826 union smb_setfileinfo sfinfo;
827 time_t t_set = time(NULL);
828 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
829 sfinfo.basic_info.in.file.fnum = fnum1;
830 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
831 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
833 /* I tried this with both + and - ve to see if it makes a different.
834 It doesn't - once the filetime is set via setfileinfo it stays that way. */
836 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
838 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
840 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
841 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
843 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
845 torture_assert_ntstatus_ok(tctx, status, "sfileinfo failed");
848 finfo2.basic_info.in.file.path = fname;
850 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
852 if (!NT_STATUS_IS_OK(status)) {
853 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
856 torture_comment(tctx, "write time %s\n",
857 nt_time_string(tctx, finfo2.basic_info.out.write_time));
859 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
860 torture_comment(tctx, "Server updated write_time (correct)\n");
862 torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n");
866 /* Now try a write to see if the write time gets reset. */
868 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
869 finfo1.basic_info.in.file.fnum = fnum1;
872 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
874 if (!NT_STATUS_IS_OK(status)) {
875 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
879 torture_comment(tctx, "Modified write time %s\n",
880 nt_time_string(tctx, finfo1.basic_info.out.write_time));
883 torture_comment(tctx, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
885 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
888 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n",
889 (int)written, __location__);
893 /* Just to prove to tridge that the an smbflush has no effect on
894 the write time :-). The setfileinfo IS STICKY. JRA. */
896 torture_comment(tctx, "Doing flush after write\n");
898 flsh.flush.level = RAW_FLUSH_FLUSH;
899 flsh.flush.in.file.fnum = fnum1;
900 status = smb_raw_flush(cli->tree, &flsh);
901 if (!NT_STATUS_IS_OK(status)) {
902 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
906 /* Once the time was set using setfileinfo then it stays set - writes
907 don't have any effect. But make sure. */
908 start = timeval_current();
909 end = timeval_add(&start, (15*sec), 0);
910 while (!timeval_expired(&end)) {
911 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
913 if (!NT_STATUS_IS_OK(status)) {
914 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
918 torture_comment(tctx, "write time %s\n",
919 nt_time_string(tctx, finfo2.basic_info.out.write_time));
920 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
921 double diff = timeval_elapsed(&start);
922 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
929 smb_msleep(1 * msec);
932 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
933 torture_comment(tctx, "Server did not update write time (correct)\n");
937 smb_msleep(2 * msec);
939 fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
941 torture_result(tctx, TORTURE_FAIL, "Failed to open %s\n", fname);
945 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");
947 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
950 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n",
951 (int)written, __location__);
955 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
957 if (!NT_STATUS_IS_OK(status)) {
958 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
961 torture_comment(tctx, "write time %s\n",
962 nt_time_string(tctx, finfo2.basic_info.out.write_time));
963 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
964 torture_result(tctx, TORTURE_FAIL, "Server updated write_time (wrong!)\n");
968 torture_comment(tctx, "Closing the first fd to see if write time updated.\n");
969 smbcli_close(cli->tree, fnum1);
972 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");
974 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
977 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n",
978 (int)written, __location__);
982 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
983 finfo1.basic_info.in.file.fnum = fnum2;
985 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
987 if (!NT_STATUS_IS_OK(status)) {
988 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
991 torture_comment(tctx, "write time %s\n",
992 nt_time_string(tctx, finfo2.basic_info.out.write_time));
993 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
994 torture_result(tctx, TORTURE_FAIL, "Server updated write_time (wrong!)\n");
998 /* Once the time was set using setfileinfo then it stays set - writes
999 don't have any effect. But make sure. */
1000 start = timeval_current();
1001 end = timeval_add(&start, (15*sec), 0);
1002 while (!timeval_expired(&end)) {
1003 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1005 if (!NT_STATUS_IS_OK(status)) {
1006 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1010 torture_comment(tctx, "write time %s\n",
1011 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1012 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1013 double diff = timeval_elapsed(&start);
1014 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1021 smb_msleep(1 * msec);
1024 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1025 torture_comment(tctx, "Server did not update write time (correct)\n");
1028 torture_comment(tctx, "Closing second fd to see if write time updated.\n");
1030 smbcli_close(cli->tree, fnum2);
1033 fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
1035 torture_comment(tctx, "Failed to open %s\n", fname);
1039 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1040 finfo1.basic_info.in.file.fnum = fnum1;
1043 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1045 if (!NT_STATUS_IS_OK(status)) {
1046 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1050 torture_comment(tctx, "Second open initial write time %s\n",
1051 nt_time_string(tctx, finfo1.basic_info.out.write_time));
1053 smb_msleep(10 * msec);
1054 torture_comment(tctx, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
1056 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
1058 if (written != 10) {
1059 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n",
1060 (int)written, __location__);
1064 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1065 finfo1.basic_info.in.file.fnum = fnum1;
1067 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1069 if (!NT_STATUS_IS_OK(status)) {
1070 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1073 torture_comment(tctx, "write time %s\n",
1074 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1075 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1076 torture_result(tctx, TORTURE_FAIL, "Server updated write_time (wrong!)\n");
1080 /* Now the write time should be updated again */
1081 start = timeval_current();
1082 end = timeval_add(&start, (15*sec), 0);
1083 while (!timeval_expired(&end)) {
1084 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1086 if (!NT_STATUS_IS_OK(status)) {
1087 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1091 torture_comment(tctx, "write time %s\n",
1092 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1093 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1094 double diff = timeval_elapsed(&start);
1095 if (diff < (used_delay / (double)1000000)) {
1096 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
1097 "(expected > %.2f) (wrong!)\n",
1098 diff, used_delay / (double)1000000);
1103 torture_comment(tctx, "Server updated write_time after %.2f seconds"
1112 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1113 torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n");
1118 /* One more test to do. We should read the filetime via findfirst on the
1119 second connection to ensure it's the same. This is very easy for a Windows
1120 server but a bastard to get right on a POSIX server. JRA. */
1123 smbcli_close(cli->tree, fnum1);
1124 smbcli_unlink(cli->tree, fname);
1125 smbcli_deltree(cli->tree, BASEDIR);
1131 /* Windows does obviously not update the stat info during a write call. I
1132 * *think* this is the problem causing a spurious Excel 2003 on XP error
1133 * message when saving a file. Excel does a setfileinfo, writes, and then does
1134 * a getpath(!)info. Or so... For Samba sometimes it displays an error message
1135 * that the file might have been changed in between. What i've been able to
1136 * trace down is that this happens if the getpathinfo after the write shows a
1137 * different last write time than the setfileinfo showed. This is really
1141 static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli,
1142 struct smbcli_state *cli2)
1144 union smb_fileinfo finfo1, finfo2;
1145 const char *fname = BASEDIR "\\torture_file.txt";
1151 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1152 int normal_delay = 2000000;
1153 double sec = ((double)used_delay) / ((double)normal_delay);
1154 int msec = 1000 * sec;
1156 torture_comment(tctx, "\nRunning test_finfo_after_write\n");
1158 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1160 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1163 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1167 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1168 finfo1.basic_info.in.file.fnum = fnum1;
1170 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1172 if (!NT_STATUS_IS_OK(status)) {
1174 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1178 smb_msleep(1 * msec);
1180 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1183 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1188 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
1190 torture_result(tctx, TORTURE_FAIL, __location__": failed to open 2nd time - %s",
1191 smbcli_errstr(cli2->tree));
1196 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
1199 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1",
1205 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1206 finfo2.basic_info.in.file.path = fname;
1208 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
1210 if (!NT_STATUS_IS_OK(status)) {
1211 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s",
1217 if (finfo1.basic_info.out.create_time !=
1218 finfo2.basic_info.out.create_time) {
1219 torture_result(tctx, TORTURE_FAIL, __location__": create_time changed");
1224 if (finfo1.basic_info.out.access_time !=
1225 finfo2.basic_info.out.access_time) {
1226 torture_result(tctx, TORTURE_FAIL, __location__": access_time changed");
1231 if (finfo1.basic_info.out.write_time !=
1232 finfo2.basic_info.out.write_time) {
1233 torture_result(tctx, TORTURE_FAIL, __location__": write_time changed:\n"
1234 "write time conn 1 = %s, conn 2 = %s",
1235 nt_time_string(tctx, finfo1.basic_info.out.write_time),
1236 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1241 if (finfo1.basic_info.out.change_time !=
1242 finfo2.basic_info.out.change_time) {
1243 torture_result(tctx, TORTURE_FAIL, __location__": change_time changed");
1248 /* One of the two following calls updates the qpathinfo. */
1250 /* If you had skipped the smbcli_write on fnum2, it would
1251 * *not* have updated the stat on disk */
1253 smbcli_close(cli2->tree, fnum2);
1256 /* This call is only for the people looking at ethereal :-) */
1257 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1258 finfo2.basic_info.in.file.path = fname;
1260 status = smb_raw_pathinfo(cli->tree, tctx, &finfo2);
1262 if (!NT_STATUS_IS_OK(status)) {
1263 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1270 smbcli_close(cli->tree, fnum1);
1271 smbcli_unlink(cli->tree, fname);
1272 smbcli_deltree(cli->tree, BASEDIR);
1277 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
1278 uint64_t r = 10*1000*1000; \
1279 NTTIME g = (given).basic_info.out.write_time; \
1280 NTTIME gr = (g / r) * r; \
1281 NTTIME c = (correct).basic_info.out.write_time; \
1282 NTTIME cr = (c / r) * r; \
1283 bool strict = torture_setting_bool(tctx, "strict mode", false); \
1285 if (strict && (g cmp c)) { \
1287 } else if ((g cmp c) && (gr cmp cr)) { \
1288 /* handle filesystem without high resolution timestamps */ \
1292 torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \
1293 #given, nt_time_string(tctx, g), (unsigned long long)g, \
1294 #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \
1299 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
1300 COMPARE_WRITE_TIME_CMP(given,correct,!=)
1301 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
1302 COMPARE_WRITE_TIME_CMP(given,correct,<=)
1303 #define COMPARE_WRITE_TIME_LESS(given,correct) \
1304 COMPARE_WRITE_TIME_CMP(given,correct,>=)
1306 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
1307 NTTIME g = (given).basic_info.out.access_time; \
1308 NTTIME c = (correct).basic_info.out.access_time; \
1310 torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \
1311 #given, nt_time_string(tctx, g), \
1312 #cmp, #correct, nt_time_string(tctx, c)); \
1317 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
1318 COMPARE_ACCESS_TIME_CMP(given,correct,!=)
1320 #define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \
1321 COMPARE_ACCESS_TIME_EQUAL(given,correct); \
1322 COMPARE_WRITE_TIME_EQUAL(given,correct); \
1325 #define _DEBUG_BASIC_INFO(finfo, comment) do { \
1326 struct timeval atv; \
1327 struct timeval wtv; \
1328 struct timeval_buf atvb; \
1329 struct timeval_buf wtvb; \
1330 nttime_to_timeval(&atv, finfo.basic_info.out.access_time); \
1331 nttime_to_timeval(&wtv, finfo.basic_info.out.write_time); \
1332 torture_comment(tctx, "%s: Access(%s) Write(%s)\n", \
1334 timeval_str_buf(&atv, false, true, &atvb), \
1335 timeval_str_buf(&wtv, false, true, &wtvb)); \
1337 #define _GET_INFO_FILE(tree, finfo) do { \
1339 _status = smb_raw_fileinfo(tree, tctx, &finfo); \
1340 if (!NT_STATUS_IS_OK(_status)) { \
1342 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1343 nt_errstr(_status)); \
1346 _DEBUG_BASIC_INFO(finfo, "fileinfo(" #tree ")"); \
1348 #define _GET_INFO_PATH(tree, pinfo) do { \
1350 _status = smb_raw_pathinfo(tree, tctx, &pinfo); \
1351 if (!NT_STATUS_IS_OK(_status)) { \
1352 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \
1353 nt_errstr(_status)); \
1357 _DEBUG_BASIC_INFO(pinfo, "pathinfo(" #tree ")"); \
1359 #define GET_INFO_FILE(finfo) do { \
1360 _GET_INFO_FILE(cli->tree, finfo); \
1362 #define GET_INFO_FILE2(finfo) do { \
1363 _GET_INFO_FILE(cli2->tree, finfo); \
1365 #define GET_INFO_PATH(pinfo) do { \
1366 _GET_INFO_PATH(cli2->tree, pinfo); \
1368 #define GET_INFO_BOTH(finfo,pinfo) do { \
1369 GET_INFO_FILE(finfo); \
1370 GET_INFO_PATH(pinfo); \
1371 COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \
1374 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
1376 union smb_setfileinfo sfinfo; \
1377 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1378 sfinfo.basic_info.in.file.fnum = tfnum; \
1379 sfinfo.basic_info.in.create_time = 0; \
1380 sfinfo.basic_info.in.access_time = 0; \
1381 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1382 sfinfo.basic_info.in.change_time = 0; \
1383 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1384 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1385 if (!NT_STATUS_IS_OK(_status)) { \
1386 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1387 nt_errstr(_status)); \
1392 #define SET_INFO_FILE(finfo, wrtime) \
1393 SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
1395 #define SET_INFO_FILE_NS(finfo, wrtime, ns, tree, tfnum) do { \
1397 union smb_setfileinfo sfinfo; \
1398 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1399 sfinfo.basic_info.in.file.fnum = tfnum; \
1400 sfinfo.basic_info.in.create_time = 0; \
1401 sfinfo.basic_info.in.access_time = 0; \
1402 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1403 sfinfo.basic_info.in.write_time += (ns); \
1404 sfinfo.basic_info.in.change_time = 0; \
1405 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1406 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1407 if (!NT_STATUS_IS_OK(_status)) { \
1408 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1409 nt_errstr(_status)); \
1415 static bool test_delayed_write_update3(struct torture_context *tctx,
1416 struct smbcli_state *cli,
1417 struct smbcli_state *cli2)
1419 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1420 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1421 const char *fname = BASEDIR "\\torture_file3.txt";
1425 struct timeval start;
1427 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1428 //double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 1000000);
1429 //int normal_delay = 1000000;
1430 int normal_delay = 2000000;
1431 double sec = ((double)used_delay) / ((double)normal_delay);
1432 int msec = 1000 * sec;
1434 torture_comment(tctx, "\nRunning test_delayed_write_update3\n");
1436 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1438 torture_comment(tctx, "Open the file handle\n");
1439 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1442 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1446 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1447 finfo0.basic_info.in.file.fnum = fnum1;
1451 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1452 pinfo0.basic_info.in.file.path = fname;
1458 /* get the initial times */
1459 GET_INFO_BOTH(finfo0,pinfo0);
1462 * make sure the write time is updated 2 seconds later
1463 * calcuated from the first write
1464 * (but expect upto 5 seconds extra time for a busy server)
1466 start = timeval_current();
1467 end = timeval_add(&start, 7 * sec, 0);
1468 while (!timeval_expired(&end)) {
1470 torture_comment(tctx, "Do a write on the file handle\n");
1471 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1473 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1477 /* get the times after the write */
1478 GET_INFO_FILE(finfo1);
1480 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1481 double diff = timeval_elapsed(&start);
1482 if (diff < (used_delay / (double)1000000)) {
1483 torture_result(tctx, TORTURE_FAIL, "111Server updated write_time after %.2f seconds "
1484 "(write time update delay == %.2f) (wrong!)\n",
1485 diff, used_delay / (double)1000000);
1490 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1495 smb_msleep(0.5 * msec);
1498 GET_INFO_BOTH(finfo1,pinfo1);
1499 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1501 /* sure any further write doesn't update the write time */
1502 start = timeval_current();
1503 end = timeval_add(&start, 15 * sec, 0);
1504 while (!timeval_expired(&end)) {
1506 torture_comment(tctx, "Do a write on the file handle\n");
1507 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1509 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1513 /* get the times after the write */
1514 GET_INFO_BOTH(finfo2,pinfo2);
1516 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1517 double diff = timeval_elapsed(&start);
1518 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1524 smb_msleep(1 * msec);
1527 GET_INFO_BOTH(finfo2,pinfo2);
1528 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1529 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1530 torture_comment(tctx, "Server did not update write_time (correct)\n");
1534 smb_msleep(5 * msec);
1536 GET_INFO_BOTH(finfo3,pinfo3);
1537 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1540 * the close updates the write time to the time of the close
1541 * and not to the time of the last write!
1543 torture_comment(tctx, "Close the file handle\n");
1544 smbcli_close(cli->tree, fnum1);
1547 GET_INFO_PATH(pinfo4);
1548 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1550 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1551 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1556 smbcli_close(cli->tree, fnum1);
1557 smbcli_unlink(cli->tree, fname);
1558 smbcli_deltree(cli->tree, BASEDIR);
1564 * Show that a truncate write always updates the write time even
1565 * if an initial write has already updated the write time.
1568 static bool test_delayed_write_update3a(struct torture_context *tctx,
1569 struct smbcli_state *cli,
1570 struct smbcli_state *cli2)
1572 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1573 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1574 const char *fname = BASEDIR "\\torture_file3a.txt";
1579 struct timeval start;
1581 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1582 int normal_delay = 2000000;
1583 double sec = ((double)used_delay) / ((double)normal_delay);
1584 int msec = 1000 * sec;
1586 torture_comment(tctx, "\nRunning test_delayed_write_update3a\n");
1588 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1590 torture_comment(tctx, "Open the file handle\n");
1591 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1594 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1598 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1599 finfo0.basic_info.in.file.fnum = fnum1;
1603 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1604 pinfo0.basic_info.in.file.path = fname;
1610 /* get the initial times */
1611 GET_INFO_BOTH(finfo0,pinfo0);
1614 * sleep some time, to demonstrate the handling of write times
1615 * doesn't depend on the time since the open
1617 smb_msleep(5 * msec);
1619 /* get the initial times */
1620 GET_INFO_BOTH(finfo1,pinfo1);
1621 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1624 * make sure the write time is updated 2 seconds later
1625 * calcuated from the first write
1626 * (but expect upto 5 seconds extra time for a busy server)
1628 start = timeval_current();
1629 end = timeval_add(&start, 7 * sec, 0);
1630 while (!timeval_expired(&end)) {
1632 torture_comment(tctx, "Do a write on the file handle\n");
1633 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1635 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1639 /* get the times after the write */
1640 GET_INFO_FILE(finfo1);
1642 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1643 double diff = timeval_elapsed(&start);
1644 if (diff < (used_delay / (double)1000000)) {
1645 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1646 "(1sec == %.2f) (wrong!)\n",
1652 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1657 smb_msleep(0.5 * msec);
1660 GET_INFO_BOTH(finfo1,pinfo1);
1661 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1663 smb_msleep(3 * msec);
1666 * demonstrate that a truncate write always
1667 * updates the write time immediately
1669 for (i=0; i < 3; i++) {
1670 smb_msleep(2 * msec);
1672 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1673 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
1675 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1679 /* get the times after the write */
1680 GET_INFO_BOTH(finfo2,pinfo2);
1681 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1685 smb_msleep(3 * msec);
1687 /* sure any further write doesn't update the write time */
1688 start = timeval_current();
1689 end = timeval_add(&start, 15 * sec, 0);
1690 while (!timeval_expired(&end)) {
1692 torture_comment(tctx, "Do a write on the file handle\n");
1693 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1695 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1699 /* get the times after the write */
1700 GET_INFO_BOTH(finfo2,pinfo2);
1702 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1703 double diff = timeval_elapsed(&start);
1704 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1710 smb_msleep(1 * msec);
1713 GET_INFO_BOTH(finfo2,pinfo2);
1714 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1715 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1716 torture_comment(tctx, "Server did not update write_time (correct)\n");
1720 smb_msleep(3 * msec);
1722 /* get the initial times */
1723 GET_INFO_BOTH(finfo1,pinfo1);
1724 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
1727 * demonstrate that a truncate write always
1728 * updates the write time immediately
1730 for (i=0; i < 3; i++) {
1731 smb_msleep(2 * msec);
1733 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1734 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
1736 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1740 /* get the times after the write */
1741 GET_INFO_BOTH(finfo2,pinfo2);
1742 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1747 smb_msleep(3 * msec);
1749 GET_INFO_BOTH(finfo3,pinfo3);
1750 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1753 * the close doesn't update the write time
1755 torture_comment(tctx, "Close the file handle\n");
1756 smbcli_close(cli->tree, fnum1);
1759 GET_INFO_PATH(pinfo4);
1760 COMPARE_WRITE_TIME_EQUAL(pinfo4, pinfo3);
1762 if (pinfo4.basic_info.out.write_time == pinfo3.basic_info.out.write_time) {
1763 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1768 smbcli_close(cli->tree, fnum1);
1769 smbcli_unlink(cli->tree, fname);
1770 smbcli_deltree(cli->tree, BASEDIR);
1776 * Show a close after write updates the write timestamp to
1777 * the close time, not the last write time.
1780 static bool test_delayed_write_update3b(struct torture_context *tctx,
1781 struct smbcli_state *cli,
1782 struct smbcli_state *cli2)
1784 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1785 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1786 const char *fname = BASEDIR "\\torture_file3b.txt";
1790 struct timeval start;
1792 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1793 int normal_delay = 2000000;
1794 double sec = ((double)used_delay) / ((double)normal_delay);
1795 int msec = 1000 * sec;
1797 torture_comment(tctx, "\nRunning test_delayed_write_update3b\n");
1799 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1801 torture_comment(tctx, "Open the file handle\n");
1802 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1805 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1809 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1810 finfo0.basic_info.in.file.fnum = fnum1;
1814 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1815 pinfo0.basic_info.in.file.path = fname;
1821 /* get the initial times */
1822 GET_INFO_BOTH(finfo0,pinfo0);
1825 * sleep some time, to demonstrate the handling of write times
1826 * doesn't depend on the time since the open
1828 smb_msleep(5 * msec);
1830 /* get the initial times */
1831 GET_INFO_BOTH(finfo1,pinfo1);
1832 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1835 * make sure the write time is updated 2 seconds later
1836 * calcuated from the first write
1837 * (but expect upto 5 seconds extra time for a busy server)
1839 start = timeval_current();
1840 end = timeval_add(&start, 7 * sec, 0);
1841 while (!timeval_expired(&end)) {
1843 torture_comment(tctx, "Do a write on the file handle\n");
1844 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1846 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1850 /* get the times after the write */
1851 GET_INFO_FILE(finfo1);
1853 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1854 double diff = timeval_elapsed(&start);
1855 if (diff < (used_delay / (double)1000000)) {
1856 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
1857 "(expected > %.2f) (wrong!)\n",
1858 diff, used_delay / (double)1000000);
1863 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1864 "(write time update delay == %.2f) (correct)\n",
1865 diff, used_delay / (double)1000000);
1868 smb_msleep(0.5 * msec);
1871 GET_INFO_BOTH(finfo1,pinfo1);
1872 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1874 /* sure any further write doesn't update the write time */
1875 start = timeval_current();
1876 end = timeval_add(&start, 15 * sec, 0);
1877 while (!timeval_expired(&end)) {
1879 torture_comment(tctx, "Do a write on the file handle\n");
1880 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1882 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1886 /* get the times after the write */
1887 GET_INFO_BOTH(finfo2,pinfo2);
1889 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1890 double diff = timeval_elapsed(&start);
1891 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1897 smb_msleep(1 * msec);
1900 GET_INFO_BOTH(finfo2,pinfo2);
1901 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1902 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1903 torture_comment(tctx, "Server did not update write_time (correct)\n");
1907 smb_msleep(5 * msec);
1909 GET_INFO_BOTH(finfo3,pinfo3);
1910 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1913 * the close updates the write time to the time of the close
1914 * and not to the time of the last write!
1916 torture_comment(tctx, "Close the file handle\n");
1917 smbcli_close(cli->tree, fnum1);
1920 GET_INFO_PATH(pinfo4);
1921 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1923 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1924 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1929 smbcli_close(cli->tree, fnum1);
1930 smbcli_unlink(cli->tree, fname);
1931 smbcli_deltree(cli->tree, BASEDIR);
1937 * Check that a write after a truncate write doesn't update
1938 * the timestamp, but a truncate write after a write does.
1939 * Also prove that a close after a truncate write updates the
1940 * timestamp to current, not the time of last write.
1943 static bool test_delayed_write_update3c(struct torture_context *tctx,
1944 struct smbcli_state *cli,
1945 struct smbcli_state *cli2)
1947 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1948 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1949 const char *fname = BASEDIR "\\torture_file3c.txt";
1954 struct timeval start;
1956 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1957 int normal_delay = 2000000;
1958 double sec = ((double)used_delay) / ((double)normal_delay);
1959 int msec = 1000 * sec;
1961 torture_comment(tctx, "\nRunning test_delayed_write_update3c\n");
1963 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1965 torture_comment(tctx, "Open the file handle\n");
1966 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1969 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1973 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1974 finfo0.basic_info.in.file.fnum = fnum1;
1978 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1979 pinfo0.basic_info.in.file.path = fname;
1985 /* get the initial times */
1986 GET_INFO_BOTH(finfo0,pinfo0);
1989 * sleep some time, to demonstrate the handling of write times
1990 * doesn't depend on the time since the open
1992 smb_msleep(5 * msec);
1994 /* get the initial times */
1995 GET_INFO_BOTH(finfo1,pinfo1);
1996 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1999 * demonstrate that a truncate write always
2000 * updates the write time immediately
2002 for (i=0; i < 3; i++) {
2003 smb_msleep(2 * msec);
2005 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
2006 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2008 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2012 /* get the times after the write */
2013 GET_INFO_BOTH(finfo2,pinfo2);
2014 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2018 start = timeval_current();
2019 end = timeval_add(&start, 7 * sec, 0);
2020 while (!timeval_expired(&end)) {
2022 torture_comment(tctx, "Do a write on the file handle\n");
2023 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2025 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2029 /* get the times after the write */
2030 GET_INFO_FILE(finfo2);
2032 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2033 double diff = timeval_elapsed(&start);
2034 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2040 smb_msleep(1 * msec);
2043 GET_INFO_BOTH(finfo2,pinfo2);
2044 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2045 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2046 torture_comment(tctx, "Server did not update write_time (correct)\n");
2050 smb_msleep(5 * msec);
2052 /* get the initial times */
2053 GET_INFO_BOTH(finfo1,pinfo1);
2054 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
2057 * demonstrate that a truncate write always
2058 * updates the write time immediately
2060 for (i=0; i < 3; i++) {
2061 smb_msleep(2 * msec);
2063 torture_comment(tctx, "Do a truncate write [%d] on the file handle\n", i);
2064 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2066 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2070 /* get the times after the write */
2071 GET_INFO_BOTH(finfo2,pinfo2);
2072 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2077 smb_msleep(5 * msec);
2079 GET_INFO_BOTH(finfo2,pinfo2);
2080 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2082 /* sure any further write doesn't update the write time */
2083 start = timeval_current();
2084 end = timeval_add(&start, 15 * sec, 0);
2085 while (!timeval_expired(&end)) {
2087 torture_comment(tctx, "Do a write on the file handle\n");
2088 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2090 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2094 /* get the times after the write */
2095 GET_INFO_BOTH(finfo2,pinfo2);
2097 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2098 double diff = timeval_elapsed(&start);
2099 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2105 smb_msleep(1 * msec);
2108 GET_INFO_BOTH(finfo2,pinfo2);
2109 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2110 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2111 torture_comment(tctx, "Server did not update write_time (correct)\n");
2115 smb_msleep(5 * msec);
2117 GET_INFO_BOTH(finfo3,pinfo3);
2118 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2121 * the close updates the write time to the time of the close
2122 * and not to the time of the last write!
2124 torture_comment(tctx, "Close the file handle\n");
2125 smbcli_close(cli->tree, fnum1);
2128 GET_INFO_PATH(pinfo4);
2129 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2131 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2132 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2137 smbcli_close(cli->tree, fnum1);
2138 smbcli_unlink(cli->tree, fname);
2139 smbcli_deltree(cli->tree, BASEDIR);
2145 * Show only the first write updates the timestamp, and a close
2146 * after writes updates to current (I think this is the same
2150 static bool test_delayed_write_update4(struct torture_context *tctx,
2151 struct smbcli_state *cli,
2152 struct smbcli_state *cli2)
2154 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
2155 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
2156 const char *fname = BASEDIR "\\torture_file4.txt";
2160 struct timeval start;
2162 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2163 int normal_delay = 2000000;
2164 double sec = ((double)used_delay) / ((double)normal_delay);
2165 int msec = 1000 * sec;
2167 torture_comment(tctx, "\nRunning test_delayed_write_update4\n");
2169 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2171 torture_comment(tctx, "Open the file handle\n");
2172 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2175 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2179 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2180 finfo0.basic_info.in.file.fnum = fnum1;
2184 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2185 pinfo0.basic_info.in.file.path = fname;
2191 /* get the initial times */
2192 GET_INFO_BOTH(finfo0,pinfo0);
2195 smb_msleep(5 * msec);
2198 torture_comment(tctx, "Do a write on the file handle\n");
2199 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2201 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2206 GET_INFO_BOTH(finfo1,pinfo1);
2207 COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
2210 * make sure the write time is updated 2 seconds later
2211 * calcuated from the first write
2212 * (but expect upto 3 seconds extra time for a busy server)
2214 start = timeval_current();
2215 end = timeval_add(&start, 5 * sec, 0);
2216 while (!timeval_expired(&end)) {
2217 /* get the times after the first write */
2218 GET_INFO_FILE(finfo1);
2220 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
2221 double diff = timeval_elapsed(&start);
2222 if (diff < (used_delay / (double)1000000)) {
2223 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
2224 "(expected > %.2f) (wrong!)\n",
2225 diff, used_delay / (double)1000000);
2230 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2231 "(write time update delay == %.2f) (correct)\n",
2232 diff, used_delay / (double)1000000);
2235 smb_msleep(0.5 * msec);
2238 GET_INFO_BOTH(finfo1,pinfo1);
2239 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
2241 /* sure any further write doesn't update the write time */
2242 start = timeval_current();
2243 end = timeval_add(&start, 15 * sec, 0);
2244 while (!timeval_expired(&end)) {
2246 torture_comment(tctx, "Do a write on the file handle\n");
2247 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2249 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2253 /* get the times after the write */
2254 GET_INFO_BOTH(finfo2,pinfo2);
2256 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2257 double diff = timeval_elapsed(&start);
2258 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2264 smb_msleep(1 * msec);
2267 GET_INFO_BOTH(finfo2,pinfo2);
2268 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2269 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2270 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
2274 smb_msleep(5 * msec);
2276 GET_INFO_BOTH(finfo3,pinfo3);
2277 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2280 * the close updates the write time to the time of the close
2281 * and not to the time of the last write!
2283 torture_comment(tctx, "Close the file handle\n");
2284 smbcli_close(cli->tree, fnum1);
2287 GET_INFO_PATH(pinfo4);
2288 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2290 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2291 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2296 smbcli_close(cli->tree, fnum1);
2297 smbcli_unlink(cli->tree, fname);
2298 smbcli_deltree(cli->tree, BASEDIR);
2304 * Show writes and closes have no effect on updating times once a SETWRITETIME is done.
2307 static bool test_delayed_write_update5(struct torture_context *tctx,
2308 struct smbcli_state *cli,
2309 struct smbcli_state *cli2)
2311 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2312 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2313 const char *fname = BASEDIR "\\torture_file5.txt";
2317 struct timeval start;
2319 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2320 int normal_delay = 2000000;
2321 double sec = ((double)used_delay) / ((double)normal_delay);
2322 int msec = 1000 * sec;
2324 torture_comment(tctx, "\nRunning test_delayed_write_update5\n");
2326 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2328 torture_comment(tctx, "Open the file handle\n");
2329 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2332 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2336 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2337 finfo0.basic_info.in.file.fnum = fnum1;
2343 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2344 pinfo0.basic_info.in.file.path = fname;
2352 /* get the initial times */
2353 GET_INFO_BOTH(finfo0,pinfo0);
2356 torture_comment(tctx, "Do a write on the file handle\n");
2357 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2359 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2364 GET_INFO_BOTH(finfo1,pinfo1);
2365 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2367 torture_comment(tctx, "Set write time in the future on the file handle\n");
2368 SET_INFO_FILE(finfo0, time(NULL) + 86400);
2369 GET_INFO_BOTH(finfo2,pinfo2);
2370 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2372 torture_comment(tctx, "Set write time in the past on the file handle\n");
2373 SET_INFO_FILE(finfo0, time(NULL) - 86400);
2374 GET_INFO_BOTH(finfo2,pinfo2);
2375 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2377 /* make sure the 2 second delay from the first write are canceled */
2378 start = timeval_current();
2379 end = timeval_add(&start, 15 * sec, 0);
2380 while (!timeval_expired(&end)) {
2382 /* get the times after the first write */
2383 GET_INFO_BOTH(finfo3,pinfo3);
2385 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2386 double diff = timeval_elapsed(&start);
2387 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2393 smb_msleep(1 * msec);
2396 GET_INFO_BOTH(finfo3,pinfo3);
2397 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2398 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2399 torture_comment(tctx, "Server did not update write_time (correct)\n");
2402 /* sure any further write doesn't update the write time */
2403 start = timeval_current();
2404 end = timeval_add(&start, 15 * sec, 0);
2405 while (!timeval_expired(&end)) {
2407 torture_comment(tctx, "Do a write on the file handle\n");
2408 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2410 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2414 /* get the times after the write */
2415 GET_INFO_BOTH(finfo4,pinfo4);
2417 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2418 double diff = timeval_elapsed(&start);
2419 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2425 smb_msleep(1 * msec);
2428 GET_INFO_BOTH(finfo4,pinfo4);
2429 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2430 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2431 torture_comment(tctx, "Server did not update write_time (correct)\n");
2435 smb_msleep(5 * msec);
2437 GET_INFO_BOTH(finfo5,pinfo5);
2438 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2441 * the close doesn't update the write time
2443 torture_comment(tctx, "Close the file handle\n");
2444 smbcli_close(cli->tree, fnum1);
2447 GET_INFO_PATH(pinfo6);
2448 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2450 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2451 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2456 smbcli_close(cli->tree, fnum1);
2457 smbcli_unlink(cli->tree, fname);
2458 smbcli_deltree(cli->tree, BASEDIR);
2464 * Show truncate writes and closes have no effect on updating times once a SETWRITETIME is done.
2467 static bool test_delayed_write_update5b(struct torture_context *tctx,
2468 struct smbcli_state *cli,
2469 struct smbcli_state *cli2)
2471 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2472 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2473 const char *fname = BASEDIR "\\torture_fileb.txt";
2477 struct timeval start;
2479 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2480 int normal_delay = 2000000;
2481 double sec = ((double)used_delay) / ((double)normal_delay);
2482 int msec = 1000 * sec;
2484 torture_comment(tctx, "\nRunning test_delayed_write_update5b\n");
2486 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2488 torture_comment(tctx, "Open the file handle\n");
2489 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2492 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2496 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2497 finfo0.basic_info.in.file.fnum = fnum1;
2503 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2504 pinfo0.basic_info.in.file.path = fname;
2512 /* get the initial times */
2513 GET_INFO_BOTH(finfo0,pinfo0);
2516 torture_comment(tctx, "Do a write on the file handle\n");
2517 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2519 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2524 GET_INFO_BOTH(finfo1,pinfo1);
2525 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2527 torture_comment(tctx, "Set write time in the future on the file handle\n");
2528 SET_INFO_FILE(finfo0, time(NULL) + 86400);
2529 GET_INFO_BOTH(finfo2,pinfo2);
2530 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2532 torture_comment(tctx, "Set write time in the past on the file handle\n");
2533 SET_INFO_FILE(finfo0, time(NULL) - 86400);
2534 GET_INFO_BOTH(finfo2,pinfo2);
2535 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2537 /* make sure the 2 second delay from the first write are canceled */
2538 start = timeval_current();
2539 end = timeval_add(&start, 15 * sec, 0);
2540 while (!timeval_expired(&end)) {
2542 /* get the times after the first write */
2543 GET_INFO_BOTH(finfo3,pinfo3);
2545 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2546 double diff = timeval_elapsed(&start);
2547 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2553 smb_msleep(1 * msec);
2556 GET_INFO_BOTH(finfo3,pinfo3);
2557 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2558 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2559 torture_comment(tctx, "Server did not update write_time (correct)\n");
2562 /* Do any further write (truncates) update the write time ? */
2563 start = timeval_current();
2564 end = timeval_add(&start, 15 * sec, 0);
2565 while (!timeval_expired(&end)) {
2567 torture_comment(tctx, "Do a truncate write on the file handle\n");
2568 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
2570 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2574 /* get the times after the write */
2575 GET_INFO_BOTH(finfo4,pinfo4);
2577 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2578 double diff = timeval_elapsed(&start);
2579 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2585 smb_msleep(1 * msec);
2588 GET_INFO_BOTH(finfo4,pinfo4);
2589 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2590 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2591 torture_comment(tctx, "Server did not update write_time (correct)\n");
2595 smb_msleep(5 * msec);
2597 GET_INFO_BOTH(finfo5,pinfo5);
2598 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2601 * the close doesn't update the write time
2603 torture_comment(tctx, "Close the file handle\n");
2604 smbcli_close(cli->tree, fnum1);
2607 GET_INFO_PATH(pinfo6);
2608 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2610 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2611 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2616 smbcli_close(cli->tree, fnum1);
2617 smbcli_unlink(cli->tree, fname);
2618 smbcli_deltree(cli->tree, BASEDIR);
2624 * Open 2 handles on a file. Write one one and then set the
2625 * WRITE TIME explicitly on the other. Ensure the write time
2626 * update is cancelled. Ensure the write time is updated to
2627 * the close time when the non-explicit set handle is closed.
2631 static bool test_delayed_write_update6(struct torture_context *tctx,
2632 struct smbcli_state *cli,
2633 struct smbcli_state *cli2)
2635 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2636 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
2637 const char *fname = BASEDIR "\\torture_file6.txt";
2642 struct timeval start;
2644 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2645 int normal_delay = 2000000;
2646 double sec = ((double)used_delay) / ((double)normal_delay);
2647 int msec = 1000 * sec;
2650 torture_comment(tctx, "\nRunning test_delayed_write_update6\n");
2652 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2654 torture_comment(tctx, "Open the file handle\n");
2655 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2658 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2663 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
2664 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2667 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2672 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2673 finfo0.basic_info.in.file.fnum = fnum1;
2679 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2680 pinfo0.basic_info.in.file.path = fname;
2689 /* get the initial times */
2690 GET_INFO_BOTH(finfo0,pinfo0);
2693 torture_comment(tctx, "Do a write on the file handle\n");
2694 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2696 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2701 GET_INFO_BOTH(finfo1,pinfo1);
2702 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2704 torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
2705 SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
2706 GET_INFO_BOTH(finfo2,pinfo2);
2707 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2709 torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
2710 SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
2711 GET_INFO_BOTH(finfo2,pinfo2);
2712 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2714 /* make sure the 2 second delay from the first write are canceled */
2715 start = timeval_current();
2716 end = timeval_add(&start, 10 * sec, 0);
2717 while (!timeval_expired(&end)) {
2719 /* get the times after the first write */
2720 GET_INFO_BOTH(finfo3,pinfo3);
2722 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2723 double diff = timeval_elapsed(&start);
2724 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2730 smb_msleep(1 * msec);
2733 GET_INFO_BOTH(finfo3,pinfo3);
2734 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2735 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2736 torture_comment(tctx, "Server did not update write_time (correct)\n");
2739 /* sure any further write doesn't update the write time */
2740 start = timeval_current();
2741 end = timeval_add(&start, 10 * sec, 0);
2742 while (!timeval_expired(&end)) {
2744 torture_comment(tctx, "Do a write on the file handle\n");
2745 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2747 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2751 /* get the times after the write */
2752 GET_INFO_BOTH(finfo4,pinfo4);
2754 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2755 double diff = timeval_elapsed(&start);
2756 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2762 smb_msleep(1 * msec);
2765 GET_INFO_BOTH(finfo4,pinfo4);
2766 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2767 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2768 torture_comment(tctx, "Server did not update write_time (correct)\n");
2772 smb_msleep(5 * msec);
2774 GET_INFO_BOTH(finfo5,pinfo5);
2775 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2778 * the close updates the write time to the time of the close
2779 * as the write time was set on the 2nd handle
2781 torture_comment(tctx, "Close the file handle\n");
2782 smbcli_close(cli->tree, fnum1);
2785 GET_INFO_PATH(pinfo6);
2786 COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
2788 if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
2789 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2792 /* See what the second write handle thinks the time is ? */
2793 finfo5.basic_info.in.file.fnum = fnum2;
2794 GET_INFO_FILE2(finfo5);
2795 COMPARE_WRITE_TIME_EQUAL(finfo5, pinfo6);
2797 /* See if we have lost the sticky write time on handle2 */
2798 smb_msleep(3 * msec);
2799 torture_comment(tctx, "Have we lost the sticky write time ?\n");
2801 /* Make sure any further normal write doesn't update the write time */
2802 start = timeval_current();
2803 end = timeval_add(&start, 10 * sec, 0);
2804 while (!timeval_expired(&end)) {
2806 torture_comment(tctx, "Do a write on the second file handle\n");
2807 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
2809 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2813 /* get the times after the write */
2814 GET_INFO_FILE2(finfo5);
2815 GET_INFO_PATH(pinfo6);
2817 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2818 double diff = timeval_elapsed(&start);
2819 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2825 smb_msleep(1 * msec);
2828 /* What about a truncate write ? */
2829 start = timeval_current();
2830 end = timeval_add(&start, 10 * sec, 0);
2831 while (!timeval_expired(&end)) {
2833 torture_comment(tctx, "Do a truncate write on the second file handle\n");
2834 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 0);
2836 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2840 /* get the times after the write */
2841 GET_INFO_FILE2(finfo5);
2842 GET_INFO_PATH(pinfo6);
2844 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2845 double diff = timeval_elapsed(&start);
2846 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2852 smb_msleep(1 * msec);
2856 /* keep the 2nd handle open and rerun tests */
2863 * closing the 2nd handle will cause no write time update
2864 * as the write time was explicit set on this handle
2866 torture_comment(tctx, "Close the 2nd file handle\n");
2867 smbcli_close(cli2->tree, fnum2);
2870 GET_INFO_PATH(pinfo7);
2871 COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
2873 if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
2874 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2879 smbcli_close(cli->tree, fnum1);
2881 smbcli_close(cli2->tree, fnum2);
2882 smbcli_unlink(cli->tree, fname);
2883 smbcli_deltree(cli->tree, BASEDIR);
2888 static bool test_delayed_write_update7(struct torture_context *tctx, struct smbcli_state *cli)
2890 union smb_open open_parms;
2891 union smb_fileinfo finfo1, finfo2, finfo3;
2892 const char *fname = BASEDIR "\\torture_file7.txt";
2896 TALLOC_CTX *mem_ctx;
2898 torture_comment(tctx, "\nRunning test_delayed_write_update7 (timestamp resolution test)\n");
2900 mem_ctx = talloc_init("test_delayed_write_update7");
2901 if (!mem_ctx) return false;
2903 ZERO_STRUCT(finfo1);
2904 ZERO_STRUCT(finfo2);
2905 ZERO_STRUCT(finfo3);
2906 ZERO_STRUCT(open_parms);
2908 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2910 /* Create the file. */
2911 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2913 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
2917 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2918 finfo1.basic_info.in.file.fnum = fnum1;
2922 /* Get the initial timestamps. */
2923 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
2925 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
2927 /* Set the pending write time to a value with ns. */
2928 SET_INFO_FILE_NS(finfo, time(NULL) + 86400, 103, cli->tree, fnum1);
2930 /* Get the current pending write time by fnum. */
2931 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
2933 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
2935 /* Ensure the time is actually different. */
2936 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2937 torture_result(tctx, TORTURE_FAIL,
2938 "setfileinfo time matches original fileinfo time");
2942 /* Get the current pending write time by path. */
2943 finfo3.basic_info.in.file.path = fname;
2944 status = smb_raw_pathinfo(cli->tree, tctx, &finfo3);
2946 if (finfo2.basic_info.out.write_time != finfo3.basic_info.out.write_time) {
2947 torture_result(tctx, TORTURE_FAIL,
2948 "qpathinfo time doesn't match fileinfo time");
2952 /* Now close the file. Re-open and check that the write
2953 time is identical to the one we wrote. */
2955 smbcli_close(cli->tree, fnum1);
2957 open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
2958 open_parms.ntcreatex.in.flags = 0;
2959 open_parms.ntcreatex.in.access_mask = SEC_GENERIC_READ;
2960 open_parms.ntcreatex.in.file_attr = 0;
2961 open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE|
2962 NTCREATEX_SHARE_ACCESS_READ|
2963 NTCREATEX_SHARE_ACCESS_WRITE;
2964 open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
2965 open_parms.ntcreatex.in.create_options = 0;
2966 open_parms.ntcreatex.in.fname = fname;
2968 status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
2969 talloc_free(mem_ctx);
2971 if (!NT_STATUS_IS_OK(status)) {
2972 torture_result(tctx, TORTURE_FAIL,
2973 "setfileinfo time matches original fileinfo time");
2977 fnum1 = open_parms.ntcreatex.out.file.fnum;
2979 /* Check the returned time matches. */
2980 if (open_parms.ntcreatex.out.write_time != finfo2.basic_info.out.write_time) {
2981 torture_result(tctx, TORTURE_FAIL,
2982 "final open time does not match set time");
2988 smbcli_close(cli->tree, fnum1);
2990 smbcli_unlink(cli->tree, fname);
2991 smbcli_deltree(cli->tree, BASEDIR);
2996 Test if creating a file in a directory with an open handle updates the
2997 write timestamp (it should).
2999 static bool test_directory_update8(struct torture_context *tctx, struct smbcli_state *cli)
3001 union smb_fileinfo dir_info1, dir_info2;
3002 union smb_open open_parms;
3003 const char *fname = BASEDIR "\\torture_file.txt";
3008 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
3009 int normal_delay = 2000000;
3010 double sec = ((double)used_delay) / ((double)normal_delay);
3011 int msec = 1000 * sec;
3012 TALLOC_CTX *mem_ctx = talloc_init("test_delayed_write_update8");
3014 if (!mem_ctx) return false;
3016 torture_comment(tctx, "\nRunning test directory write update\n");
3018 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
3020 /* Open a handle on the directory - and leave it open. */
3021 ZERO_STRUCT(open_parms);
3022 open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
3023 open_parms.ntcreatex.in.flags = 0;
3024 open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ;
3025 open_parms.ntcreatex.in.file_attr = 0;
3026 open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE|
3027 NTCREATEX_SHARE_ACCESS_READ|
3028 NTCREATEX_SHARE_ACCESS_WRITE;
3029 open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
3030 open_parms.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
3031 open_parms.ntcreatex.in.fname = BASEDIR;
3033 status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
3034 talloc_free(mem_ctx);
3036 if (!NT_STATUS_IS_OK(status)) {
3037 torture_result(tctx, TORTURE_FAIL,
3038 "failed to open directory handle");
3043 fnum1 = open_parms.ntcreatex.out.file.fnum;
3045 /* Store the returned write time. */
3046 ZERO_STRUCT(dir_info1);
3047 dir_info1.basic_info.out.write_time = open_parms.ntcreatex.out.write_time;
3049 torture_comment(tctx, "Initial write time %s\n",
3050 nt_time_string(tctx, dir_info1.basic_info.out.write_time));
3053 smb_msleep(3 * msec);
3055 /* Now create a file within the directory. */
3056 fnum2 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
3058 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
3062 smbcli_close(cli->tree, fnum2);
3064 /* Read the directory write time again. */
3065 ZERO_STRUCT(dir_info2);
3066 dir_info2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
3067 dir_info2.basic_info.in.file.fnum = fnum1;
3069 status = smb_raw_fileinfo(cli->tree, tctx, &dir_info2);
3071 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
3073 /* Ensure it's been incremented. */
3074 COMPARE_WRITE_TIME_GREATER(dir_info2, dir_info1);
3076 torture_comment(tctx, "Updated write time %s\n",
3077 nt_time_string(tctx, dir_info2.basic_info.out.write_time));
3082 smbcli_close(cli->tree, fnum1);
3083 smbcli_unlink(cli->tree, fname);
3084 smbcli_deltree(cli->tree, BASEDIR);
3089 #undef COMPARE_WRITE_TIME_CMP
3090 #undef COMPARE_ACCESS_TIME_CMP
3092 #define COMPARE_TIME_CMP(given, gelem, correct, celem, cmp) do { \
3093 const uint64_t _r = 10*1000*1000; \
3094 NTTIME _g = (given).basic_info.out.gelem; \
3095 NTTIME _gr = (_g / _r) * _r; \
3096 NTTIME _c = (correct).basic_info.out.celem; \
3097 NTTIME _cr = (_c / _r) * _r; \
3098 bool _strict = torture_setting_bool(tctx, "strict mode", false); \
3099 const char *_err = NULL; \
3100 if (_strict && (_g cmp _c)) { \
3102 } else if ((_g cmp _c) && (_gr cmp _cr)) { \
3103 /* handle filesystem without high resolution timestamps */ \
3106 if (_err != NULL) { \
3107 struct timeval _gtv; \
3108 struct timeval _ctv; \
3109 struct timeval_buf _gtvb; \
3110 struct timeval_buf _ctvb; \
3111 nttime_to_timeval(&_gtv, _g); \
3112 nttime_to_timeval(&_ctv, _c); \
3113 torture_result(tctx, TORTURE_FAIL, \
3114 __location__": %s wrong (%s.%s)%s %s (%s.%s)%s", \
3117 timeval_str_buf(&_gtv, false, true, &_gtvb), \
3120 timeval_str_buf(&_ctv, false, true, &_ctvb)); \
3125 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
3126 COMPARE_TIME_CMP(given, write_time, correct, write_time, cmp); \
3128 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
3129 COMPARE_WRITE_TIME_CMP(given,correct,!=)
3130 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
3131 COMPARE_WRITE_TIME_CMP(given,correct,<=)
3133 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
3134 COMPARE_TIME_CMP(given, access_time, correct, access_time, cmp); \
3136 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
3137 COMPARE_ACCESS_TIME_CMP(given,correct,!=)
3138 #define COMPARE_ACCESS_TIME_GREATER(given,correct) \
3139 COMPARE_ACCESS_TIME_CMP(given,correct,<=)
3141 #define COMPARE_CHANGE_TIME_CMP(given, correct, cmp) do { \
3142 COMPARE_TIME_CMP(given, change_time, correct, change_time, cmp); \
3144 #define COMPARE_CHANGE_TIME_EQUAL(given,correct) \
3145 COMPARE_CHANGE_TIME_CMP(given,correct,!=)
3146 #define COMPARE_CHANGE_TIME_GREATER(given,correct) \
3147 COMPARE_CHANGE_TIME_CMP(given,correct,<=)
3149 #define COMPARE_CREATE_TIME_CMP(given, correct, cmp) do { \
3150 COMPARE_TIME_CMP(given, create_time, correct, create_time, cmp); \
3152 #define COMPARE_CREATE_TIME_EQUAL(given,correct) \
3153 COMPARE_CREATE_TIME_CMP(given,correct,!=)
3155 #define COMPARE_ALL_TIMES_EQUAL(given,correct) do { \
3156 COMPARE_WRITE_TIME_EQUAL(given,correct); \
3157 COMPARE_CHANGE_TIME_EQUAL(given,correct); \
3158 COMPARE_ACCESS_TIME_EQUAL(given,correct); \
3159 COMPARE_CREATE_TIME_EQUAL(given,correct); \
3162 #define COMPARE_TIMES_AFTER_WRITE(given,correct) do { \
3163 COMPARE_WRITE_TIME_GREATER(given,correct); \
3164 COMPARE_CHANGE_TIME_GREATER(given,correct); \
3165 COMPARE_ACCESS_TIME_EQUAL(given,correct); \
3166 COMPARE_CREATE_TIME_EQUAL(given,correct); \
3167 COMPARE_TIME_CMP(given, change_time, given, write_time, !=); \
3170 struct test_delaywrite_delaywrite1_state {
3171 struct torture_context *tctx;
3172 struct smbcli_state *cli1;
3173 struct smbcli_state *cli2;
3179 static bool test_delaywrite_delaywrite1_get_info(void *private_data,
3180 union smb_fileinfo *finfo)
3182 struct test_delaywrite_delaywrite1_state *state =
3183 (struct test_delaywrite_delaywrite1_state *)private_data;
3184 struct torture_context *tctx = state->tctx;
3185 struct smbcli_state *cli = state->cli1;
3186 struct smbcli_state *cli2 = state->cli2;
3187 union smb_fileinfo t1finfo;
3188 union smb_fileinfo t2finfo;
3191 ZERO_STRUCTP(finfo);
3193 ZERO_STRUCT(t1finfo);
3194 t1finfo.basic_info.level = RAW_FILEINFO_BASIC_INFORMATION;
3195 t1finfo.basic_info.in.file.fnum = state->fnum1;
3197 ZERO_STRUCT(t2finfo);
3198 t2finfo.basic_info.level = RAW_FILEINFO_BASIC_INFORMATION;
3199 t2finfo.basic_info.in.file.fnum = state->fnum2;
3201 GET_INFO_FILE2(t2finfo);
3202 GET_INFO_FILE(t1finfo);
3203 if (t1finfo.basic_info.out.write_time != t2finfo.basic_info.out.write_time) {
3205 * There was a race, get it again on handle 2,
3206 * but then they have to match.
3208 GET_INFO_FILE2(t2finfo);
3210 COMPARE_ALL_TIMES_EQUAL(t1finfo, t2finfo);
3212 finfo->basic_info.out = t1finfo.basic_info.out;
3217 static bool test_delaywrite_delaywrite1_write_data(void *private_data)
3219 struct test_delaywrite_delaywrite1_state *state =
3220 (struct test_delaywrite_delaywrite1_state *)private_data;
3221 struct torture_context *tctx = state->tctx;
3225 nwritten = smbcli_write(state->cli1->tree, state->fnum1, 0, "x", 0, 1);
3226 torture_assert_int_equal_goto(tctx, nwritten, 1,
3227 ret, done, "smbcli_write");
3233 static bool test_delaywrite_delaywrite1_close(void *private_data,
3234 union smb_fileinfo *finfo)
3236 struct test_delaywrite_delaywrite1_state *state =
3237 (struct test_delaywrite_delaywrite1_state *)private_data;
3238 struct torture_context *tctx = state->tctx;
3239 struct smbcli_state *cli2 = state->cli2;
3240 union smb_fileinfo t2finfo;
3241 union smb_fileinfo t2pinfo;
3244 ZERO_STRUCTP(finfo);
3247 * the close updates the write time to the time of the close
3248 * and not to the time of the last write!
3250 torture_comment(tctx, "Close the file handle\n");
3251 smbcli_close(state->cli1->tree, state->fnum1);
3254 ZERO_STRUCT(t2finfo);
3255 t2finfo.basic_info.level = RAW_FILEINFO_BASIC_INFO;
3256 t2finfo.basic_info.in.file.fnum = state->fnum2;
3257 ZERO_STRUCT(t2pinfo);
3258 t2pinfo.basic_info.level = RAW_FILEINFO_BASIC_INFO;
3259 t2pinfo.basic_info.in.file.path = state->fname;
3261 GET_INFO_FILE2(t2finfo);
3263 smbcli_close(state->cli2->tree, state->fnum2);
3266 GET_INFO_PATH(t2pinfo);
3267 COMPARE_ALL_TIMES_EQUAL(t2pinfo, t2finfo);
3269 finfo->basic_info.out = t2pinfo.basic_info.out;
3275 static bool test_delaywrite_delaywrite1(struct torture_context *tctx,
3276 struct smbcli_state *cli,
3277 struct smbcli_state *cli2)
3279 struct test_delaywrite_delaywrite1_state state = {
3286 const char *fname = BASEDIR "\\torture_file3.txt";
3288 //double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
3289 //double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 1000000);
3290 double normal_delay = 1000000;
3291 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", normal_delay);
3292 //double normal_delay = 1000000;
3293 //int normal_delay = 2000000;
3296 torture_comment(tctx, "\nRunning test_delayed_write_update3\n");
3298 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
3300 torture_comment(tctx, "Open the file handle\n");
3301 state.fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
3302 if (state.fnum1 == -1) {
3304 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
3307 state.fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
3308 if (state.fnum2 == -1) {
3310 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
3314 state.fname = fname;
3316 ok = test_delay_writetime1(tctx, used_delay, normal_delay,
3318 test_delaywrite_delaywrite1_get_info,
3319 test_delaywrite_delaywrite1_write_data,
3320 NULL, /* close_cb */
3322 torture_assert_goto(tctx, ok, ret, done, "test_delay_writetime1(1)");
3323 ok = test_delay_writetime1(tctx, used_delay, normal_delay,
3325 test_delaywrite_delaywrite1_get_info,
3326 test_delaywrite_delaywrite1_write_data,
3327 test_delaywrite_delaywrite1_close,
3329 torture_assert_goto(tctx, ok, ret, done, "test_delay_writetime1(2)");
3332 if (state.fnum1 != -1) {
3333 smbcli_close(cli->tree, state.fnum1);
3335 if (state.fnum2 != -1) {
3336 smbcli_close(cli2->tree, state.fnum2);
3338 smbcli_unlink(cli->tree, fname);
3339 smbcli_deltree(cli->tree, BASEDIR);
3345 testing of delayed update of write_time
3347 struct torture_suite *torture_delay_write(TALLOC_CTX *ctx)
3349 struct torture_suite *suite = torture_suite_create(ctx, "delaywrite");
3351 torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
3352 torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
3353 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate", test_delayed_write_update1);
3354 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a);
3355 torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b);
3356 torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c);
3357 torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
3358 torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
3359 torture_suite_add_2smb_test(suite, "delayed update of write time 3a", test_delayed_write_update3a);
3360 torture_suite_add_2smb_test(suite, "delayed update of write time 3b", test_delayed_write_update3b);
3361 torture_suite_add_2smb_test(suite, "delayed update of write time 3c", test_delayed_write_update3c);
3362 torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
3363 torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
3364 torture_suite_add_2smb_test(suite, "delayed update of write time 5b", test_delayed_write_update5b);
3365 torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
3366 torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7);
3367 torture_suite_add_1smb_test(suite, "directory timestamp update test", test_directory_update8);
3368 torture_suite_add_2smb_test(suite, "delaywrite1", test_delaywrite_delaywrite1);