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 const char *fname = BASEDIR "\\torture_file.txt";
47 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
48 int normal_delay = 2000000;
49 double sec = ((double)used_delay) / ((double)normal_delay);
50 int msec = 1000 * sec;
52 torture_comment(tctx, "\nRunning test_delayed_write_update\n");
54 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
56 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
57 torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
58 "Failed to open %s", fname));
60 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
61 finfo1.basic_info.in.file.fnum = fnum1;
64 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
65 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
67 torture_comment(tctx, "Initial write time %s\n",
68 nt_time_string(tctx, finfo1.basic_info.out.write_time));
70 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
71 torture_assert_int_equal(tctx, written, 1,
72 "unexpected number of bytes written");
74 start = timeval_current();
75 end = timeval_add(&start, (120 * sec), 0);
76 while (!timeval_expired(&end)) {
77 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
79 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
81 torture_comment(tctx, "write time %s\n",
82 nt_time_string(tctx, finfo2.basic_info.out.write_time));
84 if (finfo1.basic_info.out.write_time !=
85 finfo2.basic_info.out.write_time)
87 double diff = timeval_elapsed(&start);
90 diff >= (used_delay / (double)1000000),
92 "Server updated write_time after %.2f "
93 "seconds (expected >= %.2f)\n",
94 diff, used_delay/(double)1000000));
96 torture_comment(tctx, "Server updated write_time after %.2f seconds (correct)\n",
101 smb_msleep(1 * msec);
104 torture_assert_u64_not_equal(tctx,
105 finfo2.basic_info.out.write_time,
106 finfo1.basic_info.out.write_time,
107 "Server did not update write time within "
111 smbcli_close(cli->tree, fnum1);
112 smbcli_unlink(cli->tree, fname);
113 smbcli_deltree(cli->tree, BASEDIR);
118 static bool test_delayed_write_update1(struct torture_context *tctx, struct smbcli_state *cli)
120 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
121 const char *fname = BASEDIR "\\torture_file1.txt";
126 struct timeval start;
128 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
129 int normal_delay = 2000000;
130 double sec = ((double)used_delay) / ((double)normal_delay);
131 int msec = 1000 * sec;
136 torture_comment(tctx, "\nRunning test_delayed_write_update1\n");
138 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
140 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
141 torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
142 "Failed to open %s", fname));
144 memset(buf, 'x', 2048);
145 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
147 /* 3 second delay to ensure we get past any 2 second time
148 granularity (older systems may have that) */
149 smb_msleep(3 * msec);
151 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
152 finfo1.all_info.in.file.fnum = fnum1;
155 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
156 pinfo4.all_info.in.file.path = fname;
158 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
160 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
162 torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048,
163 "file size not as expected after write(2048)");
165 torture_comment(tctx, "Initial write time %s\n",
166 nt_time_string(tctx, finfo1.all_info.out.write_time));
168 /* 3 second delay to ensure we get past any 2 second time
169 granularity (older systems may have that) */
170 smb_msleep(3 * msec);
172 /* Do a zero length SMBwrite call to truncate. */
173 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
174 torture_assert_int_equal(tctx, written, 0,
175 "unexpected number of bytes written");
177 start = timeval_current();
178 end = timeval_add(&start, (120 * sec), 0);
181 while (!timeval_expired(&end)) {
182 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
184 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
186 torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 1024,
187 "file not truncated to expected size "
190 torture_comment(tctx, "write time %s\n",
191 nt_time_string(tctx, finfo2.all_info.out.write_time));
193 if (finfo1.all_info.out.write_time !=
194 finfo2.all_info.out.write_time)
201 smb_msleep(1 * msec);
205 torture_assert(tctx, updated,
206 "Server did not update write time within 120 seconds");
208 torture_assert(tctx, first, talloc_asprintf(tctx,
209 "Server did not update write time immediately but only "
210 "after %.2f seconds!", timeval_elapsed(&start)));
212 torture_comment(tctx, "Server updated write time immediately. Good!\n");
215 smb_msleep(2 * msec);
217 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
218 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
219 torture_assert_int_equal(tctx, written, 1,
220 "unexpected number of bytes written");
222 start = timeval_current();
223 end = timeval_add(&start, (10*sec), 0);
224 while (!timeval_expired(&end)) {
225 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
227 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
229 torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 1024,
230 "file not truncated to expected size "
233 torture_comment(tctx, "write time %s\n",
234 nt_time_string(tctx, finfo3.all_info.out.write_time));
236 torture_assert_u64_equal(tctx,
237 finfo3.all_info.out.write_time,
238 finfo2.all_info.out.write_time,
239 talloc_asprintf(tctx,
240 "Server updated write time "
241 "after %.2f seconds (wrong!)",
242 timeval_elapsed(&start)));
245 smb_msleep(1 * msec);
248 torture_comment(tctx, "Server did not update write time within 10 "
252 smb_msleep(2 * msec);
254 /* the close should trigger an write time update */
255 smbcli_close(cli->tree, fnum1);
258 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
259 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
261 torture_assert_u64_not_equal(tctx,
262 pinfo4.all_info.out.write_time,
263 finfo3.all_info.out.write_time,
264 "Server did not update write time on "
267 pinfo4.all_info.out.write_time > finfo3.all_info.out.write_time,
268 "Server updated write time on close, but to an earlier point "
271 torture_comment(tctx, "Server updated write time on close (correct)\n");
274 smbcli_close(cli->tree, fnum1);
275 smbcli_unlink(cli->tree, fname);
276 smbcli_deltree(cli->tree, BASEDIR);
281 /* Updating with a SMBwrite of zero length
282 * changes the write time immediately - even on expand. */
284 static bool test_delayed_write_update1a(struct torture_context *tctx, struct smbcli_state *cli)
286 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
287 const char *fname = BASEDIR "\\torture_file1a.txt";
292 struct timeval start;
294 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
295 int normal_delay = 2000000;
296 double sec = ((double)used_delay) / ((double)normal_delay);
297 int msec = 1000 * sec;
302 torture_comment(tctx, "\nRunning test_delayed_write_update1a\n");
304 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
306 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
307 torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
308 "Failed to open %s", fname));
310 memset(buf, 'x', 2048);
311 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
313 /* 3 second delay to ensure we get past any 2 second time
314 granularity (older systems may have that) */
315 smb_msleep(3 * msec);
317 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
318 finfo1.all_info.in.file.fnum = fnum1;
321 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
322 pinfo4.all_info.in.file.path = fname;
324 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
326 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
328 torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048,
329 "file size not as expected after write(2048)");
331 torture_comment(tctx, "Initial write time %s\n",
332 nt_time_string(tctx, finfo1.all_info.out.write_time));
334 /* Do a zero length SMBwrite call to truncate. */
335 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
337 torture_assert_int_equal(tctx, written, 0,
338 "unexpected number of bytes written");
340 start = timeval_current();
341 end = timeval_add(&start, (120*sec), 0);
344 while (!timeval_expired(&end)) {
345 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
347 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
349 torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 10240,
350 "file not truncated to expected size "
353 torture_comment(tctx, "write time %s\n",
354 nt_time_string(tctx, finfo2.all_info.out.write_time));
356 if (finfo1.all_info.out.write_time !=
357 finfo2.all_info.out.write_time)
364 smb_msleep(1 * msec);
368 torture_assert(tctx, updated,
369 "Server did not update write time within 120 seconds");
371 torture_assert(tctx, first, talloc_asprintf(tctx,
372 "Server did not update write time immediately but only "
373 "after %.2f seconds!", timeval_elapsed(&start)));
375 torture_comment(tctx, "Server updated write time immediately. Good!\n");
378 smb_msleep(2 * msec);
380 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
381 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
383 torture_assert_int_equal(tctx, written, 1,
384 "unexpected number of bytes written");
386 start = timeval_current();
387 end = timeval_add(&start, (10*sec), 0);
388 while (!timeval_expired(&end)) {
389 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
391 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
393 torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 10240,
394 "file not truncated to expected size "
397 torture_comment(tctx, "write time %s\n",
398 nt_time_string(tctx, finfo3.all_info.out.write_time));
400 torture_assert_u64_equal(tctx,
401 finfo3.all_info.out.write_time,
402 finfo2.all_info.out.write_time,
403 talloc_asprintf(tctx,
404 "Server updated write time "
405 "after %.2f seconds (wrong!)",
406 timeval_elapsed(&start)));
409 smb_msleep(1 * msec);
412 torture_comment(tctx, "Server did not update write time within 10 "
415 /* the close should trigger an write time update */
416 smbcli_close(cli->tree, fnum1);
419 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
420 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
422 torture_assert_u64_not_equal(tctx,
423 pinfo4.all_info.out.write_time,
424 finfo3.all_info.out.write_time,
425 "Server did not update write time on "
428 pinfo4.all_info.out.write_time > finfo3.all_info.out.write_time,
429 "Server updated write time on close, but to an earlier point "
432 torture_comment(tctx, "Server updated write time on close (correct)\n");
435 smbcli_close(cli->tree, fnum1);
436 smbcli_unlink(cli->tree, fname);
437 smbcli_deltree(cli->tree, BASEDIR);
442 /* Updating with a SET_FILE_END_OF_FILE_INFO
443 * changes the write time immediately - even on expand. */
445 static bool test_delayed_write_update1b(struct torture_context *tctx, struct smbcli_state *cli)
447 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
448 const char *fname = BASEDIR "\\torture_file1b.txt";
453 struct timeval start;
455 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
456 int normal_delay = 2000000;
457 double sec = ((double)used_delay) / ((double)normal_delay);
458 int msec = 1000 * sec;
463 torture_comment(tctx, "\nRunning test_delayed_write_update1b\n");
465 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
467 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
468 torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
469 "Failed to open %s", fname));
471 memset(buf, 'x', 2048);
472 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
474 /* 3 second delay to ensure we get past any 2 second time
475 granularity (older systems may have that) */
476 smb_msleep(3 * msec);
478 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
479 finfo1.all_info.in.file.fnum = fnum1;
482 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
483 pinfo4.all_info.in.file.path = fname;
485 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
487 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
489 torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048,
490 "file size not as expected after write(2048)");
492 torture_comment(tctx, "Initial write time %s\n",
493 nt_time_string(tctx, finfo1.all_info.out.write_time));
495 /* Do a SET_END_OF_FILE_INFO call to truncate. */
496 status = smbcli_ftruncate(cli->tree, fnum1, (uint64_t)10240);
498 torture_assert_ntstatus_ok(tctx, status, "SET_END_OF_FILE failed");
500 start = timeval_current();
501 end = timeval_add(&start, (120*sec), 0);
504 while (!timeval_expired(&end)) {
505 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
507 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
509 torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 10240,
510 "file not truncated to expected size "
513 torture_comment(tctx, "write time %s\n",
514 nt_time_string(tctx, finfo2.all_info.out.write_time));
516 if (finfo1.all_info.out.write_time !=
517 finfo2.all_info.out.write_time)
524 smb_msleep(1 * msec);
528 torture_assert(tctx, updated,
529 "Server did not update write time within 120 seconds");
531 torture_assert(tctx, first, talloc_asprintf(tctx,
532 "Server did not update write time immediately but only "
533 "after %.2f seconds!", timeval_elapsed(&start)));
535 torture_comment(tctx, "Server updated write time immediately. Good!\n");
538 smb_msleep(2 * msec);
540 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
541 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
543 torture_assert_int_equal(tctx, written, 1,
544 "unexpected number of bytes written");
546 start = timeval_current();
547 end = timeval_add(&start, (10*sec), 0);
548 while (!timeval_expired(&end)) {
549 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
551 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
553 torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 10240,
554 "file not truncated to expected size "
557 torture_comment(tctx, "write time %s\n",
558 nt_time_string(tctx, finfo3.all_info.out.write_time));
560 torture_assert_u64_equal(tctx,
561 finfo3.all_info.out.write_time,
562 finfo2.all_info.out.write_time,
563 talloc_asprintf(tctx,
564 "Server updated write time "
565 "after %.2f seconds (wrong!)",
566 timeval_elapsed(&start)));
569 smb_msleep(1 * msec);
572 torture_comment(tctx, "Server did not update write time within 10 "
575 /* the close should trigger an write time update */
576 smbcli_close(cli->tree, fnum1);
579 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
580 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
582 torture_assert_u64_not_equal(tctx,
583 pinfo4.all_info.out.write_time,
584 finfo3.all_info.out.write_time,
585 "Server did not update write time on "
588 pinfo4.all_info.out.write_time > finfo3.all_info.out.write_time,
589 "Server updated write time on close, but to an earlier point "
592 torture_comment(tctx, "Server updated write time on close (correct)\n");
595 smbcli_close(cli->tree, fnum1);
596 smbcli_unlink(cli->tree, fname);
597 smbcli_deltree(cli->tree, BASEDIR);
602 /* Updating with a SET_ALLOCATION_INFO (truncate) does so immediately. */
604 static bool test_delayed_write_update1c(struct torture_context *tctx, struct smbcli_state *cli)
606 union smb_setfileinfo parms;
607 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
608 const char *fname = BASEDIR "\\torture_file1c.txt";
613 struct timeval start;
615 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
616 int normal_delay = 2000000;
617 double sec = ((double)used_delay) / ((double)normal_delay);
618 int msec = 1000 * sec;
623 torture_comment(tctx, "\nRunning test_delayed_write_update1c\n");
625 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
627 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
628 torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
629 "Failed to open %s", fname));
631 memset(buf, 'x', 2048);
632 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
634 /* 3 second delay to ensure we get past any 2 second time
635 granularity (older systems may have that) */
636 smb_msleep(3 * msec);
638 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
639 finfo1.all_info.in.file.fnum = fnum1;
642 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
643 pinfo4.all_info.in.file.path = fname;
645 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
647 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
649 torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048,
650 "file size not as expected after write(2048)");
652 torture_comment(tctx, "Initial write time %s\n",
653 nt_time_string(tctx, finfo1.all_info.out.write_time));
655 /* Do a SET_ALLOCATION_SIZE call to truncate. */
656 parms.allocation_info.level = RAW_SFILEINFO_ALLOCATION_INFO;
657 parms.allocation_info.in.file.fnum = fnum1;
658 parms.allocation_info.in.alloc_size = 0;
660 status = smb_raw_setfileinfo(cli->tree, &parms);
662 torture_assert_ntstatus_ok(tctx, status,
663 "RAW_SFILEINFO_ALLOCATION_INFO failed");
665 start = timeval_current();
666 end = timeval_add(&start, (120*sec), 0);
669 while (!timeval_expired(&end)) {
670 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
672 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
674 torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 0,
675 "file not truncated to expected size "
678 torture_comment(tctx, "write time %s\n",
679 nt_time_string(tctx, finfo2.all_info.out.write_time));
681 if (finfo1.all_info.out.write_time !=
682 finfo2.all_info.out.write_time)
689 smb_msleep(1 * msec);
693 torture_assert(tctx, updated,
694 "Server did not update write time within 120 seconds");
696 torture_assert(tctx, first, talloc_asprintf(tctx,
697 "Server did not update write time immediately but only "
698 "after %.2f seconds!", timeval_elapsed(&start)));
700 torture_comment(tctx, "Server updated write time immediately. Good!\n");
703 smb_msleep(2 * msec);
705 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
706 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
707 torture_assert_int_equal(tctx, written, 1,
708 "Unexpected number of bytes written");
710 start = timeval_current();
711 end = timeval_add(&start, (10*sec), 0);
712 while (!timeval_expired(&end)) {
713 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
715 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
717 torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 1,
718 "file not expaneded");
720 torture_comment(tctx, "write time %s\n",
721 nt_time_string(tctx, finfo3.all_info.out.write_time));
723 torture_assert_u64_equal(tctx,
724 finfo3.all_info.out.write_time,
725 finfo2.all_info.out.write_time,
726 talloc_asprintf(tctx,
727 "Server updated write time "
728 "after %.2f seconds (wrong!)",
729 timeval_elapsed(&start)));
732 smb_msleep(1 * msec);
735 torture_comment(tctx, "Server did not update write time within 10 "
738 /* the close should trigger an write time update */
739 smbcli_close(cli->tree, fnum1);
742 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
743 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
745 torture_assert_u64_not_equal(tctx,
746 pinfo4.all_info.out.write_time,
747 finfo3.all_info.out.write_time,
748 "Server did not update write time on "
751 pinfo4.all_info.out.write_time > finfo3.all_info.out.write_time,
752 "Server updated write time on close, but to an earlier point "
756 smbcli_close(cli->tree, fnum1);
757 smbcli_unlink(cli->tree, fname);
758 smbcli_deltree(cli->tree, BASEDIR);
764 * Do as above, but using 2 connections.
767 static bool test_delayed_write_update2(struct torture_context *tctx, struct smbcli_state *cli,
768 struct smbcli_state *cli2)
770 union smb_fileinfo finfo1, finfo2;
771 const char *fname = BASEDIR "\\torture_file.txt";
777 struct timeval start;
779 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
780 int normal_delay = 2000000;
781 double sec = ((double)used_delay) / ((double)normal_delay);
782 int msec = 1000 * sec;
783 union smb_flush flsh;
785 torture_comment(tctx, "\nRunning test_delayed_write_update2\n");
787 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
789 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
791 torture_comment(tctx, "Failed to open %s\n", fname);
795 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
796 finfo1.basic_info.in.file.fnum = fnum1;
799 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
801 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
803 torture_comment(tctx, "Initial write time %s\n",
804 nt_time_string(tctx, finfo1.basic_info.out.write_time));
806 /* 3 second delay to ensure we get past any 2 second time
807 granularity (older systems may have that) */
808 smb_msleep(3 * msec);
811 /* Try using setfileinfo instead of write to update write time. */
812 union smb_setfileinfo sfinfo;
813 time_t t_set = time(NULL);
814 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
815 sfinfo.basic_info.in.file.fnum = fnum1;
816 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
817 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
819 /* I tried this with both + and - ve to see if it makes a different.
820 It doesn't - once the filetime is set via setfileinfo it stays that way. */
822 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
824 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
826 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
827 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
829 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
831 torture_assert_ntstatus_ok(tctx, status, "sfileinfo failed");
834 finfo2.basic_info.in.file.path = fname;
836 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
838 if (!NT_STATUS_IS_OK(status)) {
839 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
842 torture_comment(tctx, "write time %s\n",
843 nt_time_string(tctx, finfo2.basic_info.out.write_time));
845 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
846 torture_comment(tctx, "Server updated write_time (correct)\n");
848 torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n");
852 /* Now try a write to see if the write time gets reset. */
854 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
855 finfo1.basic_info.in.file.fnum = fnum1;
858 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
860 if (!NT_STATUS_IS_OK(status)) {
861 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
865 torture_comment(tctx, "Modified write time %s\n",
866 nt_time_string(tctx, finfo1.basic_info.out.write_time));
869 torture_comment(tctx, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
871 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
874 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n",
875 (int)written, __location__);
879 /* Just to prove to tridge that the an smbflush has no effect on
880 the write time :-). The setfileinfo IS STICKY. JRA. */
882 torture_comment(tctx, "Doing flush after write\n");
884 flsh.flush.level = RAW_FLUSH_FLUSH;
885 flsh.flush.in.file.fnum = fnum1;
886 status = smb_raw_flush(cli->tree, &flsh);
887 if (!NT_STATUS_IS_OK(status)) {
888 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
892 /* Once the time was set using setfileinfo then it stays set - writes
893 don't have any effect. But make sure. */
894 start = timeval_current();
895 end = timeval_add(&start, (15*sec), 0);
896 while (!timeval_expired(&end)) {
897 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
899 if (!NT_STATUS_IS_OK(status)) {
900 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
904 torture_comment(tctx, "write time %s\n",
905 nt_time_string(tctx, finfo2.basic_info.out.write_time));
906 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
907 double diff = timeval_elapsed(&start);
908 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
915 smb_msleep(1 * msec);
918 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
919 torture_comment(tctx, "Server did not update write time (correct)\n");
923 smb_msleep(2 * msec);
925 fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
927 torture_result(tctx, TORTURE_FAIL, "Failed to open %s\n", fname);
931 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");
933 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
936 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n",
937 (int)written, __location__);
941 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
943 if (!NT_STATUS_IS_OK(status)) {
944 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
947 torture_comment(tctx, "write time %s\n",
948 nt_time_string(tctx, finfo2.basic_info.out.write_time));
949 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
950 torture_result(tctx, TORTURE_FAIL, "Server updated write_time (wrong!)\n");
954 torture_comment(tctx, "Closing the first fd to see if write time updated.\n");
955 smbcli_close(cli->tree, fnum1);
958 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");
960 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
963 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n",
964 (int)written, __location__);
968 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
969 finfo1.basic_info.in.file.fnum = fnum2;
971 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
973 if (!NT_STATUS_IS_OK(status)) {
974 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
977 torture_comment(tctx, "write time %s\n",
978 nt_time_string(tctx, finfo2.basic_info.out.write_time));
979 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
980 torture_result(tctx, TORTURE_FAIL, "Server updated write_time (wrong!)\n");
984 /* Once the time was set using setfileinfo then it stays set - writes
985 don't have any effect. But make sure. */
986 start = timeval_current();
987 end = timeval_add(&start, (15*sec), 0);
988 while (!timeval_expired(&end)) {
989 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
991 if (!NT_STATUS_IS_OK(status)) {
992 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
996 torture_comment(tctx, "write time %s\n",
997 nt_time_string(tctx, finfo2.basic_info.out.write_time));
998 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
999 double diff = timeval_elapsed(&start);
1000 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1007 smb_msleep(1 * msec);
1010 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1011 torture_comment(tctx, "Server did not update write time (correct)\n");
1014 torture_comment(tctx, "Closing second fd to see if write time updated.\n");
1016 smbcli_close(cli->tree, fnum2);
1019 fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
1021 torture_comment(tctx, "Failed to open %s\n", fname);
1025 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1026 finfo1.basic_info.in.file.fnum = fnum1;
1029 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1031 if (!NT_STATUS_IS_OK(status)) {
1032 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1036 torture_comment(tctx, "Second open initial write time %s\n",
1037 nt_time_string(tctx, finfo1.basic_info.out.write_time));
1039 smb_msleep(10 * msec);
1040 torture_comment(tctx, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
1042 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
1044 if (written != 10) {
1045 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n",
1046 (int)written, __location__);
1050 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1051 finfo1.basic_info.in.file.fnum = fnum1;
1053 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1055 if (!NT_STATUS_IS_OK(status)) {
1056 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1059 torture_comment(tctx, "write time %s\n",
1060 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1061 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1062 torture_result(tctx, TORTURE_FAIL, "Server updated write_time (wrong!)\n");
1066 /* Now the write time should be updated again */
1067 start = timeval_current();
1068 end = timeval_add(&start, (15*sec), 0);
1069 while (!timeval_expired(&end)) {
1070 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1072 if (!NT_STATUS_IS_OK(status)) {
1073 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1077 torture_comment(tctx, "write time %s\n",
1078 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1079 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1080 double diff = timeval_elapsed(&start);
1081 if (diff < (used_delay / (double)1000000)) {
1082 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
1083 "(expected > %.2f) (wrong!)\n",
1084 diff, used_delay / (double)1000000);
1089 torture_comment(tctx, "Server updated write_time after %.2f seconds"
1098 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1099 torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n");
1104 /* One more test to do. We should read the filetime via findfirst on the
1105 second connection to ensure it's the same. This is very easy for a Windows
1106 server but a bastard to get right on a POSIX server. JRA. */
1109 smbcli_close(cli->tree, fnum1);
1110 smbcli_unlink(cli->tree, fname);
1111 smbcli_deltree(cli->tree, BASEDIR);
1117 /* Windows does obviously not update the stat info during a write call. I
1118 * *think* this is the problem causing a spurious Excel 2003 on XP error
1119 * message when saving a file. Excel does a setfileinfo, writes, and then does
1120 * a getpath(!)info. Or so... For Samba sometimes it displays an error message
1121 * that the file might have been changed in between. What i've been able to
1122 * trace down is that this happens if the getpathinfo after the write shows a
1123 * different last write time than the setfileinfo showed. This is really
1127 static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli,
1128 struct smbcli_state *cli2)
1130 union smb_fileinfo finfo1, finfo2;
1131 const char *fname = BASEDIR "\\torture_file.txt";
1137 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1138 int normal_delay = 2000000;
1139 double sec = ((double)used_delay) / ((double)normal_delay);
1140 int msec = 1000 * sec;
1142 torture_comment(tctx, "\nRunning test_finfo_after_write\n");
1144 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1146 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1149 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1153 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1154 finfo1.basic_info.in.file.fnum = fnum1;
1156 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1158 if (!NT_STATUS_IS_OK(status)) {
1160 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1164 smb_msleep(1 * msec);
1166 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1169 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1174 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
1176 torture_result(tctx, TORTURE_FAIL, __location__": failed to open 2nd time - %s",
1177 smbcli_errstr(cli2->tree));
1182 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
1185 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1",
1191 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1192 finfo2.basic_info.in.file.path = fname;
1194 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
1196 if (!NT_STATUS_IS_OK(status)) {
1197 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s",
1203 if (finfo1.basic_info.out.create_time !=
1204 finfo2.basic_info.out.create_time) {
1205 torture_result(tctx, TORTURE_FAIL, __location__": create_time changed");
1210 if (finfo1.basic_info.out.access_time !=
1211 finfo2.basic_info.out.access_time) {
1212 torture_result(tctx, TORTURE_FAIL, __location__": access_time changed");
1217 if (finfo1.basic_info.out.write_time !=
1218 finfo2.basic_info.out.write_time) {
1219 torture_result(tctx, TORTURE_FAIL, __location__": write_time changed:\n"
1220 "write time conn 1 = %s, conn 2 = %s",
1221 nt_time_string(tctx, finfo1.basic_info.out.write_time),
1222 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1227 if (finfo1.basic_info.out.change_time !=
1228 finfo2.basic_info.out.change_time) {
1229 torture_result(tctx, TORTURE_FAIL, __location__": change_time changed");
1234 /* One of the two following calls updates the qpathinfo. */
1236 /* If you had skipped the smbcli_write on fnum2, it would
1237 * *not* have updated the stat on disk */
1239 smbcli_close(cli2->tree, fnum2);
1242 /* This call is only for the people looking at ethereal :-) */
1243 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1244 finfo2.basic_info.in.file.path = fname;
1246 status = smb_raw_pathinfo(cli->tree, tctx, &finfo2);
1248 if (!NT_STATUS_IS_OK(status)) {
1249 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1256 smbcli_close(cli->tree, fnum1);
1257 smbcli_unlink(cli->tree, fname);
1258 smbcli_deltree(cli->tree, BASEDIR);
1263 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
1264 uint64_t r = 10*1000*1000; \
1265 NTTIME g = (given).basic_info.out.write_time; \
1266 NTTIME gr = (g / r) * r; \
1267 NTTIME c = (correct).basic_info.out.write_time; \
1268 NTTIME cr = (c / r) * r; \
1269 bool strict = torture_setting_bool(tctx, "strict mode", false); \
1271 if (strict && (g cmp c)) { \
1273 } else if ((g cmp c) && (gr cmp cr)) { \
1274 /* handle filesystem without high resolution timestamps */ \
1278 torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \
1279 #given, nt_time_string(tctx, g), (unsigned long long)g, \
1280 #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \
1285 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
1286 COMPARE_WRITE_TIME_CMP(given,correct,!=)
1287 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
1288 COMPARE_WRITE_TIME_CMP(given,correct,<=)
1289 #define COMPARE_WRITE_TIME_LESS(given,correct) \
1290 COMPARE_WRITE_TIME_CMP(given,correct,>=)
1292 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
1293 NTTIME g = (given).basic_info.out.access_time; \
1294 NTTIME c = (correct).basic_info.out.access_time; \
1296 torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \
1297 #given, nt_time_string(tctx, g), \
1298 #cmp, #correct, nt_time_string(tctx, c)); \
1303 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
1304 COMPARE_ACCESS_TIME_CMP(given,correct,!=)
1306 #define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \
1307 COMPARE_ACCESS_TIME_EQUAL(given,correct); \
1308 COMPARE_WRITE_TIME_EQUAL(given,correct); \
1311 #define _DEBUG_BASIC_INFO(finfo, comment) do { \
1312 struct timeval atv; \
1313 struct timeval wtv; \
1314 struct timeval_buf atvb; \
1315 struct timeval_buf wtvb; \
1316 nttime_to_timeval(&atv, finfo.basic_info.out.access_time); \
1317 nttime_to_timeval(&wtv, finfo.basic_info.out.write_time); \
1318 torture_comment(tctx, "%s: Access(%s) Write(%s)\n", \
1320 timeval_str_buf(&atv, false, true, &atvb), \
1321 timeval_str_buf(&wtv, false, true, &wtvb)); \
1323 #define _GET_INFO_FILE(tree, finfo) do { \
1325 _status = smb_raw_fileinfo(tree, tctx, &finfo); \
1326 if (!NT_STATUS_IS_OK(_status)) { \
1328 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1329 nt_errstr(_status)); \
1332 _DEBUG_BASIC_INFO(finfo, "fileinfo(" #tree ")"); \
1334 #define _GET_INFO_PATH(tree, pinfo) do { \
1336 _status = smb_raw_pathinfo(tree, tctx, &pinfo); \
1337 if (!NT_STATUS_IS_OK(_status)) { \
1338 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \
1339 nt_errstr(_status)); \
1343 _DEBUG_BASIC_INFO(pinfo, "pathinfo(" #tree ")"); \
1345 #define GET_INFO_FILE(finfo) do { \
1346 _GET_INFO_FILE(cli->tree, finfo); \
1348 #define GET_INFO_FILE2(finfo) do { \
1349 _GET_INFO_FILE(cli2->tree, finfo); \
1351 #define GET_INFO_PATH(pinfo) do { \
1352 _GET_INFO_PATH(cli2->tree, pinfo); \
1354 #define GET_INFO_BOTH(finfo,pinfo) do { \
1355 GET_INFO_FILE(finfo); \
1356 GET_INFO_PATH(pinfo); \
1357 COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \
1360 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
1362 union smb_setfileinfo sfinfo; \
1363 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1364 sfinfo.basic_info.in.file.fnum = tfnum; \
1365 sfinfo.basic_info.in.create_time = 0; \
1366 sfinfo.basic_info.in.access_time = 0; \
1367 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1368 sfinfo.basic_info.in.change_time = 0; \
1369 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1370 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1371 if (!NT_STATUS_IS_OK(_status)) { \
1372 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1373 nt_errstr(_status)); \
1378 #define SET_INFO_FILE(finfo, wrtime) \
1379 SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
1381 #define SET_INFO_FILE_NS(finfo, wrtime, ns, tree, tfnum) do { \
1383 union smb_setfileinfo sfinfo; \
1384 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1385 sfinfo.basic_info.in.file.fnum = tfnum; \
1386 sfinfo.basic_info.in.create_time = 0; \
1387 sfinfo.basic_info.in.access_time = 0; \
1388 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1389 sfinfo.basic_info.in.write_time += (ns); \
1390 sfinfo.basic_info.in.change_time = 0; \
1391 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1392 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1393 if (!NT_STATUS_IS_OK(_status)) { \
1394 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1395 nt_errstr(_status)); \
1401 static bool test_delayed_write_update3(struct torture_context *tctx,
1402 struct smbcli_state *cli,
1403 struct smbcli_state *cli2)
1405 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1406 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1407 const char *fname = BASEDIR "\\torture_file3.txt";
1411 struct timeval start;
1413 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1414 //double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 1000000);
1415 //int normal_delay = 1000000;
1416 int normal_delay = 2000000;
1417 double sec = ((double)used_delay) / ((double)normal_delay);
1418 int msec = 1000 * sec;
1420 torture_comment(tctx, "\nRunning test_delayed_write_update3\n");
1422 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1424 torture_comment(tctx, "Open the file handle\n");
1425 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1428 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1432 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1433 finfo0.basic_info.in.file.fnum = fnum1;
1437 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1438 pinfo0.basic_info.in.file.path = fname;
1444 /* get the initial times */
1445 GET_INFO_BOTH(finfo0,pinfo0);
1448 * make sure the write time is updated 2 seconds later
1449 * calcuated from the first write
1450 * (but expect upto 5 seconds extra time for a busy server)
1452 start = timeval_current();
1453 end = timeval_add(&start, 7 * sec, 0);
1454 while (!timeval_expired(&end)) {
1456 torture_comment(tctx, "Do a write on the file handle\n");
1457 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1459 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1463 /* get the times after the write */
1464 GET_INFO_FILE(finfo1);
1466 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1467 double diff = timeval_elapsed(&start);
1468 if (diff < (used_delay / (double)1000000)) {
1469 torture_result(tctx, TORTURE_FAIL, "111Server updated write_time after %.2f seconds "
1470 "(write time update delay == %.2f) (wrong!)\n",
1471 diff, used_delay / (double)1000000);
1476 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1481 smb_msleep(0.5 * msec);
1484 GET_INFO_BOTH(finfo1,pinfo1);
1485 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1487 /* sure any further write doesn't update the write time */
1488 start = timeval_current();
1489 end = timeval_add(&start, 15 * sec, 0);
1490 while (!timeval_expired(&end)) {
1492 torture_comment(tctx, "Do a write on the file handle\n");
1493 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1495 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1499 /* get the times after the write */
1500 GET_INFO_BOTH(finfo2,pinfo2);
1502 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1503 double diff = timeval_elapsed(&start);
1504 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1510 smb_msleep(1 * msec);
1513 GET_INFO_BOTH(finfo2,pinfo2);
1514 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1515 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1516 torture_comment(tctx, "Server did not update write_time (correct)\n");
1520 smb_msleep(5 * msec);
1522 GET_INFO_BOTH(finfo3,pinfo3);
1523 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1526 * the close updates the write time to the time of the close
1527 * and not to the time of the last write!
1529 torture_comment(tctx, "Close the file handle\n");
1530 smbcli_close(cli->tree, fnum1);
1533 GET_INFO_PATH(pinfo4);
1534 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1536 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1537 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1542 smbcli_close(cli->tree, fnum1);
1543 smbcli_unlink(cli->tree, fname);
1544 smbcli_deltree(cli->tree, BASEDIR);
1550 * Show that a truncate write always updates the write time even
1551 * if an initial write has already updated the write time.
1554 static bool test_delayed_write_update3a(struct torture_context *tctx,
1555 struct smbcli_state *cli,
1556 struct smbcli_state *cli2)
1558 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1559 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1560 const char *fname = BASEDIR "\\torture_file3a.txt";
1565 struct timeval start;
1567 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1568 int normal_delay = 2000000;
1569 double sec = ((double)used_delay) / ((double)normal_delay);
1570 int msec = 1000 * sec;
1572 torture_comment(tctx, "\nRunning test_delayed_write_update3a\n");
1574 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1576 torture_comment(tctx, "Open the file handle\n");
1577 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1580 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1584 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1585 finfo0.basic_info.in.file.fnum = fnum1;
1589 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1590 pinfo0.basic_info.in.file.path = fname;
1596 /* get the initial times */
1597 GET_INFO_BOTH(finfo0,pinfo0);
1600 * sleep some time, to demonstrate the handling of write times
1601 * doesn't depend on the time since the open
1603 smb_msleep(5 * msec);
1605 /* get the initial times */
1606 GET_INFO_BOTH(finfo1,pinfo1);
1607 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1610 * make sure the write time is updated 2 seconds later
1611 * calcuated from the first write
1612 * (but expect upto 5 seconds extra time for a busy server)
1614 start = timeval_current();
1615 end = timeval_add(&start, 7 * sec, 0);
1616 while (!timeval_expired(&end)) {
1618 torture_comment(tctx, "Do a write on the file handle\n");
1619 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1621 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1625 /* get the times after the write */
1626 GET_INFO_FILE(finfo1);
1628 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1629 double diff = timeval_elapsed(&start);
1630 if (diff < (used_delay / (double)1000000)) {
1631 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1632 "(1sec == %.2f) (wrong!)\n",
1638 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1643 smb_msleep(0.5 * msec);
1646 GET_INFO_BOTH(finfo1,pinfo1);
1647 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1649 smb_msleep(3 * msec);
1652 * demonstrate that a truncate write always
1653 * updates the write time immediately
1655 for (i=0; i < 3; i++) {
1656 smb_msleep(2 * msec);
1658 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1659 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
1661 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1665 /* get the times after the write */
1666 GET_INFO_BOTH(finfo2,pinfo2);
1667 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1671 smb_msleep(3 * msec);
1673 /* sure any further write doesn't update the write time */
1674 start = timeval_current();
1675 end = timeval_add(&start, 15 * sec, 0);
1676 while (!timeval_expired(&end)) {
1678 torture_comment(tctx, "Do a write on the file handle\n");
1679 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1681 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1685 /* get the times after the write */
1686 GET_INFO_BOTH(finfo2,pinfo2);
1688 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1689 double diff = timeval_elapsed(&start);
1690 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1696 smb_msleep(1 * msec);
1699 GET_INFO_BOTH(finfo2,pinfo2);
1700 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1701 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1702 torture_comment(tctx, "Server did not update write_time (correct)\n");
1706 smb_msleep(3 * msec);
1708 /* get the initial times */
1709 GET_INFO_BOTH(finfo1,pinfo1);
1710 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
1713 * demonstrate that a truncate write always
1714 * updates the write time immediately
1716 for (i=0; i < 3; i++) {
1717 smb_msleep(2 * msec);
1719 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1720 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
1722 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1726 /* get the times after the write */
1727 GET_INFO_BOTH(finfo2,pinfo2);
1728 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1733 smb_msleep(3 * msec);
1735 GET_INFO_BOTH(finfo3,pinfo3);
1736 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1739 * the close doesn't update the write time
1741 torture_comment(tctx, "Close the file handle\n");
1742 smbcli_close(cli->tree, fnum1);
1745 GET_INFO_PATH(pinfo4);
1746 COMPARE_WRITE_TIME_EQUAL(pinfo4, pinfo3);
1748 if (pinfo4.basic_info.out.write_time == pinfo3.basic_info.out.write_time) {
1749 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1754 smbcli_close(cli->tree, fnum1);
1755 smbcli_unlink(cli->tree, fname);
1756 smbcli_deltree(cli->tree, BASEDIR);
1762 * Show a close after write updates the write timestamp to
1763 * the close time, not the last write time.
1766 static bool test_delayed_write_update3b(struct torture_context *tctx,
1767 struct smbcli_state *cli,
1768 struct smbcli_state *cli2)
1770 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1771 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1772 const char *fname = BASEDIR "\\torture_file3b.txt";
1776 struct timeval start;
1778 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1779 int normal_delay = 2000000;
1780 double sec = ((double)used_delay) / ((double)normal_delay);
1781 int msec = 1000 * sec;
1783 torture_comment(tctx, "\nRunning test_delayed_write_update3b\n");
1785 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1787 torture_comment(tctx, "Open the file handle\n");
1788 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1791 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1795 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1796 finfo0.basic_info.in.file.fnum = fnum1;
1800 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1801 pinfo0.basic_info.in.file.path = fname;
1807 /* get the initial times */
1808 GET_INFO_BOTH(finfo0,pinfo0);
1811 * sleep some time, to demonstrate the handling of write times
1812 * doesn't depend on the time since the open
1814 smb_msleep(5 * msec);
1816 /* get the initial times */
1817 GET_INFO_BOTH(finfo1,pinfo1);
1818 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1821 * make sure the write time is updated 2 seconds later
1822 * calcuated from the first write
1823 * (but expect upto 5 seconds extra time for a busy server)
1825 start = timeval_current();
1826 end = timeval_add(&start, 7 * sec, 0);
1827 while (!timeval_expired(&end)) {
1829 torture_comment(tctx, "Do a write on the file handle\n");
1830 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1832 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1836 /* get the times after the write */
1837 GET_INFO_FILE(finfo1);
1839 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1840 double diff = timeval_elapsed(&start);
1841 if (diff < (used_delay / (double)1000000)) {
1842 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
1843 "(expected > %.2f) (wrong!)\n",
1844 diff, used_delay / (double)1000000);
1849 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1850 "(write time update delay == %.2f) (correct)\n",
1851 diff, used_delay / (double)1000000);
1854 smb_msleep(0.5 * msec);
1857 GET_INFO_BOTH(finfo1,pinfo1);
1858 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1860 /* sure any further write doesn't update the write time */
1861 start = timeval_current();
1862 end = timeval_add(&start, 15 * sec, 0);
1863 while (!timeval_expired(&end)) {
1865 torture_comment(tctx, "Do a write on the file handle\n");
1866 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1868 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1872 /* get the times after the write */
1873 GET_INFO_BOTH(finfo2,pinfo2);
1875 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1876 double diff = timeval_elapsed(&start);
1877 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1883 smb_msleep(1 * msec);
1886 GET_INFO_BOTH(finfo2,pinfo2);
1887 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1888 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1889 torture_comment(tctx, "Server did not update write_time (correct)\n");
1893 smb_msleep(5 * msec);
1895 GET_INFO_BOTH(finfo3,pinfo3);
1896 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1899 * the close updates the write time to the time of the close
1900 * and not to the time of the last write!
1902 torture_comment(tctx, "Close the file handle\n");
1903 smbcli_close(cli->tree, fnum1);
1906 GET_INFO_PATH(pinfo4);
1907 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1909 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1910 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1915 smbcli_close(cli->tree, fnum1);
1916 smbcli_unlink(cli->tree, fname);
1917 smbcli_deltree(cli->tree, BASEDIR);
1923 * Check that a write after a truncate write doesn't update
1924 * the timestamp, but a truncate write after a write does.
1925 * Also prove that a close after a truncate write updates the
1926 * timestamp to current, not the time of last write.
1929 static bool test_delayed_write_update3c(struct torture_context *tctx,
1930 struct smbcli_state *cli,
1931 struct smbcli_state *cli2)
1933 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1934 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1935 const char *fname = BASEDIR "\\torture_file3c.txt";
1940 struct timeval start;
1942 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1943 int normal_delay = 2000000;
1944 double sec = ((double)used_delay) / ((double)normal_delay);
1945 int msec = 1000 * sec;
1947 torture_comment(tctx, "\nRunning test_delayed_write_update3c\n");
1949 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1951 torture_comment(tctx, "Open the file handle\n");
1952 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1955 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1959 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1960 finfo0.basic_info.in.file.fnum = fnum1;
1964 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1965 pinfo0.basic_info.in.file.path = fname;
1971 /* get the initial times */
1972 GET_INFO_BOTH(finfo0,pinfo0);
1975 * sleep some time, to demonstrate the handling of write times
1976 * doesn't depend on the time since the open
1978 smb_msleep(5 * msec);
1980 /* get the initial times */
1981 GET_INFO_BOTH(finfo1,pinfo1);
1982 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1985 * demonstrate that a truncate write always
1986 * updates the write time immediately
1988 for (i=0; i < 3; i++) {
1989 smb_msleep(2 * msec);
1991 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1992 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
1994 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1998 /* get the times after the write */
1999 GET_INFO_BOTH(finfo2,pinfo2);
2000 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2004 start = timeval_current();
2005 end = timeval_add(&start, 7 * sec, 0);
2006 while (!timeval_expired(&end)) {
2008 torture_comment(tctx, "Do a write on the file handle\n");
2009 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2011 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2015 /* get the times after the write */
2016 GET_INFO_FILE(finfo2);
2018 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2019 double diff = timeval_elapsed(&start);
2020 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2026 smb_msleep(1 * msec);
2029 GET_INFO_BOTH(finfo2,pinfo2);
2030 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2031 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2032 torture_comment(tctx, "Server did not update write_time (correct)\n");
2036 smb_msleep(5 * msec);
2038 /* get the initial times */
2039 GET_INFO_BOTH(finfo1,pinfo1);
2040 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
2043 * demonstrate that a truncate write always
2044 * updates the write time immediately
2046 for (i=0; i < 3; i++) {
2047 smb_msleep(2 * msec);
2049 torture_comment(tctx, "Do a truncate write [%d] on the file handle\n", i);
2050 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2052 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2056 /* get the times after the write */
2057 GET_INFO_BOTH(finfo2,pinfo2);
2058 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2063 smb_msleep(5 * msec);
2065 GET_INFO_BOTH(finfo2,pinfo2);
2066 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2068 /* sure any further write doesn't update the write time */
2069 start = timeval_current();
2070 end = timeval_add(&start, 15 * sec, 0);
2071 while (!timeval_expired(&end)) {
2073 torture_comment(tctx, "Do a write on the file handle\n");
2074 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2076 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2080 /* get the times after the write */
2081 GET_INFO_BOTH(finfo2,pinfo2);
2083 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2084 double diff = timeval_elapsed(&start);
2085 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2091 smb_msleep(1 * msec);
2094 GET_INFO_BOTH(finfo2,pinfo2);
2095 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2096 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2097 torture_comment(tctx, "Server did not update write_time (correct)\n");
2101 smb_msleep(5 * msec);
2103 GET_INFO_BOTH(finfo3,pinfo3);
2104 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2107 * the close updates the write time to the time of the close
2108 * and not to the time of the last write!
2110 torture_comment(tctx, "Close the file handle\n");
2111 smbcli_close(cli->tree, fnum1);
2114 GET_INFO_PATH(pinfo4);
2115 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2117 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2118 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2123 smbcli_close(cli->tree, fnum1);
2124 smbcli_unlink(cli->tree, fname);
2125 smbcli_deltree(cli->tree, BASEDIR);
2131 * Show only the first write updates the timestamp, and a close
2132 * after writes updates to current (I think this is the same
2136 static bool test_delayed_write_update4(struct torture_context *tctx,
2137 struct smbcli_state *cli,
2138 struct smbcli_state *cli2)
2140 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
2141 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
2142 const char *fname = BASEDIR "\\torture_file4.txt";
2146 struct timeval start;
2148 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2149 int normal_delay = 2000000;
2150 double sec = ((double)used_delay) / ((double)normal_delay);
2151 int msec = 1000 * sec;
2153 torture_comment(tctx, "\nRunning test_delayed_write_update4\n");
2155 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2157 torture_comment(tctx, "Open the file handle\n");
2158 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2161 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2165 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2166 finfo0.basic_info.in.file.fnum = fnum1;
2170 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2171 pinfo0.basic_info.in.file.path = fname;
2177 /* get the initial times */
2178 GET_INFO_BOTH(finfo0,pinfo0);
2181 smb_msleep(5 * msec);
2184 torture_comment(tctx, "Do a write on the file handle\n");
2185 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2187 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2192 GET_INFO_BOTH(finfo1,pinfo1);
2193 COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
2196 * make sure the write time is updated 2 seconds later
2197 * calcuated from the first write
2198 * (but expect upto 3 seconds extra time for a busy server)
2200 start = timeval_current();
2201 end = timeval_add(&start, 5 * sec, 0);
2202 while (!timeval_expired(&end)) {
2203 /* get the times after the first write */
2204 GET_INFO_FILE(finfo1);
2206 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
2207 double diff = timeval_elapsed(&start);
2208 if (diff < (used_delay / (double)1000000)) {
2209 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
2210 "(expected > %.2f) (wrong!)\n",
2211 diff, used_delay / (double)1000000);
2216 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2217 "(write time update delay == %.2f) (correct)\n",
2218 diff, used_delay / (double)1000000);
2221 smb_msleep(0.5 * msec);
2224 GET_INFO_BOTH(finfo1,pinfo1);
2225 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
2227 /* sure any further write doesn't update the write time */
2228 start = timeval_current();
2229 end = timeval_add(&start, 15 * sec, 0);
2230 while (!timeval_expired(&end)) {
2232 torture_comment(tctx, "Do a write on the file handle\n");
2233 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2235 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2239 /* get the times after the write */
2240 GET_INFO_BOTH(finfo2,pinfo2);
2242 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2243 double diff = timeval_elapsed(&start);
2244 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2250 smb_msleep(1 * msec);
2253 GET_INFO_BOTH(finfo2,pinfo2);
2254 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2255 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2256 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
2260 smb_msleep(5 * msec);
2262 GET_INFO_BOTH(finfo3,pinfo3);
2263 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2266 * the close updates the write time to the time of the close
2267 * and not to the time of the last write!
2269 torture_comment(tctx, "Close the file handle\n");
2270 smbcli_close(cli->tree, fnum1);
2273 GET_INFO_PATH(pinfo4);
2274 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2276 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2277 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2282 smbcli_close(cli->tree, fnum1);
2283 smbcli_unlink(cli->tree, fname);
2284 smbcli_deltree(cli->tree, BASEDIR);
2290 * Show writes and closes have no effect on updating times once a SETWRITETIME is done.
2293 static bool test_delayed_write_update5(struct torture_context *tctx,
2294 struct smbcli_state *cli,
2295 struct smbcli_state *cli2)
2297 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2298 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2299 const char *fname = BASEDIR "\\torture_file5.txt";
2303 struct timeval start;
2305 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2306 int normal_delay = 2000000;
2307 double sec = ((double)used_delay) / ((double)normal_delay);
2308 int msec = 1000 * sec;
2310 torture_comment(tctx, "\nRunning test_delayed_write_update5\n");
2312 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2314 torture_comment(tctx, "Open the file handle\n");
2315 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2318 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2322 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2323 finfo0.basic_info.in.file.fnum = fnum1;
2329 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2330 pinfo0.basic_info.in.file.path = fname;
2338 /* get the initial times */
2339 GET_INFO_BOTH(finfo0,pinfo0);
2342 torture_comment(tctx, "Do a write on the file handle\n");
2343 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2345 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2350 GET_INFO_BOTH(finfo1,pinfo1);
2351 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2353 torture_comment(tctx, "Set write time in the future on the file handle\n");
2354 SET_INFO_FILE(finfo0, time(NULL) + 86400);
2355 GET_INFO_BOTH(finfo2,pinfo2);
2356 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2358 torture_comment(tctx, "Set write time in the past on the file handle\n");
2359 SET_INFO_FILE(finfo0, time(NULL) - 86400);
2360 GET_INFO_BOTH(finfo2,pinfo2);
2361 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2363 /* make sure the 2 second delay from the first write are canceled */
2364 start = timeval_current();
2365 end = timeval_add(&start, 15 * sec, 0);
2366 while (!timeval_expired(&end)) {
2368 /* get the times after the first write */
2369 GET_INFO_BOTH(finfo3,pinfo3);
2371 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2372 double diff = timeval_elapsed(&start);
2373 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2379 smb_msleep(1 * msec);
2382 GET_INFO_BOTH(finfo3,pinfo3);
2383 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2384 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2385 torture_comment(tctx, "Server did not update write_time (correct)\n");
2388 /* sure any further write doesn't update the write time */
2389 start = timeval_current();
2390 end = timeval_add(&start, 15 * sec, 0);
2391 while (!timeval_expired(&end)) {
2393 torture_comment(tctx, "Do a write on the file handle\n");
2394 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2396 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2400 /* get the times after the write */
2401 GET_INFO_BOTH(finfo4,pinfo4);
2403 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2404 double diff = timeval_elapsed(&start);
2405 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2411 smb_msleep(1 * msec);
2414 GET_INFO_BOTH(finfo4,pinfo4);
2415 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2416 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2417 torture_comment(tctx, "Server did not update write_time (correct)\n");
2421 smb_msleep(5 * msec);
2423 GET_INFO_BOTH(finfo5,pinfo5);
2424 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2427 * the close doesn't update the write time
2429 torture_comment(tctx, "Close the file handle\n");
2430 smbcli_close(cli->tree, fnum1);
2433 GET_INFO_PATH(pinfo6);
2434 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2436 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2437 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2442 smbcli_close(cli->tree, fnum1);
2443 smbcli_unlink(cli->tree, fname);
2444 smbcli_deltree(cli->tree, BASEDIR);
2450 * Show truncate writes and closes have no effect on updating times once a SETWRITETIME is done.
2453 static bool test_delayed_write_update5b(struct torture_context *tctx,
2454 struct smbcli_state *cli,
2455 struct smbcli_state *cli2)
2457 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2458 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2459 const char *fname = BASEDIR "\\torture_fileb.txt";
2463 struct timeval start;
2465 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2466 int normal_delay = 2000000;
2467 double sec = ((double)used_delay) / ((double)normal_delay);
2468 int msec = 1000 * sec;
2470 torture_comment(tctx, "\nRunning test_delayed_write_update5b\n");
2472 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2474 torture_comment(tctx, "Open the file handle\n");
2475 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2478 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2482 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2483 finfo0.basic_info.in.file.fnum = fnum1;
2489 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2490 pinfo0.basic_info.in.file.path = fname;
2498 /* get the initial times */
2499 GET_INFO_BOTH(finfo0,pinfo0);
2502 torture_comment(tctx, "Do a write on the file handle\n");
2503 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2505 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2510 GET_INFO_BOTH(finfo1,pinfo1);
2511 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2513 torture_comment(tctx, "Set write time in the future on the file handle\n");
2514 SET_INFO_FILE(finfo0, time(NULL) + 86400);
2515 GET_INFO_BOTH(finfo2,pinfo2);
2516 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2518 torture_comment(tctx, "Set write time in the past on the file handle\n");
2519 SET_INFO_FILE(finfo0, time(NULL) - 86400);
2520 GET_INFO_BOTH(finfo2,pinfo2);
2521 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2523 /* make sure the 2 second delay from the first write are canceled */
2524 start = timeval_current();
2525 end = timeval_add(&start, 15 * sec, 0);
2526 while (!timeval_expired(&end)) {
2528 /* get the times after the first write */
2529 GET_INFO_BOTH(finfo3,pinfo3);
2531 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2532 double diff = timeval_elapsed(&start);
2533 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2539 smb_msleep(1 * msec);
2542 GET_INFO_BOTH(finfo3,pinfo3);
2543 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2544 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2545 torture_comment(tctx, "Server did not update write_time (correct)\n");
2548 /* Do any further write (truncates) update the write time ? */
2549 start = timeval_current();
2550 end = timeval_add(&start, 15 * sec, 0);
2551 while (!timeval_expired(&end)) {
2553 torture_comment(tctx, "Do a truncate write on the file handle\n");
2554 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
2556 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2560 /* get the times after the write */
2561 GET_INFO_BOTH(finfo4,pinfo4);
2563 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2564 double diff = timeval_elapsed(&start);
2565 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2571 smb_msleep(1 * msec);
2574 GET_INFO_BOTH(finfo4,pinfo4);
2575 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2576 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2577 torture_comment(tctx, "Server did not update write_time (correct)\n");
2581 smb_msleep(5 * msec);
2583 GET_INFO_BOTH(finfo5,pinfo5);
2584 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2587 * the close doesn't update the write time
2589 torture_comment(tctx, "Close the file handle\n");
2590 smbcli_close(cli->tree, fnum1);
2593 GET_INFO_PATH(pinfo6);
2594 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2596 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2597 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2602 smbcli_close(cli->tree, fnum1);
2603 smbcli_unlink(cli->tree, fname);
2604 smbcli_deltree(cli->tree, BASEDIR);
2610 * Open 2 handles on a file. Write one one and then set the
2611 * WRITE TIME explicitly on the other. Ensure the write time
2612 * update is cancelled. Ensure the write time is updated to
2613 * the close time when the non-explicit set handle is closed.
2617 static bool test_delayed_write_update6(struct torture_context *tctx,
2618 struct smbcli_state *cli,
2619 struct smbcli_state *cli2)
2621 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2622 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
2623 const char *fname = BASEDIR "\\torture_file6.txt";
2628 struct timeval start;
2630 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2631 int normal_delay = 2000000;
2632 double sec = ((double)used_delay) / ((double)normal_delay);
2633 int msec = 1000 * sec;
2636 torture_comment(tctx, "\nRunning test_delayed_write_update6\n");
2638 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2640 torture_comment(tctx, "Open the file handle\n");
2641 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2644 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2649 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
2650 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2653 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2658 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2659 finfo0.basic_info.in.file.fnum = fnum1;
2665 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2666 pinfo0.basic_info.in.file.path = fname;
2675 /* get the initial times */
2676 GET_INFO_BOTH(finfo0,pinfo0);
2679 torture_comment(tctx, "Do a write on the file handle\n");
2680 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2682 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2687 GET_INFO_BOTH(finfo1,pinfo1);
2688 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2690 torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
2691 SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
2692 GET_INFO_BOTH(finfo2,pinfo2);
2693 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2695 torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
2696 SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
2697 GET_INFO_BOTH(finfo2,pinfo2);
2698 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2700 /* make sure the 2 second delay from the first write are canceled */
2701 start = timeval_current();
2702 end = timeval_add(&start, 10 * sec, 0);
2703 while (!timeval_expired(&end)) {
2705 /* get the times after the first write */
2706 GET_INFO_BOTH(finfo3,pinfo3);
2708 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2709 double diff = timeval_elapsed(&start);
2710 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2716 smb_msleep(1 * msec);
2719 GET_INFO_BOTH(finfo3,pinfo3);
2720 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2721 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2722 torture_comment(tctx, "Server did not update write_time (correct)\n");
2725 /* sure any further write doesn't update the write time */
2726 start = timeval_current();
2727 end = timeval_add(&start, 10 * sec, 0);
2728 while (!timeval_expired(&end)) {
2730 torture_comment(tctx, "Do a write on the file handle\n");
2731 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2733 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2737 /* get the times after the write */
2738 GET_INFO_BOTH(finfo4,pinfo4);
2740 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2741 double diff = timeval_elapsed(&start);
2742 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2748 smb_msleep(1 * msec);
2751 GET_INFO_BOTH(finfo4,pinfo4);
2752 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2753 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2754 torture_comment(tctx, "Server did not update write_time (correct)\n");
2758 smb_msleep(5 * msec);
2760 GET_INFO_BOTH(finfo5,pinfo5);
2761 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2764 * the close updates the write time to the time of the close
2765 * as the write time was set on the 2nd handle
2767 torture_comment(tctx, "Close the file handle\n");
2768 smbcli_close(cli->tree, fnum1);
2771 GET_INFO_PATH(pinfo6);
2772 COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
2774 if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
2775 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2778 /* See what the second write handle thinks the time is ? */
2779 finfo5.basic_info.in.file.fnum = fnum2;
2780 GET_INFO_FILE2(finfo5);
2781 COMPARE_WRITE_TIME_EQUAL(finfo5, pinfo6);
2783 /* See if we have lost the sticky write time on handle2 */
2784 smb_msleep(3 * msec);
2785 torture_comment(tctx, "Have we lost the sticky write time ?\n");
2787 /* Make sure any further normal write doesn't update the write time */
2788 start = timeval_current();
2789 end = timeval_add(&start, 10 * sec, 0);
2790 while (!timeval_expired(&end)) {
2792 torture_comment(tctx, "Do a write on the second file handle\n");
2793 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
2795 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2799 /* get the times after the write */
2800 GET_INFO_FILE2(finfo5);
2801 GET_INFO_PATH(pinfo6);
2803 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2804 double diff = timeval_elapsed(&start);
2805 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2811 smb_msleep(1 * msec);
2814 /* What about a truncate write ? */
2815 start = timeval_current();
2816 end = timeval_add(&start, 10 * sec, 0);
2817 while (!timeval_expired(&end)) {
2819 torture_comment(tctx, "Do a truncate write on the second file handle\n");
2820 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 0);
2822 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2826 /* get the times after the write */
2827 GET_INFO_FILE2(finfo5);
2828 GET_INFO_PATH(pinfo6);
2830 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2831 double diff = timeval_elapsed(&start);
2832 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2838 smb_msleep(1 * msec);
2842 /* keep the 2nd handle open and rerun tests */
2849 * closing the 2nd handle will cause no write time update
2850 * as the write time was explicit set on this handle
2852 torture_comment(tctx, "Close the 2nd file handle\n");
2853 smbcli_close(cli2->tree, fnum2);
2856 GET_INFO_PATH(pinfo7);
2857 COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
2859 if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
2860 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2865 smbcli_close(cli->tree, fnum1);
2867 smbcli_close(cli2->tree, fnum2);
2868 smbcli_unlink(cli->tree, fname);
2869 smbcli_deltree(cli->tree, BASEDIR);
2874 static bool test_delayed_write_update7(struct torture_context *tctx, struct smbcli_state *cli)
2876 union smb_open open_parms;
2877 union smb_fileinfo finfo1, finfo2, finfo3;
2878 const char *fname = BASEDIR "\\torture_file7.txt";
2882 TALLOC_CTX *mem_ctx;
2884 torture_comment(tctx, "\nRunning test_delayed_write_update7 (timestamp resolution test)\n");
2886 mem_ctx = talloc_init("test_delayed_write_update7");
2887 if (!mem_ctx) return false;
2889 ZERO_STRUCT(finfo1);
2890 ZERO_STRUCT(finfo2);
2891 ZERO_STRUCT(finfo3);
2892 ZERO_STRUCT(open_parms);
2894 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2896 /* Create the file. */
2897 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2899 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
2903 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2904 finfo1.basic_info.in.file.fnum = fnum1;
2908 /* Get the initial timestamps. */
2909 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
2911 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
2913 /* Set the pending write time to a value with ns. */
2914 SET_INFO_FILE_NS(finfo, time(NULL) + 86400, 103, cli->tree, fnum1);
2916 /* Get the current pending write time by fnum. */
2917 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
2919 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
2921 /* Ensure the time is actually different. */
2922 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2923 torture_result(tctx, TORTURE_FAIL,
2924 "setfileinfo time matches original fileinfo time");
2928 /* Get the current pending write time by path. */
2929 finfo3.basic_info.in.file.path = fname;
2930 status = smb_raw_pathinfo(cli->tree, tctx, &finfo3);
2932 if (finfo2.basic_info.out.write_time != finfo3.basic_info.out.write_time) {
2933 torture_result(tctx, TORTURE_FAIL,
2934 "qpathinfo time doesn't match fileinfo time");
2938 /* Now close the file. Re-open and check that the write
2939 time is identical to the one we wrote. */
2941 smbcli_close(cli->tree, fnum1);
2943 open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
2944 open_parms.ntcreatex.in.flags = 0;
2945 open_parms.ntcreatex.in.access_mask = SEC_GENERIC_READ;
2946 open_parms.ntcreatex.in.file_attr = 0;
2947 open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE|
2948 NTCREATEX_SHARE_ACCESS_READ|
2949 NTCREATEX_SHARE_ACCESS_WRITE;
2950 open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
2951 open_parms.ntcreatex.in.create_options = 0;
2952 open_parms.ntcreatex.in.fname = fname;
2954 status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
2955 talloc_free(mem_ctx);
2957 if (!NT_STATUS_IS_OK(status)) {
2958 torture_result(tctx, TORTURE_FAIL,
2959 "setfileinfo time matches original fileinfo time");
2963 fnum1 = open_parms.ntcreatex.out.file.fnum;
2965 /* Check the returned time matches. */
2966 if (open_parms.ntcreatex.out.write_time != finfo2.basic_info.out.write_time) {
2967 torture_result(tctx, TORTURE_FAIL,
2968 "final open time does not match set time");
2974 smbcli_close(cli->tree, fnum1);
2976 smbcli_unlink(cli->tree, fname);
2977 smbcli_deltree(cli->tree, BASEDIR);
2982 Test if creating a file in a directory with an open handle updates the
2983 write timestamp (it should).
2985 static bool test_directory_update8(struct torture_context *tctx, struct smbcli_state *cli)
2987 union smb_fileinfo dir_info1, dir_info2;
2988 union smb_open open_parms;
2989 const char *fname = BASEDIR "\\torture_file.txt";
2994 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2995 int normal_delay = 2000000;
2996 double sec = ((double)used_delay) / ((double)normal_delay);
2997 int msec = 1000 * sec;
2998 TALLOC_CTX *mem_ctx = talloc_init("test_delayed_write_update8");
3000 if (!mem_ctx) return false;
3002 torture_comment(tctx, "\nRunning test directory write update\n");
3004 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
3006 /* Open a handle on the directory - and leave it open. */
3007 ZERO_STRUCT(open_parms);
3008 open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
3009 open_parms.ntcreatex.in.flags = 0;
3010 open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ;
3011 open_parms.ntcreatex.in.file_attr = 0;
3012 open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE|
3013 NTCREATEX_SHARE_ACCESS_READ|
3014 NTCREATEX_SHARE_ACCESS_WRITE;
3015 open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
3016 open_parms.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
3017 open_parms.ntcreatex.in.fname = BASEDIR;
3019 status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
3020 talloc_free(mem_ctx);
3022 if (!NT_STATUS_IS_OK(status)) {
3023 torture_result(tctx, TORTURE_FAIL,
3024 "failed to open directory handle");
3029 fnum1 = open_parms.ntcreatex.out.file.fnum;
3031 /* Store the returned write time. */
3032 ZERO_STRUCT(dir_info1);
3033 dir_info1.basic_info.out.write_time = open_parms.ntcreatex.out.write_time;
3035 torture_comment(tctx, "Initial write time %s\n",
3036 nt_time_string(tctx, dir_info1.basic_info.out.write_time));
3039 smb_msleep(3 * msec);
3041 /* Now create a file within the directory. */
3042 fnum2 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
3044 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
3048 smbcli_close(cli->tree, fnum2);
3050 /* Read the directory write time again. */
3051 ZERO_STRUCT(dir_info2);
3052 dir_info2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
3053 dir_info2.basic_info.in.file.fnum = fnum1;
3055 status = smb_raw_fileinfo(cli->tree, tctx, &dir_info2);
3057 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
3059 /* Ensure it's been incremented. */
3060 COMPARE_WRITE_TIME_GREATER(dir_info2, dir_info1);
3062 torture_comment(tctx, "Updated write time %s\n",
3063 nt_time_string(tctx, dir_info2.basic_info.out.write_time));
3068 smbcli_close(cli->tree, fnum1);
3069 smbcli_unlink(cli->tree, fname);
3070 smbcli_deltree(cli->tree, BASEDIR);
3075 #undef COMPARE_WRITE_TIME_CMP
3076 #undef COMPARE_ACCESS_TIME_CMP
3078 #define COMPARE_TIME_CMP(given, gelem, correct, celem, cmp) do { \
3079 const uint64_t _r = 10*1000*1000; \
3080 NTTIME _g = (given).basic_info.out.gelem; \
3081 NTTIME _gr = (_g / _r) * _r; \
3082 NTTIME _c = (correct).basic_info.out.celem; \
3083 NTTIME _cr = (_c / _r) * _r; \
3084 bool _strict = torture_setting_bool(tctx, "strict mode", false); \
3085 const char *_err = NULL; \
3086 if (_strict && (_g cmp _c)) { \
3088 } else if ((_g cmp _c) && (_gr cmp _cr)) { \
3089 /* handle filesystem without high resolution timestamps */ \
3092 if (_err != NULL) { \
3093 struct timeval _gtv; \
3094 struct timeval _ctv; \
3095 struct timeval_buf _gtvb; \
3096 struct timeval_buf _ctvb; \
3097 nttime_to_timeval(&_gtv, _g); \
3098 nttime_to_timeval(&_ctv, _c); \
3099 torture_result(tctx, TORTURE_FAIL, \
3100 __location__": %s wrong (%s.%s)%s %s (%s.%s)%s", \
3103 timeval_str_buf(&_gtv, false, true, &_gtvb), \
3106 timeval_str_buf(&_ctv, false, true, &_ctvb)); \
3111 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
3112 COMPARE_TIME_CMP(given, write_time, correct, write_time, cmp); \
3114 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
3115 COMPARE_WRITE_TIME_CMP(given,correct,!=)
3116 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
3117 COMPARE_WRITE_TIME_CMP(given,correct,<=)
3119 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
3120 COMPARE_TIME_CMP(given, access_time, correct, access_time, cmp); \
3122 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
3123 COMPARE_ACCESS_TIME_CMP(given,correct,!=)
3124 #define COMPARE_ACCESS_TIME_GREATER(given,correct) \
3125 COMPARE_ACCESS_TIME_CMP(given,correct,<=)
3127 #define COMPARE_CHANGE_TIME_CMP(given, correct, cmp) do { \
3128 COMPARE_TIME_CMP(given, change_time, correct, change_time, cmp); \
3130 #define COMPARE_CHANGE_TIME_EQUAL(given,correct) \
3131 COMPARE_CHANGE_TIME_CMP(given,correct,!=)
3132 #define COMPARE_CHANGE_TIME_GREATER(given,correct) \
3133 COMPARE_CHANGE_TIME_CMP(given,correct,<=)
3135 #define COMPARE_CREATE_TIME_CMP(given, correct, cmp) do { \
3136 COMPARE_TIME_CMP(given, create_time, correct, create_time, cmp); \
3138 #define COMPARE_CREATE_TIME_EQUAL(given,correct) \
3139 COMPARE_CREATE_TIME_CMP(given,correct,!=)
3141 #define COMPARE_ALL_TIMES_EQUAL(given,correct) do { \
3142 COMPARE_WRITE_TIME_EQUAL(given,correct); \
3143 COMPARE_CHANGE_TIME_EQUAL(given,correct); \
3144 COMPARE_ACCESS_TIME_EQUAL(given,correct); \
3145 COMPARE_CREATE_TIME_EQUAL(given,correct); \
3148 #define COMPARE_TIMES_AFTER_WRITE(given,correct) do { \
3149 COMPARE_WRITE_TIME_GREATER(given,correct); \
3150 COMPARE_CHANGE_TIME_GREATER(given,correct); \
3151 COMPARE_ACCESS_TIME_EQUAL(given,correct); \
3152 COMPARE_CREATE_TIME_EQUAL(given,correct); \
3153 COMPARE_TIME_CMP(given, change_time, given, write_time, !=); \
3156 struct test_delaywrite_delaywrite1_state {
3157 struct torture_context *tctx;
3158 struct smbcli_state *cli1;
3159 struct smbcli_state *cli2;
3165 static bool test_delaywrite_delaywrite1_get_info(void *private_data,
3166 union smb_fileinfo *finfo)
3168 struct test_delaywrite_delaywrite1_state *state =
3169 (struct test_delaywrite_delaywrite1_state *)private_data;
3170 struct torture_context *tctx = state->tctx;
3171 struct smbcli_state *cli = state->cli1;
3172 struct smbcli_state *cli2 = state->cli2;
3173 union smb_fileinfo t1finfo;
3174 union smb_fileinfo t2finfo;
3177 ZERO_STRUCTP(finfo);
3179 ZERO_STRUCT(t1finfo);
3180 t1finfo.basic_info.level = RAW_FILEINFO_BASIC_INFORMATION;
3181 t1finfo.basic_info.in.file.fnum = state->fnum1;
3183 ZERO_STRUCT(t2finfo);
3184 t2finfo.basic_info.level = RAW_FILEINFO_BASIC_INFORMATION;
3185 t2finfo.basic_info.in.file.fnum = state->fnum2;
3187 GET_INFO_FILE2(t2finfo);
3188 GET_INFO_FILE(t1finfo);
3189 if (t1finfo.basic_info.out.write_time != t2finfo.basic_info.out.write_time) {
3191 * There was a race, get it again on handle 2,
3192 * but then they have to match.
3194 GET_INFO_FILE2(t2finfo);
3196 COMPARE_ALL_TIMES_EQUAL(t1finfo, t2finfo);
3198 finfo->basic_info.out = t1finfo.basic_info.out;
3203 static bool test_delaywrite_delaywrite1_write_data(void *private_data)
3205 struct test_delaywrite_delaywrite1_state *state =
3206 (struct test_delaywrite_delaywrite1_state *)private_data;
3207 struct torture_context *tctx = state->tctx;
3211 nwritten = smbcli_write(state->cli1->tree, state->fnum1, 0, "x", 0, 1);
3212 torture_assert_int_equal_goto(tctx, nwritten, 1,
3213 ret, done, "smbcli_write");
3219 static bool test_delaywrite_delaywrite1_close(void *private_data,
3220 union smb_fileinfo *finfo)
3222 struct test_delaywrite_delaywrite1_state *state =
3223 (struct test_delaywrite_delaywrite1_state *)private_data;
3224 struct torture_context *tctx = state->tctx;
3225 struct smbcli_state *cli2 = state->cli2;
3226 union smb_fileinfo t2finfo;
3227 union smb_fileinfo t2pinfo;
3230 ZERO_STRUCTP(finfo);
3233 * the close updates the write time to the time of the close
3234 * and not to the time of the last write!
3236 torture_comment(tctx, "Close the file handle\n");
3237 smbcli_close(state->cli1->tree, state->fnum1);
3240 ZERO_STRUCT(t2finfo);
3241 t2finfo.basic_info.level = RAW_FILEINFO_BASIC_INFO;
3242 t2finfo.basic_info.in.file.fnum = state->fnum2;
3243 ZERO_STRUCT(t2pinfo);
3244 t2pinfo.basic_info.level = RAW_FILEINFO_BASIC_INFO;
3245 t2pinfo.basic_info.in.file.path = state->fname;
3247 GET_INFO_FILE2(t2finfo);
3249 smbcli_close(state->cli2->tree, state->fnum2);
3252 GET_INFO_PATH(t2pinfo);
3253 COMPARE_ALL_TIMES_EQUAL(t2pinfo, t2finfo);
3255 finfo->basic_info.out = t2pinfo.basic_info.out;
3261 static bool test_delaywrite_delaywrite1(struct torture_context *tctx,
3262 struct smbcli_state *cli,
3263 struct smbcli_state *cli2)
3265 struct test_delaywrite_delaywrite1_state state = {
3272 const char *fname = BASEDIR "\\torture_file3.txt";
3274 double normal_delay = 1000000;
3278 used_delay = torture_setting_int(tctx, "writetimeupdatedelay",
3281 torture_comment(tctx, "\nRunning test_delayed_write_update3\n");
3283 torture_assert(tctx, torture_setup_dir(cli, BASEDIR),
3284 "Failed to setup up test directory: " BASEDIR);
3286 torture_comment(tctx, "Open the file handle\n");
3287 state.fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
3288 torture_assert_int_not_equal_goto(tctx, state.fnum1, -1,
3290 "unable to open fnum1");
3291 state.fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
3292 torture_assert_int_not_equal_goto(tctx, state.fnum2, -1,
3294 "unable to open fnum2");
3296 state.fname = fname;
3298 ok = test_smb_timestamp_writetime1(tctx, used_delay, normal_delay,
3300 test_delaywrite_delaywrite1_get_info,
3301 test_delaywrite_delaywrite1_write_data,
3302 NULL, /* close_cb */
3304 torture_assert_goto(tctx, ok, ret, done,
3305 "test_smb_timestamp_writetime1(1)");
3306 ok = test_smb_timestamp_writetime1(tctx, used_delay, normal_delay,
3308 test_delaywrite_delaywrite1_get_info,
3309 test_delaywrite_delaywrite1_write_data,
3310 test_delaywrite_delaywrite1_close,
3312 torture_assert_goto(tctx, ok, ret, done,
3313 "test_smb_timestamp_writetime1(2)");
3316 if (state.fnum1 != -1) {
3317 smbcli_close(cli->tree, state.fnum1);
3319 if (state.fnum2 != -1) {
3320 smbcli_close(cli2->tree, state.fnum2);
3322 smbcli_unlink(cli->tree, fname);
3323 smbcli_deltree(cli->tree, BASEDIR);
3329 testing of delayed update of write_time
3331 struct torture_suite *torture_delay_write(TALLOC_CTX *ctx)
3333 struct torture_suite *suite = torture_suite_create(ctx, "delaywrite");
3335 torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
3336 torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
3337 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate", test_delayed_write_update1);
3338 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a);
3339 torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b);
3340 torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c);
3341 torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
3342 torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
3343 torture_suite_add_2smb_test(suite, "delayed update of write time 3a", test_delayed_write_update3a);
3344 torture_suite_add_2smb_test(suite, "delayed update of write time 3b", test_delayed_write_update3b);
3345 torture_suite_add_2smb_test(suite, "delayed update of write time 3c", test_delayed_write_update3c);
3346 torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
3347 torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
3348 torture_suite_add_2smb_test(suite, "delayed update of write time 5b", test_delayed_write_update5b);
3349 torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
3350 torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7);
3351 torture_suite_add_1smb_test(suite, "directory timestamp update test", test_directory_update8);
3352 torture_suite_add_2smb_test(suite, "delaywrite1", test_delaywrite_delaywrite1);