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 pinfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
76 pinfo1.basic_info.in.file.path = fname;
77 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo1);
78 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
80 start = timeval_current();
81 end = timeval_add(&start, (120 * sec), 0);
82 while (!timeval_expired(&end)) {
83 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
85 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
87 torture_comment(tctx, "write time %s\n",
88 nt_time_string(tctx, finfo2.basic_info.out.write_time));
90 if (finfo1.basic_info.out.write_time !=
91 finfo2.basic_info.out.write_time)
93 double diff = timeval_elapsed(&start);
96 diff >= (used_delay / (double)1000000),
98 "Server updated write_time after %.2f "
99 "seconds (expected >= %.2f)\n",
100 diff, used_delay/(double)1000000));
102 torture_comment(tctx, "Server updated write_time after %.2f seconds (correct)\n",
107 smb_msleep(1 * msec);
110 torture_assert_u64_not_equal(tctx,
111 finfo2.basic_info.out.write_time,
112 finfo1.basic_info.out.write_time,
113 "Server did not update write time within "
117 smbcli_close(cli->tree, fnum1);
118 smbcli_unlink(cli->tree, fname);
119 smbcli_deltree(cli->tree, BASEDIR);
124 static bool test_delayed_write_update1(struct torture_context *tctx, struct smbcli_state *cli)
126 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
127 const char *fname = BASEDIR "\\torture_file1.txt";
132 struct timeval start;
134 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
135 int normal_delay = 2000000;
136 double sec = ((double)used_delay) / ((double)normal_delay);
137 int msec = 1000 * sec;
142 torture_comment(tctx, "\nRunning test_delayed_write_update1\n");
144 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
146 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
147 torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
148 "Failed to open %s", fname));
150 memset(buf, 'x', 2048);
151 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
153 /* 3 second delay to ensure we get past any 2 second time
154 granularity (older systems may have that) */
155 smb_msleep(3 * msec);
157 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
158 finfo1.all_info.in.file.fnum = fnum1;
161 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
162 pinfo4.all_info.in.file.path = fname;
164 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
166 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
168 torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048,
169 "file size not as expected after write(2048)");
171 torture_comment(tctx, "Initial write time %s\n",
172 nt_time_string(tctx, finfo1.all_info.out.write_time));
174 /* 3 second delay to ensure we get past any 2 second time
175 granularity (older systems may have that) */
176 smb_msleep(3 * msec);
178 /* Do a zero length SMBwrite call to truncate. */
179 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
180 torture_assert_int_equal(tctx, written, 0,
181 "unexpected number of bytes written");
183 start = timeval_current();
184 end = timeval_add(&start, (120 * sec), 0);
187 while (!timeval_expired(&end)) {
188 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
190 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
192 torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 1024,
193 "file not truncated to expected size "
196 torture_comment(tctx, "write time %s\n",
197 nt_time_string(tctx, finfo2.all_info.out.write_time));
199 if (finfo1.all_info.out.write_time !=
200 finfo2.all_info.out.write_time)
207 smb_msleep(1 * msec);
211 torture_assert(tctx, updated,
212 "Server did not update write time within 120 seconds");
214 torture_assert(tctx, first, talloc_asprintf(tctx,
215 "Server did not update write time immediately but only "
216 "after %.2f seconds!", timeval_elapsed(&start)));
218 torture_comment(tctx, "Server updated write time immediately. Good!\n");
221 smb_msleep(2 * msec);
223 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
224 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
225 torture_assert_int_equal(tctx, written, 1,
226 "unexpected number of bytes written");
228 start = timeval_current();
229 end = timeval_add(&start, (10*sec), 0);
230 while (!timeval_expired(&end)) {
231 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
233 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
235 torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 1024,
236 "file not truncated to expected size "
239 torture_comment(tctx, "write time %s\n",
240 nt_time_string(tctx, finfo3.all_info.out.write_time));
242 torture_assert_u64_equal(tctx,
243 finfo3.all_info.out.write_time,
244 finfo2.all_info.out.write_time,
245 talloc_asprintf(tctx,
246 "Server updated write time "
247 "after %.2f seconds (wrong!)",
248 timeval_elapsed(&start)));
251 smb_msleep(1 * msec);
254 torture_comment(tctx, "Server did not update write time within 10 "
258 smb_msleep(2 * msec);
260 /* the close should trigger an write time update */
261 smbcli_close(cli->tree, fnum1);
264 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
265 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
267 torture_assert_u64_not_equal(tctx,
268 pinfo4.all_info.out.write_time,
269 finfo3.all_info.out.write_time,
270 "Server did not update write time on "
273 pinfo4.all_info.out.write_time > finfo3.all_info.out.write_time,
274 "Server updated write time on close, but to an earlier point "
277 torture_comment(tctx, "Server updated write time on close (correct)\n");
280 smbcli_close(cli->tree, fnum1);
281 smbcli_unlink(cli->tree, fname);
282 smbcli_deltree(cli->tree, BASEDIR);
287 /* Updating with a SMBwrite of zero length
288 * changes the write time immediately - even on expand. */
290 static bool test_delayed_write_update1a(struct torture_context *tctx, struct smbcli_state *cli)
292 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
293 const char *fname = BASEDIR "\\torture_file1a.txt";
298 struct timeval start;
300 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
301 int normal_delay = 2000000;
302 double sec = ((double)used_delay) / ((double)normal_delay);
303 int msec = 1000 * sec;
308 torture_comment(tctx, "\nRunning test_delayed_write_update1a\n");
310 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
312 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
313 torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
314 "Failed to open %s", fname));
316 memset(buf, 'x', 2048);
317 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
319 /* 3 second delay to ensure we get past any 2 second time
320 granularity (older systems may have that) */
321 smb_msleep(3 * msec);
323 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
324 finfo1.all_info.in.file.fnum = fnum1;
327 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
328 pinfo4.all_info.in.file.path = fname;
330 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
332 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
334 torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048,
335 "file size not as expected after write(2048)");
337 torture_comment(tctx, "Initial write time %s\n",
338 nt_time_string(tctx, finfo1.all_info.out.write_time));
340 /* Do a zero length SMBwrite call to truncate. */
341 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
343 torture_assert_int_equal(tctx, written, 0,
344 "unexpected number of bytes written");
346 start = timeval_current();
347 end = timeval_add(&start, (120*sec), 0);
350 while (!timeval_expired(&end)) {
351 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
353 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
355 torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 10240,
356 "file not truncated to expected size "
359 torture_comment(tctx, "write time %s\n",
360 nt_time_string(tctx, finfo2.all_info.out.write_time));
362 if (finfo1.all_info.out.write_time !=
363 finfo2.all_info.out.write_time)
370 smb_msleep(1 * msec);
374 torture_assert(tctx, updated,
375 "Server did not update write time within 120 seconds");
377 torture_assert(tctx, first, talloc_asprintf(tctx,
378 "Server did not update write time immediately but only "
379 "after %.2f seconds!", timeval_elapsed(&start)));
381 torture_comment(tctx, "Server updated write time immediately. Good!\n");
384 smb_msleep(2 * msec);
386 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
387 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
389 torture_assert_int_equal(tctx, written, 1,
390 "unexpected number of bytes written");
392 start = timeval_current();
393 end = timeval_add(&start, (10*sec), 0);
394 while (!timeval_expired(&end)) {
395 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
397 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
399 torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 10240,
400 "file not truncated to expected size "
403 torture_comment(tctx, "write time %s\n",
404 nt_time_string(tctx, finfo3.all_info.out.write_time));
406 torture_assert_u64_equal(tctx,
407 finfo3.all_info.out.write_time,
408 finfo2.all_info.out.write_time,
409 talloc_asprintf(tctx,
410 "Server updated write time "
411 "after %.2f seconds (wrong!)",
412 timeval_elapsed(&start)));
415 smb_msleep(1 * msec);
418 torture_comment(tctx, "Server did not update write time within 10 "
421 /* the close should trigger an write time update */
422 smbcli_close(cli->tree, fnum1);
425 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
426 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
428 torture_assert_u64_not_equal(tctx,
429 pinfo4.all_info.out.write_time,
430 finfo3.all_info.out.write_time,
431 "Server did not update write time on "
434 pinfo4.all_info.out.write_time > finfo3.all_info.out.write_time,
435 "Server updated write time on close, but to an earlier point "
438 torture_comment(tctx, "Server updated write time on close (correct)\n");
441 smbcli_close(cli->tree, fnum1);
442 smbcli_unlink(cli->tree, fname);
443 smbcli_deltree(cli->tree, BASEDIR);
448 /* Updating with a SET_FILE_END_OF_FILE_INFO
449 * changes the write time immediately - even on expand. */
451 static bool test_delayed_write_update1b(struct torture_context *tctx, struct smbcli_state *cli)
453 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
454 const char *fname = BASEDIR "\\torture_file1b.txt";
459 struct timeval start;
461 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
462 int normal_delay = 2000000;
463 double sec = ((double)used_delay) / ((double)normal_delay);
464 int msec = 1000 * sec;
469 torture_comment(tctx, "\nRunning test_delayed_write_update1b\n");
471 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
473 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
474 torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
475 "Failed to open %s", fname));
477 memset(buf, 'x', 2048);
478 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
480 /* 3 second delay to ensure we get past any 2 second time
481 granularity (older systems may have that) */
482 smb_msleep(3 * msec);
484 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
485 finfo1.all_info.in.file.fnum = fnum1;
488 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
489 pinfo4.all_info.in.file.path = fname;
491 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
493 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
495 torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048,
496 "file size not as expected after write(2048)");
498 torture_comment(tctx, "Initial write time %s\n",
499 nt_time_string(tctx, finfo1.all_info.out.write_time));
501 /* Do a SET_END_OF_FILE_INFO call to truncate. */
502 status = smbcli_ftruncate(cli->tree, fnum1, (uint64_t)10240);
504 torture_assert_ntstatus_ok(tctx, status, "SET_END_OF_FILE failed");
506 start = timeval_current();
507 end = timeval_add(&start, (120*sec), 0);
510 while (!timeval_expired(&end)) {
511 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
513 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
515 torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 10240,
516 "file not truncated to expected size "
519 torture_comment(tctx, "write time %s\n",
520 nt_time_string(tctx, finfo2.all_info.out.write_time));
522 if (finfo1.all_info.out.write_time !=
523 finfo2.all_info.out.write_time)
530 smb_msleep(1 * msec);
534 torture_assert(tctx, updated,
535 "Server did not update write time within 120 seconds");
537 torture_assert(tctx, first, talloc_asprintf(tctx,
538 "Server did not update write time immediately but only "
539 "after %.2f seconds!", timeval_elapsed(&start)));
541 torture_comment(tctx, "Server updated write time immediately. Good!\n");
544 smb_msleep(2 * msec);
546 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
547 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
549 torture_assert_int_equal(tctx, written, 1,
550 "unexpected number of bytes written");
552 start = timeval_current();
553 end = timeval_add(&start, (10*sec), 0);
554 while (!timeval_expired(&end)) {
555 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
557 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
559 torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 10240,
560 "file not truncated to expected size "
563 torture_comment(tctx, "write time %s\n",
564 nt_time_string(tctx, finfo3.all_info.out.write_time));
566 torture_assert_u64_equal(tctx,
567 finfo3.all_info.out.write_time,
568 finfo2.all_info.out.write_time,
569 talloc_asprintf(tctx,
570 "Server updated write time "
571 "after %.2f seconds (wrong!)",
572 timeval_elapsed(&start)));
575 smb_msleep(1 * msec);
578 torture_comment(tctx, "Server did not update write time within 10 "
581 /* the close should trigger an write time update */
582 smbcli_close(cli->tree, fnum1);
585 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
586 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
588 torture_assert_u64_not_equal(tctx,
589 pinfo4.all_info.out.write_time,
590 finfo3.all_info.out.write_time,
591 "Server did not update write time on "
594 pinfo4.all_info.out.write_time > finfo3.all_info.out.write_time,
595 "Server updated write time on close, but to an earlier point "
598 torture_comment(tctx, "Server updated write time on close (correct)\n");
601 smbcli_close(cli->tree, fnum1);
602 smbcli_unlink(cli->tree, fname);
603 smbcli_deltree(cli->tree, BASEDIR);
608 /* Updating with a SET_ALLOCATION_INFO (truncate) does so immediately. */
610 static bool test_delayed_write_update1c(struct torture_context *tctx, struct smbcli_state *cli)
612 union smb_setfileinfo parms;
613 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
614 const char *fname = BASEDIR "\\torture_file1c.txt";
619 struct timeval start;
621 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
622 int normal_delay = 2000000;
623 double sec = ((double)used_delay) / ((double)normal_delay);
624 int msec = 1000 * sec;
629 torture_comment(tctx, "\nRunning test_delayed_write_update1c\n");
631 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
633 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
634 torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
635 "Failed to open %s", fname));
637 memset(buf, 'x', 2048);
638 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
640 /* 3 second delay to ensure we get past any 2 second time
641 granularity (older systems may have that) */
642 smb_msleep(3 * msec);
644 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
645 finfo1.all_info.in.file.fnum = fnum1;
648 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
649 pinfo4.all_info.in.file.path = fname;
651 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
653 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
655 torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048,
656 "file size not as expected after write(2048)");
658 torture_comment(tctx, "Initial write time %s\n",
659 nt_time_string(tctx, finfo1.all_info.out.write_time));
661 /* Do a SET_ALLOCATION_SIZE call to truncate. */
662 parms.allocation_info.level = RAW_SFILEINFO_ALLOCATION_INFO;
663 parms.allocation_info.in.file.fnum = fnum1;
664 parms.allocation_info.in.alloc_size = 0;
666 status = smb_raw_setfileinfo(cli->tree, &parms);
668 torture_assert_ntstatus_ok(tctx, status,
669 "RAW_SFILEINFO_ALLOCATION_INFO failed");
671 start = timeval_current();
672 end = timeval_add(&start, (120*sec), 0);
675 while (!timeval_expired(&end)) {
676 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
678 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
680 torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 0,
681 "file not truncated to expected size "
684 torture_comment(tctx, "write time %s\n",
685 nt_time_string(tctx, finfo2.all_info.out.write_time));
687 if (finfo1.all_info.out.write_time !=
688 finfo2.all_info.out.write_time)
695 smb_msleep(1 * msec);
699 torture_assert(tctx, updated,
700 "Server did not update write time within 120 seconds");
702 torture_assert(tctx, first, talloc_asprintf(tctx,
703 "Server did not update write time immediately but only "
704 "after %.2f seconds!", timeval_elapsed(&start)));
706 torture_comment(tctx, "Server updated write time immediately. Good!\n");
709 smb_msleep(2 * msec);
711 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
712 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
713 torture_assert_int_equal(tctx, written, 1,
714 "Unexpected number of bytes written");
716 start = timeval_current();
717 end = timeval_add(&start, (10*sec), 0);
718 while (!timeval_expired(&end)) {
719 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
721 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
723 torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 1,
724 "file not expaneded");
726 torture_comment(tctx, "write time %s\n",
727 nt_time_string(tctx, finfo3.all_info.out.write_time));
729 torture_assert_u64_equal(tctx,
730 finfo3.all_info.out.write_time,
731 finfo2.all_info.out.write_time,
732 talloc_asprintf(tctx,
733 "Server updated write time "
734 "after %.2f seconds (wrong!)",
735 timeval_elapsed(&start)));
738 smb_msleep(1 * msec);
741 torture_comment(tctx, "Server did not update write time within 10 "
744 /* the close should trigger an write time update */
745 smbcli_close(cli->tree, fnum1);
748 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
749 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
751 torture_assert_u64_not_equal(tctx,
752 pinfo4.all_info.out.write_time,
753 finfo3.all_info.out.write_time,
754 "Server did not update write time on "
757 pinfo4.all_info.out.write_time > finfo3.all_info.out.write_time,
758 "Server updated write time on close, but to an earlier point "
762 smbcli_close(cli->tree, fnum1);
763 smbcli_unlink(cli->tree, fname);
764 smbcli_deltree(cli->tree, BASEDIR);
770 * Do as above, but using 2 connections.
773 static bool test_delayed_write_update2(struct torture_context *tctx, struct smbcli_state *cli,
774 struct smbcli_state *cli2)
776 union smb_fileinfo finfo1, finfo2;
777 const char *fname = BASEDIR "\\torture_file.txt";
783 struct timeval start;
785 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
786 int normal_delay = 2000000;
787 double sec = ((double)used_delay) / ((double)normal_delay);
788 int msec = 1000 * sec;
789 union smb_flush flsh;
791 torture_comment(tctx, "\nRunning test_delayed_write_update2\n");
793 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
795 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
797 torture_comment(tctx, "Failed to open %s\n", fname);
801 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
802 finfo1.basic_info.in.file.fnum = fnum1;
805 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
807 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
809 torture_comment(tctx, "Initial write time %s\n",
810 nt_time_string(tctx, finfo1.basic_info.out.write_time));
812 /* 3 second delay to ensure we get past any 2 second time
813 granularity (older systems may have that) */
814 smb_msleep(3 * msec);
817 /* Try using setfileinfo instead of write to update write time. */
818 union smb_setfileinfo sfinfo;
819 time_t t_set = time(NULL);
820 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
821 sfinfo.basic_info.in.file.fnum = fnum1;
822 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
823 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
825 /* I tried this with both + and - ve to see if it makes a different.
826 It doesn't - once the filetime is set via setfileinfo it stays that way. */
828 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
830 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
832 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
833 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
835 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
837 torture_assert_ntstatus_ok(tctx, status, "sfileinfo failed");
840 finfo2.basic_info.in.file.path = fname;
842 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
844 if (!NT_STATUS_IS_OK(status)) {
845 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
848 torture_comment(tctx, "write time %s\n",
849 nt_time_string(tctx, finfo2.basic_info.out.write_time));
851 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
852 torture_comment(tctx, "Server updated write_time (correct)\n");
854 torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n");
858 /* Now try a write to see if the write time gets reset. */
860 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
861 finfo1.basic_info.in.file.fnum = fnum1;
864 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
866 if (!NT_STATUS_IS_OK(status)) {
867 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
871 torture_comment(tctx, "Modified write time %s\n",
872 nt_time_string(tctx, finfo1.basic_info.out.write_time));
875 torture_comment(tctx, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
877 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
880 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n",
881 (int)written, __location__);
885 /* Just to prove to tridge that the an smbflush has no effect on
886 the write time :-). The setfileinfo IS STICKY. JRA. */
888 torture_comment(tctx, "Doing flush after write\n");
890 flsh.flush.level = RAW_FLUSH_FLUSH;
891 flsh.flush.in.file.fnum = fnum1;
892 status = smb_raw_flush(cli->tree, &flsh);
893 if (!NT_STATUS_IS_OK(status)) {
894 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
898 /* Once the time was set using setfileinfo then it stays set - writes
899 don't have any effect. But make sure. */
900 start = timeval_current();
901 end = timeval_add(&start, (15*sec), 0);
902 while (!timeval_expired(&end)) {
903 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
905 if (!NT_STATUS_IS_OK(status)) {
906 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
910 torture_comment(tctx, "write time %s\n",
911 nt_time_string(tctx, finfo2.basic_info.out.write_time));
912 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
913 double diff = timeval_elapsed(&start);
914 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
921 smb_msleep(1 * msec);
924 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
925 torture_comment(tctx, "Server did not update write time (correct)\n");
929 smb_msleep(2 * msec);
931 fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
933 torture_result(tctx, TORTURE_FAIL, "Failed to open %s\n", fname);
937 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");
939 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
942 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n",
943 (int)written, __location__);
947 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
949 if (!NT_STATUS_IS_OK(status)) {
950 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
953 torture_comment(tctx, "write time %s\n",
954 nt_time_string(tctx, finfo2.basic_info.out.write_time));
955 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
956 torture_result(tctx, TORTURE_FAIL, "Server updated write_time (wrong!)\n");
960 torture_comment(tctx, "Closing the first fd to see if write time updated.\n");
961 smbcli_close(cli->tree, fnum1);
964 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");
966 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
969 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n",
970 (int)written, __location__);
974 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
975 finfo1.basic_info.in.file.fnum = fnum2;
977 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
979 if (!NT_STATUS_IS_OK(status)) {
980 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
983 torture_comment(tctx, "write time %s\n",
984 nt_time_string(tctx, finfo2.basic_info.out.write_time));
985 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
986 torture_result(tctx, TORTURE_FAIL, "Server updated write_time (wrong!)\n");
990 /* Once the time was set using setfileinfo then it stays set - writes
991 don't have any effect. But make sure. */
992 start = timeval_current();
993 end = timeval_add(&start, (15*sec), 0);
994 while (!timeval_expired(&end)) {
995 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
997 if (!NT_STATUS_IS_OK(status)) {
998 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1002 torture_comment(tctx, "write time %s\n",
1003 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1004 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1005 double diff = timeval_elapsed(&start);
1006 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1013 smb_msleep(1 * msec);
1016 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1017 torture_comment(tctx, "Server did not update write time (correct)\n");
1020 torture_comment(tctx, "Closing second fd to see if write time updated.\n");
1022 smbcli_close(cli->tree, fnum2);
1025 fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
1027 torture_comment(tctx, "Failed to open %s\n", fname);
1031 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1032 finfo1.basic_info.in.file.fnum = fnum1;
1035 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1037 if (!NT_STATUS_IS_OK(status)) {
1038 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1042 torture_comment(tctx, "Second open initial write time %s\n",
1043 nt_time_string(tctx, finfo1.basic_info.out.write_time));
1045 smb_msleep(10 * msec);
1046 torture_comment(tctx, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
1048 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
1050 if (written != 10) {
1051 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n",
1052 (int)written, __location__);
1056 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1057 finfo1.basic_info.in.file.fnum = fnum1;
1059 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1061 if (!NT_STATUS_IS_OK(status)) {
1062 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1065 torture_comment(tctx, "write time %s\n",
1066 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1067 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1068 torture_result(tctx, TORTURE_FAIL, "Server updated write_time (wrong!)\n");
1072 /* Now the write time should be updated again */
1073 start = timeval_current();
1074 end = timeval_add(&start, (15*sec), 0);
1075 while (!timeval_expired(&end)) {
1076 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1078 if (!NT_STATUS_IS_OK(status)) {
1079 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1083 torture_comment(tctx, "write time %s\n",
1084 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1085 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1086 double diff = timeval_elapsed(&start);
1087 if (diff < (used_delay / (double)1000000)) {
1088 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
1089 "(expected > %.2f) (wrong!)\n",
1090 diff, used_delay / (double)1000000);
1095 torture_comment(tctx, "Server updated write_time after %.2f seconds"
1104 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1105 torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n");
1110 /* One more test to do. We should read the filetime via findfirst on the
1111 second connection to ensure it's the same. This is very easy for a Windows
1112 server but a bastard to get right on a POSIX server. JRA. */
1115 smbcli_close(cli->tree, fnum1);
1116 smbcli_unlink(cli->tree, fname);
1117 smbcli_deltree(cli->tree, BASEDIR);
1123 /* Windows does obviously not update the stat info during a write call. I
1124 * *think* this is the problem causing a spurious Excel 2003 on XP error
1125 * message when saving a file. Excel does a setfileinfo, writes, and then does
1126 * a getpath(!)info. Or so... For Samba sometimes it displays an error message
1127 * that the file might have been changed in between. What i've been able to
1128 * trace down is that this happens if the getpathinfo after the write shows a
1129 * different last write time than the setfileinfo showed. This is really
1133 static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli,
1134 struct smbcli_state *cli2)
1136 union smb_fileinfo finfo1, finfo2;
1137 const char *fname = BASEDIR "\\torture_file.txt";
1143 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1144 int normal_delay = 2000000;
1145 double sec = ((double)used_delay) / ((double)normal_delay);
1146 int msec = 1000 * sec;
1148 torture_comment(tctx, "\nRunning test_finfo_after_write\n");
1150 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1152 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1155 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1159 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1160 finfo1.basic_info.in.file.fnum = fnum1;
1162 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1164 if (!NT_STATUS_IS_OK(status)) {
1166 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1170 smb_msleep(1 * msec);
1172 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1175 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1180 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
1182 torture_result(tctx, TORTURE_FAIL, __location__": failed to open 2nd time - %s",
1183 smbcli_errstr(cli2->tree));
1188 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
1191 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1",
1197 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1198 finfo2.basic_info.in.file.path = fname;
1200 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
1202 if (!NT_STATUS_IS_OK(status)) {
1203 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s",
1209 if (finfo1.basic_info.out.create_time !=
1210 finfo2.basic_info.out.create_time) {
1211 torture_result(tctx, TORTURE_FAIL, __location__": create_time changed");
1216 if (finfo1.basic_info.out.access_time !=
1217 finfo2.basic_info.out.access_time) {
1218 torture_result(tctx, TORTURE_FAIL, __location__": access_time changed");
1223 if (finfo1.basic_info.out.write_time !=
1224 finfo2.basic_info.out.write_time) {
1225 torture_result(tctx, TORTURE_FAIL, __location__": write_time changed:\n"
1226 "write time conn 1 = %s, conn 2 = %s",
1227 nt_time_string(tctx, finfo1.basic_info.out.write_time),
1228 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1233 if (finfo1.basic_info.out.change_time !=
1234 finfo2.basic_info.out.change_time) {
1235 torture_result(tctx, TORTURE_FAIL, __location__": change_time changed");
1240 /* One of the two following calls updates the qpathinfo. */
1242 /* If you had skipped the smbcli_write on fnum2, it would
1243 * *not* have updated the stat on disk */
1245 smbcli_close(cli2->tree, fnum2);
1248 /* This call is only for the people looking at ethereal :-) */
1249 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1250 finfo2.basic_info.in.file.path = fname;
1252 status = smb_raw_pathinfo(cli->tree, tctx, &finfo2);
1254 if (!NT_STATUS_IS_OK(status)) {
1255 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1262 smbcli_close(cli->tree, fnum1);
1263 smbcli_unlink(cli->tree, fname);
1264 smbcli_deltree(cli->tree, BASEDIR);
1269 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
1270 uint64_t r = 10*1000*1000; \
1271 NTTIME g = (given).basic_info.out.write_time; \
1272 NTTIME gr = (g / r) * r; \
1273 NTTIME c = (correct).basic_info.out.write_time; \
1274 NTTIME cr = (c / r) * r; \
1275 bool strict = torture_setting_bool(tctx, "strict mode", false); \
1277 if (strict && (g cmp c)) { \
1279 } else if ((g cmp c) && (gr cmp cr)) { \
1280 /* handle filesystem without high resolution timestamps */ \
1284 torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \
1285 #given, nt_time_string(tctx, g), (unsigned long long)g, \
1286 #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \
1291 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
1292 COMPARE_WRITE_TIME_CMP(given,correct,!=)
1293 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
1294 COMPARE_WRITE_TIME_CMP(given,correct,<=)
1295 #define COMPARE_WRITE_TIME_LESS(given,correct) \
1296 COMPARE_WRITE_TIME_CMP(given,correct,>=)
1298 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
1299 NTTIME g = (given).basic_info.out.access_time; \
1300 NTTIME c = (correct).basic_info.out.access_time; \
1302 torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \
1303 #given, nt_time_string(tctx, g), \
1304 #cmp, #correct, nt_time_string(tctx, c)); \
1309 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
1310 COMPARE_ACCESS_TIME_CMP(given,correct,!=)
1312 #define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \
1313 COMPARE_ACCESS_TIME_EQUAL(given,correct); \
1314 COMPARE_WRITE_TIME_EQUAL(given,correct); \
1317 #define _DEBUG_BASIC_INFO(finfo, comment) do { \
1318 struct timeval atv; \
1319 struct timeval wtv; \
1320 struct timeval_buf atvb; \
1321 struct timeval_buf wtvb; \
1322 nttime_to_timeval(&atv, finfo.basic_info.out.access_time); \
1323 nttime_to_timeval(&wtv, finfo.basic_info.out.write_time); \
1324 torture_comment(tctx, "%s: Access(%s) Write(%s)\n", \
1326 timeval_str_buf(&atv, false, true, &atvb), \
1327 timeval_str_buf(&wtv, false, true, &wtvb)); \
1329 #define _GET_INFO_FILE(tree, finfo) do { \
1331 _status = smb_raw_fileinfo(tree, tctx, &finfo); \
1332 if (!NT_STATUS_IS_OK(_status)) { \
1334 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1335 nt_errstr(_status)); \
1338 _DEBUG_BASIC_INFO(finfo, "fileinfo(" #tree ")"); \
1340 #define _GET_INFO_PATH(tree, pinfo) do { \
1342 _status = smb_raw_pathinfo(tree, tctx, &pinfo); \
1343 if (!NT_STATUS_IS_OK(_status)) { \
1344 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \
1345 nt_errstr(_status)); \
1349 _DEBUG_BASIC_INFO(pinfo, "pathinfo(" #tree ")"); \
1351 #define GET_INFO_FILE(finfo) do { \
1352 _GET_INFO_FILE(cli->tree, finfo); \
1354 #define GET_INFO_FILE2(finfo) do { \
1355 _GET_INFO_FILE(cli2->tree, finfo); \
1357 #define GET_INFO_PATH(pinfo) do { \
1358 _GET_INFO_PATH(cli2->tree, pinfo); \
1360 #define GET_INFO_BOTH(finfo,pinfo) do { \
1361 GET_INFO_FILE(finfo); \
1362 GET_INFO_PATH(pinfo); \
1363 COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \
1366 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
1368 union smb_setfileinfo sfinfo; \
1369 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1370 sfinfo.basic_info.in.file.fnum = tfnum; \
1371 sfinfo.basic_info.in.create_time = 0; \
1372 sfinfo.basic_info.in.access_time = 0; \
1373 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1374 sfinfo.basic_info.in.change_time = 0; \
1375 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1376 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1377 if (!NT_STATUS_IS_OK(_status)) { \
1378 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1379 nt_errstr(_status)); \
1384 #define SET_INFO_FILE(finfo, wrtime) \
1385 SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
1387 #define SET_INFO_FILE_NS(finfo, wrtime, ns, tree, tfnum) do { \
1389 union smb_setfileinfo sfinfo; \
1390 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1391 sfinfo.basic_info.in.file.fnum = tfnum; \
1392 sfinfo.basic_info.in.create_time = 0; \
1393 sfinfo.basic_info.in.access_time = 0; \
1394 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1395 sfinfo.basic_info.in.write_time += (ns); \
1396 sfinfo.basic_info.in.change_time = 0; \
1397 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1398 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1399 if (!NT_STATUS_IS_OK(_status)) { \
1400 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1401 nt_errstr(_status)); \
1407 static bool test_delayed_write_update3(struct torture_context *tctx,
1408 struct smbcli_state *cli,
1409 struct smbcli_state *cli2)
1411 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1412 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1413 const char *fname = BASEDIR "\\torture_file3.txt";
1417 struct timeval start;
1419 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1420 //double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 1000000);
1421 //int normal_delay = 1000000;
1422 int normal_delay = 2000000;
1423 double sec = ((double)used_delay) / ((double)normal_delay);
1424 int msec = 1000 * sec;
1426 torture_comment(tctx, "\nRunning test_delayed_write_update3\n");
1428 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1430 torture_comment(tctx, "Open the file handle\n");
1431 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1434 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1438 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1439 finfo0.basic_info.in.file.fnum = fnum1;
1443 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1444 pinfo0.basic_info.in.file.path = fname;
1450 /* get the initial times */
1451 GET_INFO_BOTH(finfo0,pinfo0);
1454 * make sure the write time is updated 2 seconds later
1455 * calcuated from the first write
1456 * (but expect upto 5 seconds extra time for a busy server)
1458 start = timeval_current();
1459 end = timeval_add(&start, 7 * sec, 0);
1460 while (!timeval_expired(&end)) {
1462 torture_comment(tctx, "Do a write on the file handle\n");
1463 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1465 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1469 /* get the times after the write */
1470 GET_INFO_FILE(finfo1);
1472 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1473 double diff = timeval_elapsed(&start);
1474 if (diff < (used_delay / (double)1000000)) {
1475 torture_result(tctx, TORTURE_FAIL, "111Server updated write_time after %.2f seconds "
1476 "(write time update delay == %.2f) (wrong!)\n",
1477 diff, used_delay / (double)1000000);
1482 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1487 smb_msleep(0.5 * msec);
1490 GET_INFO_BOTH(finfo1,pinfo1);
1491 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1493 /* sure any further write doesn't update the write time */
1494 start = timeval_current();
1495 end = timeval_add(&start, 15 * sec, 0);
1496 while (!timeval_expired(&end)) {
1498 torture_comment(tctx, "Do a write on the file handle\n");
1499 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1501 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1505 /* get the times after the write */
1506 GET_INFO_BOTH(finfo2,pinfo2);
1508 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1509 double diff = timeval_elapsed(&start);
1510 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1516 smb_msleep(1 * msec);
1519 GET_INFO_BOTH(finfo2,pinfo2);
1520 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1521 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1522 torture_comment(tctx, "Server did not update write_time (correct)\n");
1526 smb_msleep(5 * msec);
1528 GET_INFO_BOTH(finfo3,pinfo3);
1529 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1532 * the close updates the write time to the time of the close
1533 * and not to the time of the last write!
1535 torture_comment(tctx, "Close the file handle\n");
1536 smbcli_close(cli->tree, fnum1);
1539 GET_INFO_PATH(pinfo4);
1540 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1542 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1543 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1548 smbcli_close(cli->tree, fnum1);
1549 smbcli_unlink(cli->tree, fname);
1550 smbcli_deltree(cli->tree, BASEDIR);
1556 * Show that a truncate write always updates the write time even
1557 * if an initial write has already updated the write time.
1560 static bool test_delayed_write_update3a(struct torture_context *tctx,
1561 struct smbcli_state *cli,
1562 struct smbcli_state *cli2)
1564 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1565 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1566 const char *fname = BASEDIR "\\torture_file3a.txt";
1571 struct timeval start;
1573 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1574 int normal_delay = 2000000;
1575 double sec = ((double)used_delay) / ((double)normal_delay);
1576 int msec = 1000 * sec;
1578 torture_comment(tctx, "\nRunning test_delayed_write_update3a\n");
1580 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1582 torture_comment(tctx, "Open the file handle\n");
1583 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1586 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1590 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1591 finfo0.basic_info.in.file.fnum = fnum1;
1595 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1596 pinfo0.basic_info.in.file.path = fname;
1602 /* get the initial times */
1603 GET_INFO_BOTH(finfo0,pinfo0);
1606 * sleep some time, to demonstrate the handling of write times
1607 * doesn't depend on the time since the open
1609 smb_msleep(5 * msec);
1611 /* get the initial times */
1612 GET_INFO_BOTH(finfo1,pinfo1);
1613 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1616 * make sure the write time is updated 2 seconds later
1617 * calcuated from the first write
1618 * (but expect upto 5 seconds extra time for a busy server)
1620 start = timeval_current();
1621 end = timeval_add(&start, 7 * sec, 0);
1622 while (!timeval_expired(&end)) {
1624 torture_comment(tctx, "Do a write on the file handle\n");
1625 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1627 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1631 /* get the times after the write */
1632 GET_INFO_FILE(finfo1);
1634 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1635 double diff = timeval_elapsed(&start);
1636 if (diff < (used_delay / (double)1000000)) {
1637 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1638 "(1sec == %.2f) (wrong!)\n",
1644 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1649 smb_msleep(0.5 * msec);
1652 GET_INFO_BOTH(finfo1,pinfo1);
1653 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1655 smb_msleep(3 * msec);
1658 * demonstrate that a truncate write always
1659 * updates the write time immediately
1661 for (i=0; i < 3; i++) {
1662 smb_msleep(2 * msec);
1664 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1665 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
1667 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1671 /* get the times after the write */
1672 GET_INFO_BOTH(finfo2,pinfo2);
1673 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1677 smb_msleep(3 * msec);
1679 /* sure any further write doesn't update the write time */
1680 start = timeval_current();
1681 end = timeval_add(&start, 15 * sec, 0);
1682 while (!timeval_expired(&end)) {
1684 torture_comment(tctx, "Do a write on the file handle\n");
1685 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1687 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1691 /* get the times after the write */
1692 GET_INFO_BOTH(finfo2,pinfo2);
1694 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1695 double diff = timeval_elapsed(&start);
1696 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1702 smb_msleep(1 * msec);
1705 GET_INFO_BOTH(finfo2,pinfo2);
1706 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1707 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1708 torture_comment(tctx, "Server did not update write_time (correct)\n");
1712 smb_msleep(3 * msec);
1714 /* get the initial times */
1715 GET_INFO_BOTH(finfo1,pinfo1);
1716 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
1719 * demonstrate that a truncate write always
1720 * updates the write time immediately
1722 for (i=0; i < 3; i++) {
1723 smb_msleep(2 * msec);
1725 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1726 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
1728 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1732 /* get the times after the write */
1733 GET_INFO_BOTH(finfo2,pinfo2);
1734 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1739 smb_msleep(3 * msec);
1741 GET_INFO_BOTH(finfo3,pinfo3);
1742 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1745 * the close doesn't update the write time
1747 torture_comment(tctx, "Close the file handle\n");
1748 smbcli_close(cli->tree, fnum1);
1751 GET_INFO_PATH(pinfo4);
1752 COMPARE_WRITE_TIME_EQUAL(pinfo4, pinfo3);
1754 if (pinfo4.basic_info.out.write_time == pinfo3.basic_info.out.write_time) {
1755 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1760 smbcli_close(cli->tree, fnum1);
1761 smbcli_unlink(cli->tree, fname);
1762 smbcli_deltree(cli->tree, BASEDIR);
1768 * Show a close after write updates the write timestamp to
1769 * the close time, not the last write time.
1772 static bool test_delayed_write_update3b(struct torture_context *tctx,
1773 struct smbcli_state *cli,
1774 struct smbcli_state *cli2)
1776 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1777 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1778 const char *fname = BASEDIR "\\torture_file3b.txt";
1782 struct timeval start;
1784 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1785 int normal_delay = 2000000;
1786 double sec = ((double)used_delay) / ((double)normal_delay);
1787 int msec = 1000 * sec;
1789 torture_comment(tctx, "\nRunning test_delayed_write_update3b\n");
1791 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1793 torture_comment(tctx, "Open the file handle\n");
1794 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1797 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1801 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1802 finfo0.basic_info.in.file.fnum = fnum1;
1806 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1807 pinfo0.basic_info.in.file.path = fname;
1813 /* get the initial times */
1814 GET_INFO_BOTH(finfo0,pinfo0);
1817 * sleep some time, to demonstrate the handling of write times
1818 * doesn't depend on the time since the open
1820 smb_msleep(5 * msec);
1822 /* get the initial times */
1823 GET_INFO_BOTH(finfo1,pinfo1);
1824 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1827 * make sure the write time is updated 2 seconds later
1828 * calcuated from the first write
1829 * (but expect upto 5 seconds extra time for a busy server)
1831 start = timeval_current();
1832 end = timeval_add(&start, 7 * sec, 0);
1833 while (!timeval_expired(&end)) {
1835 torture_comment(tctx, "Do a write on the file handle\n");
1836 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1838 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1842 /* get the times after the write */
1843 GET_INFO_FILE(finfo1);
1845 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1846 double diff = timeval_elapsed(&start);
1847 if (diff < (used_delay / (double)1000000)) {
1848 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
1849 "(expected > %.2f) (wrong!)\n",
1850 diff, used_delay / (double)1000000);
1855 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1856 "(write time update delay == %.2f) (correct)\n",
1857 diff, used_delay / (double)1000000);
1860 smb_msleep(0.5 * msec);
1863 GET_INFO_BOTH(finfo1,pinfo1);
1864 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1866 /* sure any further write doesn't update the write time */
1867 start = timeval_current();
1868 end = timeval_add(&start, 15 * sec, 0);
1869 while (!timeval_expired(&end)) {
1871 torture_comment(tctx, "Do a write on the file handle\n");
1872 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1874 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1878 /* get the times after the write */
1879 GET_INFO_BOTH(finfo2,pinfo2);
1881 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1882 double diff = timeval_elapsed(&start);
1883 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1889 smb_msleep(1 * msec);
1892 GET_INFO_BOTH(finfo2,pinfo2);
1893 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1894 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1895 torture_comment(tctx, "Server did not update write_time (correct)\n");
1899 smb_msleep(5 * msec);
1901 GET_INFO_BOTH(finfo3,pinfo3);
1902 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1905 * the close updates the write time to the time of the close
1906 * and not to the time of the last write!
1908 torture_comment(tctx, "Close the file handle\n");
1909 smbcli_close(cli->tree, fnum1);
1912 GET_INFO_PATH(pinfo4);
1913 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1915 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1916 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1921 smbcli_close(cli->tree, fnum1);
1922 smbcli_unlink(cli->tree, fname);
1923 smbcli_deltree(cli->tree, BASEDIR);
1929 * Check that a write after a truncate write doesn't update
1930 * the timestamp, but a truncate write after a write does.
1931 * Also prove that a close after a truncate write updates the
1932 * timestamp to current, not the time of last write.
1935 static bool test_delayed_write_update3c(struct torture_context *tctx,
1936 struct smbcli_state *cli,
1937 struct smbcli_state *cli2)
1939 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1940 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1941 const char *fname = BASEDIR "\\torture_file3c.txt";
1946 struct timeval start;
1948 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1949 int normal_delay = 2000000;
1950 double sec = ((double)used_delay) / ((double)normal_delay);
1951 int msec = 1000 * sec;
1953 torture_comment(tctx, "\nRunning test_delayed_write_update3c\n");
1955 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1957 torture_comment(tctx, "Open the file handle\n");
1958 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1961 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1965 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1966 finfo0.basic_info.in.file.fnum = fnum1;
1970 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1971 pinfo0.basic_info.in.file.path = fname;
1977 /* get the initial times */
1978 GET_INFO_BOTH(finfo0,pinfo0);
1981 * sleep some time, to demonstrate the handling of write times
1982 * doesn't depend on the time since the open
1984 smb_msleep(5 * msec);
1986 /* get the initial times */
1987 GET_INFO_BOTH(finfo1,pinfo1);
1988 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1991 * demonstrate that a truncate write always
1992 * updates the write time immediately
1994 for (i=0; i < 3; i++) {
1995 smb_msleep(2 * msec);
1997 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1998 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2000 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2004 /* get the times after the write */
2005 GET_INFO_BOTH(finfo2,pinfo2);
2006 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2010 start = timeval_current();
2011 end = timeval_add(&start, 7 * sec, 0);
2012 while (!timeval_expired(&end)) {
2014 torture_comment(tctx, "Do a write on the file handle\n");
2015 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2017 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2021 /* get the times after the write */
2022 GET_INFO_FILE(finfo2);
2024 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2025 double diff = timeval_elapsed(&start);
2026 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2032 smb_msleep(1 * msec);
2035 GET_INFO_BOTH(finfo2,pinfo2);
2036 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2037 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2038 torture_comment(tctx, "Server did not update write_time (correct)\n");
2042 smb_msleep(5 * msec);
2044 /* get the initial times */
2045 GET_INFO_BOTH(finfo1,pinfo1);
2046 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
2049 * demonstrate that a truncate write always
2050 * updates the write time immediately
2052 for (i=0; i < 3; i++) {
2053 smb_msleep(2 * msec);
2055 torture_comment(tctx, "Do a truncate write [%d] on the file handle\n", i);
2056 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2058 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2062 /* get the times after the write */
2063 GET_INFO_BOTH(finfo2,pinfo2);
2064 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2069 smb_msleep(5 * msec);
2071 GET_INFO_BOTH(finfo2,pinfo2);
2072 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2074 /* sure any further write doesn't update the write time */
2075 start = timeval_current();
2076 end = timeval_add(&start, 15 * sec, 0);
2077 while (!timeval_expired(&end)) {
2079 torture_comment(tctx, "Do a write on the file handle\n");
2080 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2082 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2086 /* get the times after the write */
2087 GET_INFO_BOTH(finfo2,pinfo2);
2089 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2090 double diff = timeval_elapsed(&start);
2091 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2097 smb_msleep(1 * msec);
2100 GET_INFO_BOTH(finfo2,pinfo2);
2101 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2102 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2103 torture_comment(tctx, "Server did not update write_time (correct)\n");
2107 smb_msleep(5 * msec);
2109 GET_INFO_BOTH(finfo3,pinfo3);
2110 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2113 * the close updates the write time to the time of the close
2114 * and not to the time of the last write!
2116 torture_comment(tctx, "Close the file handle\n");
2117 smbcli_close(cli->tree, fnum1);
2120 GET_INFO_PATH(pinfo4);
2121 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2123 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2124 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2129 smbcli_close(cli->tree, fnum1);
2130 smbcli_unlink(cli->tree, fname);
2131 smbcli_deltree(cli->tree, BASEDIR);
2137 * Show only the first write updates the timestamp, and a close
2138 * after writes updates to current (I think this is the same
2142 static bool test_delayed_write_update4(struct torture_context *tctx,
2143 struct smbcli_state *cli,
2144 struct smbcli_state *cli2)
2146 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
2147 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
2148 const char *fname = BASEDIR "\\torture_file4.txt";
2152 struct timeval start;
2154 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2155 int normal_delay = 2000000;
2156 double sec = ((double)used_delay) / ((double)normal_delay);
2157 int msec = 1000 * sec;
2159 torture_comment(tctx, "\nRunning test_delayed_write_update4\n");
2161 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2163 torture_comment(tctx, "Open the file handle\n");
2164 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2167 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2171 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2172 finfo0.basic_info.in.file.fnum = fnum1;
2176 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2177 pinfo0.basic_info.in.file.path = fname;
2183 /* get the initial times */
2184 GET_INFO_BOTH(finfo0,pinfo0);
2187 smb_msleep(5 * msec);
2190 torture_comment(tctx, "Do a write on the file handle\n");
2191 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2193 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2198 GET_INFO_BOTH(finfo1,pinfo1);
2199 COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
2202 * make sure the write time is updated 2 seconds later
2203 * calcuated from the first write
2204 * (but expect upto 3 seconds extra time for a busy server)
2206 start = timeval_current();
2207 end = timeval_add(&start, 5 * sec, 0);
2208 while (!timeval_expired(&end)) {
2209 /* get the times after the first write */
2210 GET_INFO_FILE(finfo1);
2212 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
2213 double diff = timeval_elapsed(&start);
2214 if (diff < (used_delay / (double)1000000)) {
2215 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
2216 "(expected > %.2f) (wrong!)\n",
2217 diff, used_delay / (double)1000000);
2222 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2223 "(write time update delay == %.2f) (correct)\n",
2224 diff, used_delay / (double)1000000);
2227 smb_msleep(0.5 * msec);
2230 GET_INFO_BOTH(finfo1,pinfo1);
2231 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
2233 /* sure any further write doesn't update the write time */
2234 start = timeval_current();
2235 end = timeval_add(&start, 15 * sec, 0);
2236 while (!timeval_expired(&end)) {
2238 torture_comment(tctx, "Do a write on the file handle\n");
2239 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2241 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2245 /* get the times after the write */
2246 GET_INFO_BOTH(finfo2,pinfo2);
2248 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2249 double diff = timeval_elapsed(&start);
2250 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2256 smb_msleep(1 * msec);
2259 GET_INFO_BOTH(finfo2,pinfo2);
2260 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2261 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2262 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
2266 smb_msleep(5 * msec);
2268 GET_INFO_BOTH(finfo3,pinfo3);
2269 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2272 * the close updates the write time to the time of the close
2273 * and not to the time of the last write!
2275 torture_comment(tctx, "Close the file handle\n");
2276 smbcli_close(cli->tree, fnum1);
2279 GET_INFO_PATH(pinfo4);
2280 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2282 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2283 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2288 smbcli_close(cli->tree, fnum1);
2289 smbcli_unlink(cli->tree, fname);
2290 smbcli_deltree(cli->tree, BASEDIR);
2296 * Show writes and closes have no effect on updating times once a SETWRITETIME is done.
2299 static bool test_delayed_write_update5(struct torture_context *tctx,
2300 struct smbcli_state *cli,
2301 struct smbcli_state *cli2)
2303 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2304 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2305 const char *fname = BASEDIR "\\torture_file5.txt";
2309 struct timeval start;
2311 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2312 int normal_delay = 2000000;
2313 double sec = ((double)used_delay) / ((double)normal_delay);
2314 int msec = 1000 * sec;
2316 torture_comment(tctx, "\nRunning test_delayed_write_update5\n");
2318 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2320 torture_comment(tctx, "Open the file handle\n");
2321 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2324 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2328 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2329 finfo0.basic_info.in.file.fnum = fnum1;
2335 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2336 pinfo0.basic_info.in.file.path = fname;
2344 /* get the initial times */
2345 GET_INFO_BOTH(finfo0,pinfo0);
2348 torture_comment(tctx, "Do a write on the file handle\n");
2349 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2351 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2356 GET_INFO_BOTH(finfo1,pinfo1);
2357 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2359 torture_comment(tctx, "Set write time in the future on the file handle\n");
2360 SET_INFO_FILE(finfo0, time(NULL) + 86400);
2361 GET_INFO_BOTH(finfo2,pinfo2);
2362 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2364 torture_comment(tctx, "Set write time in the past on the file handle\n");
2365 SET_INFO_FILE(finfo0, time(NULL) - 86400);
2366 GET_INFO_BOTH(finfo2,pinfo2);
2367 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2369 /* make sure the 2 second delay from the first write are canceled */
2370 start = timeval_current();
2371 end = timeval_add(&start, 15 * sec, 0);
2372 while (!timeval_expired(&end)) {
2374 /* get the times after the first write */
2375 GET_INFO_BOTH(finfo3,pinfo3);
2377 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2378 double diff = timeval_elapsed(&start);
2379 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2385 smb_msleep(1 * msec);
2388 GET_INFO_BOTH(finfo3,pinfo3);
2389 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2390 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2391 torture_comment(tctx, "Server did not update write_time (correct)\n");
2394 /* sure any further write doesn't update the write time */
2395 start = timeval_current();
2396 end = timeval_add(&start, 15 * sec, 0);
2397 while (!timeval_expired(&end)) {
2399 torture_comment(tctx, "Do a write on the file handle\n");
2400 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2402 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2406 /* get the times after the write */
2407 GET_INFO_BOTH(finfo4,pinfo4);
2409 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2410 double diff = timeval_elapsed(&start);
2411 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2417 smb_msleep(1 * msec);
2420 GET_INFO_BOTH(finfo4,pinfo4);
2421 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2422 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2423 torture_comment(tctx, "Server did not update write_time (correct)\n");
2427 smb_msleep(5 * msec);
2429 GET_INFO_BOTH(finfo5,pinfo5);
2430 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2433 * the close doesn't update the write time
2435 torture_comment(tctx, "Close the file handle\n");
2436 smbcli_close(cli->tree, fnum1);
2439 GET_INFO_PATH(pinfo6);
2440 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2442 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2443 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2448 smbcli_close(cli->tree, fnum1);
2449 smbcli_unlink(cli->tree, fname);
2450 smbcli_deltree(cli->tree, BASEDIR);
2456 * Show truncate writes and closes have no effect on updating times once a SETWRITETIME is done.
2459 static bool test_delayed_write_update5b(struct torture_context *tctx,
2460 struct smbcli_state *cli,
2461 struct smbcli_state *cli2)
2463 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2464 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2465 const char *fname = BASEDIR "\\torture_fileb.txt";
2469 struct timeval start;
2471 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2472 int normal_delay = 2000000;
2473 double sec = ((double)used_delay) / ((double)normal_delay);
2474 int msec = 1000 * sec;
2476 torture_comment(tctx, "\nRunning test_delayed_write_update5b\n");
2478 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2480 torture_comment(tctx, "Open the file handle\n");
2481 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2484 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2488 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2489 finfo0.basic_info.in.file.fnum = fnum1;
2495 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2496 pinfo0.basic_info.in.file.path = fname;
2504 /* get the initial times */
2505 GET_INFO_BOTH(finfo0,pinfo0);
2508 torture_comment(tctx, "Do a write on the file handle\n");
2509 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2511 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2516 GET_INFO_BOTH(finfo1,pinfo1);
2517 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2519 torture_comment(tctx, "Set write time in the future on the file handle\n");
2520 SET_INFO_FILE(finfo0, time(NULL) + 86400);
2521 GET_INFO_BOTH(finfo2,pinfo2);
2522 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2524 torture_comment(tctx, "Set write time in the past on the file handle\n");
2525 SET_INFO_FILE(finfo0, time(NULL) - 86400);
2526 GET_INFO_BOTH(finfo2,pinfo2);
2527 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2529 /* make sure the 2 second delay from the first write are canceled */
2530 start = timeval_current();
2531 end = timeval_add(&start, 15 * sec, 0);
2532 while (!timeval_expired(&end)) {
2534 /* get the times after the first write */
2535 GET_INFO_BOTH(finfo3,pinfo3);
2537 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2538 double diff = timeval_elapsed(&start);
2539 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2545 smb_msleep(1 * msec);
2548 GET_INFO_BOTH(finfo3,pinfo3);
2549 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2550 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2551 torture_comment(tctx, "Server did not update write_time (correct)\n");
2554 /* Do any further write (truncates) update the write time ? */
2555 start = timeval_current();
2556 end = timeval_add(&start, 15 * sec, 0);
2557 while (!timeval_expired(&end)) {
2559 torture_comment(tctx, "Do a truncate write on the file handle\n");
2560 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
2562 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2566 /* get the times after the write */
2567 GET_INFO_BOTH(finfo4,pinfo4);
2569 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2570 double diff = timeval_elapsed(&start);
2571 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2577 smb_msleep(1 * msec);
2580 GET_INFO_BOTH(finfo4,pinfo4);
2581 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2582 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2583 torture_comment(tctx, "Server did not update write_time (correct)\n");
2587 smb_msleep(5 * msec);
2589 GET_INFO_BOTH(finfo5,pinfo5);
2590 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2593 * the close doesn't update the write time
2595 torture_comment(tctx, "Close the file handle\n");
2596 smbcli_close(cli->tree, fnum1);
2599 GET_INFO_PATH(pinfo6);
2600 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2602 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2603 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2608 smbcli_close(cli->tree, fnum1);
2609 smbcli_unlink(cli->tree, fname);
2610 smbcli_deltree(cli->tree, BASEDIR);
2616 * Open 2 handles on a file. Write one one and then set the
2617 * WRITE TIME explicitly on the other. Ensure the write time
2618 * update is cancelled. Ensure the write time is updated to
2619 * the close time when the non-explicit set handle is closed.
2623 static bool test_delayed_write_update6(struct torture_context *tctx,
2624 struct smbcli_state *cli,
2625 struct smbcli_state *cli2)
2627 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2628 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
2629 const char *fname = BASEDIR "\\torture_file6.txt";
2634 struct timeval start;
2636 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2637 int normal_delay = 2000000;
2638 double sec = ((double)used_delay) / ((double)normal_delay);
2639 int msec = 1000 * sec;
2642 torture_comment(tctx, "\nRunning test_delayed_write_update6\n");
2644 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2646 torture_comment(tctx, "Open the file handle\n");
2647 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2650 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2655 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
2656 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2659 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2664 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2665 finfo0.basic_info.in.file.fnum = fnum1;
2671 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2672 pinfo0.basic_info.in.file.path = fname;
2681 /* get the initial times */
2682 GET_INFO_BOTH(finfo0,pinfo0);
2685 torture_comment(tctx, "Do a write on the file handle\n");
2686 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2688 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2693 GET_INFO_BOTH(finfo1,pinfo1);
2694 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2696 torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
2697 SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
2698 GET_INFO_BOTH(finfo2,pinfo2);
2699 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2701 torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
2702 SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
2703 GET_INFO_BOTH(finfo2,pinfo2);
2704 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2706 /* make sure the 2 second delay from the first write are canceled */
2707 start = timeval_current();
2708 end = timeval_add(&start, 10 * sec, 0);
2709 while (!timeval_expired(&end)) {
2711 /* get the times after the first write */
2712 GET_INFO_BOTH(finfo3,pinfo3);
2714 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2715 double diff = timeval_elapsed(&start);
2716 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2722 smb_msleep(1 * msec);
2725 GET_INFO_BOTH(finfo3,pinfo3);
2726 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2727 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2728 torture_comment(tctx, "Server did not update write_time (correct)\n");
2731 /* sure any further write doesn't update the write time */
2732 start = timeval_current();
2733 end = timeval_add(&start, 10 * sec, 0);
2734 while (!timeval_expired(&end)) {
2736 torture_comment(tctx, "Do a write on the file handle\n");
2737 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2739 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2743 /* get the times after the write */
2744 GET_INFO_BOTH(finfo4,pinfo4);
2746 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2747 double diff = timeval_elapsed(&start);
2748 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2754 smb_msleep(1 * msec);
2757 GET_INFO_BOTH(finfo4,pinfo4);
2758 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2759 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2760 torture_comment(tctx, "Server did not update write_time (correct)\n");
2764 smb_msleep(5 * msec);
2766 GET_INFO_BOTH(finfo5,pinfo5);
2767 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2770 * the close updates the write time to the time of the close
2771 * as the write time was set on the 2nd handle
2773 torture_comment(tctx, "Close the file handle\n");
2774 smbcli_close(cli->tree, fnum1);
2777 GET_INFO_PATH(pinfo6);
2778 COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
2780 if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
2781 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2784 /* See what the second write handle thinks the time is ? */
2785 finfo5.basic_info.in.file.fnum = fnum2;
2786 GET_INFO_FILE2(finfo5);
2787 COMPARE_WRITE_TIME_EQUAL(finfo5, pinfo6);
2789 /* See if we have lost the sticky write time on handle2 */
2790 smb_msleep(3 * msec);
2791 torture_comment(tctx, "Have we lost the sticky write time ?\n");
2793 /* Make sure any further normal write doesn't update the write time */
2794 start = timeval_current();
2795 end = timeval_add(&start, 10 * sec, 0);
2796 while (!timeval_expired(&end)) {
2798 torture_comment(tctx, "Do a write on the second file handle\n");
2799 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
2801 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2805 /* get the times after the write */
2806 GET_INFO_FILE2(finfo5);
2807 GET_INFO_PATH(pinfo6);
2809 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2810 double diff = timeval_elapsed(&start);
2811 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2817 smb_msleep(1 * msec);
2820 /* What about a truncate write ? */
2821 start = timeval_current();
2822 end = timeval_add(&start, 10 * sec, 0);
2823 while (!timeval_expired(&end)) {
2825 torture_comment(tctx, "Do a truncate write on the second file handle\n");
2826 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 0);
2828 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2832 /* get the times after the write */
2833 GET_INFO_FILE2(finfo5);
2834 GET_INFO_PATH(pinfo6);
2836 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2837 double diff = timeval_elapsed(&start);
2838 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2844 smb_msleep(1 * msec);
2848 /* keep the 2nd handle open and rerun tests */
2855 * closing the 2nd handle will cause no write time update
2856 * as the write time was explicit set on this handle
2858 torture_comment(tctx, "Close the 2nd file handle\n");
2859 smbcli_close(cli2->tree, fnum2);
2862 GET_INFO_PATH(pinfo7);
2863 COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
2865 if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
2866 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2871 smbcli_close(cli->tree, fnum1);
2873 smbcli_close(cli2->tree, fnum2);
2874 smbcli_unlink(cli->tree, fname);
2875 smbcli_deltree(cli->tree, BASEDIR);
2880 static bool test_delayed_write_update7(struct torture_context *tctx, struct smbcli_state *cli)
2882 union smb_open open_parms;
2883 union smb_fileinfo finfo1, finfo2, finfo3;
2884 const char *fname = BASEDIR "\\torture_file7.txt";
2888 TALLOC_CTX *mem_ctx;
2890 torture_comment(tctx, "\nRunning test_delayed_write_update7 (timestamp resolution test)\n");
2892 mem_ctx = talloc_init("test_delayed_write_update7");
2893 if (!mem_ctx) return false;
2895 ZERO_STRUCT(finfo1);
2896 ZERO_STRUCT(finfo2);
2897 ZERO_STRUCT(finfo3);
2898 ZERO_STRUCT(open_parms);
2900 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2902 /* Create the file. */
2903 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2905 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
2909 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2910 finfo1.basic_info.in.file.fnum = fnum1;
2914 /* Get the initial timestamps. */
2915 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
2917 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
2919 /* Set the pending write time to a value with ns. */
2920 SET_INFO_FILE_NS(finfo, time(NULL) + 86400, 103, cli->tree, fnum1);
2922 /* Get the current pending write time by fnum. */
2923 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
2925 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
2927 /* Ensure the time is actually different. */
2928 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2929 torture_result(tctx, TORTURE_FAIL,
2930 "setfileinfo time matches original fileinfo time");
2934 /* Get the current pending write time by path. */
2935 finfo3.basic_info.in.file.path = fname;
2936 status = smb_raw_pathinfo(cli->tree, tctx, &finfo3);
2938 if (finfo2.basic_info.out.write_time != finfo3.basic_info.out.write_time) {
2939 torture_result(tctx, TORTURE_FAIL,
2940 "qpathinfo time doesn't match fileinfo time");
2944 /* Now close the file. Re-open and check that the write
2945 time is identical to the one we wrote. */
2947 smbcli_close(cli->tree, fnum1);
2949 open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
2950 open_parms.ntcreatex.in.flags = 0;
2951 open_parms.ntcreatex.in.access_mask = SEC_GENERIC_READ;
2952 open_parms.ntcreatex.in.file_attr = 0;
2953 open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE|
2954 NTCREATEX_SHARE_ACCESS_READ|
2955 NTCREATEX_SHARE_ACCESS_WRITE;
2956 open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
2957 open_parms.ntcreatex.in.create_options = 0;
2958 open_parms.ntcreatex.in.fname = fname;
2960 status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
2961 talloc_free(mem_ctx);
2963 if (!NT_STATUS_IS_OK(status)) {
2964 torture_result(tctx, TORTURE_FAIL,
2965 "setfileinfo time matches original fileinfo time");
2969 fnum1 = open_parms.ntcreatex.out.file.fnum;
2971 /* Check the returned time matches. */
2972 if (open_parms.ntcreatex.out.write_time != finfo2.basic_info.out.write_time) {
2973 torture_result(tctx, TORTURE_FAIL,
2974 "final open time does not match set time");
2980 smbcli_close(cli->tree, fnum1);
2982 smbcli_unlink(cli->tree, fname);
2983 smbcli_deltree(cli->tree, BASEDIR);
2988 Test if creating a file in a directory with an open handle updates the
2989 write timestamp (it should).
2991 static bool test_directory_update8(struct torture_context *tctx, struct smbcli_state *cli)
2993 union smb_fileinfo dir_info1, dir_info2;
2994 union smb_open open_parms;
2995 const char *fname = BASEDIR "\\torture_file.txt";
3000 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
3001 int normal_delay = 2000000;
3002 double sec = ((double)used_delay) / ((double)normal_delay);
3003 int msec = 1000 * sec;
3004 TALLOC_CTX *mem_ctx = talloc_init("test_delayed_write_update8");
3006 if (!mem_ctx) return false;
3008 torture_comment(tctx, "\nRunning test directory write update\n");
3010 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
3012 /* Open a handle on the directory - and leave it open. */
3013 ZERO_STRUCT(open_parms);
3014 open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
3015 open_parms.ntcreatex.in.flags = 0;
3016 open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ;
3017 open_parms.ntcreatex.in.file_attr = 0;
3018 open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE|
3019 NTCREATEX_SHARE_ACCESS_READ|
3020 NTCREATEX_SHARE_ACCESS_WRITE;
3021 open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
3022 open_parms.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
3023 open_parms.ntcreatex.in.fname = BASEDIR;
3025 status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
3026 talloc_free(mem_ctx);
3028 if (!NT_STATUS_IS_OK(status)) {
3029 torture_result(tctx, TORTURE_FAIL,
3030 "failed to open directory handle");
3035 fnum1 = open_parms.ntcreatex.out.file.fnum;
3037 /* Store the returned write time. */
3038 ZERO_STRUCT(dir_info1);
3039 dir_info1.basic_info.out.write_time = open_parms.ntcreatex.out.write_time;
3041 torture_comment(tctx, "Initial write time %s\n",
3042 nt_time_string(tctx, dir_info1.basic_info.out.write_time));
3045 smb_msleep(3 * msec);
3047 /* Now create a file within the directory. */
3048 fnum2 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
3050 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
3054 smbcli_close(cli->tree, fnum2);
3056 /* Read the directory write time again. */
3057 ZERO_STRUCT(dir_info2);
3058 dir_info2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
3059 dir_info2.basic_info.in.file.fnum = fnum1;
3061 status = smb_raw_fileinfo(cli->tree, tctx, &dir_info2);
3063 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
3065 /* Ensure it's been incremented. */
3066 COMPARE_WRITE_TIME_GREATER(dir_info2, dir_info1);
3068 torture_comment(tctx, "Updated write time %s\n",
3069 nt_time_string(tctx, dir_info2.basic_info.out.write_time));
3074 smbcli_close(cli->tree, fnum1);
3075 smbcli_unlink(cli->tree, fname);
3076 smbcli_deltree(cli->tree, BASEDIR);
3081 #undef COMPARE_WRITE_TIME_CMP
3082 #undef COMPARE_ACCESS_TIME_CMP
3084 #define COMPARE_TIME_CMP(given, gelem, correct, celem, cmp) do { \
3085 const uint64_t _r = 10*1000*1000; \
3086 NTTIME _g = (given).basic_info.out.gelem; \
3087 NTTIME _gr = (_g / _r) * _r; \
3088 NTTIME _c = (correct).basic_info.out.celem; \
3089 NTTIME _cr = (_c / _r) * _r; \
3090 bool _strict = torture_setting_bool(tctx, "strict mode", false); \
3091 const char *_err = NULL; \
3092 if (_strict && (_g cmp _c)) { \
3094 } else if ((_g cmp _c) && (_gr cmp _cr)) { \
3095 /* handle filesystem without high resolution timestamps */ \
3098 if (_err != NULL) { \
3099 struct timeval _gtv; \
3100 struct timeval _ctv; \
3101 struct timeval_buf _gtvb; \
3102 struct timeval_buf _ctvb; \
3103 nttime_to_timeval(&_gtv, _g); \
3104 nttime_to_timeval(&_ctv, _c); \
3105 torture_result(tctx, TORTURE_FAIL, \
3106 __location__": %s wrong (%s.%s)%s %s (%s.%s)%s", \
3109 timeval_str_buf(&_gtv, false, true, &_gtvb), \
3112 timeval_str_buf(&_ctv, false, true, &_ctvb)); \
3117 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
3118 COMPARE_TIME_CMP(given, write_time, correct, write_time, cmp); \
3120 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
3121 COMPARE_WRITE_TIME_CMP(given,correct,!=)
3122 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
3123 COMPARE_WRITE_TIME_CMP(given,correct,<=)
3125 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
3126 COMPARE_TIME_CMP(given, access_time, correct, access_time, cmp); \
3128 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
3129 COMPARE_ACCESS_TIME_CMP(given,correct,!=)
3130 #define COMPARE_ACCESS_TIME_GREATER(given,correct) \
3131 COMPARE_ACCESS_TIME_CMP(given,correct,<=)
3133 #define COMPARE_CHANGE_TIME_CMP(given, correct, cmp) do { \
3134 COMPARE_TIME_CMP(given, change_time, correct, change_time, cmp); \
3136 #define COMPARE_CHANGE_TIME_EQUAL(given,correct) \
3137 COMPARE_CHANGE_TIME_CMP(given,correct,!=)
3138 #define COMPARE_CHANGE_TIME_GREATER(given,correct) \
3139 COMPARE_CHANGE_TIME_CMP(given,correct,<=)
3141 #define COMPARE_CREATE_TIME_CMP(given, correct, cmp) do { \
3142 COMPARE_TIME_CMP(given, create_time, correct, create_time, cmp); \
3144 #define COMPARE_CREATE_TIME_EQUAL(given,correct) \
3145 COMPARE_CREATE_TIME_CMP(given,correct,!=)
3147 #define COMPARE_ALL_TIMES_EQUAL(given,correct) do { \
3148 COMPARE_WRITE_TIME_EQUAL(given,correct); \
3149 COMPARE_CHANGE_TIME_EQUAL(given,correct); \
3150 COMPARE_ACCESS_TIME_EQUAL(given,correct); \
3151 COMPARE_CREATE_TIME_EQUAL(given,correct); \
3154 #define COMPARE_TIMES_AFTER_WRITE(given,correct) do { \
3155 COMPARE_WRITE_TIME_GREATER(given,correct); \
3156 COMPARE_CHANGE_TIME_GREATER(given,correct); \
3157 COMPARE_ACCESS_TIME_EQUAL(given,correct); \
3158 COMPARE_CREATE_TIME_EQUAL(given,correct); \
3159 COMPARE_TIME_CMP(given, change_time, given, write_time, !=); \
3162 struct test_delaywrite_delaywrite1_state {
3163 struct torture_context *tctx;
3164 struct smbcli_state *cli1;
3165 struct smbcli_state *cli2;
3171 static bool test_delaywrite_delaywrite1_get_info(void *private_data,
3172 union smb_fileinfo *finfo)
3174 struct test_delaywrite_delaywrite1_state *state =
3175 (struct test_delaywrite_delaywrite1_state *)private_data;
3176 struct torture_context *tctx = state->tctx;
3177 struct smbcli_state *cli = state->cli1;
3178 struct smbcli_state *cli2 = state->cli2;
3179 union smb_fileinfo t1finfo;
3180 union smb_fileinfo t2finfo;
3183 ZERO_STRUCTP(finfo);
3185 ZERO_STRUCT(t1finfo);
3186 t1finfo.basic_info.level = RAW_FILEINFO_BASIC_INFORMATION;
3187 t1finfo.basic_info.in.file.fnum = state->fnum1;
3189 ZERO_STRUCT(t2finfo);
3190 t2finfo.basic_info.level = RAW_FILEINFO_BASIC_INFORMATION;
3191 t2finfo.basic_info.in.file.fnum = state->fnum2;
3193 GET_INFO_FILE2(t2finfo);
3194 GET_INFO_FILE(t1finfo);
3195 if (t1finfo.basic_info.out.write_time != t2finfo.basic_info.out.write_time) {
3197 * There was a race, get it again on handle 2,
3198 * but then they have to match.
3200 GET_INFO_FILE2(t2finfo);
3202 COMPARE_ALL_TIMES_EQUAL(t1finfo, t2finfo);
3204 finfo->basic_info.out = t1finfo.basic_info.out;
3209 static bool test_delaywrite_delaywrite1_write_data(void *private_data)
3211 struct test_delaywrite_delaywrite1_state *state =
3212 (struct test_delaywrite_delaywrite1_state *)private_data;
3213 struct torture_context *tctx = state->tctx;
3217 nwritten = smbcli_write(state->cli1->tree, state->fnum1, 0, "x", 0, 1);
3218 torture_assert_int_equal_goto(tctx, nwritten, 1,
3219 ret, done, "smbcli_write");
3225 static bool test_delaywrite_delaywrite1_close(void *private_data,
3226 union smb_fileinfo *finfo)
3228 struct test_delaywrite_delaywrite1_state *state =
3229 (struct test_delaywrite_delaywrite1_state *)private_data;
3230 struct torture_context *tctx = state->tctx;
3231 struct smbcli_state *cli2 = state->cli2;
3232 union smb_fileinfo t2finfo;
3233 union smb_fileinfo t2pinfo;
3236 ZERO_STRUCTP(finfo);
3239 * the close updates the write time to the time of the close
3240 * and not to the time of the last write!
3242 torture_comment(tctx, "Close the file handle\n");
3243 smbcli_close(state->cli1->tree, state->fnum1);
3246 ZERO_STRUCT(t2finfo);
3247 t2finfo.basic_info.level = RAW_FILEINFO_BASIC_INFO;
3248 t2finfo.basic_info.in.file.fnum = state->fnum2;
3249 ZERO_STRUCT(t2pinfo);
3250 t2pinfo.basic_info.level = RAW_FILEINFO_BASIC_INFO;
3251 t2pinfo.basic_info.in.file.path = state->fname;
3253 GET_INFO_FILE2(t2finfo);
3255 smbcli_close(state->cli2->tree, state->fnum2);
3258 GET_INFO_PATH(t2pinfo);
3259 COMPARE_ALL_TIMES_EQUAL(t2pinfo, t2finfo);
3261 finfo->basic_info.out = t2pinfo.basic_info.out;
3267 static bool test_delaywrite_delaywrite1(struct torture_context *tctx,
3268 struct smbcli_state *cli,
3269 struct smbcli_state *cli2)
3271 struct test_delaywrite_delaywrite1_state state = {
3278 const char *fname = BASEDIR "\\torture_file3.txt";
3280 //double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
3281 //double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 1000000);
3282 double normal_delay = 1000000;
3283 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", normal_delay);
3284 //double normal_delay = 1000000;
3285 //int normal_delay = 2000000;
3288 torture_comment(tctx, "\nRunning test_delayed_write_update3\n");
3290 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
3292 torture_comment(tctx, "Open the file handle\n");
3293 state.fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
3294 if (state.fnum1 == -1) {
3296 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
3299 state.fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
3300 if (state.fnum2 == -1) {
3302 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
3306 state.fname = fname;
3308 ok = test_delay_writetime1(tctx, used_delay, normal_delay,
3310 test_delaywrite_delaywrite1_get_info,
3311 test_delaywrite_delaywrite1_write_data,
3312 NULL, /* close_cb */
3314 torture_assert_goto(tctx, ok, ret, done, "test_delay_writetime1(1)");
3315 ok = test_delay_writetime1(tctx, used_delay, normal_delay,
3317 test_delaywrite_delaywrite1_get_info,
3318 test_delaywrite_delaywrite1_write_data,
3319 test_delaywrite_delaywrite1_close,
3321 torture_assert_goto(tctx, ok, ret, done, "test_delay_writetime1(2)");
3324 if (state.fnum1 != -1) {
3325 smbcli_close(cli->tree, state.fnum1);
3327 if (state.fnum2 != -1) {
3328 smbcli_close(cli2->tree, state.fnum2);
3330 smbcli_unlink(cli->tree, fname);
3331 smbcli_deltree(cli->tree, BASEDIR);
3337 testing of delayed update of write_time
3339 struct torture_suite *torture_delay_write(TALLOC_CTX *ctx)
3341 struct torture_suite *suite = torture_suite_create(ctx, "delaywrite");
3343 torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
3344 torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
3345 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate", test_delayed_write_update1);
3346 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a);
3347 torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b);
3348 torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c);
3349 torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
3350 torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
3351 torture_suite_add_2smb_test(suite, "delayed update of write time 3a", test_delayed_write_update3a);
3352 torture_suite_add_2smb_test(suite, "delayed update of write time 3b", test_delayed_write_update3b);
3353 torture_suite_add_2smb_test(suite, "delayed update of write time 3c", test_delayed_write_update3c);
3354 torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
3355 torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
3356 torture_suite_add_2smb_test(suite, "delayed update of write time 5b", test_delayed_write_update5b);
3357 torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
3358 torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7);
3359 torture_suite_add_1smb_test(suite, "directory timestamp update test", test_directory_update8);
3360 torture_suite_add_2smb_test(suite, "delaywrite1", test_delaywrite_delaywrite1);