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"
34 #define W2K8R2_TIMEDELAY_SECS 1
35 #define W2K3_TIMEDELAY_SECS 2
36 #define TIMEDELAY_SECS W2K3_TIMEDELAY_SECS
38 #define BASEDIR "\\delaywrite"
40 static bool test_delayed_write_update(struct torture_context
*tctx
, struct smbcli_state
*cli
)
42 union smb_fileinfo finfo1
, finfo2
;
43 const char *fname
= BASEDIR
"\\torture_file.txt";
50 int used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
51 int normal_delay
= 2000000;
52 double sec
= ((double)used_delay
) / ((double)normal_delay
);
53 int msec
= 1000 * sec
;
55 torture_comment(tctx
, "\nRunning test_delayed_write_update\n");
57 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
59 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
61 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s", fname
);
65 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
66 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
69 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
71 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
73 torture_comment(tctx
, "Initial write time %s\n",
74 nt_time_string(tctx
, finfo1
.basic_info
.out
.write_time
));
76 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
79 torture_result(tctx
, TORTURE_FAIL
,
80 "write failed - wrote %d bytes (%s)\n",
81 (int)written
, __location__
);
85 start
= timeval_current();
86 end
= timeval_add(&start
, (120*sec
), 0);
87 while (!timeval_expired(&end
)) {
88 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
90 if (!NT_STATUS_IS_OK(status
)) {
91 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
95 torture_comment(tctx
, "write time %s\n",
96 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
97 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
98 double diff
= timeval_elapsed(&start
);
99 if (diff
< (TIMEDELAY_SECS
* sec
* 0.3)) { /* 0.3 to cope with vmware timing */
100 torture_comment(tctx
, "Server updated write_time after %.2f seconds"
101 "(1 sec == %.2f)(wrong!)\n",
107 torture_comment(tctx
, "Server updated write_time after %.2f seconds"
108 "(1 sec == %.2f)(correct)\n",
113 smb_msleep(1 * msec
);
116 if (finfo1
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
117 torture_result(tctx
, TORTURE_FAIL
,
118 "Server did not update write time (wrong!)");
124 smbcli_close(cli
->tree
, fnum1
);
125 smbcli_unlink(cli
->tree
, fname
);
126 smbcli_deltree(cli
->tree
, BASEDIR
);
131 static bool test_delayed_write_update1(struct torture_context
*tctx
, struct smbcli_state
*cli
)
133 union smb_fileinfo finfo1
, finfo2
, finfo3
, pinfo4
;
134 const char *fname
= BASEDIR
"\\torture_file1.txt";
139 struct timeval start
;
141 int used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
142 int normal_delay
= 2000000;
143 double sec
= ((double)used_delay
) / ((double)normal_delay
);
144 int msec
= 1000 * sec
;
147 torture_comment(tctx
, "\nRunning test_delayed_write_update1\n");
149 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
151 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
153 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s", fname
);
157 memset(buf
, 'x', 2048);
158 written
= smbcli_write(cli
->tree
, fnum1
, 0, buf
, 0, 2048);
160 /* 3 second delay to ensure we get past any 2 second time
161 granularity (older systems may have that) */
162 smb_msleep(3 * msec
);
164 finfo1
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
165 finfo1
.all_info
.in
.file
.fnum
= fnum1
;
168 pinfo4
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
169 pinfo4
.all_info
.in
.file
.path
= fname
;
171 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
173 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
175 torture_comment(tctx
, "Initial write time %s\n",
176 nt_time_string(tctx
, finfo1
.all_info
.out
.write_time
));
178 /* 3 second delay to ensure we get past any 2 second time
179 granularity (older systems may have that) */
180 smb_msleep(3 * msec
);
182 /* Do a zero length SMBwrite call to truncate. */
183 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 1024, 0);
186 torture_result(tctx
, TORTURE_FAIL
,
187 "write failed - wrote %d bytes (%s)\n",
188 (int)written
, __location__
);
192 start
= timeval_current();
193 end
= timeval_add(&start
, (120*sec
), 0);
194 while (!timeval_expired(&end
)) {
195 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
197 if (!NT_STATUS_IS_OK(status
)) {
198 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
203 if (finfo2
.all_info
.out
.size
!= 1024) {
204 torture_result(tctx
, TORTURE_FAIL
,
205 "file not truncated, size = %u (should be 1024)",
206 (unsigned int)finfo2
.all_info
.out
.size
);
211 torture_comment(tctx
, "write time %s\n",
212 nt_time_string(tctx
, finfo2
.all_info
.out
.write_time
));
213 if (finfo1
.all_info
.out
.write_time
!= finfo2
.all_info
.out
.write_time
) {
214 double diff
= timeval_elapsed(&start
);
215 if (diff
> (0.25 * sec
* 0.75)) { /* 0.75 to cope with vmware timing */
216 torture_comment(tctx
, "After SMBwrite truncate "
217 "server updated write_time after %.2f seconds"
218 "(1 sec == %.2f)(wrong!)\n",
224 torture_comment(tctx
, "After SMBwrite truncate "
225 "server updated write_time after %.2f seconds"
226 "(1 sec == %.2f)(correct)\n",
231 smb_msleep(1 * msec
);
234 if (finfo1
.all_info
.out
.write_time
== finfo2
.all_info
.out
.write_time
) {
235 torture_result(tctx
, TORTURE_FAIL
,
236 "Server did not update write time (wrong!)");
241 smb_msleep(2 * msec
);
243 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
244 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 0, 1);
247 torture_result(tctx
, TORTURE_FAIL
,
248 "write failed - wrote %d bytes (%s)",
249 (int)written
, __location__
);
253 start
= timeval_current();
254 end
= timeval_add(&start
, (10*sec
), 0);
255 while (!timeval_expired(&end
)) {
256 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo3
);
258 if (!NT_STATUS_IS_OK(status
)) {
259 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
264 if (finfo3
.all_info
.out
.size
!= 1024) {
265 DEBUG(0, ("file not truncated, size = %u (should be 1024)\n",
266 (unsigned int)finfo3
.all_info
.out
.size
));
271 torture_comment(tctx
, "write time %s\n",
272 nt_time_string(tctx
, finfo3
.all_info
.out
.write_time
));
273 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
274 double diff
= timeval_elapsed(&start
);
276 torture_comment(tctx
, "server updated write_time after %.2f seconds"
277 "(1 sec == %.2f)(wrong)\n",
282 smb_msleep(1 * msec
);
285 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
286 torture_result(tctx
, TORTURE_FAIL
,
287 "Server updated write time (wrong!)");
292 smb_msleep(2 * msec
);
294 /* the close should trigger an write time update */
295 smbcli_close(cli
->tree
, fnum1
);
298 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &pinfo4
);
299 torture_assert_ntstatus_ok(tctx
, status
, "pathinfo failed");
301 if (finfo3
.all_info
.out
.write_time
== pinfo4
.all_info
.out
.write_time
) {
302 torture_result(tctx
, TORTURE_FAIL
,
303 "Server did not update write time on close (wrong!)");
305 } else if (finfo3
.all_info
.out
.write_time
< pinfo4
.all_info
.out
.write_time
) {
306 torture_comment(tctx
, "Server updated write time on close (correct)\n");
310 smbcli_close(cli
->tree
, fnum1
);
311 smbcli_unlink(cli
->tree
, fname
);
312 smbcli_deltree(cli
->tree
, BASEDIR
);
317 /* Updating with a SMBwrite of zero length
318 * changes the write time immediately - even on expand. */
320 static bool test_delayed_write_update1a(struct torture_context
*tctx
, struct smbcli_state
*cli
)
322 union smb_fileinfo finfo1
, finfo2
, finfo3
, pinfo4
;
323 const char *fname
= BASEDIR
"\\torture_file1a.txt";
328 struct timeval start
;
330 int used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
331 int normal_delay
= 2000000;
332 double sec
= ((double)used_delay
) / ((double)normal_delay
);
333 int msec
= 1000 * sec
;
336 torture_comment(tctx
, "\nRunning test_delayed_write_update1a\n");
338 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
340 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
342 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s", fname
);
346 memset(buf
, 'x', 2048);
347 written
= smbcli_write(cli
->tree
, fnum1
, 0, buf
, 0, 2048);
349 /* 3 second delay to ensure we get past any 2 second time
350 granularity (older systems may have that) */
351 smb_msleep(3 * msec
);
353 finfo1
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
354 finfo1
.all_info
.in
.file
.fnum
= fnum1
;
357 pinfo4
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
358 pinfo4
.all_info
.in
.file
.path
= fname
;
360 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
362 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
364 torture_comment(tctx
, "Initial write time %s\n",
365 nt_time_string(tctx
, finfo1
.all_info
.out
.write_time
));
367 /* Do a zero length SMBwrite call to truncate. */
368 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 10240, 0);
371 torture_result(tctx
, TORTURE_FAIL
, "write failed - wrote %d bytes (%s)",
372 (int)written
, __location__
);
376 start
= timeval_current();
377 end
= timeval_add(&start
, (120*sec
), 0);
378 while (!timeval_expired(&end
)) {
379 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
381 if (!NT_STATUS_IS_OK(status
)) {
382 torture_result(tctx
, TORTURE_FAIL
, "fileinfo failed: %s",
388 if (finfo2
.all_info
.out
.size
!= 10240) {
389 torture_result(tctx
, TORTURE_FAIL
,
390 "file not truncated, size = %u (should be 10240)",
391 (unsigned int)finfo2
.all_info
.out
.size
);
396 torture_comment(tctx
, "write time %s\n",
397 nt_time_string(tctx
, finfo2
.all_info
.out
.write_time
));
398 if (finfo1
.all_info
.out
.write_time
!= finfo2
.all_info
.out
.write_time
) {
399 double diff
= timeval_elapsed(&start
);
400 if (diff
> (0.25 * sec
* 0.75)) { /* 0.75 to cope with vmware timing */
401 torture_comment(tctx
, "After SMBwrite truncate "
402 "server updated write_time after %.2f seconds"
403 "(1 sec == %.2f)(wrong!)\n",
409 torture_comment(tctx
, "After SMBwrite truncate "
410 "server updated write_time after %.2f seconds"
411 "(1 sec == %.2f)(correct)\n",
416 smb_msleep(1 * msec
);
419 if (finfo1
.all_info
.out
.write_time
== finfo2
.all_info
.out
.write_time
) {
420 torture_result(tctx
, TORTURE_FAIL
,
421 "Server did not update write time (wrong!)");
426 smb_msleep(2 * msec
);
428 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
429 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 0, 1);
431 torture_assert_int_equal(tctx
, written
, 1,
432 "unexpected number of bytes written");
434 start
= timeval_current();
435 end
= timeval_add(&start
, (10*sec
), 0);
436 while (!timeval_expired(&end
)) {
437 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo3
);
439 if (!NT_STATUS_IS_OK(status
)) {
440 torture_result(tctx
, TORTURE_FAIL
, "fileinfo failed: %s\n",
446 if (finfo3
.all_info
.out
.size
!= 10240) {
447 torture_result(tctx
, TORTURE_FAIL
,
448 "file not truncated, size = %u (should be 10240)",
449 (unsigned int)finfo3
.all_info
.out
.size
);
454 torture_comment(tctx
, "write time %s\n",
455 nt_time_string(tctx
, finfo3
.all_info
.out
.write_time
));
456 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
457 double diff
= timeval_elapsed(&start
);
459 torture_comment(tctx
, "server updated write_time after %.2f seconds"
460 "(1 sec == %.2f)(correct)\n",
465 smb_msleep(1 * msec
);
468 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
469 torture_result(tctx
, TORTURE_FAIL
,
470 "Server updated write time (wrong!)");
474 /* the close should trigger an write time update */
475 smbcli_close(cli
->tree
, fnum1
);
478 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &pinfo4
);
479 torture_assert_ntstatus_ok(tctx
, status
, "pathinfo failed");
481 if (finfo3
.all_info
.out
.write_time
== pinfo4
.all_info
.out
.write_time
) {
482 torture_result(tctx
, TORTURE_FAIL
,
483 "Server did not update write time on close (wrong!)");
485 } else if (finfo3
.all_info
.out
.write_time
< pinfo4
.all_info
.out
.write_time
) {
486 torture_comment(tctx
, "Server updated write time on close (correct)\n");
490 smbcli_close(cli
->tree
, fnum1
);
491 smbcli_unlink(cli
->tree
, fname
);
492 smbcli_deltree(cli
->tree
, BASEDIR
);
497 /* Updating with a SET_FILE_END_OF_FILE_INFO
498 * changes the write time immediately - even on expand. */
500 static bool test_delayed_write_update1b(struct torture_context
*tctx
, struct smbcli_state
*cli
)
502 union smb_fileinfo finfo1
, finfo2
, finfo3
, pinfo4
;
503 const char *fname
= BASEDIR
"\\torture_file1b.txt";
508 struct timeval start
;
510 int used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
511 int normal_delay
= 2000000;
512 double sec
= ((double)used_delay
) / ((double)normal_delay
);
513 int msec
= 1000 * sec
;
516 torture_comment(tctx
, "\nRunning test_delayed_write_update1b\n");
518 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
520 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
522 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s", fname
);
526 memset(buf
, 'x', 2048);
527 written
= smbcli_write(cli
->tree
, fnum1
, 0, buf
, 0, 2048);
529 /* 3 second delay to ensure we get past any 2 second time
530 granularity (older systems may have that) */
531 smb_msleep(3 * msec
);
533 finfo1
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
534 finfo1
.all_info
.in
.file
.fnum
= fnum1
;
537 pinfo4
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
538 pinfo4
.all_info
.in
.file
.path
= fname
;
540 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
542 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
544 torture_comment(tctx
, "Initial write time %s\n",
545 nt_time_string(tctx
, finfo1
.all_info
.out
.write_time
));
547 /* Do a SET_END_OF_FILE_INFO call to truncate. */
548 status
= smbcli_ftruncate(cli
->tree
, fnum1
, (uint64_t)10240);
550 torture_assert_ntstatus_ok(tctx
, status
, "SET_END_OF_FILE failed");
552 start
= timeval_current();
553 end
= timeval_add(&start
, (120*sec
), 0);
554 while (!timeval_expired(&end
)) {
555 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
557 if (!NT_STATUS_IS_OK(status
)) {
558 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
563 if (finfo2
.all_info
.out
.size
!= 10240) {
564 torture_result(tctx
, TORTURE_FAIL
,
565 "file not truncated (size = %u, should be 10240)",
566 (unsigned int)finfo2
.all_info
.out
.size
);
571 torture_comment(tctx
, "write time %s\n",
572 nt_time_string(tctx
, finfo2
.all_info
.out
.write_time
));
573 if (finfo1
.all_info
.out
.write_time
!= finfo2
.all_info
.out
.write_time
) {
574 double diff
= timeval_elapsed(&start
);
575 if (diff
> (0.25 * sec
* 0.75)) { /* 0.75 to cope with vmware timing */
576 torture_result(tctx
, TORTURE_FAIL
,
577 "After SET_END_OF_FILE truncate "
578 "server updated write_time after %.2f seconds"
579 "(1 sec == %.2f)(wrong!)",
585 torture_comment(tctx
, "After SET_END_OF_FILE truncate "
586 "server updated write_time after %.2f seconds"
587 "(1 sec == %.2f)(correct)\n",
592 smb_msleep(1 * msec
);
595 if (finfo1
.all_info
.out
.write_time
== finfo2
.all_info
.out
.write_time
) {
596 torture_result(tctx
, TORTURE_FAIL
,
597 "Server did not update write time (wrong!)");
602 smb_msleep(2 * msec
);
604 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
605 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 0, 1);
607 torture_assert_int_equal(tctx
, written
, 1,
608 "unexpected number of bytes written");
610 start
= timeval_current();
611 end
= timeval_add(&start
, (10*sec
), 0);
612 while (!timeval_expired(&end
)) {
613 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo3
);
615 if (!NT_STATUS_IS_OK(status
)) {
616 torture_result(tctx
, TORTURE_FAIL
,
617 "fileinfo failed: %s", nt_errstr(status
));
622 if (finfo3
.all_info
.out
.size
!= 10240) {
623 DEBUG(0, ("file not truncated (size = %u, should be 10240)\n",
624 (unsigned int)finfo3
.all_info
.out
.size
));
629 torture_comment(tctx
, "write time %s\n",
630 nt_time_string(tctx
, finfo3
.all_info
.out
.write_time
));
631 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
632 double diff
= timeval_elapsed(&start
);
634 torture_comment(tctx
, "server updated write_time after %.2f seconds"
635 "(1 sec == %.2f)(correct)\n",
640 smb_msleep(1 * msec
);
643 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
644 torture_result(tctx
, TORTURE_FAIL
, "Server updated write time (wrong!)\n");
648 /* the close should trigger an write time update */
649 smbcli_close(cli
->tree
, fnum1
);
652 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &pinfo4
);
653 torture_assert_ntstatus_ok(tctx
, status
, "pathinfo failed");
655 if (finfo3
.all_info
.out
.write_time
== pinfo4
.all_info
.out
.write_time
) {
656 torture_result(tctx
, TORTURE_FAIL
, "Server did not update write time on close (wrong!)\n");
658 } else if (finfo3
.all_info
.out
.write_time
< pinfo4
.all_info
.out
.write_time
) {
659 torture_comment(tctx
, "Server updated write time on close (correct)\n");
663 smbcli_close(cli
->tree
, fnum1
);
664 smbcli_unlink(cli
->tree
, fname
);
665 smbcli_deltree(cli
->tree
, BASEDIR
);
670 /* Updating with a SET_ALLOCATION_INFO (truncate) does so immediately. */
672 static bool test_delayed_write_update1c(struct torture_context
*tctx
, struct smbcli_state
*cli
)
674 union smb_setfileinfo parms
;
675 union smb_fileinfo finfo1
, finfo2
, finfo3
, pinfo4
;
676 const char *fname
= BASEDIR
"\\torture_file1c.txt";
681 struct timeval start
;
683 int used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
684 int normal_delay
= 2000000;
685 double sec
= ((double)used_delay
) / ((double)normal_delay
);
686 int msec
= 1000 * sec
;
689 torture_comment(tctx
, "\nRunning test_delayed_write_update1c\n");
691 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
693 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
695 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s", fname
);
699 memset(buf
, 'x', 2048);
700 written
= smbcli_write(cli
->tree
, fnum1
, 0, buf
, 0, 2048);
702 /* 3 second delay to ensure we get past any 2 second time
703 granularity (older systems may have that) */
704 smb_msleep(3 * msec
);
706 finfo1
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
707 finfo1
.all_info
.in
.file
.fnum
= fnum1
;
710 pinfo4
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
711 pinfo4
.all_info
.in
.file
.path
= fname
;
713 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
715 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
717 torture_comment(tctx
, "Initial write time %s\n",
718 nt_time_string(tctx
, finfo1
.all_info
.out
.write_time
));
720 /* Do a SET_ALLOCATION_SIZE call to truncate. */
721 parms
.allocation_info
.level
= RAW_SFILEINFO_ALLOCATION_INFO
;
722 parms
.allocation_info
.in
.file
.fnum
= fnum1
;
723 parms
.allocation_info
.in
.alloc_size
= 0;
725 status
= smb_raw_setfileinfo(cli
->tree
, &parms
);
727 torture_assert_ntstatus_ok(tctx
, status
,
728 "RAW_SFILEINFO_ALLOCATION_INFO failed");
730 start
= timeval_current();
731 end
= timeval_add(&start
, (120*sec
), 0);
732 while (!timeval_expired(&end
)) {
733 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
735 if (!NT_STATUS_IS_OK(status
)) {
736 torture_result(tctx
, TORTURE_FAIL
, "fileinfo failed: %s",
742 if (finfo2
.all_info
.out
.size
!= 0) {
743 torture_result(tctx
, TORTURE_FAIL
,
744 "file not truncated (size = %u, should be 10240)",
745 (unsigned int)finfo2
.all_info
.out
.size
);
750 torture_comment(tctx
, "write time %s\n",
751 nt_time_string(tctx
, finfo2
.all_info
.out
.write_time
));
752 if (finfo1
.all_info
.out
.write_time
!= finfo2
.all_info
.out
.write_time
) {
753 double diff
= timeval_elapsed(&start
);
754 if (diff
> (0.25 * sec
* 0.75)) { /* 0.75 to cope with vmware timing */
755 torture_comment(tctx
, "After SET_ALLOCATION_INFO truncate "
756 "server updated write_time after %.2f seconds"
757 "(1 sec == %.2f)(wrong!)\n",
763 torture_comment(tctx
, "After SET_ALLOCATION_INFO truncate "
764 "server updated write_time after %.2f seconds"
765 "(1 sec == %.2f)(correct)\n",
770 smb_msleep(1 * msec
);
773 if (finfo1
.all_info
.out
.write_time
== finfo2
.all_info
.out
.write_time
) {
774 torture_result(tctx
, TORTURE_FAIL
,
775 "Server did not update write time (wrong!)");
780 smb_msleep(2 * msec
);
782 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
783 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 0, 1);
784 torture_assert_int_equal(tctx
, written
, 1,
785 "Unexpected number of bytes written");
787 start
= timeval_current();
788 end
= timeval_add(&start
, (10*sec
), 0);
789 while (!timeval_expired(&end
)) {
790 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo3
);
792 if (!NT_STATUS_IS_OK(status
)) {
793 torture_result(tctx
, TORTURE_FAIL
, "fileinfo failed: %s",
799 if (finfo3
.all_info
.out
.size
!= 1) {
800 torture_result(tctx
, TORTURE_FAIL
, "file not expanded");
805 torture_comment(tctx
, "write time %s\n",
806 nt_time_string(tctx
, finfo3
.all_info
.out
.write_time
));
807 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
808 double diff
= timeval_elapsed(&start
);
810 torture_comment(tctx
, "server updated write_time after %.2f seconds"
811 "(1 sec == %.2f)(correct)\n",
816 smb_msleep(1 * msec
);
819 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
820 torture_result(tctx
, TORTURE_FAIL
,
821 "Server updated write time (wrong!)");
825 /* the close should trigger an write time update */
826 smbcli_close(cli
->tree
, fnum1
);
829 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &pinfo4
);
830 torture_assert_ntstatus_ok(tctx
, status
, "pathinfo failed");
832 if (finfo3
.all_info
.out
.write_time
== pinfo4
.all_info
.out
.write_time
) {
833 torture_result(tctx
, TORTURE_FAIL
, "Server did not update write time on close (wrong!)\n");
835 } else if (finfo3
.all_info
.out
.write_time
< pinfo4
.all_info
.out
.write_time
) {
836 torture_comment(tctx
, "Server updated write time on close (correct)\n");
840 smbcli_close(cli
->tree
, fnum1
);
841 smbcli_unlink(cli
->tree
, fname
);
842 smbcli_deltree(cli
->tree
, BASEDIR
);
848 * Do as above, but using 2 connections.
851 static bool test_delayed_write_update2(struct torture_context
*tctx
, struct smbcli_state
*cli
,
852 struct smbcli_state
*cli2
)
854 union smb_fileinfo finfo1
, finfo2
;
855 const char *fname
= BASEDIR
"\\torture_file.txt";
861 struct timeval start
;
863 int used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
864 int normal_delay
= 2000000;
865 double sec
= ((double)used_delay
) / ((double)normal_delay
);
866 int msec
= 1000 * sec
;
867 union smb_flush flsh
;
869 torture_comment(tctx
, "\nRunning test_delayed_write_update2\n");
871 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
873 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
875 torture_comment(tctx
, "Failed to open %s\n", fname
);
879 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
880 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
883 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
885 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
887 torture_comment(tctx
, "Initial write time %s\n",
888 nt_time_string(tctx
, finfo1
.basic_info
.out
.write_time
));
890 /* 3 second delay to ensure we get past any 2 second time
891 granularity (older systems may have that) */
892 smb_msleep(3 * msec
);
895 /* Try using setfileinfo instead of write to update write time. */
896 union smb_setfileinfo sfinfo
;
897 time_t t_set
= time(NULL
);
898 sfinfo
.basic_info
.level
= RAW_SFILEINFO_BASIC_INFO
;
899 sfinfo
.basic_info
.in
.file
.fnum
= fnum1
;
900 sfinfo
.basic_info
.in
.create_time
= finfo1
.basic_info
.out
.create_time
;
901 sfinfo
.basic_info
.in
.access_time
= finfo1
.basic_info
.out
.access_time
;
903 /* I tried this with both + and - ve to see if it makes a different.
904 It doesn't - once the filetime is set via setfileinfo it stays that way. */
906 unix_to_nt_time(&sfinfo
.basic_info
.in
.write_time
, t_set
- 30000);
908 unix_to_nt_time(&sfinfo
.basic_info
.in
.write_time
, t_set
+ 30000);
910 sfinfo
.basic_info
.in
.change_time
= finfo1
.basic_info
.out
.change_time
;
911 sfinfo
.basic_info
.in
.attrib
= finfo1
.basic_info
.out
.attrib
;
913 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
915 torture_assert_ntstatus_ok(tctx
, status
, "sfileinfo failed");
918 finfo2
.basic_info
.in
.file
.path
= fname
;
920 status
= smb_raw_pathinfo(cli2
->tree
, tctx
, &finfo2
);
922 if (!NT_STATUS_IS_OK(status
)) {
923 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
926 torture_comment(tctx
, "write time %s\n",
927 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
929 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
930 torture_comment(tctx
, "Server updated write_time (correct)\n");
932 torture_result(tctx
, TORTURE_FAIL
, "Server did not update write time (wrong!)\n");
936 /* Now try a write to see if the write time gets reset. */
938 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
939 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
942 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
944 if (!NT_STATUS_IS_OK(status
)) {
945 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
949 torture_comment(tctx
, "Modified write time %s\n",
950 nt_time_string(tctx
, finfo1
.basic_info
.out
.write_time
));
953 torture_comment(tctx
, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
955 written
= smbcli_write(cli
->tree
, fnum1
, 0, "0123456789", 1, 10);
958 torture_comment(tctx
, "write failed - wrote %d bytes (%s)\n",
959 (int)written
, __location__
);
963 /* Just to prove to tridge that the an smbflush has no effect on
964 the write time :-). The setfileinfo IS STICKY. JRA. */
966 torture_comment(tctx
, "Doing flush after write\n");
968 flsh
.flush
.level
= RAW_FLUSH_FLUSH
;
969 flsh
.flush
.in
.file
.fnum
= fnum1
;
970 status
= smb_raw_flush(cli
->tree
, &flsh
);
971 if (!NT_STATUS_IS_OK(status
)) {
972 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status
)));
976 /* Once the time was set using setfileinfo then it stays set - writes
977 don't have any effect. But make sure. */
978 start
= timeval_current();
979 end
= timeval_add(&start
, (15*sec
), 0);
980 while (!timeval_expired(&end
)) {
981 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
983 if (!NT_STATUS_IS_OK(status
)) {
984 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
988 torture_comment(tctx
, "write time %s\n",
989 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
990 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
991 double diff
= timeval_elapsed(&start
);
992 torture_comment(tctx
, "Server updated write_time after %.2f seconds"
993 "(1sec == %.2f) (wrong!)\n",
999 smb_msleep(1 * msec
);
1002 if (finfo1
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
1003 torture_comment(tctx
, "Server did not update write time (correct)\n");
1007 smb_msleep(2 * msec
);
1009 fnum2
= smbcli_open(cli
->tree
, fname
, O_RDWR
, DENY_NONE
);
1011 torture_comment(tctx
, "Failed to open %s\n", fname
);
1015 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");
1017 written
= smbcli_write(cli
->tree
, fnum2
, 0, "0123456789", 11, 10);
1019 if (written
!= 10) {
1020 torture_comment(tctx
, "write failed - wrote %d bytes (%s)\n",
1021 (int)written
, __location__
);
1025 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
1027 if (!NT_STATUS_IS_OK(status
)) {
1028 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
1031 torture_comment(tctx
, "write time %s\n",
1032 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
1033 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
1034 torture_comment(tctx
, "Server updated write_time (wrong!)\n");
1038 torture_comment(tctx
, "Closing the first fd to see if write time updated.\n");
1039 smbcli_close(cli
->tree
, fnum1
);
1042 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");
1044 written
= smbcli_write(cli
->tree
, fnum2
, 0, "0123456789", 21, 10);
1046 if (written
!= 10) {
1047 torture_comment(tctx
, "write failed - wrote %d bytes (%s)\n",
1048 (int)written
, __location__
);
1052 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1053 finfo1
.basic_info
.in
.file
.fnum
= fnum2
;
1055 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
1057 if (!NT_STATUS_IS_OK(status
)) {
1058 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
1061 torture_comment(tctx
, "write time %s\n",
1062 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
1063 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
1064 torture_comment(tctx
, "Server updated write_time (wrong!)\n");
1068 /* Once the time was set using setfileinfo then it stays set - writes
1069 don't have any effect. But make sure. */
1070 start
= timeval_current();
1071 end
= timeval_add(&start
, (15*sec
), 0);
1072 while (!timeval_expired(&end
)) {
1073 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
1075 if (!NT_STATUS_IS_OK(status
)) {
1076 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
1080 torture_comment(tctx
, "write time %s\n",
1081 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
1082 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
1083 double diff
= timeval_elapsed(&start
);
1084 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
1085 "(1sec == %.2f) (wrong!)\n",
1091 smb_msleep(1 * msec
);
1094 if (finfo1
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
1095 torture_comment(tctx
, "Server did not update write time (correct)\n");
1098 torture_comment(tctx
, "Closing second fd to see if write time updated.\n");
1100 smbcli_close(cli
->tree
, fnum2
);
1103 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
, DENY_NONE
);
1105 torture_comment(tctx
, "Failed to open %s\n", fname
);
1109 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1110 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
1113 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
1115 if (!NT_STATUS_IS_OK(status
)) {
1116 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
1120 torture_comment(tctx
, "Second open initial write time %s\n",
1121 nt_time_string(tctx
, finfo1
.basic_info
.out
.write_time
));
1123 smb_msleep(10 * msec
);
1124 torture_comment(tctx
, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
1126 written
= smbcli_write(cli
->tree
, fnum1
, 0, "0123456789", 31, 10);
1128 if (written
!= 10) {
1129 torture_comment(tctx
, "write failed - wrote %d bytes (%s)\n",
1130 (int)written
, __location__
);
1134 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1135 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
1137 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
1139 if (!NT_STATUS_IS_OK(status
)) {
1140 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
1143 torture_comment(tctx
, "write time %s\n",
1144 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
1145 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
1146 torture_comment(tctx
, "Server updated write_time (wrong!)\n");
1150 /* Now the write time should be updated again */
1151 start
= timeval_current();
1152 end
= timeval_add(&start
, (15*sec
), 0);
1153 while (!timeval_expired(&end
)) {
1154 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
1156 if (!NT_STATUS_IS_OK(status
)) {
1157 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
1161 torture_comment(tctx
, "write time %s\n",
1162 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
1163 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
1164 double diff
= timeval_elapsed(&start
);
1165 if (diff
< (TIMEDELAY_SECS
* sec
* 0.3)) { /* 0.3 to cope with vmware timing */
1166 torture_comment(tctx
, "Server updated write_time after %.2f seconds"
1167 "(1sec == %.2f) (wrong!)\n",
1173 torture_comment(tctx
, "Server updated write_time after %.2f seconds"
1174 "(1sec == %.2f) (correct)\n",
1182 if (finfo1
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
1183 torture_result(tctx
, TORTURE_FAIL
, "Server did not update write time (wrong!)\n");
1188 /* One more test to do. We should read the filetime via findfirst on the
1189 second connection to ensure it's the same. This is very easy for a Windows
1190 server but a bastard to get right on a POSIX server. JRA. */
1193 smbcli_close(cli
->tree
, fnum1
);
1194 smbcli_unlink(cli
->tree
, fname
);
1195 smbcli_deltree(cli
->tree
, BASEDIR
);
1201 /* Windows does obviously not update the stat info during a write call. I
1202 * *think* this is the problem causing a spurious Excel 2003 on XP error
1203 * message when saving a file. Excel does a setfileinfo, writes, and then does
1204 * a getpath(!)info. Or so... For Samba sometimes it displays an error message
1205 * that the file might have been changed in between. What i've been able to
1206 * trace down is that this happens if the getpathinfo after the write shows a
1207 * different last write time than the setfileinfo showed. This is really
1211 static bool test_finfo_after_write(struct torture_context
*tctx
, struct smbcli_state
*cli
,
1212 struct smbcli_state
*cli2
)
1214 union smb_fileinfo finfo1
, finfo2
;
1215 const char *fname
= BASEDIR
"\\torture_file.txt";
1221 int used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
1222 int normal_delay
= 2000000;
1223 double sec
= ((double)used_delay
) / ((double)normal_delay
);
1224 int msec
= 1000 * sec
;
1226 torture_comment(tctx
, "\nRunning test_finfo_after_write\n");
1228 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1230 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
1233 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
1237 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1238 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
1240 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
1242 if (!NT_STATUS_IS_OK(status
)) {
1244 torture_result(tctx
, TORTURE_FAIL
, __location__
": fileinfo failed: %s", nt_errstr(status
));
1248 smb_msleep(1 * msec
);
1250 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1253 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1258 fnum2
= smbcli_open(cli2
->tree
, fname
, O_RDWR
, DENY_NONE
);
1260 torture_result(tctx
, TORTURE_FAIL
, __location__
": failed to open 2nd time - %s",
1261 smbcli_errstr(cli2
->tree
));
1266 written
= smbcli_write(cli2
->tree
, fnum2
, 0, "x", 0, 1);
1269 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1",
1275 finfo2
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1276 finfo2
.basic_info
.in
.file
.path
= fname
;
1278 status
= smb_raw_pathinfo(cli2
->tree
, tctx
, &finfo2
);
1280 if (!NT_STATUS_IS_OK(status
)) {
1281 torture_result(tctx
, TORTURE_FAIL
, __location__
": fileinfo failed: %s",
1287 if (finfo1
.basic_info
.out
.create_time
!=
1288 finfo2
.basic_info
.out
.create_time
) {
1289 torture_result(tctx
, TORTURE_FAIL
, __location__
": create_time changed");
1294 if (finfo1
.basic_info
.out
.access_time
!=
1295 finfo2
.basic_info
.out
.access_time
) {
1296 torture_result(tctx
, TORTURE_FAIL
, __location__
": access_time changed");
1301 if (finfo1
.basic_info
.out
.write_time
!=
1302 finfo2
.basic_info
.out
.write_time
) {
1303 torture_result(tctx
, TORTURE_FAIL
, __location__
": write_time changed:\n"
1304 "write time conn 1 = %s, conn 2 = %s",
1305 nt_time_string(tctx
, finfo1
.basic_info
.out
.write_time
),
1306 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
1311 if (finfo1
.basic_info
.out
.change_time
!=
1312 finfo2
.basic_info
.out
.change_time
) {
1313 torture_result(tctx
, TORTURE_FAIL
, __location__
": change_time changed");
1318 /* One of the two following calls updates the qpathinfo. */
1320 /* If you had skipped the smbcli_write on fnum2, it would
1321 * *not* have updated the stat on disk */
1323 smbcli_close(cli2
->tree
, fnum2
);
1326 /* This call is only for the people looking at ethereal :-) */
1327 finfo2
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1328 finfo2
.basic_info
.in
.file
.path
= fname
;
1330 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo2
);
1332 if (!NT_STATUS_IS_OK(status
)) {
1333 torture_result(tctx
, TORTURE_FAIL
, __location__
": fileinfo failed: %s", nt_errstr(status
));
1340 smbcli_close(cli
->tree
, fnum1
);
1341 smbcli_unlink(cli
->tree
, fname
);
1342 smbcli_deltree(cli
->tree
, BASEDIR
);
1347 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
1348 uint64_t r = 10*1000*1000; \
1349 NTTIME g = (given).basic_info.out.write_time; \
1350 NTTIME gr = (g / r) * r; \
1351 NTTIME c = (correct).basic_info.out.write_time; \
1352 NTTIME cr = (c / r) * r; \
1353 bool strict = torture_setting_bool(tctx, "strict mode", false); \
1355 if (strict && (g cmp c)) { \
1357 } else if ((g cmp c) && (gr cmp cr)) { \
1358 /* handle filesystem without high resolution timestamps */ \
1362 torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \
1363 #given, nt_time_string(tctx, g), (unsigned long long)g, \
1364 #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \
1369 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
1370 COMPARE_WRITE_TIME_CMP(given,correct,!=)
1371 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
1372 COMPARE_WRITE_TIME_CMP(given,correct,<=)
1373 #define COMPARE_WRITE_TIME_LESS(given,correct) \
1374 COMPARE_WRITE_TIME_CMP(given,correct,>=)
1376 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
1377 NTTIME g = (given).basic_info.out.access_time; \
1378 NTTIME c = (correct).basic_info.out.access_time; \
1380 torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \
1381 #given, nt_time_string(tctx, g), \
1382 #cmp, #correct, nt_time_string(tctx, c)); \
1387 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
1388 COMPARE_ACCESS_TIME_CMP(given,correct,!=)
1390 #define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \
1391 COMPARE_ACCESS_TIME_EQUAL(given,correct); \
1392 COMPARE_WRITE_TIME_EQUAL(given,correct); \
1395 #define GET_INFO_FILE(finfo) do { \
1397 _status = smb_raw_fileinfo(cli->tree, tctx, &finfo); \
1398 if (!NT_STATUS_IS_OK(_status)) { \
1400 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1401 nt_errstr(_status)); \
1404 torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1405 nt_time_string(tctx, finfo.basic_info.out.access_time), \
1406 nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1408 #define GET_INFO_FILE2(finfo) do { \
1410 _status = smb_raw_fileinfo(cli2->tree, tctx, &finfo); \
1411 if (!NT_STATUS_IS_OK(_status)) { \
1413 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1414 nt_errstr(_status)); \
1417 torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1418 nt_time_string(tctx, finfo.basic_info.out.access_time), \
1419 nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1421 #define GET_INFO_PATH(pinfo) do { \
1423 _status = smb_raw_pathinfo(cli2->tree, tctx, &pinfo); \
1424 if (!NT_STATUS_IS_OK(_status)) { \
1425 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \
1426 nt_errstr(_status)); \
1430 torture_comment(tctx, "pathinfo: Access(%s) Write(%s)\n", \
1431 nt_time_string(tctx, pinfo.basic_info.out.access_time), \
1432 nt_time_string(tctx, pinfo.basic_info.out.write_time)); \
1434 #define GET_INFO_BOTH(finfo,pinfo) do { \
1435 GET_INFO_FILE(finfo); \
1436 GET_INFO_PATH(pinfo); \
1437 COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \
1440 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
1442 union smb_setfileinfo sfinfo; \
1443 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1444 sfinfo.basic_info.in.file.fnum = tfnum; \
1445 sfinfo.basic_info.in.create_time = 0; \
1446 sfinfo.basic_info.in.access_time = 0; \
1447 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1448 sfinfo.basic_info.in.change_time = 0; \
1449 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1450 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1451 if (!NT_STATUS_IS_OK(_status)) { \
1452 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1453 nt_errstr(_status)); \
1458 #define SET_INFO_FILE(finfo, wrtime) \
1459 SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
1461 #define SET_INFO_FILE_NS(finfo, wrtime, ns, tree, tfnum) do { \
1463 union smb_setfileinfo sfinfo; \
1464 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1465 sfinfo.basic_info.in.file.fnum = tfnum; \
1466 sfinfo.basic_info.in.create_time = 0; \
1467 sfinfo.basic_info.in.access_time = 0; \
1468 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1469 sfinfo.basic_info.in.write_time += (ns); \
1470 sfinfo.basic_info.in.change_time = 0; \
1471 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1472 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1473 if (!NT_STATUS_IS_OK(_status)) { \
1474 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1475 nt_errstr(_status)); \
1481 static bool test_delayed_write_update3(struct torture_context
*tctx
,
1482 struct smbcli_state
*cli
,
1483 struct smbcli_state
*cli2
)
1485 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
;
1486 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
;
1487 const char *fname
= BASEDIR
"\\torture_file3.txt";
1491 struct timeval start
;
1493 int used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
1494 int normal_delay
= 2000000;
1495 double sec
= ((double)used_delay
) / ((double)normal_delay
);
1496 int msec
= 1000 * sec
;
1498 torture_comment(tctx
, "\nRunning test_delayed_write_update3\n");
1500 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1502 torture_comment(tctx
, "Open the file handle\n");
1503 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
1506 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
1510 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1511 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
1515 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1516 pinfo0
.basic_info
.in
.file
.path
= fname
;
1522 /* get the initial times */
1523 GET_INFO_BOTH(finfo0
,pinfo0
);
1526 * make sure the write time is updated 2 seconds later
1527 * calcuated from the first write
1528 * (but expect upto 5 seconds extra time for a busy server)
1530 start
= timeval_current();
1531 end
= timeval_add(&start
, 7 * sec
, 0);
1532 while (!timeval_expired(&end
)) {
1534 torture_comment(tctx
, "Do a write on the file handle\n");
1535 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1537 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1541 /* get the times after the write */
1542 GET_INFO_FILE(finfo1
);
1544 if (finfo1
.basic_info
.out
.write_time
> finfo0
.basic_info
.out
.write_time
) {
1545 double diff
= timeval_elapsed(&start
);
1546 if (diff
< (TIMEDELAY_SECS
* sec
* 0.3)) { /* 0.3 to cope with vmware timing */
1547 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
1548 "(1sec == %.2f) (wrong!)\n",
1554 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
1555 "(1sec == %.2f) (correct)\n",
1559 smb_msleep(0.5 * msec
);
1562 GET_INFO_BOTH(finfo1
,pinfo1
);
1563 COMPARE_WRITE_TIME_GREATER(pinfo1
, pinfo0
);
1565 /* sure any further write doesn't update the write time */
1566 start
= timeval_current();
1567 end
= timeval_add(&start
, 15 * sec
, 0);
1568 while (!timeval_expired(&end
)) {
1570 torture_comment(tctx
, "Do a write on the file handle\n");
1571 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1573 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1577 /* get the times after the write */
1578 GET_INFO_BOTH(finfo2
,pinfo2
);
1580 if (finfo2
.basic_info
.out
.write_time
> finfo1
.basic_info
.out
.write_time
) {
1581 double diff
= timeval_elapsed(&start
);
1582 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
1583 "(1sec == %.2f) (wrong!)\n",
1588 smb_msleep(1 * msec
);
1591 GET_INFO_BOTH(finfo2
,pinfo2
);
1592 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
1593 if (finfo2
.basic_info
.out
.write_time
== finfo1
.basic_info
.out
.write_time
) {
1594 torture_comment(tctx
, "Server did not update write_time (correct)\n");
1598 smb_msleep(5 * msec
);
1600 GET_INFO_BOTH(finfo3
,pinfo3
);
1601 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
1604 * the close updates the write time to the time of the close
1605 * and not to the time of the last write!
1607 torture_comment(tctx
, "Close the file handle\n");
1608 smbcli_close(cli
->tree
, fnum1
);
1611 GET_INFO_PATH(pinfo4
);
1612 COMPARE_WRITE_TIME_GREATER(pinfo4
, pinfo3
);
1614 if (pinfo4
.basic_info
.out
.write_time
> pinfo3
.basic_info
.out
.write_time
) {
1615 torture_comment(tctx
, "Server updated the write_time on close (correct)\n");
1620 smbcli_close(cli
->tree
, fnum1
);
1621 smbcli_unlink(cli
->tree
, fname
);
1622 smbcli_deltree(cli
->tree
, BASEDIR
);
1628 * Show that a truncate write always updates the write time even
1629 * if an initial write has already updated the write time.
1632 static bool test_delayed_write_update3a(struct torture_context
*tctx
,
1633 struct smbcli_state
*cli
,
1634 struct smbcli_state
*cli2
)
1636 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
;
1637 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
;
1638 const char *fname
= BASEDIR
"\\torture_file3a.txt";
1643 struct timeval start
;
1645 int used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
1646 int normal_delay
= 2000000;
1647 double sec
= ((double)used_delay
) / ((double)normal_delay
);
1648 int msec
= 1000 * sec
;
1650 torture_comment(tctx
, "\nRunning test_delayed_write_update3a\n");
1652 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1654 torture_comment(tctx
, "Open the file handle\n");
1655 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
1658 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
1662 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1663 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
1667 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1668 pinfo0
.basic_info
.in
.file
.path
= fname
;
1674 /* get the initial times */
1675 GET_INFO_BOTH(finfo0
,pinfo0
);
1678 * sleep some time, to demonstrate the handling of write times
1679 * doesn't depend on the time since the open
1681 smb_msleep(5 * msec
);
1683 /* get the initial times */
1684 GET_INFO_BOTH(finfo1
,pinfo1
);
1685 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo0
);
1688 * make sure the write time is updated 2 seconds later
1689 * calcuated from the first write
1690 * (but expect upto 5 seconds extra time for a busy server)
1692 start
= timeval_current();
1693 end
= timeval_add(&start
, 7 * sec
, 0);
1694 while (!timeval_expired(&end
)) {
1696 torture_comment(tctx
, "Do a write on the file handle\n");
1697 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1699 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1703 /* get the times after the write */
1704 GET_INFO_FILE(finfo1
);
1706 if (finfo1
.basic_info
.out
.write_time
> finfo0
.basic_info
.out
.write_time
) {
1707 double diff
= timeval_elapsed(&start
);
1708 if (diff
< (TIMEDELAY_SECS
* sec
* 0.3)) { /* 0.3 to cope with vmware timing */
1709 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
1710 "(1sec == %.2f) (wrong!)\n",
1716 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
1717 "(1sec == %.2f) (correct)\n",
1721 smb_msleep(0.5 * msec
);
1724 GET_INFO_BOTH(finfo1
,pinfo1
);
1725 COMPARE_WRITE_TIME_GREATER(pinfo1
, pinfo0
);
1727 smb_msleep(3 * msec
);
1730 * demonstrate that a truncate write always
1731 * updates the write time immediately
1733 for (i
=0; i
< 3; i
++) {
1734 smb_msleep(2 * msec
);
1736 torture_comment(tctx
, "Do a truncate SMBwrite [%d] on the file handle\n", i
);
1737 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 10240, 0);
1739 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 0", (int)written
);
1743 /* get the times after the write */
1744 GET_INFO_BOTH(finfo2
,pinfo2
);
1745 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
1749 smb_msleep(3 * msec
);
1751 /* sure any further write doesn't update the write time */
1752 start
= timeval_current();
1753 end
= timeval_add(&start
, 15 * sec
, 0);
1754 while (!timeval_expired(&end
)) {
1756 torture_comment(tctx
, "Do a write on the file handle\n");
1757 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1759 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1763 /* get the times after the write */
1764 GET_INFO_BOTH(finfo2
,pinfo2
);
1766 if (finfo2
.basic_info
.out
.write_time
> finfo1
.basic_info
.out
.write_time
) {
1767 double diff
= timeval_elapsed(&start
);
1768 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
1769 "(1sec == %.2f) (wrong!)\n",
1774 smb_msleep(1 * msec
);
1777 GET_INFO_BOTH(finfo2
,pinfo2
);
1778 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
1779 if (finfo2
.basic_info
.out
.write_time
== finfo1
.basic_info
.out
.write_time
) {
1780 torture_comment(tctx
, "Server did not update write_time (correct)\n");
1784 smb_msleep(3 * msec
);
1786 /* get the initial times */
1787 GET_INFO_BOTH(finfo1
,pinfo1
);
1788 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo2
);
1791 * demonstrate that a truncate write always
1792 * updates the write time immediately
1794 for (i
=0; i
< 3; i
++) {
1795 smb_msleep(2 * msec
);
1797 torture_comment(tctx
, "Do a truncate SMBwrite [%d] on the file handle\n", i
);
1798 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 512, 0);
1800 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 0", (int)written
);
1804 /* get the times after the write */
1805 GET_INFO_BOTH(finfo2
,pinfo2
);
1806 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
1811 smb_msleep(3 * msec
);
1813 GET_INFO_BOTH(finfo3
,pinfo3
);
1814 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
1817 * the close doesn't update the write time
1819 torture_comment(tctx
, "Close the file handle\n");
1820 smbcli_close(cli
->tree
, fnum1
);
1823 GET_INFO_PATH(pinfo4
);
1824 COMPARE_WRITE_TIME_EQUAL(pinfo4
, pinfo3
);
1826 if (pinfo4
.basic_info
.out
.write_time
== pinfo3
.basic_info
.out
.write_time
) {
1827 torture_comment(tctx
, "Server did not update the write_time on close (correct)\n");
1832 smbcli_close(cli
->tree
, fnum1
);
1833 smbcli_unlink(cli
->tree
, fname
);
1834 smbcli_deltree(cli
->tree
, BASEDIR
);
1840 * Show a close after write updates the write timestamp to
1841 * the close time, not the last write time.
1844 static bool test_delayed_write_update3b(struct torture_context
*tctx
,
1845 struct smbcli_state
*cli
,
1846 struct smbcli_state
*cli2
)
1848 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
;
1849 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
;
1850 const char *fname
= BASEDIR
"\\torture_file3b.txt";
1854 struct timeval start
;
1856 int used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
1857 int normal_delay
= 2000000;
1858 double sec
= ((double)used_delay
) / ((double)normal_delay
);
1859 int msec
= 1000 * sec
;
1861 torture_comment(tctx
, "\nRunning test_delayed_write_update3b\n");
1863 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1865 torture_comment(tctx
, "Open the file handle\n");
1866 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
1869 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
1873 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1874 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
1878 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1879 pinfo0
.basic_info
.in
.file
.path
= fname
;
1885 /* get the initial times */
1886 GET_INFO_BOTH(finfo0
,pinfo0
);
1889 * sleep some time, to demonstrate the handling of write times
1890 * doesn't depend on the time since the open
1892 smb_msleep(5 * msec
);
1894 /* get the initial times */
1895 GET_INFO_BOTH(finfo1
,pinfo1
);
1896 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo0
);
1899 * make sure the write time is updated 2 seconds later
1900 * calcuated from the first write
1901 * (but expect upto 5 seconds extra time for a busy server)
1903 start
= timeval_current();
1904 end
= timeval_add(&start
, 7 * sec
, 0);
1905 while (!timeval_expired(&end
)) {
1907 torture_comment(tctx
, "Do a write on the file handle\n");
1908 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1910 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1914 /* get the times after the write */
1915 GET_INFO_FILE(finfo1
);
1917 if (finfo1
.basic_info
.out
.write_time
> finfo0
.basic_info
.out
.write_time
) {
1918 double diff
= timeval_elapsed(&start
);
1919 if (diff
< (TIMEDELAY_SECS
* sec
* 0.3)) { /* 0.3 to cope with vmware timing */
1920 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
1921 "(1sec == %.2f) (wrong!)\n",
1927 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
1928 "(1sec == %.2f) (correct)\n",
1932 smb_msleep(0.5 * msec
);
1935 GET_INFO_BOTH(finfo1
,pinfo1
);
1936 COMPARE_WRITE_TIME_GREATER(pinfo1
, pinfo0
);
1938 /* sure any further write doesn't update the write time */
1939 start
= timeval_current();
1940 end
= timeval_add(&start
, 15 * sec
, 0);
1941 while (!timeval_expired(&end
)) {
1943 torture_comment(tctx
, "Do a write on the file handle\n");
1944 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1946 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1950 /* get the times after the write */
1951 GET_INFO_BOTH(finfo2
,pinfo2
);
1953 if (finfo2
.basic_info
.out
.write_time
> finfo1
.basic_info
.out
.write_time
) {
1954 double diff
= timeval_elapsed(&start
);
1955 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
1956 "(1sec == %.2f) (wrong!)\n",
1961 smb_msleep(1 * msec
);
1964 GET_INFO_BOTH(finfo2
,pinfo2
);
1965 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
1966 if (finfo2
.basic_info
.out
.write_time
== finfo1
.basic_info
.out
.write_time
) {
1967 torture_comment(tctx
, "Server did not update write_time (correct)\n");
1971 smb_msleep(5 * msec
);
1973 GET_INFO_BOTH(finfo3
,pinfo3
);
1974 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
1977 * the close updates the write time to the time of the close
1978 * and not to the time of the last write!
1980 torture_comment(tctx
, "Close the file handle\n");
1981 smbcli_close(cli
->tree
, fnum1
);
1984 GET_INFO_PATH(pinfo4
);
1985 COMPARE_WRITE_TIME_GREATER(pinfo4
, pinfo3
);
1987 if (pinfo4
.basic_info
.out
.write_time
> pinfo3
.basic_info
.out
.write_time
) {
1988 torture_comment(tctx
, "Server updated the write_time on close (correct)\n");
1993 smbcli_close(cli
->tree
, fnum1
);
1994 smbcli_unlink(cli
->tree
, fname
);
1995 smbcli_deltree(cli
->tree
, BASEDIR
);
2001 * Check that a write after a truncate write doesn't update
2002 * the timestamp, but a truncate write after a write does.
2003 * Also prove that a close after a truncate write updates the
2004 * timestamp to current, not the time of last write.
2007 static bool test_delayed_write_update3c(struct torture_context
*tctx
,
2008 struct smbcli_state
*cli
,
2009 struct smbcli_state
*cli2
)
2011 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
, finfo4
;
2012 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
, pinfo5
;
2013 const char *fname
= BASEDIR
"\\torture_file3c.txt";
2018 struct timeval start
;
2020 int used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
2021 int normal_delay
= 2000000;
2022 double sec
= ((double)used_delay
) / ((double)normal_delay
);
2023 int msec
= 1000 * sec
;
2025 torture_comment(tctx
, "\nRunning test_delayed_write_update3c\n");
2027 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
2029 torture_comment(tctx
, "Open the file handle\n");
2030 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2033 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
2037 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2038 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
2043 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2044 pinfo0
.basic_info
.in
.file
.path
= fname
;
2051 /* get the initial times */
2052 GET_INFO_BOTH(finfo0
,pinfo0
);
2055 * sleep some time, to demonstrate the handling of write times
2056 * doesn't depend on the time since the open
2058 smb_msleep(5 * msec
);
2060 /* get the initial times */
2061 GET_INFO_BOTH(finfo1
,pinfo1
);
2062 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo0
);
2065 * demonstrate that a truncate write always
2066 * updates the write time immediately
2068 for (i
=0; i
< 3; i
++) {
2069 smb_msleep(2 * msec
);
2071 torture_comment(tctx
, "Do a truncate SMBwrite [%d] on the file handle\n", i
);
2072 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 512, 0);
2074 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 0", (int)written
);
2078 /* get the times after the write */
2079 GET_INFO_BOTH(finfo2
,pinfo2
);
2080 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
2084 start
= timeval_current();
2085 end
= timeval_add(&start
, 7 * sec
, 0);
2086 while (!timeval_expired(&end
)) {
2088 torture_comment(tctx
, "Do a write on the file handle\n");
2089 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2091 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2095 /* get the times after the write */
2096 GET_INFO_FILE(finfo2
);
2098 if (finfo2
.basic_info
.out
.write_time
> finfo1
.basic_info
.out
.write_time
) {
2099 double diff
= timeval_elapsed(&start
);
2100 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
2101 "(1sec == %.2f) (wrong!)\n",
2106 smb_msleep(1 * msec
);
2109 GET_INFO_BOTH(finfo2
,pinfo2
);
2110 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
2111 if (finfo2
.basic_info
.out
.write_time
== finfo1
.basic_info
.out
.write_time
) {
2112 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2116 smb_msleep(5 * msec
);
2118 /* get the initial times */
2119 GET_INFO_BOTH(finfo1
,pinfo1
);
2120 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo2
);
2123 * demonstrate that a truncate write always
2124 * updates the write time immediately
2126 for (i
=0; i
< 3; i
++) {
2127 smb_msleep(2 * msec
);
2129 torture_comment(tctx
, "Do a truncate write [%d] on the file handle\n", i
);
2130 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 512, 0);
2132 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 0", (int)written
);
2136 /* get the times after the write */
2137 GET_INFO_BOTH(finfo2
,pinfo2
);
2138 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
2143 smb_msleep(5 * msec
);
2145 GET_INFO_BOTH(finfo2
,pinfo2
);
2146 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
2148 /* sure any further write doesn't update the write time */
2149 start
= timeval_current();
2150 end
= timeval_add(&start
, 15 * sec
, 0);
2151 while (!timeval_expired(&end
)) {
2153 torture_comment(tctx
, "Do a write on the file handle\n");
2154 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2156 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2160 /* get the times after the write */
2161 GET_INFO_BOTH(finfo2
,pinfo2
);
2163 if (finfo2
.basic_info
.out
.write_time
> finfo1
.basic_info
.out
.write_time
) {
2164 double diff
= timeval_elapsed(&start
);
2165 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
2166 "(1sec == %.2f) (wrong!)\n",
2171 smb_msleep(1 * msec
);
2174 GET_INFO_BOTH(finfo2
,pinfo2
);
2175 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
2176 if (finfo2
.basic_info
.out
.write_time
== finfo1
.basic_info
.out
.write_time
) {
2177 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2181 smb_msleep(5 * msec
);
2183 GET_INFO_BOTH(finfo3
,pinfo3
);
2184 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
2187 * the close updates the write time to the time of the close
2188 * and not to the time of the last write!
2190 torture_comment(tctx
, "Close the file handle\n");
2191 smbcli_close(cli
->tree
, fnum1
);
2194 GET_INFO_PATH(pinfo4
);
2195 COMPARE_WRITE_TIME_GREATER(pinfo4
, pinfo3
);
2197 if (pinfo4
.basic_info
.out
.write_time
> pinfo3
.basic_info
.out
.write_time
) {
2198 torture_comment(tctx
, "Server updated the write_time on close (correct)\n");
2203 smbcli_close(cli
->tree
, fnum1
);
2204 smbcli_unlink(cli
->tree
, fname
);
2205 smbcli_deltree(cli
->tree
, BASEDIR
);
2211 * Show only the first write updates the timestamp, and a close
2212 * after writes updates to current (I think this is the same
2216 static bool test_delayed_write_update4(struct torture_context
*tctx
,
2217 struct smbcli_state
*cli
,
2218 struct smbcli_state
*cli2
)
2220 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
;
2221 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
;
2222 const char *fname
= BASEDIR
"\\torture_file4.txt";
2226 struct timeval start
;
2228 int used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
2229 int normal_delay
= 2000000;
2230 double sec
= ((double)used_delay
) / ((double)normal_delay
);
2231 int msec
= 1000 * sec
;
2233 torture_comment(tctx
, "\nRunning test_delayed_write_update4\n");
2235 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
2237 torture_comment(tctx
, "Open the file handle\n");
2238 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2241 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
2245 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2246 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
2250 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2251 pinfo0
.basic_info
.in
.file
.path
= fname
;
2257 /* get the initial times */
2258 GET_INFO_BOTH(finfo0
,pinfo0
);
2261 smb_msleep(5 * msec
);
2264 torture_comment(tctx
, "Do a write on the file handle\n");
2265 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2267 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2272 GET_INFO_BOTH(finfo1
,pinfo1
);
2273 COMPARE_WRITE_TIME_EQUAL(finfo1
,finfo0
);
2276 * make sure the write time is updated 2 seconds later
2277 * calcuated from the first write
2278 * (but expect upto 3 seconds extra time for a busy server)
2280 start
= timeval_current();
2281 end
= timeval_add(&start
, 5 * sec
, 0);
2282 while (!timeval_expired(&end
)) {
2283 /* get the times after the first write */
2284 GET_INFO_FILE(finfo1
);
2286 if (finfo1
.basic_info
.out
.write_time
> finfo0
.basic_info
.out
.write_time
) {
2287 double diff
= timeval_elapsed(&start
);
2288 if (diff
< (TIMEDELAY_SECS
* sec
* 0.3)) { /* 0.3 to cope with vmware timing */
2289 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
2290 "(1sec == %.2f) (wrong!)\n",
2296 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
2297 "(1sec == %.2f) (correct)\n",
2301 smb_msleep(0.5 * msec
);
2304 GET_INFO_BOTH(finfo1
,pinfo1
);
2305 COMPARE_WRITE_TIME_GREATER(pinfo1
, pinfo0
);
2307 /* sure any further write doesn't update the write time */
2308 start
= timeval_current();
2309 end
= timeval_add(&start
, 15 * sec
, 0);
2310 while (!timeval_expired(&end
)) {
2312 torture_comment(tctx
, "Do a write on the file handle\n");
2313 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2315 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2319 /* get the times after the write */
2320 GET_INFO_BOTH(finfo2
,pinfo2
);
2322 if (finfo2
.basic_info
.out
.write_time
> finfo1
.basic_info
.out
.write_time
) {
2323 double diff
= timeval_elapsed(&start
);
2324 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
2325 "(1sec == %.2f) (wrong!)\n",
2330 smb_msleep(1 * msec
);
2333 GET_INFO_BOTH(finfo2
,pinfo2
);
2334 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
2335 if (finfo2
.basic_info
.out
.write_time
== finfo1
.basic_info
.out
.write_time
) {
2336 torture_comment(tctx
, "Server did not updatewrite_time (correct)\n");
2340 smb_msleep(5 * msec
);
2342 GET_INFO_BOTH(finfo3
,pinfo3
);
2343 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
2346 * the close updates the write time to the time of the close
2347 * and not to the time of the last write!
2349 torture_comment(tctx
, "Close the file handle\n");
2350 smbcli_close(cli
->tree
, fnum1
);
2353 GET_INFO_PATH(pinfo4
);
2354 COMPARE_WRITE_TIME_GREATER(pinfo4
, pinfo3
);
2356 if (pinfo4
.basic_info
.out
.write_time
> pinfo3
.basic_info
.out
.write_time
) {
2357 torture_comment(tctx
, "Server updated the write_time on close (correct)\n");
2362 smbcli_close(cli
->tree
, fnum1
);
2363 smbcli_unlink(cli
->tree
, fname
);
2364 smbcli_deltree(cli
->tree
, BASEDIR
);
2370 * Show writes and closes have no effect on updating times once a SETWRITETIME is done.
2373 static bool test_delayed_write_update5(struct torture_context
*tctx
,
2374 struct smbcli_state
*cli
,
2375 struct smbcli_state
*cli2
)
2377 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
, finfo4
, finfo5
;
2378 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
, pinfo5
, pinfo6
;
2379 const char *fname
= BASEDIR
"\\torture_file5.txt";
2383 struct timeval start
;
2385 int used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
2386 int normal_delay
= 2000000;
2387 double sec
= ((double)used_delay
) / ((double)normal_delay
);
2388 int msec
= 1000 * sec
;
2390 torture_comment(tctx
, "\nRunning test_delayed_write_update5\n");
2392 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
2394 torture_comment(tctx
, "Open the file handle\n");
2395 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2398 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
2402 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2403 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
2409 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2410 pinfo0
.basic_info
.in
.file
.path
= fname
;
2418 /* get the initial times */
2419 GET_INFO_BOTH(finfo0
,pinfo0
);
2422 torture_comment(tctx
, "Do a write on the file handle\n");
2423 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2425 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2430 GET_INFO_BOTH(finfo1
,pinfo1
);
2431 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo0
);
2433 torture_comment(tctx
, "Set write time in the future on the file handle\n");
2434 SET_INFO_FILE(finfo0
, time(NULL
) + 86400);
2435 GET_INFO_BOTH(finfo2
,pinfo2
);
2436 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
2438 torture_comment(tctx
, "Set write time in the past on the file handle\n");
2439 SET_INFO_FILE(finfo0
, time(NULL
) - 86400);
2440 GET_INFO_BOTH(finfo2
,pinfo2
);
2441 COMPARE_WRITE_TIME_LESS(finfo2
, finfo1
);
2443 /* make sure the 2 second delay from the first write are canceled */
2444 start
= timeval_current();
2445 end
= timeval_add(&start
, 15 * sec
, 0);
2446 while (!timeval_expired(&end
)) {
2448 /* get the times after the first write */
2449 GET_INFO_BOTH(finfo3
,pinfo3
);
2451 if (finfo3
.basic_info
.out
.write_time
> finfo2
.basic_info
.out
.write_time
) {
2452 double diff
= timeval_elapsed(&start
);
2453 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
2454 "(1sec == %.2f) (wrong!)\n",
2459 smb_msleep(1 * msec
);
2462 GET_INFO_BOTH(finfo3
,pinfo3
);
2463 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
2464 if (finfo3
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
2465 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2468 /* sure any further write doesn't update the write time */
2469 start
= timeval_current();
2470 end
= timeval_add(&start
, 15 * sec
, 0);
2471 while (!timeval_expired(&end
)) {
2473 torture_comment(tctx
, "Do a write on the file handle\n");
2474 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2476 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2480 /* get the times after the write */
2481 GET_INFO_BOTH(finfo4
,pinfo4
);
2483 if (finfo4
.basic_info
.out
.write_time
> finfo3
.basic_info
.out
.write_time
) {
2484 double diff
= timeval_elapsed(&start
);
2485 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
2486 "(1sec == %.2f) (wrong!)\n",
2491 smb_msleep(1 * msec
);
2494 GET_INFO_BOTH(finfo4
,pinfo4
);
2495 COMPARE_WRITE_TIME_EQUAL(finfo4
, finfo3
);
2496 if (finfo4
.basic_info
.out
.write_time
== finfo3
.basic_info
.out
.write_time
) {
2497 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2501 smb_msleep(5 * msec
);
2503 GET_INFO_BOTH(finfo5
,pinfo5
);
2504 COMPARE_WRITE_TIME_EQUAL(finfo5
, finfo4
);
2507 * the close doesn't update the write time
2509 torture_comment(tctx
, "Close the file handle\n");
2510 smbcli_close(cli
->tree
, fnum1
);
2513 GET_INFO_PATH(pinfo6
);
2514 COMPARE_WRITE_TIME_EQUAL(pinfo6
, pinfo5
);
2516 if (pinfo6
.basic_info
.out
.write_time
== pinfo5
.basic_info
.out
.write_time
) {
2517 torture_comment(tctx
, "Server did not update the write_time on close (correct)\n");
2522 smbcli_close(cli
->tree
, fnum1
);
2523 smbcli_unlink(cli
->tree
, fname
);
2524 smbcli_deltree(cli
->tree
, BASEDIR
);
2530 * Show truncate writes and closes have no effect on updating times once a SETWRITETIME is done.
2533 static bool test_delayed_write_update5b(struct torture_context
*tctx
,
2534 struct smbcli_state
*cli
,
2535 struct smbcli_state
*cli2
)
2537 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
, finfo4
, finfo5
;
2538 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
, pinfo5
, pinfo6
;
2539 const char *fname
= BASEDIR
"\\torture_fileb.txt";
2543 struct timeval start
;
2545 int used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
2546 int normal_delay
= 2000000;
2547 double sec
= ((double)used_delay
) / ((double)normal_delay
);
2548 int msec
= 1000 * sec
;
2550 torture_comment(tctx
, "\nRunning test_delayed_write_update5b\n");
2552 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
2554 torture_comment(tctx
, "Open the file handle\n");
2555 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2558 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
2562 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2563 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
2569 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2570 pinfo0
.basic_info
.in
.file
.path
= fname
;
2578 /* get the initial times */
2579 GET_INFO_BOTH(finfo0
,pinfo0
);
2582 torture_comment(tctx
, "Do a write on the file handle\n");
2583 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2585 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2590 GET_INFO_BOTH(finfo1
,pinfo1
);
2591 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo0
);
2593 torture_comment(tctx
, "Set write time in the future on the file handle\n");
2594 SET_INFO_FILE(finfo0
, time(NULL
) + 86400);
2595 GET_INFO_BOTH(finfo2
,pinfo2
);
2596 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
2598 torture_comment(tctx
, "Set write time in the past on the file handle\n");
2599 SET_INFO_FILE(finfo0
, time(NULL
) - 86400);
2600 GET_INFO_BOTH(finfo2
,pinfo2
);
2601 COMPARE_WRITE_TIME_LESS(finfo2
, finfo1
);
2603 /* make sure the 2 second delay from the first write are canceled */
2604 start
= timeval_current();
2605 end
= timeval_add(&start
, 15 * sec
, 0);
2606 while (!timeval_expired(&end
)) {
2608 /* get the times after the first write */
2609 GET_INFO_BOTH(finfo3
,pinfo3
);
2611 if (finfo3
.basic_info
.out
.write_time
> finfo2
.basic_info
.out
.write_time
) {
2612 double diff
= timeval_elapsed(&start
);
2613 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
2614 "(1sec == %.2f) (wrong!)\n",
2619 smb_msleep(1 * msec
);
2622 GET_INFO_BOTH(finfo3
,pinfo3
);
2623 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
2624 if (finfo3
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
2625 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2628 /* Do any further write (truncates) update the write time ? */
2629 start
= timeval_current();
2630 end
= timeval_add(&start
, 15 * sec
, 0);
2631 while (!timeval_expired(&end
)) {
2633 torture_comment(tctx
, "Do a truncate write on the file handle\n");
2634 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 1024, 0);
2636 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2640 /* get the times after the write */
2641 GET_INFO_BOTH(finfo4
,pinfo4
);
2643 if (finfo4
.basic_info
.out
.write_time
> finfo3
.basic_info
.out
.write_time
) {
2644 double diff
= timeval_elapsed(&start
);
2645 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
2646 "(1sec == %.2f) (wrong!)\n",
2651 smb_msleep(1 * msec
);
2654 GET_INFO_BOTH(finfo4
,pinfo4
);
2655 COMPARE_WRITE_TIME_EQUAL(finfo4
, finfo3
);
2656 if (finfo4
.basic_info
.out
.write_time
== finfo3
.basic_info
.out
.write_time
) {
2657 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2661 smb_msleep(5 * msec
);
2663 GET_INFO_BOTH(finfo5
,pinfo5
);
2664 COMPARE_WRITE_TIME_EQUAL(finfo5
, finfo4
);
2667 * the close doesn't update the write time
2669 torture_comment(tctx
, "Close the file handle\n");
2670 smbcli_close(cli
->tree
, fnum1
);
2673 GET_INFO_PATH(pinfo6
);
2674 COMPARE_WRITE_TIME_EQUAL(pinfo6
, pinfo5
);
2676 if (pinfo6
.basic_info
.out
.write_time
== pinfo5
.basic_info
.out
.write_time
) {
2677 torture_comment(tctx
, "Server did not update the write_time on close (correct)\n");
2682 smbcli_close(cli
->tree
, fnum1
);
2683 smbcli_unlink(cli
->tree
, fname
);
2684 smbcli_deltree(cli
->tree
, BASEDIR
);
2690 * Open 2 handles on a file. Write one one and then set the
2691 * WRITE TIME explicitly on the other. Ensure the write time
2692 * update is cancelled. Ensure the write time is updated to
2693 * the close time when the non-explicit set handle is closed.
2697 static bool test_delayed_write_update6(struct torture_context
*tctx
,
2698 struct smbcli_state
*cli
,
2699 struct smbcli_state
*cli2
)
2701 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
, finfo4
, finfo5
;
2702 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
, pinfo5
, pinfo6
, pinfo7
;
2703 const char *fname
= BASEDIR
"\\torture_file6.txt";
2708 struct timeval start
;
2710 int used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
2711 int normal_delay
= 2000000;
2712 double sec
= ((double)used_delay
) / ((double)normal_delay
);
2713 int msec
= 1000 * sec
;
2716 torture_comment(tctx
, "\nRunning test_delayed_write_update6\n");
2718 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
2720 torture_comment(tctx
, "Open the file handle\n");
2721 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2724 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
2729 torture_comment(tctx
, "Open the 2nd file handle on 2nd connection\n");
2730 fnum2
= smbcli_open(cli2
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2733 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
2738 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2739 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
2745 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2746 pinfo0
.basic_info
.in
.file
.path
= fname
;
2755 /* get the initial times */
2756 GET_INFO_BOTH(finfo0
,pinfo0
);
2759 torture_comment(tctx
, "Do a write on the file handle\n");
2760 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2762 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2767 GET_INFO_BOTH(finfo1
,pinfo1
);
2768 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo0
);
2770 torture_comment(tctx
, "Set write time in the future on the 2nd file handle\n");
2771 SET_INFO_FILE_EX(finfo0
, time(NULL
) + 86400, cli2
->tree
, fnum2
);
2772 GET_INFO_BOTH(finfo2
,pinfo2
);
2773 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
2775 torture_comment(tctx
, "Set write time in the past on the 2nd file handle\n");
2776 SET_INFO_FILE_EX(finfo0
, time(NULL
) - 86400, cli2
->tree
, fnum2
);
2777 GET_INFO_BOTH(finfo2
,pinfo2
);
2778 COMPARE_WRITE_TIME_LESS(finfo2
, finfo1
);
2780 /* make sure the 2 second delay from the first write are canceled */
2781 start
= timeval_current();
2782 end
= timeval_add(&start
, 10 * sec
, 0);
2783 while (!timeval_expired(&end
)) {
2785 /* get the times after the first write */
2786 GET_INFO_BOTH(finfo3
,pinfo3
);
2788 if (finfo3
.basic_info
.out
.write_time
> finfo2
.basic_info
.out
.write_time
) {
2789 double diff
= timeval_elapsed(&start
);
2790 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
2791 "(1sec == %.2f) (wrong!)\n",
2796 smb_msleep(1 * msec
);
2799 GET_INFO_BOTH(finfo3
,pinfo3
);
2800 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
2801 if (finfo3
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
2802 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2805 /* sure any further write doesn't update the write time */
2806 start
= timeval_current();
2807 end
= timeval_add(&start
, 10 * sec
, 0);
2808 while (!timeval_expired(&end
)) {
2810 torture_comment(tctx
, "Do a write on the file handle\n");
2811 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2813 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2817 /* get the times after the write */
2818 GET_INFO_BOTH(finfo4
,pinfo4
);
2820 if (finfo4
.basic_info
.out
.write_time
> finfo3
.basic_info
.out
.write_time
) {
2821 double diff
= timeval_elapsed(&start
);
2822 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
2823 "(1sec == %.2f) (wrong!)\n",
2828 smb_msleep(1 * msec
);
2831 GET_INFO_BOTH(finfo4
,pinfo4
);
2832 COMPARE_WRITE_TIME_EQUAL(finfo4
, finfo3
);
2833 if (finfo4
.basic_info
.out
.write_time
== finfo3
.basic_info
.out
.write_time
) {
2834 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2838 smb_msleep(5 * msec
);
2840 GET_INFO_BOTH(finfo5
,pinfo5
);
2841 COMPARE_WRITE_TIME_EQUAL(finfo5
, finfo4
);
2844 * the close updates the write time to the time of the close
2845 * as the write time was set on the 2nd handle
2847 torture_comment(tctx
, "Close the file handle\n");
2848 smbcli_close(cli
->tree
, fnum1
);
2851 GET_INFO_PATH(pinfo6
);
2852 COMPARE_WRITE_TIME_GREATER(pinfo6
, pinfo5
);
2854 if (pinfo6
.basic_info
.out
.write_time
> pinfo5
.basic_info
.out
.write_time
) {
2855 torture_comment(tctx
, "Server updated the write_time on close (correct)\n");
2858 /* See what the second write handle thinks the time is ? */
2859 finfo5
.basic_info
.in
.file
.fnum
= fnum2
;
2860 GET_INFO_FILE2(finfo5
);
2861 COMPARE_WRITE_TIME_EQUAL(finfo5
, pinfo6
);
2863 /* See if we have lost the sticky write time on handle2 */
2864 smb_msleep(3 * msec
);
2865 torture_comment(tctx
, "Have we lost the sticky write time ?\n");
2867 /* Make sure any further normal write doesn't update the write time */
2868 start
= timeval_current();
2869 end
= timeval_add(&start
, 10 * sec
, 0);
2870 while (!timeval_expired(&end
)) {
2872 torture_comment(tctx
, "Do a write on the second file handle\n");
2873 written
= smbcli_write(cli2
->tree
, fnum2
, 0, "x", 0, 1);
2875 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2879 /* get the times after the write */
2880 GET_INFO_FILE2(finfo5
);
2881 GET_INFO_PATH(pinfo6
);
2883 if (finfo5
.basic_info
.out
.write_time
> pinfo6
.basic_info
.out
.write_time
) {
2884 double diff
= timeval_elapsed(&start
);
2885 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
2886 "(1sec == %.2f) (wrong!)\n",
2891 smb_msleep(1 * msec
);
2894 /* What about a truncate write ? */
2895 start
= timeval_current();
2896 end
= timeval_add(&start
, 10 * sec
, 0);
2897 while (!timeval_expired(&end
)) {
2899 torture_comment(tctx
, "Do a truncate write on the second file handle\n");
2900 written
= smbcli_write(cli2
->tree
, fnum2
, 0, "x", 0, 0);
2902 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2906 /* get the times after the write */
2907 GET_INFO_FILE2(finfo5
);
2908 GET_INFO_PATH(pinfo6
);
2910 if (finfo5
.basic_info
.out
.write_time
> pinfo6
.basic_info
.out
.write_time
) {
2911 double diff
= timeval_elapsed(&start
);
2912 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
2913 "(1sec == %.2f) (wrong!)\n",
2918 smb_msleep(1 * msec
);
2922 /* keep the 2nd handle open and rerun tests */
2929 * closing the 2nd handle will cause no write time update
2930 * as the write time was explicit set on this handle
2932 torture_comment(tctx
, "Close the 2nd file handle\n");
2933 smbcli_close(cli2
->tree
, fnum2
);
2936 GET_INFO_PATH(pinfo7
);
2937 COMPARE_WRITE_TIME_EQUAL(pinfo7
, pinfo6
);
2939 if (pinfo7
.basic_info
.out
.write_time
== pinfo6
.basic_info
.out
.write_time
) {
2940 torture_comment(tctx
, "Server did not update the write_time on close (correct)\n");
2945 smbcli_close(cli
->tree
, fnum1
);
2947 smbcli_close(cli2
->tree
, fnum2
);
2948 smbcli_unlink(cli
->tree
, fname
);
2949 smbcli_deltree(cli
->tree
, BASEDIR
);
2954 static bool test_delayed_write_update7(struct torture_context
*tctx
, struct smbcli_state
*cli
)
2956 union smb_open open_parms
;
2957 union smb_fileinfo finfo1
, finfo2
, finfo3
;
2958 const char *fname
= BASEDIR
"\\torture_file7.txt";
2962 TALLOC_CTX
*mem_ctx
;
2964 torture_comment(tctx
, "\nRunning test_delayed_write_update7 (timestamp resolution test)\n");
2966 mem_ctx
= talloc_init("test_delayed_write_update7");
2967 if (!mem_ctx
) return false;
2969 ZERO_STRUCT(finfo1
);
2970 ZERO_STRUCT(finfo2
);
2971 ZERO_STRUCT(finfo3
);
2972 ZERO_STRUCT(open_parms
);
2974 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
2976 /* Create the file. */
2977 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2979 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s", fname
);
2983 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2984 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
2988 /* Get the initial timestamps. */
2989 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
2991 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
2993 /* Set the pending write time to a value with ns. */
2994 SET_INFO_FILE_NS(finfo
, time(NULL
) + 86400, 103, cli
->tree
, fnum1
);
2996 /* Get the current pending write time by fnum. */
2997 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
2999 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
3001 /* Ensure the time is actually different. */
3002 if (finfo1
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
3003 torture_result(tctx
, TORTURE_FAIL
,
3004 "setfileinfo time matches original fileinfo time");
3008 /* Get the current pending write time by path. */
3009 finfo3
.basic_info
.in
.file
.path
= fname
;
3010 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo3
);
3012 if (finfo2
.basic_info
.out
.write_time
!= finfo3
.basic_info
.out
.write_time
) {
3013 torture_result(tctx
, TORTURE_FAIL
,
3014 "qpathinfo time doens't match fileinfo time");
3018 /* Now close the file. Re-open and check that the write
3019 time is identical to the one we wrote. */
3021 smbcli_close(cli
->tree
, fnum1
);
3023 open_parms
.ntcreatex
.level
= RAW_OPEN_NTCREATEX
;
3024 open_parms
.ntcreatex
.in
.flags
= 0;
3025 open_parms
.ntcreatex
.in
.access_mask
= SEC_GENERIC_READ
;
3026 open_parms
.ntcreatex
.in
.file_attr
= 0;
3027 open_parms
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_DELETE
|
3028 NTCREATEX_SHARE_ACCESS_READ
|
3029 NTCREATEX_SHARE_ACCESS_WRITE
;
3030 open_parms
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
3031 open_parms
.ntcreatex
.in
.create_options
= 0;
3032 open_parms
.ntcreatex
.in
.fname
= fname
;
3034 status
= smb_raw_open(cli
->tree
, mem_ctx
, &open_parms
);
3035 talloc_free(mem_ctx
);
3037 if (!NT_STATUS_IS_OK(status
)) {
3038 torture_result(tctx
, TORTURE_FAIL
,
3039 "setfileinfo time matches original fileinfo time");
3043 fnum1
= open_parms
.ntcreatex
.out
.file
.fnum
;
3045 /* Check the returned time matches. */
3046 if (open_parms
.ntcreatex
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
3047 torture_result(tctx
, TORTURE_FAIL
,
3048 "final open time does not match set time");
3054 smbcli_close(cli
->tree
, fnum1
);
3056 smbcli_unlink(cli
->tree
, fname
);
3057 smbcli_deltree(cli
->tree
, BASEDIR
);
3062 testing of delayed update of write_time
3064 struct torture_suite
*torture_delay_write(void)
3066 struct torture_suite
*suite
= torture_suite_create(talloc_autofree_context(), "delaywrite");
3068 torture_suite_add_2smb_test(suite
, "finfo update on close", test_finfo_after_write
);
3069 torture_suite_add_1smb_test(suite
, "delayed update of write time", test_delayed_write_update
);
3070 torture_suite_add_1smb_test(suite
, "update of write time and SMBwrite truncate", test_delayed_write_update1
);
3071 torture_suite_add_1smb_test(suite
, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a
);
3072 torture_suite_add_1smb_test(suite
, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b
);
3073 torture_suite_add_1smb_test(suite
, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c
);
3074 torture_suite_add_2smb_test(suite
, "delayed update of write time using 2 connections", test_delayed_write_update2
);
3075 torture_suite_add_2smb_test(suite
, "delayed update of write time 3", test_delayed_write_update3
);
3076 torture_suite_add_2smb_test(suite
, "delayed update of write time 3a", test_delayed_write_update3a
);
3077 torture_suite_add_2smb_test(suite
, "delayed update of write time 3b", test_delayed_write_update3b
);
3078 torture_suite_add_2smb_test(suite
, "delayed update of write time 3c", test_delayed_write_update3c
);
3079 torture_suite_add_2smb_test(suite
, "delayed update of write time 4", test_delayed_write_update4
);
3080 torture_suite_add_2smb_test(suite
, "delayed update of write time 5", test_delayed_write_update5
);
3081 torture_suite_add_2smb_test(suite
, "delayed update of write time 5b", test_delayed_write_update5b
);
3082 torture_suite_add_2smb_test(suite
, "delayed update of write time 6", test_delayed_write_update6
);
3083 torture_suite_add_1smb_test(suite
, "timestamp resolution test", test_delayed_write_update7
);
3084 torture_suite_add_1smb_test(suite
, "timestamp resolution test", test_delayed_write_update7
);