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 BASEDIR "\\delaywrite"
36 static bool test_delayed_write_update(struct torture_context
*tctx
, struct smbcli_state
*cli
)
38 union smb_fileinfo finfo1
, finfo2
;
39 const char *fname
= BASEDIR
"\\torture_file.txt";
46 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
47 int normal_delay
= 2000000;
48 double sec
= ((double)used_delay
) / ((double)normal_delay
);
49 int msec
= 1000 * sec
;
51 torture_comment(tctx
, "\nRunning test_delayed_write_update\n");
53 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
55 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
57 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s", fname
);
61 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
62 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
65 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
67 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
69 torture_comment(tctx
, "Initial write time %s\n",
70 nt_time_string(tctx
, finfo1
.basic_info
.out
.write_time
));
72 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
75 torture_result(tctx
, TORTURE_FAIL
,
76 "write failed - wrote %d bytes (%s)\n",
77 (int)written
, __location__
);
81 start
= timeval_current();
82 end
= timeval_add(&start
, (120 * sec
), 0);
83 while (!timeval_expired(&end
)) {
84 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
86 if (!NT_STATUS_IS_OK(status
)) {
87 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
91 torture_comment(tctx
, "write time %s\n",
92 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
93 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
94 double diff
= timeval_elapsed(&start
);
95 if (diff
< (used_delay
/ (double)1000000)) {
96 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds"
97 "(expected > %.2f) (wrong!)\n",
98 diff
, used_delay
/ (double)1000000);
103 torture_comment(tctx
, "Server updated write_time after %.2f seconds (correct)\n",
108 smb_msleep(1 * msec
);
111 if (finfo1
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
112 torture_result(tctx
, TORTURE_FAIL
,
113 "Server did not update write time (wrong!)");
119 smbcli_close(cli
->tree
, fnum1
);
120 smbcli_unlink(cli
->tree
, fname
);
121 smbcli_deltree(cli
->tree
, BASEDIR
);
126 static bool test_delayed_write_update1(struct torture_context
*tctx
, struct smbcli_state
*cli
)
128 union smb_fileinfo finfo1
, finfo2
, finfo3
, pinfo4
;
129 const char *fname
= BASEDIR
"\\torture_file1.txt";
134 struct timeval start
;
136 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
137 int normal_delay
= 2000000;
138 double sec
= ((double)used_delay
) / ((double)normal_delay
);
139 int msec
= 1000 * sec
;
142 torture_comment(tctx
, "\nRunning test_delayed_write_update1\n");
144 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
146 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
148 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s", fname
);
152 memset(buf
, 'x', 2048);
153 written
= smbcli_write(cli
->tree
, fnum1
, 0, buf
, 0, 2048);
155 /* 3 second delay to ensure we get past any 2 second time
156 granularity (older systems may have that) */
157 smb_msleep(3 * msec
);
159 finfo1
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
160 finfo1
.all_info
.in
.file
.fnum
= fnum1
;
163 pinfo4
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
164 pinfo4
.all_info
.in
.file
.path
= fname
;
166 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
168 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
170 torture_comment(tctx
, "Initial write time %s\n",
171 nt_time_string(tctx
, finfo1
.all_info
.out
.write_time
));
173 /* 3 second delay to ensure we get past any 2 second time
174 granularity (older systems may have that) */
175 smb_msleep(3 * msec
);
177 /* Do a zero length SMBwrite call to truncate. */
178 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 1024, 0);
181 torture_result(tctx
, TORTURE_FAIL
,
182 "write failed - wrote %d bytes (%s)\n",
183 (int)written
, __location__
);
187 start
= timeval_current();
188 end
= timeval_add(&start
, (120 * sec
), 0);
189 while (!timeval_expired(&end
)) {
190 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
192 if (!NT_STATUS_IS_OK(status
)) {
193 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
198 if (finfo2
.all_info
.out
.size
!= 1024) {
199 torture_result(tctx
, TORTURE_FAIL
,
200 "file not truncated, size = %u (should be 1024)",
201 (unsigned int)finfo2
.all_info
.out
.size
);
206 torture_comment(tctx
, "write time %s\n",
207 nt_time_string(tctx
, finfo2
.all_info
.out
.write_time
));
208 if (finfo1
.all_info
.out
.write_time
!= finfo2
.all_info
.out
.write_time
) {
209 double diff
= timeval_elapsed(&start
);
210 if (diff
> (0.25 * (used_delay
/ (double)1000000))) {
211 torture_result(tctx
, TORTURE_FAIL
, "After SMBwrite truncate "
212 "server updated write_time after %.2f seconds"
213 "(write time update dealy == %.2f)(wrong!)\n",
214 diff
, used_delay
/ (double)1000000);
219 torture_comment(tctx
, "After SMBwrite truncate "
220 "server updated write_time after %.2f seconds"
221 "(1 sec == %.2f)(correct)\n",
222 diff
, used_delay
/ (double)1000000);
226 smb_msleep(1 * msec
);
229 if (finfo1
.all_info
.out
.write_time
== finfo2
.all_info
.out
.write_time
) {
230 torture_result(tctx
, TORTURE_FAIL
,
231 "Server did not update write time (wrong!)");
236 smb_msleep(2 * msec
);
238 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
239 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 0, 1);
242 torture_result(tctx
, TORTURE_FAIL
,
243 "write failed - wrote %d bytes (%s)",
244 (int)written
, __location__
);
248 start
= timeval_current();
249 end
= timeval_add(&start
, (10*sec
), 0);
250 while (!timeval_expired(&end
)) {
251 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo3
);
253 if (!NT_STATUS_IS_OK(status
)) {
254 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
259 if (finfo3
.all_info
.out
.size
!= 1024) {
260 DEBUG(0, ("file not truncated, size = %u (should be 1024)\n",
261 (unsigned int)finfo3
.all_info
.out
.size
));
266 torture_comment(tctx
, "write time %s\n",
267 nt_time_string(tctx
, finfo3
.all_info
.out
.write_time
));
268 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
269 double diff
= timeval_elapsed(&start
);
271 torture_comment(tctx
, "server updated write_time after %.2f seconds"
277 smb_msleep(1 * msec
);
280 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
281 torture_result(tctx
, TORTURE_FAIL
,
282 "Server updated write time (wrong!)");
287 smb_msleep(2 * msec
);
289 /* the close should trigger an write time update */
290 smbcli_close(cli
->tree
, fnum1
);
293 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &pinfo4
);
294 torture_assert_ntstatus_ok(tctx
, status
, "pathinfo failed");
296 if (finfo3
.all_info
.out
.write_time
== pinfo4
.all_info
.out
.write_time
) {
297 torture_result(tctx
, TORTURE_FAIL
,
298 "Server did not update write time on close (wrong!)");
300 } else if (finfo3
.all_info
.out
.write_time
< pinfo4
.all_info
.out
.write_time
) {
301 torture_comment(tctx
, "Server updated write time on close (correct)\n");
305 smbcli_close(cli
->tree
, fnum1
);
306 smbcli_unlink(cli
->tree
, fname
);
307 smbcli_deltree(cli
->tree
, BASEDIR
);
312 /* Updating with a SMBwrite of zero length
313 * changes the write time immediately - even on expand. */
315 static bool test_delayed_write_update1a(struct torture_context
*tctx
, struct smbcli_state
*cli
)
317 union smb_fileinfo finfo1
, finfo2
, finfo3
, pinfo4
;
318 const char *fname
= BASEDIR
"\\torture_file1a.txt";
323 struct timeval start
;
325 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
326 int normal_delay
= 2000000;
327 double sec
= ((double)used_delay
) / ((double)normal_delay
);
328 int msec
= 1000 * sec
;
331 torture_comment(tctx
, "\nRunning test_delayed_write_update1a\n");
333 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
335 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
337 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s", fname
);
341 memset(buf
, 'x', 2048);
342 written
= smbcli_write(cli
->tree
, fnum1
, 0, buf
, 0, 2048);
344 /* 3 second delay to ensure we get past any 2 second time
345 granularity (older systems may have that) */
346 smb_msleep(3 * msec
);
348 finfo1
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
349 finfo1
.all_info
.in
.file
.fnum
= fnum1
;
352 pinfo4
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
353 pinfo4
.all_info
.in
.file
.path
= fname
;
355 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
357 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
359 torture_comment(tctx
, "Initial write time %s\n",
360 nt_time_string(tctx
, finfo1
.all_info
.out
.write_time
));
362 /* Do a zero length SMBwrite call to truncate. */
363 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 10240, 0);
366 torture_result(tctx
, TORTURE_FAIL
, "write failed - wrote %d bytes (%s)",
367 (int)written
, __location__
);
371 start
= timeval_current();
372 end
= timeval_add(&start
, (120*sec
), 0);
373 while (!timeval_expired(&end
)) {
374 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
376 if (!NT_STATUS_IS_OK(status
)) {
377 torture_result(tctx
, TORTURE_FAIL
, "fileinfo failed: %s",
383 if (finfo2
.all_info
.out
.size
!= 10240) {
384 torture_result(tctx
, TORTURE_FAIL
,
385 "file not truncated, size = %u (should be 10240)",
386 (unsigned int)finfo2
.all_info
.out
.size
);
391 torture_comment(tctx
, "write time %s\n",
392 nt_time_string(tctx
, finfo2
.all_info
.out
.write_time
));
393 if (finfo1
.all_info
.out
.write_time
!= finfo2
.all_info
.out
.write_time
) {
394 double diff
= timeval_elapsed(&start
);
395 if (diff
> (0.25 * (used_delay
/ (double)1000000))) {
396 torture_result(tctx
, TORTURE_FAIL
, "After SMBwrite truncate "
397 "server updated write_time after %.2f seconds"
398 "(write time update delay == %.2f)(wrong!)\n",
399 diff
, used_delay
/ (double)1000000);
404 torture_comment(tctx
, "After SMBwrite truncate "
405 "server updated write_time after %.2f seconds"
406 "(write time update delay == %.2f)(correct)\n",
407 diff
, used_delay
/ (double)1000000);
411 smb_msleep(1 * msec
);
414 if (finfo1
.all_info
.out
.write_time
== finfo2
.all_info
.out
.write_time
) {
415 torture_result(tctx
, TORTURE_FAIL
,
416 "Server did not update write time (wrong!)");
421 smb_msleep(2 * msec
);
423 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
424 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 0, 1);
426 torture_assert_int_equal(tctx
, written
, 1,
427 "unexpected number of bytes written");
429 start
= timeval_current();
430 end
= timeval_add(&start
, (10*sec
), 0);
431 while (!timeval_expired(&end
)) {
432 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo3
);
434 if (!NT_STATUS_IS_OK(status
)) {
435 torture_result(tctx
, TORTURE_FAIL
, "fileinfo failed: %s\n",
441 if (finfo3
.all_info
.out
.size
!= 10240) {
442 torture_result(tctx
, TORTURE_FAIL
,
443 "file not truncated, size = %u (should be 10240)",
444 (unsigned int)finfo3
.all_info
.out
.size
);
449 torture_comment(tctx
, "write time %s\n",
450 nt_time_string(tctx
, finfo3
.all_info
.out
.write_time
));
451 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
452 double diff
= timeval_elapsed(&start
);
454 torture_result(tctx
, TORTURE_FAIL
, "server updated write_time after %.2f seconds"
455 "(write time update delay == %.2f)(correct)\n",
456 diff
, used_delay
/ (double)1000000);
460 smb_msleep(1 * msec
);
463 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
464 torture_result(tctx
, TORTURE_FAIL
,
465 "Server updated write time (wrong!)");
469 /* the close should trigger an write time update */
470 smbcli_close(cli
->tree
, fnum1
);
473 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &pinfo4
);
474 torture_assert_ntstatus_ok(tctx
, status
, "pathinfo failed");
476 if (finfo3
.all_info
.out
.write_time
== pinfo4
.all_info
.out
.write_time
) {
477 torture_result(tctx
, TORTURE_FAIL
,
478 "Server did not update write time on close (wrong!)");
480 } else if (finfo3
.all_info
.out
.write_time
< pinfo4
.all_info
.out
.write_time
) {
481 torture_comment(tctx
, "Server updated write time on close (correct)\n");
485 smbcli_close(cli
->tree
, fnum1
);
486 smbcli_unlink(cli
->tree
, fname
);
487 smbcli_deltree(cli
->tree
, BASEDIR
);
492 /* Updating with a SET_FILE_END_OF_FILE_INFO
493 * changes the write time immediately - even on expand. */
495 static bool test_delayed_write_update1b(struct torture_context
*tctx
, struct smbcli_state
*cli
)
497 union smb_fileinfo finfo1
, finfo2
, finfo3
, pinfo4
;
498 const char *fname
= BASEDIR
"\\torture_file1b.txt";
503 struct timeval start
;
505 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
506 int normal_delay
= 2000000;
507 double sec
= ((double)used_delay
) / ((double)normal_delay
);
508 int msec
= 1000 * sec
;
511 torture_comment(tctx
, "\nRunning test_delayed_write_update1b\n");
513 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
515 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
517 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s", fname
);
521 memset(buf
, 'x', 2048);
522 written
= smbcli_write(cli
->tree
, fnum1
, 0, buf
, 0, 2048);
524 /* 3 second delay to ensure we get past any 2 second time
525 granularity (older systems may have that) */
526 smb_msleep(3 * msec
);
528 finfo1
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
529 finfo1
.all_info
.in
.file
.fnum
= fnum1
;
532 pinfo4
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
533 pinfo4
.all_info
.in
.file
.path
= fname
;
535 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
537 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
539 torture_comment(tctx
, "Initial write time %s\n",
540 nt_time_string(tctx
, finfo1
.all_info
.out
.write_time
));
542 /* Do a SET_END_OF_FILE_INFO call to truncate. */
543 status
= smbcli_ftruncate(cli
->tree
, fnum1
, (uint64_t)10240);
545 torture_assert_ntstatus_ok(tctx
, status
, "SET_END_OF_FILE failed");
547 start
= timeval_current();
548 end
= timeval_add(&start
, (120*sec
), 0);
549 while (!timeval_expired(&end
)) {
550 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
552 if (!NT_STATUS_IS_OK(status
)) {
553 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
558 if (finfo2
.all_info
.out
.size
!= 10240) {
559 torture_result(tctx
, TORTURE_FAIL
,
560 "file not truncated (size = %u, should be 10240)",
561 (unsigned int)finfo2
.all_info
.out
.size
);
566 torture_comment(tctx
, "write time %s\n",
567 nt_time_string(tctx
, finfo2
.all_info
.out
.write_time
));
568 if (finfo1
.all_info
.out
.write_time
!= finfo2
.all_info
.out
.write_time
) {
569 double diff
= timeval_elapsed(&start
);
570 if (diff
> (0.25 * (used_delay
/ (double)1000000))) {
571 torture_result(tctx
, TORTURE_FAIL
,
572 "After SET_END_OF_FILE truncate "
573 "server updated write_time after %.2f seconds"
574 "(write time update delay == %.2f)(wrong!)",
575 diff
, used_delay
/ (double)1000000);
580 torture_comment(tctx
, "After SET_END_OF_FILE truncate "
581 "server updated write_time after %.2f seconds"
582 "(write time update delay == %.2f)(correct)\n",
583 diff
, used_delay
/ (double)1000000);
587 smb_msleep(1 * msec
);
590 if (finfo1
.all_info
.out
.write_time
== finfo2
.all_info
.out
.write_time
) {
591 torture_result(tctx
, TORTURE_FAIL
,
592 "Server did not update write time (wrong!)");
597 smb_msleep(2 * msec
);
599 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
600 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 0, 1);
602 torture_assert_int_equal(tctx
, written
, 1,
603 "unexpected number of bytes written");
605 start
= timeval_current();
606 end
= timeval_add(&start
, (10*sec
), 0);
607 while (!timeval_expired(&end
)) {
608 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo3
);
610 if (!NT_STATUS_IS_OK(status
)) {
611 torture_result(tctx
, TORTURE_FAIL
,
612 "fileinfo failed: %s", nt_errstr(status
));
617 if (finfo3
.all_info
.out
.size
!= 10240) {
618 DEBUG(0, ("file not truncated (size = %u, should be 10240)\n",
619 (unsigned int)finfo3
.all_info
.out
.size
));
624 torture_comment(tctx
, "write time %s\n",
625 nt_time_string(tctx
, finfo3
.all_info
.out
.write_time
));
626 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
627 double diff
= timeval_elapsed(&start
);
629 torture_comment(tctx
, "server updated write_time after %.2f seconds"
630 "(write time update delay == %.2f)(correct)\n",
631 diff
, used_delay
/ (double)1000000);
635 smb_msleep(1 * msec
);
638 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
639 torture_result(tctx
, TORTURE_FAIL
, "Server updated write time (wrong!)\n");
643 /* the close should trigger an write time update */
644 smbcli_close(cli
->tree
, fnum1
);
647 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &pinfo4
);
648 torture_assert_ntstatus_ok(tctx
, status
, "pathinfo failed");
650 if (finfo3
.all_info
.out
.write_time
== pinfo4
.all_info
.out
.write_time
) {
651 torture_result(tctx
, TORTURE_FAIL
, "Server did not update write time on close (wrong!)\n");
653 } else if (finfo3
.all_info
.out
.write_time
< pinfo4
.all_info
.out
.write_time
) {
654 torture_comment(tctx
, "Server updated write time on close (correct)\n");
658 smbcli_close(cli
->tree
, fnum1
);
659 smbcli_unlink(cli
->tree
, fname
);
660 smbcli_deltree(cli
->tree
, BASEDIR
);
665 /* Updating with a SET_ALLOCATION_INFO (truncate) does so immediately. */
667 static bool test_delayed_write_update1c(struct torture_context
*tctx
, struct smbcli_state
*cli
)
669 union smb_setfileinfo parms
;
670 union smb_fileinfo finfo1
, finfo2
, finfo3
, pinfo4
;
671 const char *fname
= BASEDIR
"\\torture_file1c.txt";
676 struct timeval start
;
678 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
679 int normal_delay
= 2000000;
680 double sec
= ((double)used_delay
) / ((double)normal_delay
);
681 int msec
= 1000 * sec
;
684 torture_comment(tctx
, "\nRunning test_delayed_write_update1c\n");
686 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
688 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
690 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s", fname
);
694 memset(buf
, 'x', 2048);
695 written
= smbcli_write(cli
->tree
, fnum1
, 0, buf
, 0, 2048);
697 /* 3 second delay to ensure we get past any 2 second time
698 granularity (older systems may have that) */
699 smb_msleep(3 * msec
);
701 finfo1
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
702 finfo1
.all_info
.in
.file
.fnum
= fnum1
;
705 pinfo4
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
706 pinfo4
.all_info
.in
.file
.path
= fname
;
708 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
710 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
712 torture_comment(tctx
, "Initial write time %s\n",
713 nt_time_string(tctx
, finfo1
.all_info
.out
.write_time
));
715 /* Do a SET_ALLOCATION_SIZE call to truncate. */
716 parms
.allocation_info
.level
= RAW_SFILEINFO_ALLOCATION_INFO
;
717 parms
.allocation_info
.in
.file
.fnum
= fnum1
;
718 parms
.allocation_info
.in
.alloc_size
= 0;
720 status
= smb_raw_setfileinfo(cli
->tree
, &parms
);
722 torture_assert_ntstatus_ok(tctx
, status
,
723 "RAW_SFILEINFO_ALLOCATION_INFO failed");
725 start
= timeval_current();
726 end
= timeval_add(&start
, (120*sec
), 0);
727 while (!timeval_expired(&end
)) {
728 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
730 if (!NT_STATUS_IS_OK(status
)) {
731 torture_result(tctx
, TORTURE_FAIL
, "fileinfo failed: %s",
737 if (finfo2
.all_info
.out
.size
!= 0) {
738 torture_result(tctx
, TORTURE_FAIL
,
739 "file not truncated (size = %u, should be 10240)",
740 (unsigned int)finfo2
.all_info
.out
.size
);
745 torture_comment(tctx
, "write time %s\n",
746 nt_time_string(tctx
, finfo2
.all_info
.out
.write_time
));
747 if (finfo1
.all_info
.out
.write_time
!= finfo2
.all_info
.out
.write_time
) {
748 double diff
= timeval_elapsed(&start
);
749 if (diff
> (0.25 * (used_delay
/ (double)1000000))) {
750 torture_result(tctx
, TORTURE_FAIL
, "After SET_ALLOCATION_INFO truncate "
751 "server updated write_time after %.2f seconds"
752 "(write time update delay == %.2f)(wrong!)\n",
753 diff
, used_delay
/ (double)1000000);
758 torture_comment(tctx
, "After SET_ALLOCATION_INFO truncate "
759 "server updated write_time after %.2f seconds"
760 "(write time update delay == %.2f)(correct)\n",
761 diff
, used_delay
/ (double)1000000);
765 smb_msleep(1 * msec
);
768 if (finfo1
.all_info
.out
.write_time
== finfo2
.all_info
.out
.write_time
) {
769 torture_result(tctx
, TORTURE_FAIL
,
770 "Server did not update write time (wrong!)");
775 smb_msleep(2 * msec
);
777 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
778 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 0, 1);
779 torture_assert_int_equal(tctx
, written
, 1,
780 "Unexpected number of bytes written");
782 start
= timeval_current();
783 end
= timeval_add(&start
, (10*sec
), 0);
784 while (!timeval_expired(&end
)) {
785 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo3
);
787 if (!NT_STATUS_IS_OK(status
)) {
788 torture_result(tctx
, TORTURE_FAIL
, "fileinfo failed: %s",
794 if (finfo3
.all_info
.out
.size
!= 1) {
795 torture_result(tctx
, TORTURE_FAIL
, "file not expanded");
800 torture_comment(tctx
, "write time %s\n",
801 nt_time_string(tctx
, finfo3
.all_info
.out
.write_time
));
802 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
803 double diff
= timeval_elapsed(&start
);
805 torture_comment(tctx
, "server updated write_time after %.2f seconds"
806 "(write time update delay == %.2f)(wrong)\n",
807 diff
, used_delay
/ (double)1000000);
811 smb_msleep(1 * msec
);
814 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
815 torture_result(tctx
, TORTURE_FAIL
,
816 "Server updated write time (wrong!)");
820 /* the close should trigger an write time update */
821 smbcli_close(cli
->tree
, fnum1
);
824 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &pinfo4
);
825 torture_assert_ntstatus_ok(tctx
, status
, "pathinfo failed");
827 if (finfo3
.all_info
.out
.write_time
== pinfo4
.all_info
.out
.write_time
) {
828 torture_result(tctx
, TORTURE_FAIL
, "Server did not update write time on close (wrong!)\n");
830 } else if (finfo3
.all_info
.out
.write_time
< pinfo4
.all_info
.out
.write_time
) {
831 torture_comment(tctx
, "Server updated write time on close (correct)\n");
835 smbcli_close(cli
->tree
, fnum1
);
836 smbcli_unlink(cli
->tree
, fname
);
837 smbcli_deltree(cli
->tree
, BASEDIR
);
843 * Do as above, but using 2 connections.
846 static bool test_delayed_write_update2(struct torture_context
*tctx
, struct smbcli_state
*cli
,
847 struct smbcli_state
*cli2
)
849 union smb_fileinfo finfo1
, finfo2
;
850 const char *fname
= BASEDIR
"\\torture_file.txt";
856 struct timeval start
;
858 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
859 int normal_delay
= 2000000;
860 double sec
= ((double)used_delay
) / ((double)normal_delay
);
861 int msec
= 1000 * sec
;
862 union smb_flush flsh
;
864 torture_comment(tctx
, "\nRunning test_delayed_write_update2\n");
866 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
868 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
870 torture_comment(tctx
, "Failed to open %s\n", fname
);
874 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
875 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
878 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
880 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
882 torture_comment(tctx
, "Initial write time %s\n",
883 nt_time_string(tctx
, finfo1
.basic_info
.out
.write_time
));
885 /* 3 second delay to ensure we get past any 2 second time
886 granularity (older systems may have that) */
887 smb_msleep(3 * msec
);
890 /* Try using setfileinfo instead of write to update write time. */
891 union smb_setfileinfo sfinfo
;
892 time_t t_set
= time(NULL
);
893 sfinfo
.basic_info
.level
= RAW_SFILEINFO_BASIC_INFO
;
894 sfinfo
.basic_info
.in
.file
.fnum
= fnum1
;
895 sfinfo
.basic_info
.in
.create_time
= finfo1
.basic_info
.out
.create_time
;
896 sfinfo
.basic_info
.in
.access_time
= finfo1
.basic_info
.out
.access_time
;
898 /* I tried this with both + and - ve to see if it makes a different.
899 It doesn't - once the filetime is set via setfileinfo it stays that way. */
901 unix_to_nt_time(&sfinfo
.basic_info
.in
.write_time
, t_set
- 30000);
903 unix_to_nt_time(&sfinfo
.basic_info
.in
.write_time
, t_set
+ 30000);
905 sfinfo
.basic_info
.in
.change_time
= finfo1
.basic_info
.out
.change_time
;
906 sfinfo
.basic_info
.in
.attrib
= finfo1
.basic_info
.out
.attrib
;
908 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
910 torture_assert_ntstatus_ok(tctx
, status
, "sfileinfo failed");
913 finfo2
.basic_info
.in
.file
.path
= fname
;
915 status
= smb_raw_pathinfo(cli2
->tree
, tctx
, &finfo2
);
917 if (!NT_STATUS_IS_OK(status
)) {
918 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
921 torture_comment(tctx
, "write time %s\n",
922 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
924 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
925 torture_comment(tctx
, "Server updated write_time (correct)\n");
927 torture_result(tctx
, TORTURE_FAIL
, "Server did not update write time (wrong!)\n");
931 /* Now try a write to see if the write time gets reset. */
933 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
934 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
937 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
939 if (!NT_STATUS_IS_OK(status
)) {
940 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
944 torture_comment(tctx
, "Modified write time %s\n",
945 nt_time_string(tctx
, finfo1
.basic_info
.out
.write_time
));
948 torture_comment(tctx
, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
950 written
= smbcli_write(cli
->tree
, fnum1
, 0, "0123456789", 1, 10);
953 torture_result(tctx
, TORTURE_FAIL
, "write failed - wrote %d bytes (%s)\n",
954 (int)written
, __location__
);
958 /* Just to prove to tridge that the an smbflush has no effect on
959 the write time :-). The setfileinfo IS STICKY. JRA. */
961 torture_comment(tctx
, "Doing flush after write\n");
963 flsh
.flush
.level
= RAW_FLUSH_FLUSH
;
964 flsh
.flush
.in
.file
.fnum
= fnum1
;
965 status
= smb_raw_flush(cli
->tree
, &flsh
);
966 if (!NT_STATUS_IS_OK(status
)) {
967 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status
)));
971 /* Once the time was set using setfileinfo then it stays set - writes
972 don't have any effect. But make sure. */
973 start
= timeval_current();
974 end
= timeval_add(&start
, (15*sec
), 0);
975 while (!timeval_expired(&end
)) {
976 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
978 if (!NT_STATUS_IS_OK(status
)) {
979 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
983 torture_comment(tctx
, "write time %s\n",
984 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
985 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
986 double diff
= timeval_elapsed(&start
);
987 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds"
994 smb_msleep(1 * msec
);
997 if (finfo1
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
998 torture_comment(tctx
, "Server did not update write time (correct)\n");
1002 smb_msleep(2 * msec
);
1004 fnum2
= smbcli_open(cli
->tree
, fname
, O_RDWR
, DENY_NONE
);
1006 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s\n", fname
);
1010 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");
1012 written
= smbcli_write(cli
->tree
, fnum2
, 0, "0123456789", 11, 10);
1014 if (written
!= 10) {
1015 torture_result(tctx
, TORTURE_FAIL
, "write failed - wrote %d bytes (%s)\n",
1016 (int)written
, __location__
);
1020 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
1022 if (!NT_STATUS_IS_OK(status
)) {
1023 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
1026 torture_comment(tctx
, "write time %s\n",
1027 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
1028 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
1029 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time (wrong!)\n");
1033 torture_comment(tctx
, "Closing the first fd to see if write time updated.\n");
1034 smbcli_close(cli
->tree
, fnum1
);
1037 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");
1039 written
= smbcli_write(cli
->tree
, fnum2
, 0, "0123456789", 21, 10);
1041 if (written
!= 10) {
1042 torture_result(tctx
, TORTURE_FAIL
, "write failed - wrote %d bytes (%s)\n",
1043 (int)written
, __location__
);
1047 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1048 finfo1
.basic_info
.in
.file
.fnum
= fnum2
;
1050 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
1052 if (!NT_STATUS_IS_OK(status
)) {
1053 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
1056 torture_comment(tctx
, "write time %s\n",
1057 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
1058 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
1059 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time (wrong!)\n");
1063 /* Once the time was set using setfileinfo then it stays set - writes
1064 don't have any effect. But make sure. */
1065 start
= timeval_current();
1066 end
= timeval_add(&start
, (15*sec
), 0);
1067 while (!timeval_expired(&end
)) {
1068 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
1070 if (!NT_STATUS_IS_OK(status
)) {
1071 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
1075 torture_comment(tctx
, "write time %s\n",
1076 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
1077 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
1078 double diff
= timeval_elapsed(&start
);
1079 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
1086 smb_msleep(1 * msec
);
1089 if (finfo1
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
1090 torture_comment(tctx
, "Server did not update write time (correct)\n");
1093 torture_comment(tctx
, "Closing second fd to see if write time updated.\n");
1095 smbcli_close(cli
->tree
, fnum2
);
1098 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
, DENY_NONE
);
1100 torture_comment(tctx
, "Failed to open %s\n", fname
);
1104 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1105 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
1108 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
1110 if (!NT_STATUS_IS_OK(status
)) {
1111 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
1115 torture_comment(tctx
, "Second open initial write time %s\n",
1116 nt_time_string(tctx
, finfo1
.basic_info
.out
.write_time
));
1118 smb_msleep(10 * msec
);
1119 torture_comment(tctx
, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
1121 written
= smbcli_write(cli
->tree
, fnum1
, 0, "0123456789", 31, 10);
1123 if (written
!= 10) {
1124 torture_result(tctx
, TORTURE_FAIL
, "write failed - wrote %d bytes (%s)\n",
1125 (int)written
, __location__
);
1129 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1130 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
1132 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
1134 if (!NT_STATUS_IS_OK(status
)) {
1135 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
1138 torture_comment(tctx
, "write time %s\n",
1139 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
1140 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
1141 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time (wrong!)\n");
1145 /* Now the write time should be updated again */
1146 start
= timeval_current();
1147 end
= timeval_add(&start
, (15*sec
), 0);
1148 while (!timeval_expired(&end
)) {
1149 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
1151 if (!NT_STATUS_IS_OK(status
)) {
1152 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
1156 torture_comment(tctx
, "write time %s\n",
1157 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
1158 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
1159 double diff
= timeval_elapsed(&start
);
1160 if (diff
< (used_delay
/ (double)1000000)) {
1161 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds"
1162 "(expected > %.2f) (wrong!)\n",
1163 diff
, used_delay
/ (double)1000000);
1168 torture_comment(tctx
, "Server updated write_time after %.2f seconds"
1177 if (finfo1
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
1178 torture_result(tctx
, TORTURE_FAIL
, "Server did not update write time (wrong!)\n");
1183 /* One more test to do. We should read the filetime via findfirst on the
1184 second connection to ensure it's the same. This is very easy for a Windows
1185 server but a bastard to get right on a POSIX server. JRA. */
1188 smbcli_close(cli
->tree
, fnum1
);
1189 smbcli_unlink(cli
->tree
, fname
);
1190 smbcli_deltree(cli
->tree
, BASEDIR
);
1196 /* Windows does obviously not update the stat info during a write call. I
1197 * *think* this is the problem causing a spurious Excel 2003 on XP error
1198 * message when saving a file. Excel does a setfileinfo, writes, and then does
1199 * a getpath(!)info. Or so... For Samba sometimes it displays an error message
1200 * that the file might have been changed in between. What i've been able to
1201 * trace down is that this happens if the getpathinfo after the write shows a
1202 * different last write time than the setfileinfo showed. This is really
1206 static bool test_finfo_after_write(struct torture_context
*tctx
, struct smbcli_state
*cli
,
1207 struct smbcli_state
*cli2
)
1209 union smb_fileinfo finfo1
, finfo2
;
1210 const char *fname
= BASEDIR
"\\torture_file.txt";
1216 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
1217 int normal_delay
= 2000000;
1218 double sec
= ((double)used_delay
) / ((double)normal_delay
);
1219 int msec
= 1000 * sec
;
1221 torture_comment(tctx
, "\nRunning test_finfo_after_write\n");
1223 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1225 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
1228 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
1232 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1233 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
1235 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
1237 if (!NT_STATUS_IS_OK(status
)) {
1239 torture_result(tctx
, TORTURE_FAIL
, __location__
": fileinfo failed: %s", nt_errstr(status
));
1243 smb_msleep(1 * msec
);
1245 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1248 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1253 fnum2
= smbcli_open(cli2
->tree
, fname
, O_RDWR
, DENY_NONE
);
1255 torture_result(tctx
, TORTURE_FAIL
, __location__
": failed to open 2nd time - %s",
1256 smbcli_errstr(cli2
->tree
));
1261 written
= smbcli_write(cli2
->tree
, fnum2
, 0, "x", 0, 1);
1264 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1",
1270 finfo2
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1271 finfo2
.basic_info
.in
.file
.path
= fname
;
1273 status
= smb_raw_pathinfo(cli2
->tree
, tctx
, &finfo2
);
1275 if (!NT_STATUS_IS_OK(status
)) {
1276 torture_result(tctx
, TORTURE_FAIL
, __location__
": fileinfo failed: %s",
1282 if (finfo1
.basic_info
.out
.create_time
!=
1283 finfo2
.basic_info
.out
.create_time
) {
1284 torture_result(tctx
, TORTURE_FAIL
, __location__
": create_time changed");
1289 if (finfo1
.basic_info
.out
.access_time
!=
1290 finfo2
.basic_info
.out
.access_time
) {
1291 torture_result(tctx
, TORTURE_FAIL
, __location__
": access_time changed");
1296 if (finfo1
.basic_info
.out
.write_time
!=
1297 finfo2
.basic_info
.out
.write_time
) {
1298 torture_result(tctx
, TORTURE_FAIL
, __location__
": write_time changed:\n"
1299 "write time conn 1 = %s, conn 2 = %s",
1300 nt_time_string(tctx
, finfo1
.basic_info
.out
.write_time
),
1301 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
1306 if (finfo1
.basic_info
.out
.change_time
!=
1307 finfo2
.basic_info
.out
.change_time
) {
1308 torture_result(tctx
, TORTURE_FAIL
, __location__
": change_time changed");
1313 /* One of the two following calls updates the qpathinfo. */
1315 /* If you had skipped the smbcli_write on fnum2, it would
1316 * *not* have updated the stat on disk */
1318 smbcli_close(cli2
->tree
, fnum2
);
1321 /* This call is only for the people looking at ethereal :-) */
1322 finfo2
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1323 finfo2
.basic_info
.in
.file
.path
= fname
;
1325 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo2
);
1327 if (!NT_STATUS_IS_OK(status
)) {
1328 torture_result(tctx
, TORTURE_FAIL
, __location__
": fileinfo failed: %s", nt_errstr(status
));
1335 smbcli_close(cli
->tree
, fnum1
);
1336 smbcli_unlink(cli
->tree
, fname
);
1337 smbcli_deltree(cli
->tree
, BASEDIR
);
1342 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
1343 uint64_t r = 10*1000*1000; \
1344 NTTIME g = (given).basic_info.out.write_time; \
1345 NTTIME gr = (g / r) * r; \
1346 NTTIME c = (correct).basic_info.out.write_time; \
1347 NTTIME cr = (c / r) * r; \
1348 bool strict = torture_setting_bool(tctx, "strict mode", false); \
1350 if (strict && (g cmp c)) { \
1352 } else if ((g cmp c) && (gr cmp cr)) { \
1353 /* handle filesystem without high resolution timestamps */ \
1357 torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \
1358 #given, nt_time_string(tctx, g), (unsigned long long)g, \
1359 #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \
1364 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
1365 COMPARE_WRITE_TIME_CMP(given,correct,!=)
1366 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
1367 COMPARE_WRITE_TIME_CMP(given,correct,<=)
1368 #define COMPARE_WRITE_TIME_LESS(given,correct) \
1369 COMPARE_WRITE_TIME_CMP(given,correct,>=)
1371 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
1372 NTTIME g = (given).basic_info.out.access_time; \
1373 NTTIME c = (correct).basic_info.out.access_time; \
1375 torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \
1376 #given, nt_time_string(tctx, g), \
1377 #cmp, #correct, nt_time_string(tctx, c)); \
1382 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
1383 COMPARE_ACCESS_TIME_CMP(given,correct,!=)
1385 #define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \
1386 COMPARE_ACCESS_TIME_EQUAL(given,correct); \
1387 COMPARE_WRITE_TIME_EQUAL(given,correct); \
1390 #define GET_INFO_FILE(finfo) do { \
1392 _status = smb_raw_fileinfo(cli->tree, tctx, &finfo); \
1393 if (!NT_STATUS_IS_OK(_status)) { \
1395 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1396 nt_errstr(_status)); \
1399 torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1400 nt_time_string(tctx, finfo.basic_info.out.access_time), \
1401 nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1403 #define GET_INFO_FILE2(finfo) do { \
1405 _status = smb_raw_fileinfo(cli2->tree, tctx, &finfo); \
1406 if (!NT_STATUS_IS_OK(_status)) { \
1408 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1409 nt_errstr(_status)); \
1412 torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1413 nt_time_string(tctx, finfo.basic_info.out.access_time), \
1414 nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1416 #define GET_INFO_PATH(pinfo) do { \
1418 _status = smb_raw_pathinfo(cli2->tree, tctx, &pinfo); \
1419 if (!NT_STATUS_IS_OK(_status)) { \
1420 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \
1421 nt_errstr(_status)); \
1425 torture_comment(tctx, "pathinfo: Access(%s) Write(%s)\n", \
1426 nt_time_string(tctx, pinfo.basic_info.out.access_time), \
1427 nt_time_string(tctx, pinfo.basic_info.out.write_time)); \
1429 #define GET_INFO_BOTH(finfo,pinfo) do { \
1430 GET_INFO_FILE(finfo); \
1431 GET_INFO_PATH(pinfo); \
1432 COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \
1435 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
1437 union smb_setfileinfo sfinfo; \
1438 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1439 sfinfo.basic_info.in.file.fnum = tfnum; \
1440 sfinfo.basic_info.in.create_time = 0; \
1441 sfinfo.basic_info.in.access_time = 0; \
1442 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1443 sfinfo.basic_info.in.change_time = 0; \
1444 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1445 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1446 if (!NT_STATUS_IS_OK(_status)) { \
1447 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1448 nt_errstr(_status)); \
1453 #define SET_INFO_FILE(finfo, wrtime) \
1454 SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
1456 #define SET_INFO_FILE_NS(finfo, wrtime, ns, tree, tfnum) do { \
1458 union smb_setfileinfo sfinfo; \
1459 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1460 sfinfo.basic_info.in.file.fnum = tfnum; \
1461 sfinfo.basic_info.in.create_time = 0; \
1462 sfinfo.basic_info.in.access_time = 0; \
1463 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1464 sfinfo.basic_info.in.write_time += (ns); \
1465 sfinfo.basic_info.in.change_time = 0; \
1466 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1467 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1468 if (!NT_STATUS_IS_OK(_status)) { \
1469 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1470 nt_errstr(_status)); \
1476 static bool test_delayed_write_update3(struct torture_context
*tctx
,
1477 struct smbcli_state
*cli
,
1478 struct smbcli_state
*cli2
)
1480 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
;
1481 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
;
1482 const char *fname
= BASEDIR
"\\torture_file3.txt";
1486 struct timeval start
;
1488 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
1489 int normal_delay
= 2000000;
1490 double sec
= ((double)used_delay
) / ((double)normal_delay
);
1491 int msec
= 1000 * sec
;
1493 torture_comment(tctx
, "\nRunning test_delayed_write_update3\n");
1495 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1497 torture_comment(tctx
, "Open the file handle\n");
1498 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
1501 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
1505 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1506 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
1510 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1511 pinfo0
.basic_info
.in
.file
.path
= fname
;
1517 /* get the initial times */
1518 GET_INFO_BOTH(finfo0
,pinfo0
);
1521 * make sure the write time is updated 2 seconds later
1522 * calcuated from the first write
1523 * (but expect upto 5 seconds extra time for a busy server)
1525 start
= timeval_current();
1526 end
= timeval_add(&start
, 7 * sec
, 0);
1527 while (!timeval_expired(&end
)) {
1529 torture_comment(tctx
, "Do a write on the file handle\n");
1530 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1532 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1536 /* get the times after the write */
1537 GET_INFO_FILE(finfo1
);
1539 if (finfo1
.basic_info
.out
.write_time
> finfo0
.basic_info
.out
.write_time
) {
1540 double diff
= timeval_elapsed(&start
);
1541 if (diff
< (used_delay
/ (double)1000000)) {
1542 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
1543 "(write time update delay == %.2f) (wrong!)\n",
1544 diff
, used_delay
/ (double)1000000);
1549 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
1554 smb_msleep(0.5 * msec
);
1557 GET_INFO_BOTH(finfo1
,pinfo1
);
1558 COMPARE_WRITE_TIME_GREATER(pinfo1
, pinfo0
);
1560 /* sure any further write doesn't update the write time */
1561 start
= timeval_current();
1562 end
= timeval_add(&start
, 15 * sec
, 0);
1563 while (!timeval_expired(&end
)) {
1565 torture_comment(tctx
, "Do a write on the file handle\n");
1566 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1568 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1572 /* get the times after the write */
1573 GET_INFO_BOTH(finfo2
,pinfo2
);
1575 if (finfo2
.basic_info
.out
.write_time
> finfo1
.basic_info
.out
.write_time
) {
1576 double diff
= timeval_elapsed(&start
);
1577 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
1583 smb_msleep(1 * msec
);
1586 GET_INFO_BOTH(finfo2
,pinfo2
);
1587 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
1588 if (finfo2
.basic_info
.out
.write_time
== finfo1
.basic_info
.out
.write_time
) {
1589 torture_comment(tctx
, "Server did not update write_time (correct)\n");
1593 smb_msleep(5 * msec
);
1595 GET_INFO_BOTH(finfo3
,pinfo3
);
1596 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
1599 * the close updates the write time to the time of the close
1600 * and not to the time of the last write!
1602 torture_comment(tctx
, "Close the file handle\n");
1603 smbcli_close(cli
->tree
, fnum1
);
1606 GET_INFO_PATH(pinfo4
);
1607 COMPARE_WRITE_TIME_GREATER(pinfo4
, pinfo3
);
1609 if (pinfo4
.basic_info
.out
.write_time
> pinfo3
.basic_info
.out
.write_time
) {
1610 torture_comment(tctx
, "Server updated the write_time on close (correct)\n");
1615 smbcli_close(cli
->tree
, fnum1
);
1616 smbcli_unlink(cli
->tree
, fname
);
1617 smbcli_deltree(cli
->tree
, BASEDIR
);
1623 * Show that a truncate write always updates the write time even
1624 * if an initial write has already updated the write time.
1627 static bool test_delayed_write_update3a(struct torture_context
*tctx
,
1628 struct smbcli_state
*cli
,
1629 struct smbcli_state
*cli2
)
1631 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
;
1632 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
;
1633 const char *fname
= BASEDIR
"\\torture_file3a.txt";
1638 struct timeval start
;
1640 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
1641 int normal_delay
= 2000000;
1642 double sec
= ((double)used_delay
) / ((double)normal_delay
);
1643 int msec
= 1000 * sec
;
1645 torture_comment(tctx
, "\nRunning test_delayed_write_update3a\n");
1647 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1649 torture_comment(tctx
, "Open the file handle\n");
1650 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
1653 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
1657 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1658 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
1662 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1663 pinfo0
.basic_info
.in
.file
.path
= fname
;
1669 /* get the initial times */
1670 GET_INFO_BOTH(finfo0
,pinfo0
);
1673 * sleep some time, to demonstrate the handling of write times
1674 * doesn't depend on the time since the open
1676 smb_msleep(5 * msec
);
1678 /* get the initial times */
1679 GET_INFO_BOTH(finfo1
,pinfo1
);
1680 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo0
);
1683 * make sure the write time is updated 2 seconds later
1684 * calcuated from the first write
1685 * (but expect upto 5 seconds extra time for a busy server)
1687 start
= timeval_current();
1688 end
= timeval_add(&start
, 7 * sec
, 0);
1689 while (!timeval_expired(&end
)) {
1691 torture_comment(tctx
, "Do a write on the file handle\n");
1692 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1694 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1698 /* get the times after the write */
1699 GET_INFO_FILE(finfo1
);
1701 if (finfo1
.basic_info
.out
.write_time
> finfo0
.basic_info
.out
.write_time
) {
1702 double diff
= timeval_elapsed(&start
);
1703 if (diff
< (used_delay
/ (double)1000000)) {
1704 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
1705 "(1sec == %.2f) (wrong!)\n",
1711 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
1716 smb_msleep(0.5 * msec
);
1719 GET_INFO_BOTH(finfo1
,pinfo1
);
1720 COMPARE_WRITE_TIME_GREATER(pinfo1
, pinfo0
);
1722 smb_msleep(3 * msec
);
1725 * demonstrate that a truncate write always
1726 * updates the write time immediately
1728 for (i
=0; i
< 3; i
++) {
1729 smb_msleep(2 * msec
);
1731 torture_comment(tctx
, "Do a truncate SMBwrite [%d] on the file handle\n", i
);
1732 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 10240, 0);
1734 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 0", (int)written
);
1738 /* get the times after the write */
1739 GET_INFO_BOTH(finfo2
,pinfo2
);
1740 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
1744 smb_msleep(3 * msec
);
1746 /* sure any further write doesn't update the write time */
1747 start
= timeval_current();
1748 end
= timeval_add(&start
, 15 * sec
, 0);
1749 while (!timeval_expired(&end
)) {
1751 torture_comment(tctx
, "Do a write on the file handle\n");
1752 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1754 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1758 /* get the times after the write */
1759 GET_INFO_BOTH(finfo2
,pinfo2
);
1761 if (finfo2
.basic_info
.out
.write_time
> finfo1
.basic_info
.out
.write_time
) {
1762 double diff
= timeval_elapsed(&start
);
1763 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
1769 smb_msleep(1 * msec
);
1772 GET_INFO_BOTH(finfo2
,pinfo2
);
1773 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
1774 if (finfo2
.basic_info
.out
.write_time
== finfo1
.basic_info
.out
.write_time
) {
1775 torture_comment(tctx
, "Server did not update write_time (correct)\n");
1779 smb_msleep(3 * msec
);
1781 /* get the initial times */
1782 GET_INFO_BOTH(finfo1
,pinfo1
);
1783 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo2
);
1786 * demonstrate that a truncate write always
1787 * updates the write time immediately
1789 for (i
=0; i
< 3; i
++) {
1790 smb_msleep(2 * msec
);
1792 torture_comment(tctx
, "Do a truncate SMBwrite [%d] on the file handle\n", i
);
1793 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 512, 0);
1795 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 0", (int)written
);
1799 /* get the times after the write */
1800 GET_INFO_BOTH(finfo2
,pinfo2
);
1801 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
1806 smb_msleep(3 * msec
);
1808 GET_INFO_BOTH(finfo3
,pinfo3
);
1809 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
1812 * the close doesn't update the write time
1814 torture_comment(tctx
, "Close the file handle\n");
1815 smbcli_close(cli
->tree
, fnum1
);
1818 GET_INFO_PATH(pinfo4
);
1819 COMPARE_WRITE_TIME_EQUAL(pinfo4
, pinfo3
);
1821 if (pinfo4
.basic_info
.out
.write_time
== pinfo3
.basic_info
.out
.write_time
) {
1822 torture_comment(tctx
, "Server did not update the write_time on close (correct)\n");
1827 smbcli_close(cli
->tree
, fnum1
);
1828 smbcli_unlink(cli
->tree
, fname
);
1829 smbcli_deltree(cli
->tree
, BASEDIR
);
1835 * Show a close after write updates the write timestamp to
1836 * the close time, not the last write time.
1839 static bool test_delayed_write_update3b(struct torture_context
*tctx
,
1840 struct smbcli_state
*cli
,
1841 struct smbcli_state
*cli2
)
1843 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
;
1844 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
;
1845 const char *fname
= BASEDIR
"\\torture_file3b.txt";
1849 struct timeval start
;
1851 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
1852 int normal_delay
= 2000000;
1853 double sec
= ((double)used_delay
) / ((double)normal_delay
);
1854 int msec
= 1000 * sec
;
1856 torture_comment(tctx
, "\nRunning test_delayed_write_update3b\n");
1858 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1860 torture_comment(tctx
, "Open the file handle\n");
1861 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
1864 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
1868 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1869 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
1873 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1874 pinfo0
.basic_info
.in
.file
.path
= fname
;
1880 /* get the initial times */
1881 GET_INFO_BOTH(finfo0
,pinfo0
);
1884 * sleep some time, to demonstrate the handling of write times
1885 * doesn't depend on the time since the open
1887 smb_msleep(5 * msec
);
1889 /* get the initial times */
1890 GET_INFO_BOTH(finfo1
,pinfo1
);
1891 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo0
);
1894 * make sure the write time is updated 2 seconds later
1895 * calcuated from the first write
1896 * (but expect upto 5 seconds extra time for a busy server)
1898 start
= timeval_current();
1899 end
= timeval_add(&start
, 7 * sec
, 0);
1900 while (!timeval_expired(&end
)) {
1902 torture_comment(tctx
, "Do a write on the file handle\n");
1903 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1905 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1909 /* get the times after the write */
1910 GET_INFO_FILE(finfo1
);
1912 if (finfo1
.basic_info
.out
.write_time
> finfo0
.basic_info
.out
.write_time
) {
1913 double diff
= timeval_elapsed(&start
);
1914 if (diff
< (used_delay
/ (double)1000000)) {
1915 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds"
1916 "(expected > %.2f) (wrong!)\n",
1917 diff
, used_delay
/ (double)1000000);
1922 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
1923 "(write time update delay == %.2f) (correct)\n",
1924 diff
, used_delay
/ (double)1000000);
1927 smb_msleep(0.5 * msec
);
1930 GET_INFO_BOTH(finfo1
,pinfo1
);
1931 COMPARE_WRITE_TIME_GREATER(pinfo1
, pinfo0
);
1933 /* sure any further write doesn't update the write time */
1934 start
= timeval_current();
1935 end
= timeval_add(&start
, 15 * sec
, 0);
1936 while (!timeval_expired(&end
)) {
1938 torture_comment(tctx
, "Do a write on the file handle\n");
1939 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1941 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1945 /* get the times after the write */
1946 GET_INFO_BOTH(finfo2
,pinfo2
);
1948 if (finfo2
.basic_info
.out
.write_time
> finfo1
.basic_info
.out
.write_time
) {
1949 double diff
= timeval_elapsed(&start
);
1950 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
1956 smb_msleep(1 * msec
);
1959 GET_INFO_BOTH(finfo2
,pinfo2
);
1960 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
1961 if (finfo2
.basic_info
.out
.write_time
== finfo1
.basic_info
.out
.write_time
) {
1962 torture_comment(tctx
, "Server did not update write_time (correct)\n");
1966 smb_msleep(5 * msec
);
1968 GET_INFO_BOTH(finfo3
,pinfo3
);
1969 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
1972 * the close updates the write time to the time of the close
1973 * and not to the time of the last write!
1975 torture_comment(tctx
, "Close the file handle\n");
1976 smbcli_close(cli
->tree
, fnum1
);
1979 GET_INFO_PATH(pinfo4
);
1980 COMPARE_WRITE_TIME_GREATER(pinfo4
, pinfo3
);
1982 if (pinfo4
.basic_info
.out
.write_time
> pinfo3
.basic_info
.out
.write_time
) {
1983 torture_comment(tctx
, "Server updated the write_time on close (correct)\n");
1988 smbcli_close(cli
->tree
, fnum1
);
1989 smbcli_unlink(cli
->tree
, fname
);
1990 smbcli_deltree(cli
->tree
, BASEDIR
);
1996 * Check that a write after a truncate write doesn't update
1997 * the timestamp, but a truncate write after a write does.
1998 * Also prove that a close after a truncate write updates the
1999 * timestamp to current, not the time of last write.
2002 static bool test_delayed_write_update3c(struct torture_context
*tctx
,
2003 struct smbcli_state
*cli
,
2004 struct smbcli_state
*cli2
)
2006 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
, finfo4
;
2007 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
, pinfo5
;
2008 const char *fname
= BASEDIR
"\\torture_file3c.txt";
2013 struct timeval start
;
2015 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
2016 int normal_delay
= 2000000;
2017 double sec
= ((double)used_delay
) / ((double)normal_delay
);
2018 int msec
= 1000 * sec
;
2020 torture_comment(tctx
, "\nRunning test_delayed_write_update3c\n");
2022 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
2024 torture_comment(tctx
, "Open the file handle\n");
2025 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2028 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
2032 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2033 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
2038 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2039 pinfo0
.basic_info
.in
.file
.path
= fname
;
2046 /* get the initial times */
2047 GET_INFO_BOTH(finfo0
,pinfo0
);
2050 * sleep some time, to demonstrate the handling of write times
2051 * doesn't depend on the time since the open
2053 smb_msleep(5 * msec
);
2055 /* get the initial times */
2056 GET_INFO_BOTH(finfo1
,pinfo1
);
2057 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo0
);
2060 * demonstrate that a truncate write always
2061 * updates the write time immediately
2063 for (i
=0; i
< 3; i
++) {
2064 smb_msleep(2 * msec
);
2066 torture_comment(tctx
, "Do a truncate SMBwrite [%d] on the file handle\n", i
);
2067 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 512, 0);
2069 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 0", (int)written
);
2073 /* get the times after the write */
2074 GET_INFO_BOTH(finfo2
,pinfo2
);
2075 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
2079 start
= timeval_current();
2080 end
= timeval_add(&start
, 7 * sec
, 0);
2081 while (!timeval_expired(&end
)) {
2083 torture_comment(tctx
, "Do a write on the file handle\n");
2084 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2086 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2090 /* get the times after the write */
2091 GET_INFO_FILE(finfo2
);
2093 if (finfo2
.basic_info
.out
.write_time
> finfo1
.basic_info
.out
.write_time
) {
2094 double diff
= timeval_elapsed(&start
);
2095 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2101 smb_msleep(1 * msec
);
2104 GET_INFO_BOTH(finfo2
,pinfo2
);
2105 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
2106 if (finfo2
.basic_info
.out
.write_time
== finfo1
.basic_info
.out
.write_time
) {
2107 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2111 smb_msleep(5 * msec
);
2113 /* get the initial times */
2114 GET_INFO_BOTH(finfo1
,pinfo1
);
2115 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo2
);
2118 * demonstrate that a truncate write always
2119 * updates the write time immediately
2121 for (i
=0; i
< 3; i
++) {
2122 smb_msleep(2 * msec
);
2124 torture_comment(tctx
, "Do a truncate write [%d] on the file handle\n", i
);
2125 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 512, 0);
2127 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 0", (int)written
);
2131 /* get the times after the write */
2132 GET_INFO_BOTH(finfo2
,pinfo2
);
2133 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
2138 smb_msleep(5 * msec
);
2140 GET_INFO_BOTH(finfo2
,pinfo2
);
2141 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
2143 /* sure any further write doesn't update the write time */
2144 start
= timeval_current();
2145 end
= timeval_add(&start
, 15 * sec
, 0);
2146 while (!timeval_expired(&end
)) {
2148 torture_comment(tctx
, "Do a write on the file handle\n");
2149 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2151 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2155 /* get the times after the write */
2156 GET_INFO_BOTH(finfo2
,pinfo2
);
2158 if (finfo2
.basic_info
.out
.write_time
> finfo1
.basic_info
.out
.write_time
) {
2159 double diff
= timeval_elapsed(&start
);
2160 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2166 smb_msleep(1 * msec
);
2169 GET_INFO_BOTH(finfo2
,pinfo2
);
2170 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
2171 if (finfo2
.basic_info
.out
.write_time
== finfo1
.basic_info
.out
.write_time
) {
2172 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2176 smb_msleep(5 * msec
);
2178 GET_INFO_BOTH(finfo3
,pinfo3
);
2179 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
2182 * the close updates the write time to the time of the close
2183 * and not to the time of the last write!
2185 torture_comment(tctx
, "Close the file handle\n");
2186 smbcli_close(cli
->tree
, fnum1
);
2189 GET_INFO_PATH(pinfo4
);
2190 COMPARE_WRITE_TIME_GREATER(pinfo4
, pinfo3
);
2192 if (pinfo4
.basic_info
.out
.write_time
> pinfo3
.basic_info
.out
.write_time
) {
2193 torture_comment(tctx
, "Server updated the write_time on close (correct)\n");
2198 smbcli_close(cli
->tree
, fnum1
);
2199 smbcli_unlink(cli
->tree
, fname
);
2200 smbcli_deltree(cli
->tree
, BASEDIR
);
2206 * Show only the first write updates the timestamp, and a close
2207 * after writes updates to current (I think this is the same
2211 static bool test_delayed_write_update4(struct torture_context
*tctx
,
2212 struct smbcli_state
*cli
,
2213 struct smbcli_state
*cli2
)
2215 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
;
2216 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
;
2217 const char *fname
= BASEDIR
"\\torture_file4.txt";
2221 struct timeval start
;
2223 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
2224 int normal_delay
= 2000000;
2225 double sec
= ((double)used_delay
) / ((double)normal_delay
);
2226 int msec
= 1000 * sec
;
2228 torture_comment(tctx
, "\nRunning test_delayed_write_update4\n");
2230 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
2232 torture_comment(tctx
, "Open the file handle\n");
2233 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2236 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
2240 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2241 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
2245 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2246 pinfo0
.basic_info
.in
.file
.path
= fname
;
2252 /* get the initial times */
2253 GET_INFO_BOTH(finfo0
,pinfo0
);
2256 smb_msleep(5 * msec
);
2259 torture_comment(tctx
, "Do a write on the file handle\n");
2260 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2262 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2267 GET_INFO_BOTH(finfo1
,pinfo1
);
2268 COMPARE_WRITE_TIME_EQUAL(finfo1
,finfo0
);
2271 * make sure the write time is updated 2 seconds later
2272 * calcuated from the first write
2273 * (but expect upto 3 seconds extra time for a busy server)
2275 start
= timeval_current();
2276 end
= timeval_add(&start
, 5 * sec
, 0);
2277 while (!timeval_expired(&end
)) {
2278 /* get the times after the first write */
2279 GET_INFO_FILE(finfo1
);
2281 if (finfo1
.basic_info
.out
.write_time
> finfo0
.basic_info
.out
.write_time
) {
2282 double diff
= timeval_elapsed(&start
);
2283 if (diff
< (used_delay
/ (double)1000000)) {
2284 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds"
2285 "(expected > %.2f) (wrong!)\n",
2286 diff
, used_delay
/ (double)1000000);
2291 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
2292 "(write time update delay == %.2f) (correct)\n",
2293 diff
, used_delay
/ (double)1000000);
2296 smb_msleep(0.5 * msec
);
2299 GET_INFO_BOTH(finfo1
,pinfo1
);
2300 COMPARE_WRITE_TIME_GREATER(pinfo1
, pinfo0
);
2302 /* sure any further write doesn't update the write time */
2303 start
= timeval_current();
2304 end
= timeval_add(&start
, 15 * sec
, 0);
2305 while (!timeval_expired(&end
)) {
2307 torture_comment(tctx
, "Do a write on the file handle\n");
2308 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2310 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2314 /* get the times after the write */
2315 GET_INFO_BOTH(finfo2
,pinfo2
);
2317 if (finfo2
.basic_info
.out
.write_time
> finfo1
.basic_info
.out
.write_time
) {
2318 double diff
= timeval_elapsed(&start
);
2319 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2325 smb_msleep(1 * msec
);
2328 GET_INFO_BOTH(finfo2
,pinfo2
);
2329 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
2330 if (finfo2
.basic_info
.out
.write_time
== finfo1
.basic_info
.out
.write_time
) {
2331 torture_comment(tctx
, "Server did not updatewrite_time (correct)\n");
2335 smb_msleep(5 * msec
);
2337 GET_INFO_BOTH(finfo3
,pinfo3
);
2338 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
2341 * the close updates the write time to the time of the close
2342 * and not to the time of the last write!
2344 torture_comment(tctx
, "Close the file handle\n");
2345 smbcli_close(cli
->tree
, fnum1
);
2348 GET_INFO_PATH(pinfo4
);
2349 COMPARE_WRITE_TIME_GREATER(pinfo4
, pinfo3
);
2351 if (pinfo4
.basic_info
.out
.write_time
> pinfo3
.basic_info
.out
.write_time
) {
2352 torture_comment(tctx
, "Server updated the write_time on close (correct)\n");
2357 smbcli_close(cli
->tree
, fnum1
);
2358 smbcli_unlink(cli
->tree
, fname
);
2359 smbcli_deltree(cli
->tree
, BASEDIR
);
2365 * Show writes and closes have no effect on updating times once a SETWRITETIME is done.
2368 static bool test_delayed_write_update5(struct torture_context
*tctx
,
2369 struct smbcli_state
*cli
,
2370 struct smbcli_state
*cli2
)
2372 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
, finfo4
, finfo5
;
2373 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
, pinfo5
, pinfo6
;
2374 const char *fname
= BASEDIR
"\\torture_file5.txt";
2378 struct timeval start
;
2380 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
2381 int normal_delay
= 2000000;
2382 double sec
= ((double)used_delay
) / ((double)normal_delay
);
2383 int msec
= 1000 * sec
;
2385 torture_comment(tctx
, "\nRunning test_delayed_write_update5\n");
2387 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
2389 torture_comment(tctx
, "Open the file handle\n");
2390 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2393 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
2397 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2398 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
2404 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2405 pinfo0
.basic_info
.in
.file
.path
= fname
;
2413 /* get the initial times */
2414 GET_INFO_BOTH(finfo0
,pinfo0
);
2417 torture_comment(tctx
, "Do a write on the file handle\n");
2418 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2420 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2425 GET_INFO_BOTH(finfo1
,pinfo1
);
2426 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo0
);
2428 torture_comment(tctx
, "Set write time in the future on the file handle\n");
2429 SET_INFO_FILE(finfo0
, time(NULL
) + 86400);
2430 GET_INFO_BOTH(finfo2
,pinfo2
);
2431 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
2433 torture_comment(tctx
, "Set write time in the past on the file handle\n");
2434 SET_INFO_FILE(finfo0
, time(NULL
) - 86400);
2435 GET_INFO_BOTH(finfo2
,pinfo2
);
2436 COMPARE_WRITE_TIME_LESS(finfo2
, finfo1
);
2438 /* make sure the 2 second delay from the first write are canceled */
2439 start
= timeval_current();
2440 end
= timeval_add(&start
, 15 * sec
, 0);
2441 while (!timeval_expired(&end
)) {
2443 /* get the times after the first write */
2444 GET_INFO_BOTH(finfo3
,pinfo3
);
2446 if (finfo3
.basic_info
.out
.write_time
> finfo2
.basic_info
.out
.write_time
) {
2447 double diff
= timeval_elapsed(&start
);
2448 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2454 smb_msleep(1 * msec
);
2457 GET_INFO_BOTH(finfo3
,pinfo3
);
2458 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
2459 if (finfo3
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
2460 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2463 /* sure any further write doesn't update the write time */
2464 start
= timeval_current();
2465 end
= timeval_add(&start
, 15 * sec
, 0);
2466 while (!timeval_expired(&end
)) {
2468 torture_comment(tctx
, "Do a write on the file handle\n");
2469 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2471 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2475 /* get the times after the write */
2476 GET_INFO_BOTH(finfo4
,pinfo4
);
2478 if (finfo4
.basic_info
.out
.write_time
> finfo3
.basic_info
.out
.write_time
) {
2479 double diff
= timeval_elapsed(&start
);
2480 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2486 smb_msleep(1 * msec
);
2489 GET_INFO_BOTH(finfo4
,pinfo4
);
2490 COMPARE_WRITE_TIME_EQUAL(finfo4
, finfo3
);
2491 if (finfo4
.basic_info
.out
.write_time
== finfo3
.basic_info
.out
.write_time
) {
2492 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2496 smb_msleep(5 * msec
);
2498 GET_INFO_BOTH(finfo5
,pinfo5
);
2499 COMPARE_WRITE_TIME_EQUAL(finfo5
, finfo4
);
2502 * the close doesn't update the write time
2504 torture_comment(tctx
, "Close the file handle\n");
2505 smbcli_close(cli
->tree
, fnum1
);
2508 GET_INFO_PATH(pinfo6
);
2509 COMPARE_WRITE_TIME_EQUAL(pinfo6
, pinfo5
);
2511 if (pinfo6
.basic_info
.out
.write_time
== pinfo5
.basic_info
.out
.write_time
) {
2512 torture_comment(tctx
, "Server did not update the write_time on close (correct)\n");
2517 smbcli_close(cli
->tree
, fnum1
);
2518 smbcli_unlink(cli
->tree
, fname
);
2519 smbcli_deltree(cli
->tree
, BASEDIR
);
2525 * Show truncate writes and closes have no effect on updating times once a SETWRITETIME is done.
2528 static bool test_delayed_write_update5b(struct torture_context
*tctx
,
2529 struct smbcli_state
*cli
,
2530 struct smbcli_state
*cli2
)
2532 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
, finfo4
, finfo5
;
2533 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
, pinfo5
, pinfo6
;
2534 const char *fname
= BASEDIR
"\\torture_fileb.txt";
2538 struct timeval start
;
2540 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
2541 int normal_delay
= 2000000;
2542 double sec
= ((double)used_delay
) / ((double)normal_delay
);
2543 int msec
= 1000 * sec
;
2545 torture_comment(tctx
, "\nRunning test_delayed_write_update5b\n");
2547 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
2549 torture_comment(tctx
, "Open the file handle\n");
2550 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2553 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
2557 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2558 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
2564 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2565 pinfo0
.basic_info
.in
.file
.path
= fname
;
2573 /* get the initial times */
2574 GET_INFO_BOTH(finfo0
,pinfo0
);
2577 torture_comment(tctx
, "Do a write on the file handle\n");
2578 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2580 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2585 GET_INFO_BOTH(finfo1
,pinfo1
);
2586 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo0
);
2588 torture_comment(tctx
, "Set write time in the future on the file handle\n");
2589 SET_INFO_FILE(finfo0
, time(NULL
) + 86400);
2590 GET_INFO_BOTH(finfo2
,pinfo2
);
2591 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
2593 torture_comment(tctx
, "Set write time in the past on the file handle\n");
2594 SET_INFO_FILE(finfo0
, time(NULL
) - 86400);
2595 GET_INFO_BOTH(finfo2
,pinfo2
);
2596 COMPARE_WRITE_TIME_LESS(finfo2
, finfo1
);
2598 /* make sure the 2 second delay from the first write are canceled */
2599 start
= timeval_current();
2600 end
= timeval_add(&start
, 15 * sec
, 0);
2601 while (!timeval_expired(&end
)) {
2603 /* get the times after the first write */
2604 GET_INFO_BOTH(finfo3
,pinfo3
);
2606 if (finfo3
.basic_info
.out
.write_time
> finfo2
.basic_info
.out
.write_time
) {
2607 double diff
= timeval_elapsed(&start
);
2608 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2614 smb_msleep(1 * msec
);
2617 GET_INFO_BOTH(finfo3
,pinfo3
);
2618 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
2619 if (finfo3
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
2620 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2623 /* Do any further write (truncates) update the write time ? */
2624 start
= timeval_current();
2625 end
= timeval_add(&start
, 15 * sec
, 0);
2626 while (!timeval_expired(&end
)) {
2628 torture_comment(tctx
, "Do a truncate write on the file handle\n");
2629 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 1024, 0);
2631 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2635 /* get the times after the write */
2636 GET_INFO_BOTH(finfo4
,pinfo4
);
2638 if (finfo4
.basic_info
.out
.write_time
> finfo3
.basic_info
.out
.write_time
) {
2639 double diff
= timeval_elapsed(&start
);
2640 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2646 smb_msleep(1 * msec
);
2649 GET_INFO_BOTH(finfo4
,pinfo4
);
2650 COMPARE_WRITE_TIME_EQUAL(finfo4
, finfo3
);
2651 if (finfo4
.basic_info
.out
.write_time
== finfo3
.basic_info
.out
.write_time
) {
2652 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2656 smb_msleep(5 * msec
);
2658 GET_INFO_BOTH(finfo5
,pinfo5
);
2659 COMPARE_WRITE_TIME_EQUAL(finfo5
, finfo4
);
2662 * the close doesn't update the write time
2664 torture_comment(tctx
, "Close the file handle\n");
2665 smbcli_close(cli
->tree
, fnum1
);
2668 GET_INFO_PATH(pinfo6
);
2669 COMPARE_WRITE_TIME_EQUAL(pinfo6
, pinfo5
);
2671 if (pinfo6
.basic_info
.out
.write_time
== pinfo5
.basic_info
.out
.write_time
) {
2672 torture_comment(tctx
, "Server did not update the write_time on close (correct)\n");
2677 smbcli_close(cli
->tree
, fnum1
);
2678 smbcli_unlink(cli
->tree
, fname
);
2679 smbcli_deltree(cli
->tree
, BASEDIR
);
2685 * Open 2 handles on a file. Write one one and then set the
2686 * WRITE TIME explicitly on the other. Ensure the write time
2687 * update is cancelled. Ensure the write time is updated to
2688 * the close time when the non-explicit set handle is closed.
2692 static bool test_delayed_write_update6(struct torture_context
*tctx
,
2693 struct smbcli_state
*cli
,
2694 struct smbcli_state
*cli2
)
2696 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
, finfo4
, finfo5
;
2697 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
, pinfo5
, pinfo6
, pinfo7
;
2698 const char *fname
= BASEDIR
"\\torture_file6.txt";
2703 struct timeval start
;
2705 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
2706 int normal_delay
= 2000000;
2707 double sec
= ((double)used_delay
) / ((double)normal_delay
);
2708 int msec
= 1000 * sec
;
2711 torture_comment(tctx
, "\nRunning test_delayed_write_update6\n");
2713 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
2715 torture_comment(tctx
, "Open the file handle\n");
2716 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2719 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
2724 torture_comment(tctx
, "Open the 2nd file handle on 2nd connection\n");
2725 fnum2
= smbcli_open(cli2
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2728 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
2733 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2734 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
2740 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2741 pinfo0
.basic_info
.in
.file
.path
= fname
;
2750 /* get the initial times */
2751 GET_INFO_BOTH(finfo0
,pinfo0
);
2754 torture_comment(tctx
, "Do a write on the file handle\n");
2755 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2757 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2762 GET_INFO_BOTH(finfo1
,pinfo1
);
2763 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo0
);
2765 torture_comment(tctx
, "Set write time in the future on the 2nd file handle\n");
2766 SET_INFO_FILE_EX(finfo0
, time(NULL
) + 86400, cli2
->tree
, fnum2
);
2767 GET_INFO_BOTH(finfo2
,pinfo2
);
2768 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
2770 torture_comment(tctx
, "Set write time in the past 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_LESS(finfo2
, finfo1
);
2775 /* make sure the 2 second delay from the first write are canceled */
2776 start
= timeval_current();
2777 end
= timeval_add(&start
, 10 * sec
, 0);
2778 while (!timeval_expired(&end
)) {
2780 /* get the times after the first write */
2781 GET_INFO_BOTH(finfo3
,pinfo3
);
2783 if (finfo3
.basic_info
.out
.write_time
> finfo2
.basic_info
.out
.write_time
) {
2784 double diff
= timeval_elapsed(&start
);
2785 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2791 smb_msleep(1 * msec
);
2794 GET_INFO_BOTH(finfo3
,pinfo3
);
2795 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
2796 if (finfo3
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
2797 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2800 /* sure any further write doesn't update the write time */
2801 start
= timeval_current();
2802 end
= timeval_add(&start
, 10 * sec
, 0);
2803 while (!timeval_expired(&end
)) {
2805 torture_comment(tctx
, "Do a write on the file handle\n");
2806 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2808 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2812 /* get the times after the write */
2813 GET_INFO_BOTH(finfo4
,pinfo4
);
2815 if (finfo4
.basic_info
.out
.write_time
> finfo3
.basic_info
.out
.write_time
) {
2816 double diff
= timeval_elapsed(&start
);
2817 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2823 smb_msleep(1 * msec
);
2826 GET_INFO_BOTH(finfo4
,pinfo4
);
2827 COMPARE_WRITE_TIME_EQUAL(finfo4
, finfo3
);
2828 if (finfo4
.basic_info
.out
.write_time
== finfo3
.basic_info
.out
.write_time
) {
2829 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2833 smb_msleep(5 * msec
);
2835 GET_INFO_BOTH(finfo5
,pinfo5
);
2836 COMPARE_WRITE_TIME_EQUAL(finfo5
, finfo4
);
2839 * the close updates the write time to the time of the close
2840 * as the write time was set on the 2nd handle
2842 torture_comment(tctx
, "Close the file handle\n");
2843 smbcli_close(cli
->tree
, fnum1
);
2846 GET_INFO_PATH(pinfo6
);
2847 COMPARE_WRITE_TIME_GREATER(pinfo6
, pinfo5
);
2849 if (pinfo6
.basic_info
.out
.write_time
> pinfo5
.basic_info
.out
.write_time
) {
2850 torture_comment(tctx
, "Server updated the write_time on close (correct)\n");
2853 /* See what the second write handle thinks the time is ? */
2854 finfo5
.basic_info
.in
.file
.fnum
= fnum2
;
2855 GET_INFO_FILE2(finfo5
);
2856 COMPARE_WRITE_TIME_EQUAL(finfo5
, pinfo6
);
2858 /* See if we have lost the sticky write time on handle2 */
2859 smb_msleep(3 * msec
);
2860 torture_comment(tctx
, "Have we lost the sticky write time ?\n");
2862 /* Make sure any further normal write doesn't update the write time */
2863 start
= timeval_current();
2864 end
= timeval_add(&start
, 10 * sec
, 0);
2865 while (!timeval_expired(&end
)) {
2867 torture_comment(tctx
, "Do a write on the second file handle\n");
2868 written
= smbcli_write(cli2
->tree
, fnum2
, 0, "x", 0, 1);
2870 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2874 /* get the times after the write */
2875 GET_INFO_FILE2(finfo5
);
2876 GET_INFO_PATH(pinfo6
);
2878 if (finfo5
.basic_info
.out
.write_time
> pinfo6
.basic_info
.out
.write_time
) {
2879 double diff
= timeval_elapsed(&start
);
2880 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2886 smb_msleep(1 * msec
);
2889 /* What about a truncate write ? */
2890 start
= timeval_current();
2891 end
= timeval_add(&start
, 10 * sec
, 0);
2892 while (!timeval_expired(&end
)) {
2894 torture_comment(tctx
, "Do a truncate write on the second file handle\n");
2895 written
= smbcli_write(cli2
->tree
, fnum2
, 0, "x", 0, 0);
2897 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2901 /* get the times after the write */
2902 GET_INFO_FILE2(finfo5
);
2903 GET_INFO_PATH(pinfo6
);
2905 if (finfo5
.basic_info
.out
.write_time
> pinfo6
.basic_info
.out
.write_time
) {
2906 double diff
= timeval_elapsed(&start
);
2907 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2913 smb_msleep(1 * msec
);
2917 /* keep the 2nd handle open and rerun tests */
2924 * closing the 2nd handle will cause no write time update
2925 * as the write time was explicit set on this handle
2927 torture_comment(tctx
, "Close the 2nd file handle\n");
2928 smbcli_close(cli2
->tree
, fnum2
);
2931 GET_INFO_PATH(pinfo7
);
2932 COMPARE_WRITE_TIME_EQUAL(pinfo7
, pinfo6
);
2934 if (pinfo7
.basic_info
.out
.write_time
== pinfo6
.basic_info
.out
.write_time
) {
2935 torture_comment(tctx
, "Server did not update the write_time on close (correct)\n");
2940 smbcli_close(cli
->tree
, fnum1
);
2942 smbcli_close(cli2
->tree
, fnum2
);
2943 smbcli_unlink(cli
->tree
, fname
);
2944 smbcli_deltree(cli
->tree
, BASEDIR
);
2949 static bool test_delayed_write_update7(struct torture_context
*tctx
, struct smbcli_state
*cli
)
2951 union smb_open open_parms
;
2952 union smb_fileinfo finfo1
, finfo2
, finfo3
;
2953 const char *fname
= BASEDIR
"\\torture_file7.txt";
2957 TALLOC_CTX
*mem_ctx
;
2959 torture_comment(tctx
, "\nRunning test_delayed_write_update7 (timestamp resolution test)\n");
2961 mem_ctx
= talloc_init("test_delayed_write_update7");
2962 if (!mem_ctx
) return false;
2964 ZERO_STRUCT(finfo1
);
2965 ZERO_STRUCT(finfo2
);
2966 ZERO_STRUCT(finfo3
);
2967 ZERO_STRUCT(open_parms
);
2969 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
2971 /* Create the file. */
2972 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2974 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s", fname
);
2978 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2979 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
2983 /* Get the initial timestamps. */
2984 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
2986 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
2988 /* Set the pending write time to a value with ns. */
2989 SET_INFO_FILE_NS(finfo
, time(NULL
) + 86400, 103, cli
->tree
, fnum1
);
2991 /* Get the current pending write time by fnum. */
2992 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
2994 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
2996 /* Ensure the time is actually different. */
2997 if (finfo1
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
2998 torture_result(tctx
, TORTURE_FAIL
,
2999 "setfileinfo time matches original fileinfo time");
3003 /* Get the current pending write time by path. */
3004 finfo3
.basic_info
.in
.file
.path
= fname
;
3005 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo3
);
3007 if (finfo2
.basic_info
.out
.write_time
!= finfo3
.basic_info
.out
.write_time
) {
3008 torture_result(tctx
, TORTURE_FAIL
,
3009 "qpathinfo time doens't match fileinfo time");
3013 /* Now close the file. Re-open and check that the write
3014 time is identical to the one we wrote. */
3016 smbcli_close(cli
->tree
, fnum1
);
3018 open_parms
.ntcreatex
.level
= RAW_OPEN_NTCREATEX
;
3019 open_parms
.ntcreatex
.in
.flags
= 0;
3020 open_parms
.ntcreatex
.in
.access_mask
= SEC_GENERIC_READ
;
3021 open_parms
.ntcreatex
.in
.file_attr
= 0;
3022 open_parms
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_DELETE
|
3023 NTCREATEX_SHARE_ACCESS_READ
|
3024 NTCREATEX_SHARE_ACCESS_WRITE
;
3025 open_parms
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
3026 open_parms
.ntcreatex
.in
.create_options
= 0;
3027 open_parms
.ntcreatex
.in
.fname
= fname
;
3029 status
= smb_raw_open(cli
->tree
, mem_ctx
, &open_parms
);
3030 talloc_free(mem_ctx
);
3032 if (!NT_STATUS_IS_OK(status
)) {
3033 torture_result(tctx
, TORTURE_FAIL
,
3034 "setfileinfo time matches original fileinfo time");
3038 fnum1
= open_parms
.ntcreatex
.out
.file
.fnum
;
3040 /* Check the returned time matches. */
3041 if (open_parms
.ntcreatex
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
3042 torture_result(tctx
, TORTURE_FAIL
,
3043 "final open time does not match set time");
3049 smbcli_close(cli
->tree
, fnum1
);
3051 smbcli_unlink(cli
->tree
, fname
);
3052 smbcli_deltree(cli
->tree
, BASEDIR
);
3057 Test if creating a file in a directory with an open handle updates the
3058 write timestamp (it should).
3060 static bool test_directory_update8(struct torture_context
*tctx
, struct smbcli_state
*cli
)
3062 union smb_fileinfo dir_info1
, dir_info2
;
3063 union smb_open open_parms
;
3064 const char *fname
= BASEDIR
"\\torture_file.txt";
3069 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
3070 int normal_delay
= 2000000;
3071 double sec
= ((double)used_delay
) / ((double)normal_delay
);
3072 int msec
= 1000 * sec
;
3073 TALLOC_CTX
*mem_ctx
= talloc_init("test_delayed_write_update8");
3075 if (!mem_ctx
) return false;
3077 torture_comment(tctx
, "\nRunning test directory write update\n");
3079 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
3081 /* Open a handle on the directory - and leave it open. */
3082 ZERO_STRUCT(open_parms
);
3083 open_parms
.ntcreatex
.level
= RAW_OPEN_NTCREATEX
;
3084 open_parms
.ntcreatex
.in
.flags
= 0;
3085 open_parms
.ntcreatex
.in
.access_mask
= SEC_RIGHTS_FILE_READ
;
3086 open_parms
.ntcreatex
.in
.file_attr
= 0;
3087 open_parms
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_DELETE
|
3088 NTCREATEX_SHARE_ACCESS_READ
|
3089 NTCREATEX_SHARE_ACCESS_WRITE
;
3090 open_parms
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
3091 open_parms
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
;
3092 open_parms
.ntcreatex
.in
.fname
= BASEDIR
;
3094 status
= smb_raw_open(cli
->tree
, mem_ctx
, &open_parms
);
3095 talloc_free(mem_ctx
);
3097 if (!NT_STATUS_IS_OK(status
)) {
3098 torture_result(tctx
, TORTURE_FAIL
,
3099 "failed to open directory handle");
3104 fnum1
= open_parms
.ntcreatex
.out
.file
.fnum
;
3106 /* Store the returned write time. */
3107 ZERO_STRUCT(dir_info1
);
3108 dir_info1
.basic_info
.out
.write_time
= open_parms
.ntcreatex
.out
.write_time
;
3110 torture_comment(tctx
, "Initial write time %s\n",
3111 nt_time_string(tctx
, dir_info1
.basic_info
.out
.write_time
));
3114 smb_msleep(3 * msec
);
3116 /* Now create a file within the directory. */
3117 fnum2
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
3119 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s", fname
);
3123 smbcli_close(cli
->tree
, fnum2
);
3125 /* Read the directory write time again. */
3126 ZERO_STRUCT(dir_info2
);
3127 dir_info2
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
3128 dir_info2
.basic_info
.in
.file
.fnum
= fnum1
;
3130 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &dir_info2
);
3132 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
3134 /* Ensure it's been incremented. */
3135 COMPARE_WRITE_TIME_GREATER(dir_info2
, dir_info1
);
3137 torture_comment(tctx
, "Updated write time %s\n",
3138 nt_time_string(tctx
, dir_info2
.basic_info
.out
.write_time
));
3143 smbcli_close(cli
->tree
, fnum1
);
3144 smbcli_unlink(cli
->tree
, fname
);
3145 smbcli_deltree(cli
->tree
, BASEDIR
);
3151 testing of delayed update of write_time
3153 struct torture_suite
*torture_delay_write(void)
3155 struct torture_suite
*suite
= torture_suite_create(talloc_autofree_context(), "delaywrite");
3157 torture_suite_add_2smb_test(suite
, "finfo update on close", test_finfo_after_write
);
3158 torture_suite_add_1smb_test(suite
, "delayed update of write time", test_delayed_write_update
);
3159 torture_suite_add_1smb_test(suite
, "update of write time and SMBwrite truncate", test_delayed_write_update1
);
3160 torture_suite_add_1smb_test(suite
, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a
);
3161 torture_suite_add_1smb_test(suite
, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b
);
3162 torture_suite_add_1smb_test(suite
, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c
);
3163 torture_suite_add_2smb_test(suite
, "delayed update of write time using 2 connections", test_delayed_write_update2
);
3164 torture_suite_add_2smb_test(suite
, "delayed update of write time 3", test_delayed_write_update3
);
3165 torture_suite_add_2smb_test(suite
, "delayed update of write time 3a", test_delayed_write_update3a
);
3166 torture_suite_add_2smb_test(suite
, "delayed update of write time 3b", test_delayed_write_update3b
);
3167 torture_suite_add_2smb_test(suite
, "delayed update of write time 3c", test_delayed_write_update3c
);
3168 torture_suite_add_2smb_test(suite
, "delayed update of write time 4", test_delayed_write_update4
);
3169 torture_suite_add_2smb_test(suite
, "delayed update of write time 5", test_delayed_write_update5
);
3170 torture_suite_add_2smb_test(suite
, "delayed update of write time 5b", test_delayed_write_update5b
);
3171 torture_suite_add_2smb_test(suite
, "delayed update of write time 6", test_delayed_write_update6
);
3172 torture_suite_add_1smb_test(suite
, "timestamp resolution test", test_delayed_write_update7
);
3173 torture_suite_add_1smb_test(suite
, "timestamp resolution test", test_delayed_write_update7
);
3174 torture_suite_add_1smb_test(suite
, "directory timestamp update test", test_directory_update8
);