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
);
56 torture_assert_int_not_equal(tctx
, fnum1
, -1, talloc_asprintf(tctx
,
57 "Failed to open %s", fname
));
59 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
60 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
63 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
64 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
66 torture_comment(tctx
, "Initial write time %s\n",
67 nt_time_string(tctx
, finfo1
.basic_info
.out
.write_time
));
69 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
70 torture_assert_int_equal(tctx
, written
, 1,
71 "unexpected number of bytes written");
73 start
= timeval_current();
74 end
= timeval_add(&start
, (120 * sec
), 0);
75 while (!timeval_expired(&end
)) {
76 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
78 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
80 torture_comment(tctx
, "write time %s\n",
81 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
83 if (finfo1
.basic_info
.out
.write_time
!=
84 finfo2
.basic_info
.out
.write_time
)
86 double diff
= timeval_elapsed(&start
);
89 diff
>= (used_delay
/ (double)1000000),
91 "Server updated write_time after %.2f "
92 "seconds (expected >= %.2f)\n",
93 diff
, used_delay
/(double)1000000));
95 torture_comment(tctx
, "Server updated write_time after %.2f seconds (correct)\n",
100 smb_msleep(1 * msec
);
103 torture_assert_u64_not_equal(tctx
,
104 finfo2
.basic_info
.out
.write_time
,
105 finfo1
.basic_info
.out
.write_time
,
106 "Server did not update write time within "
110 smbcli_close(cli
->tree
, fnum1
);
111 smbcli_unlink(cli
->tree
, fname
);
112 smbcli_deltree(cli
->tree
, BASEDIR
);
117 static bool test_delayed_write_update1(struct torture_context
*tctx
, struct smbcli_state
*cli
)
119 union smb_fileinfo finfo1
, finfo2
, finfo3
, pinfo4
;
120 const char *fname
= BASEDIR
"\\torture_file1.txt";
125 struct timeval start
;
127 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
128 int normal_delay
= 2000000;
129 double sec
= ((double)used_delay
) / ((double)normal_delay
);
130 int msec
= 1000 * sec
;
133 torture_comment(tctx
, "\nRunning test_delayed_write_update1\n");
135 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
137 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
138 torture_assert_int_not_equal(tctx
, fnum1
, -1, talloc_asprintf(tctx
,
139 "Failed to open %s", fname
));
141 memset(buf
, 'x', 2048);
142 written
= smbcli_write(cli
->tree
, fnum1
, 0, buf
, 0, 2048);
144 /* 3 second delay to ensure we get past any 2 second time
145 granularity (older systems may have that) */
146 smb_msleep(3 * msec
);
148 finfo1
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
149 finfo1
.all_info
.in
.file
.fnum
= fnum1
;
152 pinfo4
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
153 pinfo4
.all_info
.in
.file
.path
= fname
;
155 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
157 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
159 torture_comment(tctx
, "Initial write time %s\n",
160 nt_time_string(tctx
, finfo1
.all_info
.out
.write_time
));
162 /* 3 second delay to ensure we get past any 2 second time
163 granularity (older systems may have that) */
164 smb_msleep(3 * msec
);
166 /* Do a zero length SMBwrite call to truncate. */
167 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 1024, 0);
168 torture_assert_int_equal(tctx
, written
, 0,
169 "unexpected number of bytes written");
171 start
= timeval_current();
172 end
= timeval_add(&start
, (120 * sec
), 0);
173 while (!timeval_expired(&end
)) {
174 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
176 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
178 torture_assert_u64_equal(tctx
, finfo2
.all_info
.out
.size
, 1024,
179 "file not truncated to expected size "
182 torture_comment(tctx
, "write time %s\n",
183 nt_time_string(tctx
, finfo2
.all_info
.out
.write_time
));
184 if (finfo1
.all_info
.out
.write_time
!= finfo2
.all_info
.out
.write_time
) {
185 double diff
= timeval_elapsed(&start
);
186 if (diff
> (0.25 * (used_delay
/ (double)1000000))) {
187 torture_result(tctx
, TORTURE_FAIL
, "After SMBwrite truncate "
188 "server updated write_time after %.2f seconds"
189 "(write time update dealy == %.2f)(wrong!)\n",
190 diff
, used_delay
/ (double)1000000);
195 torture_comment(tctx
, "After SMBwrite truncate "
196 "server updated write_time after %.2f seconds"
197 "(1 sec == %.2f)(correct)\n",
198 diff
, used_delay
/ (double)1000000);
202 smb_msleep(1 * msec
);
205 torture_assert_u64_not_equal(tctx
,
206 finfo2
.all_info
.out
.write_time
,
207 finfo1
.all_info
.out
.write_time
,
208 "Server did not update write time");
211 smb_msleep(2 * msec
);
213 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
214 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 0, 1);
215 torture_assert_int_equal(tctx
, written
, 1,
216 "unexpected number of bytes written");
218 start
= timeval_current();
219 end
= timeval_add(&start
, (10*sec
), 0);
220 while (!timeval_expired(&end
)) {
221 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo3
);
223 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
225 torture_assert_u64_equal(tctx
, finfo3
.all_info
.out
.size
, 1024,
226 "file not truncated to expected size "
229 torture_comment(tctx
, "write time %s\n",
230 nt_time_string(tctx
, finfo3
.all_info
.out
.write_time
));
231 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
232 double diff
= timeval_elapsed(&start
);
234 torture_comment(tctx
, "server updated write_time after %.2f seconds"
240 smb_msleep(1 * msec
);
243 torture_assert_u64_equal(tctx
,
244 finfo3
.all_info
.out
.write_time
,
245 finfo2
.all_info
.out
.write_time
,
246 "Server updated write time (wrong!)");
249 smb_msleep(2 * msec
);
251 /* the close should trigger an write time update */
252 smbcli_close(cli
->tree
, fnum1
);
255 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &pinfo4
);
256 torture_assert_ntstatus_ok(tctx
, status
, "pathinfo failed");
258 torture_assert_u64_not_equal(tctx
,
259 pinfo4
.all_info
.out
.write_time
,
260 finfo3
.all_info
.out
.write_time
,
261 "Server did not update write time on "
264 pinfo4
.all_info
.out
.write_time
> finfo3
.all_info
.out
.write_time
,
265 "Server updated write time on close, but to an earlier point "
268 torture_comment(tctx
, "Server updated write time on close (correct)\n");
271 smbcli_close(cli
->tree
, fnum1
);
272 smbcli_unlink(cli
->tree
, fname
);
273 smbcli_deltree(cli
->tree
, BASEDIR
);
278 /* Updating with a SMBwrite of zero length
279 * changes the write time immediately - even on expand. */
281 static bool test_delayed_write_update1a(struct torture_context
*tctx
, struct smbcli_state
*cli
)
283 union smb_fileinfo finfo1
, finfo2
, finfo3
, pinfo4
;
284 const char *fname
= BASEDIR
"\\torture_file1a.txt";
289 struct timeval start
;
291 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
292 int normal_delay
= 2000000;
293 double sec
= ((double)used_delay
) / ((double)normal_delay
);
294 int msec
= 1000 * sec
;
297 torture_comment(tctx
, "\nRunning test_delayed_write_update1a\n");
299 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
301 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
303 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s", fname
);
307 memset(buf
, 'x', 2048);
308 written
= smbcli_write(cli
->tree
, fnum1
, 0, buf
, 0, 2048);
310 /* 3 second delay to ensure we get past any 2 second time
311 granularity (older systems may have that) */
312 smb_msleep(3 * msec
);
314 finfo1
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
315 finfo1
.all_info
.in
.file
.fnum
= fnum1
;
318 pinfo4
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
319 pinfo4
.all_info
.in
.file
.path
= fname
;
321 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
323 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
325 torture_comment(tctx
, "Initial write time %s\n",
326 nt_time_string(tctx
, finfo1
.all_info
.out
.write_time
));
328 /* Do a zero length SMBwrite call to truncate. */
329 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 10240, 0);
332 torture_result(tctx
, TORTURE_FAIL
, "write failed - wrote %d bytes (%s)",
333 (int)written
, __location__
);
337 start
= timeval_current();
338 end
= timeval_add(&start
, (120*sec
), 0);
339 while (!timeval_expired(&end
)) {
340 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
342 if (!NT_STATUS_IS_OK(status
)) {
343 torture_result(tctx
, TORTURE_FAIL
, "fileinfo failed: %s",
349 if (finfo2
.all_info
.out
.size
!= 10240) {
350 torture_result(tctx
, TORTURE_FAIL
,
351 "file not truncated, size = %u (should be 10240)",
352 (unsigned int)finfo2
.all_info
.out
.size
);
357 torture_comment(tctx
, "write time %s\n",
358 nt_time_string(tctx
, finfo2
.all_info
.out
.write_time
));
359 if (finfo1
.all_info
.out
.write_time
!= finfo2
.all_info
.out
.write_time
) {
360 double diff
= timeval_elapsed(&start
);
361 if (diff
> (0.25 * (used_delay
/ (double)1000000))) {
362 torture_result(tctx
, TORTURE_FAIL
, "After SMBwrite truncate "
363 "server updated write_time after %.2f seconds"
364 "(write time update delay == %.2f)(wrong!)\n",
365 diff
, used_delay
/ (double)1000000);
370 torture_comment(tctx
, "After SMBwrite truncate "
371 "server updated write_time after %.2f seconds"
372 "(write time update delay == %.2f)(correct)\n",
373 diff
, used_delay
/ (double)1000000);
377 smb_msleep(1 * msec
);
380 if (finfo1
.all_info
.out
.write_time
== finfo2
.all_info
.out
.write_time
) {
381 torture_result(tctx
, TORTURE_FAIL
,
382 "Server did not update write time (wrong!)");
387 smb_msleep(2 * msec
);
389 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
390 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 0, 1);
392 torture_assert_int_equal(tctx
, written
, 1,
393 "unexpected number of bytes written");
395 start
= timeval_current();
396 end
= timeval_add(&start
, (10*sec
), 0);
397 while (!timeval_expired(&end
)) {
398 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo3
);
400 if (!NT_STATUS_IS_OK(status
)) {
401 torture_result(tctx
, TORTURE_FAIL
, "fileinfo failed: %s\n",
407 if (finfo3
.all_info
.out
.size
!= 10240) {
408 torture_result(tctx
, TORTURE_FAIL
,
409 "file not truncated, size = %u (should be 10240)",
410 (unsigned int)finfo3
.all_info
.out
.size
);
415 torture_comment(tctx
, "write time %s\n",
416 nt_time_string(tctx
, finfo3
.all_info
.out
.write_time
));
417 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
418 double diff
= timeval_elapsed(&start
);
420 torture_result(tctx
, TORTURE_FAIL
, "server updated write_time after %.2f seconds"
421 "(write time update delay == %.2f)(correct)\n",
422 diff
, used_delay
/ (double)1000000);
426 smb_msleep(1 * msec
);
429 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
430 torture_result(tctx
, TORTURE_FAIL
,
431 "Server updated write time (wrong!)");
435 /* the close should trigger an write time update */
436 smbcli_close(cli
->tree
, fnum1
);
439 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &pinfo4
);
440 torture_assert_ntstatus_ok(tctx
, status
, "pathinfo failed");
442 if (finfo3
.all_info
.out
.write_time
== pinfo4
.all_info
.out
.write_time
) {
443 torture_result(tctx
, TORTURE_FAIL
,
444 "Server did not update write time on close (wrong!)");
446 } else if (finfo3
.all_info
.out
.write_time
< pinfo4
.all_info
.out
.write_time
) {
447 torture_comment(tctx
, "Server updated write time on close (correct)\n");
451 smbcli_close(cli
->tree
, fnum1
);
452 smbcli_unlink(cli
->tree
, fname
);
453 smbcli_deltree(cli
->tree
, BASEDIR
);
458 /* Updating with a SET_FILE_END_OF_FILE_INFO
459 * changes the write time immediately - even on expand. */
461 static bool test_delayed_write_update1b(struct torture_context
*tctx
, struct smbcli_state
*cli
)
463 union smb_fileinfo finfo1
, finfo2
, finfo3
, pinfo4
;
464 const char *fname
= BASEDIR
"\\torture_file1b.txt";
469 struct timeval start
;
471 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
472 int normal_delay
= 2000000;
473 double sec
= ((double)used_delay
) / ((double)normal_delay
);
474 int msec
= 1000 * sec
;
477 torture_comment(tctx
, "\nRunning test_delayed_write_update1b\n");
479 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
481 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
483 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s", fname
);
487 memset(buf
, 'x', 2048);
488 written
= smbcli_write(cli
->tree
, fnum1
, 0, buf
, 0, 2048);
490 /* 3 second delay to ensure we get past any 2 second time
491 granularity (older systems may have that) */
492 smb_msleep(3 * msec
);
494 finfo1
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
495 finfo1
.all_info
.in
.file
.fnum
= fnum1
;
498 pinfo4
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
499 pinfo4
.all_info
.in
.file
.path
= fname
;
501 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
503 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
505 torture_comment(tctx
, "Initial write time %s\n",
506 nt_time_string(tctx
, finfo1
.all_info
.out
.write_time
));
508 /* Do a SET_END_OF_FILE_INFO call to truncate. */
509 status
= smbcli_ftruncate(cli
->tree
, fnum1
, (uint64_t)10240);
511 torture_assert_ntstatus_ok(tctx
, status
, "SET_END_OF_FILE failed");
513 start
= timeval_current();
514 end
= timeval_add(&start
, (120*sec
), 0);
515 while (!timeval_expired(&end
)) {
516 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
518 if (!NT_STATUS_IS_OK(status
)) {
519 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
524 if (finfo2
.all_info
.out
.size
!= 10240) {
525 torture_result(tctx
, TORTURE_FAIL
,
526 "file not truncated (size = %u, should be 10240)",
527 (unsigned int)finfo2
.all_info
.out
.size
);
532 torture_comment(tctx
, "write time %s\n",
533 nt_time_string(tctx
, finfo2
.all_info
.out
.write_time
));
534 if (finfo1
.all_info
.out
.write_time
!= finfo2
.all_info
.out
.write_time
) {
535 double diff
= timeval_elapsed(&start
);
536 if (diff
> (0.25 * (used_delay
/ (double)1000000))) {
537 torture_result(tctx
, TORTURE_FAIL
,
538 "After SET_END_OF_FILE truncate "
539 "server updated write_time after %.2f seconds"
540 "(write time update delay == %.2f)(wrong!)",
541 diff
, used_delay
/ (double)1000000);
546 torture_comment(tctx
, "After SET_END_OF_FILE truncate "
547 "server updated write_time after %.2f seconds"
548 "(write time update delay == %.2f)(correct)\n",
549 diff
, used_delay
/ (double)1000000);
553 smb_msleep(1 * msec
);
556 if (finfo1
.all_info
.out
.write_time
== finfo2
.all_info
.out
.write_time
) {
557 torture_result(tctx
, TORTURE_FAIL
,
558 "Server did not update write time (wrong!)");
563 smb_msleep(2 * msec
);
565 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
566 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 0, 1);
568 torture_assert_int_equal(tctx
, written
, 1,
569 "unexpected number of bytes written");
571 start
= timeval_current();
572 end
= timeval_add(&start
, (10*sec
), 0);
573 while (!timeval_expired(&end
)) {
574 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo3
);
576 if (!NT_STATUS_IS_OK(status
)) {
577 torture_result(tctx
, TORTURE_FAIL
,
578 "fileinfo failed: %s", nt_errstr(status
));
583 if (finfo3
.all_info
.out
.size
!= 10240) {
584 DEBUG(0, ("file not truncated (size = %u, should be 10240)\n",
585 (unsigned int)finfo3
.all_info
.out
.size
));
590 torture_comment(tctx
, "write time %s\n",
591 nt_time_string(tctx
, finfo3
.all_info
.out
.write_time
));
592 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
593 double diff
= timeval_elapsed(&start
);
595 torture_comment(tctx
, "server updated write_time after %.2f seconds"
596 "(write time update delay == %.2f)(correct)\n",
597 diff
, used_delay
/ (double)1000000);
601 smb_msleep(1 * msec
);
604 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
605 torture_result(tctx
, TORTURE_FAIL
, "Server updated write time (wrong!)\n");
609 /* the close should trigger an write time update */
610 smbcli_close(cli
->tree
, fnum1
);
613 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &pinfo4
);
614 torture_assert_ntstatus_ok(tctx
, status
, "pathinfo failed");
616 if (finfo3
.all_info
.out
.write_time
== pinfo4
.all_info
.out
.write_time
) {
617 torture_result(tctx
, TORTURE_FAIL
, "Server did not update write time on close (wrong!)\n");
619 } else if (finfo3
.all_info
.out
.write_time
< pinfo4
.all_info
.out
.write_time
) {
620 torture_comment(tctx
, "Server updated write time on close (correct)\n");
624 smbcli_close(cli
->tree
, fnum1
);
625 smbcli_unlink(cli
->tree
, fname
);
626 smbcli_deltree(cli
->tree
, BASEDIR
);
631 /* Updating with a SET_ALLOCATION_INFO (truncate) does so immediately. */
633 static bool test_delayed_write_update1c(struct torture_context
*tctx
, struct smbcli_state
*cli
)
635 union smb_setfileinfo parms
;
636 union smb_fileinfo finfo1
, finfo2
, finfo3
, pinfo4
;
637 const char *fname
= BASEDIR
"\\torture_file1c.txt";
642 struct timeval start
;
644 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
645 int normal_delay
= 2000000;
646 double sec
= ((double)used_delay
) / ((double)normal_delay
);
647 int msec
= 1000 * sec
;
650 torture_comment(tctx
, "\nRunning test_delayed_write_update1c\n");
652 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
654 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
656 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s", fname
);
660 memset(buf
, 'x', 2048);
661 written
= smbcli_write(cli
->tree
, fnum1
, 0, buf
, 0, 2048);
663 /* 3 second delay to ensure we get past any 2 second time
664 granularity (older systems may have that) */
665 smb_msleep(3 * msec
);
667 finfo1
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
668 finfo1
.all_info
.in
.file
.fnum
= fnum1
;
671 pinfo4
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
672 pinfo4
.all_info
.in
.file
.path
= fname
;
674 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
676 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
678 torture_comment(tctx
, "Initial write time %s\n",
679 nt_time_string(tctx
, finfo1
.all_info
.out
.write_time
));
681 /* Do a SET_ALLOCATION_SIZE call to truncate. */
682 parms
.allocation_info
.level
= RAW_SFILEINFO_ALLOCATION_INFO
;
683 parms
.allocation_info
.in
.file
.fnum
= fnum1
;
684 parms
.allocation_info
.in
.alloc_size
= 0;
686 status
= smb_raw_setfileinfo(cli
->tree
, &parms
);
688 torture_assert_ntstatus_ok(tctx
, status
,
689 "RAW_SFILEINFO_ALLOCATION_INFO failed");
691 start
= timeval_current();
692 end
= timeval_add(&start
, (120*sec
), 0);
693 while (!timeval_expired(&end
)) {
694 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
696 if (!NT_STATUS_IS_OK(status
)) {
697 torture_result(tctx
, TORTURE_FAIL
, "fileinfo failed: %s",
703 if (finfo2
.all_info
.out
.size
!= 0) {
704 torture_result(tctx
, TORTURE_FAIL
,
705 "file not truncated (size = %u, should be 10240)",
706 (unsigned int)finfo2
.all_info
.out
.size
);
711 torture_comment(tctx
, "write time %s\n",
712 nt_time_string(tctx
, finfo2
.all_info
.out
.write_time
));
713 if (finfo1
.all_info
.out
.write_time
!= finfo2
.all_info
.out
.write_time
) {
714 double diff
= timeval_elapsed(&start
);
715 if (diff
> (0.25 * (used_delay
/ (double)1000000))) {
716 torture_result(tctx
, TORTURE_FAIL
, "After SET_ALLOCATION_INFO truncate "
717 "server updated write_time after %.2f seconds"
718 "(write time update delay == %.2f)(wrong!)\n",
719 diff
, used_delay
/ (double)1000000);
724 torture_comment(tctx
, "After SET_ALLOCATION_INFO truncate "
725 "server updated write_time after %.2f seconds"
726 "(write time update delay == %.2f)(correct)\n",
727 diff
, used_delay
/ (double)1000000);
731 smb_msleep(1 * msec
);
734 if (finfo1
.all_info
.out
.write_time
== finfo2
.all_info
.out
.write_time
) {
735 torture_result(tctx
, TORTURE_FAIL
,
736 "Server did not update write time (wrong!)");
741 smb_msleep(2 * msec
);
743 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
744 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 0, 1);
745 torture_assert_int_equal(tctx
, written
, 1,
746 "Unexpected number of bytes written");
748 start
= timeval_current();
749 end
= timeval_add(&start
, (10*sec
), 0);
750 while (!timeval_expired(&end
)) {
751 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo3
);
753 if (!NT_STATUS_IS_OK(status
)) {
754 torture_result(tctx
, TORTURE_FAIL
, "fileinfo failed: %s",
760 if (finfo3
.all_info
.out
.size
!= 1) {
761 torture_result(tctx
, TORTURE_FAIL
, "file not expanded");
766 torture_comment(tctx
, "write time %s\n",
767 nt_time_string(tctx
, finfo3
.all_info
.out
.write_time
));
768 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
769 double diff
= timeval_elapsed(&start
);
771 torture_comment(tctx
, "server updated write_time after %.2f seconds"
772 "(write time update delay == %.2f)(wrong)\n",
773 diff
, used_delay
/ (double)1000000);
777 smb_msleep(1 * msec
);
780 if (finfo2
.all_info
.out
.write_time
!= finfo3
.all_info
.out
.write_time
) {
781 torture_result(tctx
, TORTURE_FAIL
,
782 "Server updated write time (wrong!)");
786 /* the close should trigger an write time update */
787 smbcli_close(cli
->tree
, fnum1
);
790 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &pinfo4
);
791 torture_assert_ntstatus_ok(tctx
, status
, "pathinfo failed");
793 if (finfo3
.all_info
.out
.write_time
== pinfo4
.all_info
.out
.write_time
) {
794 torture_result(tctx
, TORTURE_FAIL
, "Server did not update write time on close (wrong!)\n");
796 } else if (finfo3
.all_info
.out
.write_time
< pinfo4
.all_info
.out
.write_time
) {
797 torture_comment(tctx
, "Server updated write time on close (correct)\n");
801 smbcli_close(cli
->tree
, fnum1
);
802 smbcli_unlink(cli
->tree
, fname
);
803 smbcli_deltree(cli
->tree
, BASEDIR
);
809 * Do as above, but using 2 connections.
812 static bool test_delayed_write_update2(struct torture_context
*tctx
, struct smbcli_state
*cli
,
813 struct smbcli_state
*cli2
)
815 union smb_fileinfo finfo1
, finfo2
;
816 const char *fname
= BASEDIR
"\\torture_file.txt";
822 struct timeval start
;
824 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
825 int normal_delay
= 2000000;
826 double sec
= ((double)used_delay
) / ((double)normal_delay
);
827 int msec
= 1000 * sec
;
828 union smb_flush flsh
;
830 torture_comment(tctx
, "\nRunning test_delayed_write_update2\n");
832 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
834 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
836 torture_comment(tctx
, "Failed to open %s\n", fname
);
840 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
841 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
844 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
846 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
848 torture_comment(tctx
, "Initial write time %s\n",
849 nt_time_string(tctx
, finfo1
.basic_info
.out
.write_time
));
851 /* 3 second delay to ensure we get past any 2 second time
852 granularity (older systems may have that) */
853 smb_msleep(3 * msec
);
856 /* Try using setfileinfo instead of write to update write time. */
857 union smb_setfileinfo sfinfo
;
858 time_t t_set
= time(NULL
);
859 sfinfo
.basic_info
.level
= RAW_SFILEINFO_BASIC_INFO
;
860 sfinfo
.basic_info
.in
.file
.fnum
= fnum1
;
861 sfinfo
.basic_info
.in
.create_time
= finfo1
.basic_info
.out
.create_time
;
862 sfinfo
.basic_info
.in
.access_time
= finfo1
.basic_info
.out
.access_time
;
864 /* I tried this with both + and - ve to see if it makes a different.
865 It doesn't - once the filetime is set via setfileinfo it stays that way. */
867 unix_to_nt_time(&sfinfo
.basic_info
.in
.write_time
, t_set
- 30000);
869 unix_to_nt_time(&sfinfo
.basic_info
.in
.write_time
, t_set
+ 30000);
871 sfinfo
.basic_info
.in
.change_time
= finfo1
.basic_info
.out
.change_time
;
872 sfinfo
.basic_info
.in
.attrib
= finfo1
.basic_info
.out
.attrib
;
874 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
876 torture_assert_ntstatus_ok(tctx
, status
, "sfileinfo failed");
879 finfo2
.basic_info
.in
.file
.path
= fname
;
881 status
= smb_raw_pathinfo(cli2
->tree
, tctx
, &finfo2
);
883 if (!NT_STATUS_IS_OK(status
)) {
884 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
887 torture_comment(tctx
, "write time %s\n",
888 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
890 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
891 torture_comment(tctx
, "Server updated write_time (correct)\n");
893 torture_result(tctx
, TORTURE_FAIL
, "Server did not update write time (wrong!)\n");
897 /* Now try a write to see if the write time gets reset. */
899 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
900 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
903 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
905 if (!NT_STATUS_IS_OK(status
)) {
906 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
910 torture_comment(tctx
, "Modified write time %s\n",
911 nt_time_string(tctx
, finfo1
.basic_info
.out
.write_time
));
914 torture_comment(tctx
, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
916 written
= smbcli_write(cli
->tree
, fnum1
, 0, "0123456789", 1, 10);
919 torture_result(tctx
, TORTURE_FAIL
, "write failed - wrote %d bytes (%s)\n",
920 (int)written
, __location__
);
924 /* Just to prove to tridge that the an smbflush has no effect on
925 the write time :-). The setfileinfo IS STICKY. JRA. */
927 torture_comment(tctx
, "Doing flush after write\n");
929 flsh
.flush
.level
= RAW_FLUSH_FLUSH
;
930 flsh
.flush
.in
.file
.fnum
= fnum1
;
931 status
= smb_raw_flush(cli
->tree
, &flsh
);
932 if (!NT_STATUS_IS_OK(status
)) {
933 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status
)));
937 /* Once the time was set using setfileinfo then it stays set - writes
938 don't have any effect. But make sure. */
939 start
= timeval_current();
940 end
= timeval_add(&start
, (15*sec
), 0);
941 while (!timeval_expired(&end
)) {
942 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
944 if (!NT_STATUS_IS_OK(status
)) {
945 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
949 torture_comment(tctx
, "write time %s\n",
950 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
951 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
952 double diff
= timeval_elapsed(&start
);
953 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds"
960 smb_msleep(1 * msec
);
963 if (finfo1
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
964 torture_comment(tctx
, "Server did not update write time (correct)\n");
968 smb_msleep(2 * msec
);
970 fnum2
= smbcli_open(cli
->tree
, fname
, O_RDWR
, DENY_NONE
);
972 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s\n", fname
);
976 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");
978 written
= smbcli_write(cli
->tree
, fnum2
, 0, "0123456789", 11, 10);
981 torture_result(tctx
, TORTURE_FAIL
, "write failed - wrote %d bytes (%s)\n",
982 (int)written
, __location__
);
986 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
988 if (!NT_STATUS_IS_OK(status
)) {
989 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
992 torture_comment(tctx
, "write time %s\n",
993 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
994 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
995 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time (wrong!)\n");
999 torture_comment(tctx
, "Closing the first fd to see if write time updated.\n");
1000 smbcli_close(cli
->tree
, fnum1
);
1003 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");
1005 written
= smbcli_write(cli
->tree
, fnum2
, 0, "0123456789", 21, 10);
1007 if (written
!= 10) {
1008 torture_result(tctx
, TORTURE_FAIL
, "write failed - wrote %d bytes (%s)\n",
1009 (int)written
, __location__
);
1013 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1014 finfo1
.basic_info
.in
.file
.fnum
= fnum2
;
1016 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
1018 if (!NT_STATUS_IS_OK(status
)) {
1019 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
1022 torture_comment(tctx
, "write time %s\n",
1023 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
1024 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
1025 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time (wrong!)\n");
1029 /* Once the time was set using setfileinfo then it stays set - writes
1030 don't have any effect. But make sure. */
1031 start
= timeval_current();
1032 end
= timeval_add(&start
, (15*sec
), 0);
1033 while (!timeval_expired(&end
)) {
1034 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
1036 if (!NT_STATUS_IS_OK(status
)) {
1037 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
1041 torture_comment(tctx
, "write time %s\n",
1042 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
1043 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
1044 double diff
= timeval_elapsed(&start
);
1045 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
1052 smb_msleep(1 * msec
);
1055 if (finfo1
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
1056 torture_comment(tctx
, "Server did not update write time (correct)\n");
1059 torture_comment(tctx
, "Closing second fd to see if write time updated.\n");
1061 smbcli_close(cli
->tree
, fnum2
);
1064 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
, DENY_NONE
);
1066 torture_comment(tctx
, "Failed to open %s\n", fname
);
1070 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1071 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
1074 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
1076 if (!NT_STATUS_IS_OK(status
)) {
1077 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
1081 torture_comment(tctx
, "Second open initial write time %s\n",
1082 nt_time_string(tctx
, finfo1
.basic_info
.out
.write_time
));
1084 smb_msleep(10 * msec
);
1085 torture_comment(tctx
, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
1087 written
= smbcli_write(cli
->tree
, fnum1
, 0, "0123456789", 31, 10);
1089 if (written
!= 10) {
1090 torture_result(tctx
, TORTURE_FAIL
, "write failed - wrote %d bytes (%s)\n",
1091 (int)written
, __location__
);
1095 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1096 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
1098 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
1100 if (!NT_STATUS_IS_OK(status
)) {
1101 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
1104 torture_comment(tctx
, "write time %s\n",
1105 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
1106 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
1107 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time (wrong!)\n");
1111 /* Now the write time should be updated again */
1112 start
= timeval_current();
1113 end
= timeval_add(&start
, (15*sec
), 0);
1114 while (!timeval_expired(&end
)) {
1115 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
1117 if (!NT_STATUS_IS_OK(status
)) {
1118 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status
)));
1122 torture_comment(tctx
, "write time %s\n",
1123 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
1124 if (finfo1
.basic_info
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
1125 double diff
= timeval_elapsed(&start
);
1126 if (diff
< (used_delay
/ (double)1000000)) {
1127 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds"
1128 "(expected > %.2f) (wrong!)\n",
1129 diff
, used_delay
/ (double)1000000);
1134 torture_comment(tctx
, "Server updated write_time after %.2f seconds"
1143 if (finfo1
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
1144 torture_result(tctx
, TORTURE_FAIL
, "Server did not update write time (wrong!)\n");
1149 /* One more test to do. We should read the filetime via findfirst on the
1150 second connection to ensure it's the same. This is very easy for a Windows
1151 server but a bastard to get right on a POSIX server. JRA. */
1154 smbcli_close(cli
->tree
, fnum1
);
1155 smbcli_unlink(cli
->tree
, fname
);
1156 smbcli_deltree(cli
->tree
, BASEDIR
);
1162 /* Windows does obviously not update the stat info during a write call. I
1163 * *think* this is the problem causing a spurious Excel 2003 on XP error
1164 * message when saving a file. Excel does a setfileinfo, writes, and then does
1165 * a getpath(!)info. Or so... For Samba sometimes it displays an error message
1166 * that the file might have been changed in between. What i've been able to
1167 * trace down is that this happens if the getpathinfo after the write shows a
1168 * different last write time than the setfileinfo showed. This is really
1172 static bool test_finfo_after_write(struct torture_context
*tctx
, struct smbcli_state
*cli
,
1173 struct smbcli_state
*cli2
)
1175 union smb_fileinfo finfo1
, finfo2
;
1176 const char *fname
= BASEDIR
"\\torture_file.txt";
1182 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
1183 int normal_delay
= 2000000;
1184 double sec
= ((double)used_delay
) / ((double)normal_delay
);
1185 int msec
= 1000 * sec
;
1187 torture_comment(tctx
, "\nRunning test_finfo_after_write\n");
1189 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1191 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
1194 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
1198 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1199 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
1201 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
1203 if (!NT_STATUS_IS_OK(status
)) {
1205 torture_result(tctx
, TORTURE_FAIL
, __location__
": fileinfo failed: %s", nt_errstr(status
));
1209 smb_msleep(1 * msec
);
1211 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1214 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1219 fnum2
= smbcli_open(cli2
->tree
, fname
, O_RDWR
, DENY_NONE
);
1221 torture_result(tctx
, TORTURE_FAIL
, __location__
": failed to open 2nd time - %s",
1222 smbcli_errstr(cli2
->tree
));
1227 written
= smbcli_write(cli2
->tree
, fnum2
, 0, "x", 0, 1);
1230 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1",
1236 finfo2
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1237 finfo2
.basic_info
.in
.file
.path
= fname
;
1239 status
= smb_raw_pathinfo(cli2
->tree
, tctx
, &finfo2
);
1241 if (!NT_STATUS_IS_OK(status
)) {
1242 torture_result(tctx
, TORTURE_FAIL
, __location__
": fileinfo failed: %s",
1248 if (finfo1
.basic_info
.out
.create_time
!=
1249 finfo2
.basic_info
.out
.create_time
) {
1250 torture_result(tctx
, TORTURE_FAIL
, __location__
": create_time changed");
1255 if (finfo1
.basic_info
.out
.access_time
!=
1256 finfo2
.basic_info
.out
.access_time
) {
1257 torture_result(tctx
, TORTURE_FAIL
, __location__
": access_time changed");
1262 if (finfo1
.basic_info
.out
.write_time
!=
1263 finfo2
.basic_info
.out
.write_time
) {
1264 torture_result(tctx
, TORTURE_FAIL
, __location__
": write_time changed:\n"
1265 "write time conn 1 = %s, conn 2 = %s",
1266 nt_time_string(tctx
, finfo1
.basic_info
.out
.write_time
),
1267 nt_time_string(tctx
, finfo2
.basic_info
.out
.write_time
));
1272 if (finfo1
.basic_info
.out
.change_time
!=
1273 finfo2
.basic_info
.out
.change_time
) {
1274 torture_result(tctx
, TORTURE_FAIL
, __location__
": change_time changed");
1279 /* One of the two following calls updates the qpathinfo. */
1281 /* If you had skipped the smbcli_write on fnum2, it would
1282 * *not* have updated the stat on disk */
1284 smbcli_close(cli2
->tree
, fnum2
);
1287 /* This call is only for the people looking at ethereal :-) */
1288 finfo2
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1289 finfo2
.basic_info
.in
.file
.path
= fname
;
1291 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo2
);
1293 if (!NT_STATUS_IS_OK(status
)) {
1294 torture_result(tctx
, TORTURE_FAIL
, __location__
": fileinfo failed: %s", nt_errstr(status
));
1301 smbcli_close(cli
->tree
, fnum1
);
1302 smbcli_unlink(cli
->tree
, fname
);
1303 smbcli_deltree(cli
->tree
, BASEDIR
);
1308 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
1309 uint64_t r = 10*1000*1000; \
1310 NTTIME g = (given).basic_info.out.write_time; \
1311 NTTIME gr = (g / r) * r; \
1312 NTTIME c = (correct).basic_info.out.write_time; \
1313 NTTIME cr = (c / r) * r; \
1314 bool strict = torture_setting_bool(tctx, "strict mode", false); \
1316 if (strict && (g cmp c)) { \
1318 } else if ((g cmp c) && (gr cmp cr)) { \
1319 /* handle filesystem without high resolution timestamps */ \
1323 torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \
1324 #given, nt_time_string(tctx, g), (unsigned long long)g, \
1325 #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \
1330 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
1331 COMPARE_WRITE_TIME_CMP(given,correct,!=)
1332 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
1333 COMPARE_WRITE_TIME_CMP(given,correct,<=)
1334 #define COMPARE_WRITE_TIME_LESS(given,correct) \
1335 COMPARE_WRITE_TIME_CMP(given,correct,>=)
1337 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
1338 NTTIME g = (given).basic_info.out.access_time; \
1339 NTTIME c = (correct).basic_info.out.access_time; \
1341 torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \
1342 #given, nt_time_string(tctx, g), \
1343 #cmp, #correct, nt_time_string(tctx, c)); \
1348 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
1349 COMPARE_ACCESS_TIME_CMP(given,correct,!=)
1351 #define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \
1352 COMPARE_ACCESS_TIME_EQUAL(given,correct); \
1353 COMPARE_WRITE_TIME_EQUAL(given,correct); \
1356 #define GET_INFO_FILE(finfo) do { \
1358 _status = smb_raw_fileinfo(cli->tree, tctx, &finfo); \
1359 if (!NT_STATUS_IS_OK(_status)) { \
1361 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1362 nt_errstr(_status)); \
1365 torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1366 nt_time_string(tctx, finfo.basic_info.out.access_time), \
1367 nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1369 #define GET_INFO_FILE2(finfo) do { \
1371 _status = smb_raw_fileinfo(cli2->tree, tctx, &finfo); \
1372 if (!NT_STATUS_IS_OK(_status)) { \
1374 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1375 nt_errstr(_status)); \
1378 torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1379 nt_time_string(tctx, finfo.basic_info.out.access_time), \
1380 nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1382 #define GET_INFO_PATH(pinfo) do { \
1384 _status = smb_raw_pathinfo(cli2->tree, tctx, &pinfo); \
1385 if (!NT_STATUS_IS_OK(_status)) { \
1386 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \
1387 nt_errstr(_status)); \
1391 torture_comment(tctx, "pathinfo: Access(%s) Write(%s)\n", \
1392 nt_time_string(tctx, pinfo.basic_info.out.access_time), \
1393 nt_time_string(tctx, pinfo.basic_info.out.write_time)); \
1395 #define GET_INFO_BOTH(finfo,pinfo) do { \
1396 GET_INFO_FILE(finfo); \
1397 GET_INFO_PATH(pinfo); \
1398 COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \
1401 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
1403 union smb_setfileinfo sfinfo; \
1404 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1405 sfinfo.basic_info.in.file.fnum = tfnum; \
1406 sfinfo.basic_info.in.create_time = 0; \
1407 sfinfo.basic_info.in.access_time = 0; \
1408 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1409 sfinfo.basic_info.in.change_time = 0; \
1410 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1411 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1412 if (!NT_STATUS_IS_OK(_status)) { \
1413 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1414 nt_errstr(_status)); \
1419 #define SET_INFO_FILE(finfo, wrtime) \
1420 SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
1422 #define SET_INFO_FILE_NS(finfo, wrtime, ns, tree, tfnum) do { \
1424 union smb_setfileinfo sfinfo; \
1425 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1426 sfinfo.basic_info.in.file.fnum = tfnum; \
1427 sfinfo.basic_info.in.create_time = 0; \
1428 sfinfo.basic_info.in.access_time = 0; \
1429 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1430 sfinfo.basic_info.in.write_time += (ns); \
1431 sfinfo.basic_info.in.change_time = 0; \
1432 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1433 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1434 if (!NT_STATUS_IS_OK(_status)) { \
1435 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1436 nt_errstr(_status)); \
1442 static bool test_delayed_write_update3(struct torture_context
*tctx
,
1443 struct smbcli_state
*cli
,
1444 struct smbcli_state
*cli2
)
1446 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
;
1447 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
;
1448 const char *fname
= BASEDIR
"\\torture_file3.txt";
1452 struct timeval start
;
1454 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
1455 int normal_delay
= 2000000;
1456 double sec
= ((double)used_delay
) / ((double)normal_delay
);
1457 int msec
= 1000 * sec
;
1459 torture_comment(tctx
, "\nRunning test_delayed_write_update3\n");
1461 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1463 torture_comment(tctx
, "Open the file handle\n");
1464 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
1467 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
1471 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1472 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
1476 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1477 pinfo0
.basic_info
.in
.file
.path
= fname
;
1483 /* get the initial times */
1484 GET_INFO_BOTH(finfo0
,pinfo0
);
1487 * make sure the write time is updated 2 seconds later
1488 * calcuated from the first write
1489 * (but expect upto 5 seconds extra time for a busy server)
1491 start
= timeval_current();
1492 end
= timeval_add(&start
, 7 * sec
, 0);
1493 while (!timeval_expired(&end
)) {
1495 torture_comment(tctx
, "Do a write on the file handle\n");
1496 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1498 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1502 /* get the times after the write */
1503 GET_INFO_FILE(finfo1
);
1505 if (finfo1
.basic_info
.out
.write_time
> finfo0
.basic_info
.out
.write_time
) {
1506 double diff
= timeval_elapsed(&start
);
1507 if (diff
< (used_delay
/ (double)1000000)) {
1508 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
1509 "(write time update delay == %.2f) (wrong!)\n",
1510 diff
, used_delay
/ (double)1000000);
1515 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
1520 smb_msleep(0.5 * msec
);
1523 GET_INFO_BOTH(finfo1
,pinfo1
);
1524 COMPARE_WRITE_TIME_GREATER(pinfo1
, pinfo0
);
1526 /* sure any further write doesn't update the write time */
1527 start
= timeval_current();
1528 end
= timeval_add(&start
, 15 * sec
, 0);
1529 while (!timeval_expired(&end
)) {
1531 torture_comment(tctx
, "Do a write on the file handle\n");
1532 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1534 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1538 /* get the times after the write */
1539 GET_INFO_BOTH(finfo2
,pinfo2
);
1541 if (finfo2
.basic_info
.out
.write_time
> finfo1
.basic_info
.out
.write_time
) {
1542 double diff
= timeval_elapsed(&start
);
1543 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
1549 smb_msleep(1 * msec
);
1552 GET_INFO_BOTH(finfo2
,pinfo2
);
1553 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
1554 if (finfo2
.basic_info
.out
.write_time
== finfo1
.basic_info
.out
.write_time
) {
1555 torture_comment(tctx
, "Server did not update write_time (correct)\n");
1559 smb_msleep(5 * msec
);
1561 GET_INFO_BOTH(finfo3
,pinfo3
);
1562 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
1565 * the close updates the write time to the time of the close
1566 * and not to the time of the last write!
1568 torture_comment(tctx
, "Close the file handle\n");
1569 smbcli_close(cli
->tree
, fnum1
);
1572 GET_INFO_PATH(pinfo4
);
1573 COMPARE_WRITE_TIME_GREATER(pinfo4
, pinfo3
);
1575 if (pinfo4
.basic_info
.out
.write_time
> pinfo3
.basic_info
.out
.write_time
) {
1576 torture_comment(tctx
, "Server updated the write_time on close (correct)\n");
1581 smbcli_close(cli
->tree
, fnum1
);
1582 smbcli_unlink(cli
->tree
, fname
);
1583 smbcli_deltree(cli
->tree
, BASEDIR
);
1589 * Show that a truncate write always updates the write time even
1590 * if an initial write has already updated the write time.
1593 static bool test_delayed_write_update3a(struct torture_context
*tctx
,
1594 struct smbcli_state
*cli
,
1595 struct smbcli_state
*cli2
)
1597 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
;
1598 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
;
1599 const char *fname
= BASEDIR
"\\torture_file3a.txt";
1604 struct timeval start
;
1606 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
1607 int normal_delay
= 2000000;
1608 double sec
= ((double)used_delay
) / ((double)normal_delay
);
1609 int msec
= 1000 * sec
;
1611 torture_comment(tctx
, "\nRunning test_delayed_write_update3a\n");
1613 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1615 torture_comment(tctx
, "Open the file handle\n");
1616 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
1619 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
1623 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1624 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
1628 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1629 pinfo0
.basic_info
.in
.file
.path
= fname
;
1635 /* get the initial times */
1636 GET_INFO_BOTH(finfo0
,pinfo0
);
1639 * sleep some time, to demonstrate the handling of write times
1640 * doesn't depend on the time since the open
1642 smb_msleep(5 * msec
);
1644 /* get the initial times */
1645 GET_INFO_BOTH(finfo1
,pinfo1
);
1646 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo0
);
1649 * make sure the write time is updated 2 seconds later
1650 * calcuated from the first write
1651 * (but expect upto 5 seconds extra time for a busy server)
1653 start
= timeval_current();
1654 end
= timeval_add(&start
, 7 * sec
, 0);
1655 while (!timeval_expired(&end
)) {
1657 torture_comment(tctx
, "Do a write on the file handle\n");
1658 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1660 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1664 /* get the times after the write */
1665 GET_INFO_FILE(finfo1
);
1667 if (finfo1
.basic_info
.out
.write_time
> finfo0
.basic_info
.out
.write_time
) {
1668 double diff
= timeval_elapsed(&start
);
1669 if (diff
< (used_delay
/ (double)1000000)) {
1670 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
1671 "(1sec == %.2f) (wrong!)\n",
1677 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
1682 smb_msleep(0.5 * msec
);
1685 GET_INFO_BOTH(finfo1
,pinfo1
);
1686 COMPARE_WRITE_TIME_GREATER(pinfo1
, pinfo0
);
1688 smb_msleep(3 * msec
);
1691 * demonstrate that a truncate write always
1692 * updates the write time immediately
1694 for (i
=0; i
< 3; i
++) {
1695 smb_msleep(2 * msec
);
1697 torture_comment(tctx
, "Do a truncate SMBwrite [%d] on the file handle\n", i
);
1698 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 10240, 0);
1700 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 0", (int)written
);
1704 /* get the times after the write */
1705 GET_INFO_BOTH(finfo2
,pinfo2
);
1706 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
1710 smb_msleep(3 * msec
);
1712 /* sure any further write doesn't update the write time */
1713 start
= timeval_current();
1714 end
= timeval_add(&start
, 15 * sec
, 0);
1715 while (!timeval_expired(&end
)) {
1717 torture_comment(tctx
, "Do a write on the file handle\n");
1718 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1720 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1724 /* get the times after the write */
1725 GET_INFO_BOTH(finfo2
,pinfo2
);
1727 if (finfo2
.basic_info
.out
.write_time
> finfo1
.basic_info
.out
.write_time
) {
1728 double diff
= timeval_elapsed(&start
);
1729 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
1735 smb_msleep(1 * msec
);
1738 GET_INFO_BOTH(finfo2
,pinfo2
);
1739 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
1740 if (finfo2
.basic_info
.out
.write_time
== finfo1
.basic_info
.out
.write_time
) {
1741 torture_comment(tctx
, "Server did not update write_time (correct)\n");
1745 smb_msleep(3 * msec
);
1747 /* get the initial times */
1748 GET_INFO_BOTH(finfo1
,pinfo1
);
1749 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo2
);
1752 * demonstrate that a truncate write always
1753 * updates the write time immediately
1755 for (i
=0; i
< 3; i
++) {
1756 smb_msleep(2 * msec
);
1758 torture_comment(tctx
, "Do a truncate SMBwrite [%d] on the file handle\n", i
);
1759 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 512, 0);
1761 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 0", (int)written
);
1765 /* get the times after the write */
1766 GET_INFO_BOTH(finfo2
,pinfo2
);
1767 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
1772 smb_msleep(3 * msec
);
1774 GET_INFO_BOTH(finfo3
,pinfo3
);
1775 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
1778 * the close doesn't update the write time
1780 torture_comment(tctx
, "Close the file handle\n");
1781 smbcli_close(cli
->tree
, fnum1
);
1784 GET_INFO_PATH(pinfo4
);
1785 COMPARE_WRITE_TIME_EQUAL(pinfo4
, pinfo3
);
1787 if (pinfo4
.basic_info
.out
.write_time
== pinfo3
.basic_info
.out
.write_time
) {
1788 torture_comment(tctx
, "Server did not update the write_time on close (correct)\n");
1793 smbcli_close(cli
->tree
, fnum1
);
1794 smbcli_unlink(cli
->tree
, fname
);
1795 smbcli_deltree(cli
->tree
, BASEDIR
);
1801 * Show a close after write updates the write timestamp to
1802 * the close time, not the last write time.
1805 static bool test_delayed_write_update3b(struct torture_context
*tctx
,
1806 struct smbcli_state
*cli
,
1807 struct smbcli_state
*cli2
)
1809 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
;
1810 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
;
1811 const char *fname
= BASEDIR
"\\torture_file3b.txt";
1815 struct timeval start
;
1817 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
1818 int normal_delay
= 2000000;
1819 double sec
= ((double)used_delay
) / ((double)normal_delay
);
1820 int msec
= 1000 * sec
;
1822 torture_comment(tctx
, "\nRunning test_delayed_write_update3b\n");
1824 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1826 torture_comment(tctx
, "Open the file handle\n");
1827 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
1830 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
1834 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1835 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
1839 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1840 pinfo0
.basic_info
.in
.file
.path
= fname
;
1846 /* get the initial times */
1847 GET_INFO_BOTH(finfo0
,pinfo0
);
1850 * sleep some time, to demonstrate the handling of write times
1851 * doesn't depend on the time since the open
1853 smb_msleep(5 * msec
);
1855 /* get the initial times */
1856 GET_INFO_BOTH(finfo1
,pinfo1
);
1857 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo0
);
1860 * make sure the write time is updated 2 seconds later
1861 * calcuated from the first write
1862 * (but expect upto 5 seconds extra time for a busy server)
1864 start
= timeval_current();
1865 end
= timeval_add(&start
, 7 * sec
, 0);
1866 while (!timeval_expired(&end
)) {
1868 torture_comment(tctx
, "Do a write on the file handle\n");
1869 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1871 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1875 /* get the times after the write */
1876 GET_INFO_FILE(finfo1
);
1878 if (finfo1
.basic_info
.out
.write_time
> finfo0
.basic_info
.out
.write_time
) {
1879 double diff
= timeval_elapsed(&start
);
1880 if (diff
< (used_delay
/ (double)1000000)) {
1881 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds"
1882 "(expected > %.2f) (wrong!)\n",
1883 diff
, used_delay
/ (double)1000000);
1888 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
1889 "(write time update delay == %.2f) (correct)\n",
1890 diff
, used_delay
/ (double)1000000);
1893 smb_msleep(0.5 * msec
);
1896 GET_INFO_BOTH(finfo1
,pinfo1
);
1897 COMPARE_WRITE_TIME_GREATER(pinfo1
, pinfo0
);
1899 /* sure any further write doesn't update the write time */
1900 start
= timeval_current();
1901 end
= timeval_add(&start
, 15 * sec
, 0);
1902 while (!timeval_expired(&end
)) {
1904 torture_comment(tctx
, "Do a write on the file handle\n");
1905 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
1907 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
1911 /* get the times after the write */
1912 GET_INFO_BOTH(finfo2
,pinfo2
);
1914 if (finfo2
.basic_info
.out
.write_time
> finfo1
.basic_info
.out
.write_time
) {
1915 double diff
= timeval_elapsed(&start
);
1916 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
1922 smb_msleep(1 * msec
);
1925 GET_INFO_BOTH(finfo2
,pinfo2
);
1926 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
1927 if (finfo2
.basic_info
.out
.write_time
== finfo1
.basic_info
.out
.write_time
) {
1928 torture_comment(tctx
, "Server did not update write_time (correct)\n");
1932 smb_msleep(5 * msec
);
1934 GET_INFO_BOTH(finfo3
,pinfo3
);
1935 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
1938 * the close updates the write time to the time of the close
1939 * and not to the time of the last write!
1941 torture_comment(tctx
, "Close the file handle\n");
1942 smbcli_close(cli
->tree
, fnum1
);
1945 GET_INFO_PATH(pinfo4
);
1946 COMPARE_WRITE_TIME_GREATER(pinfo4
, pinfo3
);
1948 if (pinfo4
.basic_info
.out
.write_time
> pinfo3
.basic_info
.out
.write_time
) {
1949 torture_comment(tctx
, "Server updated the write_time on close (correct)\n");
1954 smbcli_close(cli
->tree
, fnum1
);
1955 smbcli_unlink(cli
->tree
, fname
);
1956 smbcli_deltree(cli
->tree
, BASEDIR
);
1962 * Check that a write after a truncate write doesn't update
1963 * the timestamp, but a truncate write after a write does.
1964 * Also prove that a close after a truncate write updates the
1965 * timestamp to current, not the time of last write.
1968 static bool test_delayed_write_update3c(struct torture_context
*tctx
,
1969 struct smbcli_state
*cli
,
1970 struct smbcli_state
*cli2
)
1972 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
;
1973 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
;
1974 const char *fname
= BASEDIR
"\\torture_file3c.txt";
1979 struct timeval start
;
1981 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
1982 int normal_delay
= 2000000;
1983 double sec
= ((double)used_delay
) / ((double)normal_delay
);
1984 int msec
= 1000 * sec
;
1986 torture_comment(tctx
, "\nRunning test_delayed_write_update3c\n");
1988 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1990 torture_comment(tctx
, "Open the file handle\n");
1991 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
1994 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
1998 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
1999 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
2003 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2004 pinfo0
.basic_info
.in
.file
.path
= fname
;
2010 /* get the initial times */
2011 GET_INFO_BOTH(finfo0
,pinfo0
);
2014 * sleep some time, to demonstrate the handling of write times
2015 * doesn't depend on the time since the open
2017 smb_msleep(5 * msec
);
2019 /* get the initial times */
2020 GET_INFO_BOTH(finfo1
,pinfo1
);
2021 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo0
);
2024 * demonstrate that a truncate write always
2025 * updates the write time immediately
2027 for (i
=0; i
< 3; i
++) {
2028 smb_msleep(2 * msec
);
2030 torture_comment(tctx
, "Do a truncate SMBwrite [%d] on the file handle\n", i
);
2031 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 512, 0);
2033 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 0", (int)written
);
2037 /* get the times after the write */
2038 GET_INFO_BOTH(finfo2
,pinfo2
);
2039 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
2043 start
= timeval_current();
2044 end
= timeval_add(&start
, 7 * sec
, 0);
2045 while (!timeval_expired(&end
)) {
2047 torture_comment(tctx
, "Do a write on the file handle\n");
2048 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2050 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2054 /* get the times after the write */
2055 GET_INFO_FILE(finfo2
);
2057 if (finfo2
.basic_info
.out
.write_time
> finfo1
.basic_info
.out
.write_time
) {
2058 double diff
= timeval_elapsed(&start
);
2059 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2065 smb_msleep(1 * msec
);
2068 GET_INFO_BOTH(finfo2
,pinfo2
);
2069 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
2070 if (finfo2
.basic_info
.out
.write_time
== finfo1
.basic_info
.out
.write_time
) {
2071 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2075 smb_msleep(5 * msec
);
2077 /* get the initial times */
2078 GET_INFO_BOTH(finfo1
,pinfo1
);
2079 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo2
);
2082 * demonstrate that a truncate write always
2083 * updates the write time immediately
2085 for (i
=0; i
< 3; i
++) {
2086 smb_msleep(2 * msec
);
2088 torture_comment(tctx
, "Do a truncate write [%d] on the file handle\n", i
);
2089 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 512, 0);
2091 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 0", (int)written
);
2095 /* get the times after the write */
2096 GET_INFO_BOTH(finfo2
,pinfo2
);
2097 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
2102 smb_msleep(5 * msec
);
2104 GET_INFO_BOTH(finfo2
,pinfo2
);
2105 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
2107 /* sure any further write doesn't update the write time */
2108 start
= timeval_current();
2109 end
= timeval_add(&start
, 15 * sec
, 0);
2110 while (!timeval_expired(&end
)) {
2112 torture_comment(tctx
, "Do a write on the file handle\n");
2113 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2115 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2119 /* get the times after the write */
2120 GET_INFO_BOTH(finfo2
,pinfo2
);
2122 if (finfo2
.basic_info
.out
.write_time
> finfo1
.basic_info
.out
.write_time
) {
2123 double diff
= timeval_elapsed(&start
);
2124 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2130 smb_msleep(1 * msec
);
2133 GET_INFO_BOTH(finfo2
,pinfo2
);
2134 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
2135 if (finfo2
.basic_info
.out
.write_time
== finfo1
.basic_info
.out
.write_time
) {
2136 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2140 smb_msleep(5 * msec
);
2142 GET_INFO_BOTH(finfo3
,pinfo3
);
2143 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
2146 * the close updates the write time to the time of the close
2147 * and not to the time of the last write!
2149 torture_comment(tctx
, "Close the file handle\n");
2150 smbcli_close(cli
->tree
, fnum1
);
2153 GET_INFO_PATH(pinfo4
);
2154 COMPARE_WRITE_TIME_GREATER(pinfo4
, pinfo3
);
2156 if (pinfo4
.basic_info
.out
.write_time
> pinfo3
.basic_info
.out
.write_time
) {
2157 torture_comment(tctx
, "Server updated the write_time on close (correct)\n");
2162 smbcli_close(cli
->tree
, fnum1
);
2163 smbcli_unlink(cli
->tree
, fname
);
2164 smbcli_deltree(cli
->tree
, BASEDIR
);
2170 * Show only the first write updates the timestamp, and a close
2171 * after writes updates to current (I think this is the same
2175 static bool test_delayed_write_update4(struct torture_context
*tctx
,
2176 struct smbcli_state
*cli
,
2177 struct smbcli_state
*cli2
)
2179 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
;
2180 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
;
2181 const char *fname
= BASEDIR
"\\torture_file4.txt";
2185 struct timeval start
;
2187 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
2188 int normal_delay
= 2000000;
2189 double sec
= ((double)used_delay
) / ((double)normal_delay
);
2190 int msec
= 1000 * sec
;
2192 torture_comment(tctx
, "\nRunning test_delayed_write_update4\n");
2194 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
2196 torture_comment(tctx
, "Open the file handle\n");
2197 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2200 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
2204 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2205 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
2209 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2210 pinfo0
.basic_info
.in
.file
.path
= fname
;
2216 /* get the initial times */
2217 GET_INFO_BOTH(finfo0
,pinfo0
);
2220 smb_msleep(5 * msec
);
2223 torture_comment(tctx
, "Do a write on the file handle\n");
2224 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2226 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2231 GET_INFO_BOTH(finfo1
,pinfo1
);
2232 COMPARE_WRITE_TIME_EQUAL(finfo1
,finfo0
);
2235 * make sure the write time is updated 2 seconds later
2236 * calcuated from the first write
2237 * (but expect upto 3 seconds extra time for a busy server)
2239 start
= timeval_current();
2240 end
= timeval_add(&start
, 5 * sec
, 0);
2241 while (!timeval_expired(&end
)) {
2242 /* get the times after the first write */
2243 GET_INFO_FILE(finfo1
);
2245 if (finfo1
.basic_info
.out
.write_time
> finfo0
.basic_info
.out
.write_time
) {
2246 double diff
= timeval_elapsed(&start
);
2247 if (diff
< (used_delay
/ (double)1000000)) {
2248 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds"
2249 "(expected > %.2f) (wrong!)\n",
2250 diff
, used_delay
/ (double)1000000);
2255 torture_comment(tctx
, "Server updated write_time after %.2f seconds "
2256 "(write time update delay == %.2f) (correct)\n",
2257 diff
, used_delay
/ (double)1000000);
2260 smb_msleep(0.5 * msec
);
2263 GET_INFO_BOTH(finfo1
,pinfo1
);
2264 COMPARE_WRITE_TIME_GREATER(pinfo1
, pinfo0
);
2266 /* sure any further write doesn't update the write time */
2267 start
= timeval_current();
2268 end
= timeval_add(&start
, 15 * sec
, 0);
2269 while (!timeval_expired(&end
)) {
2271 torture_comment(tctx
, "Do a write on the file handle\n");
2272 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2274 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2278 /* get the times after the write */
2279 GET_INFO_BOTH(finfo2
,pinfo2
);
2281 if (finfo2
.basic_info
.out
.write_time
> finfo1
.basic_info
.out
.write_time
) {
2282 double diff
= timeval_elapsed(&start
);
2283 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2289 smb_msleep(1 * msec
);
2292 GET_INFO_BOTH(finfo2
,pinfo2
);
2293 COMPARE_WRITE_TIME_EQUAL(finfo2
, finfo1
);
2294 if (finfo2
.basic_info
.out
.write_time
== finfo1
.basic_info
.out
.write_time
) {
2295 torture_comment(tctx
, "Server did not updatewrite_time (correct)\n");
2299 smb_msleep(5 * msec
);
2301 GET_INFO_BOTH(finfo3
,pinfo3
);
2302 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
2305 * the close updates the write time to the time of the close
2306 * and not to the time of the last write!
2308 torture_comment(tctx
, "Close the file handle\n");
2309 smbcli_close(cli
->tree
, fnum1
);
2312 GET_INFO_PATH(pinfo4
);
2313 COMPARE_WRITE_TIME_GREATER(pinfo4
, pinfo3
);
2315 if (pinfo4
.basic_info
.out
.write_time
> pinfo3
.basic_info
.out
.write_time
) {
2316 torture_comment(tctx
, "Server updated the write_time on close (correct)\n");
2321 smbcli_close(cli
->tree
, fnum1
);
2322 smbcli_unlink(cli
->tree
, fname
);
2323 smbcli_deltree(cli
->tree
, BASEDIR
);
2329 * Show writes and closes have no effect on updating times once a SETWRITETIME is done.
2332 static bool test_delayed_write_update5(struct torture_context
*tctx
,
2333 struct smbcli_state
*cli
,
2334 struct smbcli_state
*cli2
)
2336 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
, finfo4
, finfo5
;
2337 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
, pinfo5
, pinfo6
;
2338 const char *fname
= BASEDIR
"\\torture_file5.txt";
2342 struct timeval start
;
2344 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
2345 int normal_delay
= 2000000;
2346 double sec
= ((double)used_delay
) / ((double)normal_delay
);
2347 int msec
= 1000 * sec
;
2349 torture_comment(tctx
, "\nRunning test_delayed_write_update5\n");
2351 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
2353 torture_comment(tctx
, "Open the file handle\n");
2354 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2357 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
2361 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2362 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
2368 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2369 pinfo0
.basic_info
.in
.file
.path
= fname
;
2377 /* get the initial times */
2378 GET_INFO_BOTH(finfo0
,pinfo0
);
2381 torture_comment(tctx
, "Do a write on the file handle\n");
2382 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2384 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2389 GET_INFO_BOTH(finfo1
,pinfo1
);
2390 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo0
);
2392 torture_comment(tctx
, "Set write time in the future on the file handle\n");
2393 SET_INFO_FILE(finfo0
, time(NULL
) + 86400);
2394 GET_INFO_BOTH(finfo2
,pinfo2
);
2395 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
2397 torture_comment(tctx
, "Set write time in the past on the file handle\n");
2398 SET_INFO_FILE(finfo0
, time(NULL
) - 86400);
2399 GET_INFO_BOTH(finfo2
,pinfo2
);
2400 COMPARE_WRITE_TIME_LESS(finfo2
, finfo1
);
2402 /* make sure the 2 second delay from the first write are canceled */
2403 start
= timeval_current();
2404 end
= timeval_add(&start
, 15 * sec
, 0);
2405 while (!timeval_expired(&end
)) {
2407 /* get the times after the first write */
2408 GET_INFO_BOTH(finfo3
,pinfo3
);
2410 if (finfo3
.basic_info
.out
.write_time
> finfo2
.basic_info
.out
.write_time
) {
2411 double diff
= timeval_elapsed(&start
);
2412 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2418 smb_msleep(1 * msec
);
2421 GET_INFO_BOTH(finfo3
,pinfo3
);
2422 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
2423 if (finfo3
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
2424 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2427 /* sure any further write doesn't update the write time */
2428 start
= timeval_current();
2429 end
= timeval_add(&start
, 15 * sec
, 0);
2430 while (!timeval_expired(&end
)) {
2432 torture_comment(tctx
, "Do a write on the file handle\n");
2433 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2435 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2439 /* get the times after the write */
2440 GET_INFO_BOTH(finfo4
,pinfo4
);
2442 if (finfo4
.basic_info
.out
.write_time
> finfo3
.basic_info
.out
.write_time
) {
2443 double diff
= timeval_elapsed(&start
);
2444 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2450 smb_msleep(1 * msec
);
2453 GET_INFO_BOTH(finfo4
,pinfo4
);
2454 COMPARE_WRITE_TIME_EQUAL(finfo4
, finfo3
);
2455 if (finfo4
.basic_info
.out
.write_time
== finfo3
.basic_info
.out
.write_time
) {
2456 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2460 smb_msleep(5 * msec
);
2462 GET_INFO_BOTH(finfo5
,pinfo5
);
2463 COMPARE_WRITE_TIME_EQUAL(finfo5
, finfo4
);
2466 * the close doesn't update the write time
2468 torture_comment(tctx
, "Close the file handle\n");
2469 smbcli_close(cli
->tree
, fnum1
);
2472 GET_INFO_PATH(pinfo6
);
2473 COMPARE_WRITE_TIME_EQUAL(pinfo6
, pinfo5
);
2475 if (pinfo6
.basic_info
.out
.write_time
== pinfo5
.basic_info
.out
.write_time
) {
2476 torture_comment(tctx
, "Server did not update the write_time on close (correct)\n");
2481 smbcli_close(cli
->tree
, fnum1
);
2482 smbcli_unlink(cli
->tree
, fname
);
2483 smbcli_deltree(cli
->tree
, BASEDIR
);
2489 * Show truncate writes and closes have no effect on updating times once a SETWRITETIME is done.
2492 static bool test_delayed_write_update5b(struct torture_context
*tctx
,
2493 struct smbcli_state
*cli
,
2494 struct smbcli_state
*cli2
)
2496 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
, finfo4
, finfo5
;
2497 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
, pinfo5
, pinfo6
;
2498 const char *fname
= BASEDIR
"\\torture_fileb.txt";
2502 struct timeval start
;
2504 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
2505 int normal_delay
= 2000000;
2506 double sec
= ((double)used_delay
) / ((double)normal_delay
);
2507 int msec
= 1000 * sec
;
2509 torture_comment(tctx
, "\nRunning test_delayed_write_update5b\n");
2511 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
2513 torture_comment(tctx
, "Open the file handle\n");
2514 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2517 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
2521 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2522 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
2528 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2529 pinfo0
.basic_info
.in
.file
.path
= fname
;
2537 /* get the initial times */
2538 GET_INFO_BOTH(finfo0
,pinfo0
);
2541 torture_comment(tctx
, "Do a write on the file handle\n");
2542 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2544 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2549 GET_INFO_BOTH(finfo1
,pinfo1
);
2550 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo0
);
2552 torture_comment(tctx
, "Set write time in the future on the file handle\n");
2553 SET_INFO_FILE(finfo0
, time(NULL
) + 86400);
2554 GET_INFO_BOTH(finfo2
,pinfo2
);
2555 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
2557 torture_comment(tctx
, "Set write time in the past on the file handle\n");
2558 SET_INFO_FILE(finfo0
, time(NULL
) - 86400);
2559 GET_INFO_BOTH(finfo2
,pinfo2
);
2560 COMPARE_WRITE_TIME_LESS(finfo2
, finfo1
);
2562 /* make sure the 2 second delay from the first write are canceled */
2563 start
= timeval_current();
2564 end
= timeval_add(&start
, 15 * sec
, 0);
2565 while (!timeval_expired(&end
)) {
2567 /* get the times after the first write */
2568 GET_INFO_BOTH(finfo3
,pinfo3
);
2570 if (finfo3
.basic_info
.out
.write_time
> finfo2
.basic_info
.out
.write_time
) {
2571 double diff
= timeval_elapsed(&start
);
2572 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2578 smb_msleep(1 * msec
);
2581 GET_INFO_BOTH(finfo3
,pinfo3
);
2582 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
2583 if (finfo3
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
2584 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2587 /* Do any further write (truncates) update the write time ? */
2588 start
= timeval_current();
2589 end
= timeval_add(&start
, 15 * sec
, 0);
2590 while (!timeval_expired(&end
)) {
2592 torture_comment(tctx
, "Do a truncate write on the file handle\n");
2593 written
= smbcli_smbwrite(cli
->tree
, fnum1
, "x", 1024, 0);
2595 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2599 /* get the times after the write */
2600 GET_INFO_BOTH(finfo4
,pinfo4
);
2602 if (finfo4
.basic_info
.out
.write_time
> finfo3
.basic_info
.out
.write_time
) {
2603 double diff
= timeval_elapsed(&start
);
2604 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2610 smb_msleep(1 * msec
);
2613 GET_INFO_BOTH(finfo4
,pinfo4
);
2614 COMPARE_WRITE_TIME_EQUAL(finfo4
, finfo3
);
2615 if (finfo4
.basic_info
.out
.write_time
== finfo3
.basic_info
.out
.write_time
) {
2616 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2620 smb_msleep(5 * msec
);
2622 GET_INFO_BOTH(finfo5
,pinfo5
);
2623 COMPARE_WRITE_TIME_EQUAL(finfo5
, finfo4
);
2626 * the close doesn't update the write time
2628 torture_comment(tctx
, "Close the file handle\n");
2629 smbcli_close(cli
->tree
, fnum1
);
2632 GET_INFO_PATH(pinfo6
);
2633 COMPARE_WRITE_TIME_EQUAL(pinfo6
, pinfo5
);
2635 if (pinfo6
.basic_info
.out
.write_time
== pinfo5
.basic_info
.out
.write_time
) {
2636 torture_comment(tctx
, "Server did not update the write_time on close (correct)\n");
2641 smbcli_close(cli
->tree
, fnum1
);
2642 smbcli_unlink(cli
->tree
, fname
);
2643 smbcli_deltree(cli
->tree
, BASEDIR
);
2649 * Open 2 handles on a file. Write one one and then set the
2650 * WRITE TIME explicitly on the other. Ensure the write time
2651 * update is cancelled. Ensure the write time is updated to
2652 * the close time when the non-explicit set handle is closed.
2656 static bool test_delayed_write_update6(struct torture_context
*tctx
,
2657 struct smbcli_state
*cli
,
2658 struct smbcli_state
*cli2
)
2660 union smb_fileinfo finfo0
, finfo1
, finfo2
, finfo3
, finfo4
, finfo5
;
2661 union smb_fileinfo pinfo0
, pinfo1
, pinfo2
, pinfo3
, pinfo4
, pinfo5
, pinfo6
, pinfo7
;
2662 const char *fname
= BASEDIR
"\\torture_file6.txt";
2667 struct timeval start
;
2669 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
2670 int normal_delay
= 2000000;
2671 double sec
= ((double)used_delay
) / ((double)normal_delay
);
2672 int msec
= 1000 * sec
;
2675 torture_comment(tctx
, "\nRunning test_delayed_write_update6\n");
2677 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
2679 torture_comment(tctx
, "Open the file handle\n");
2680 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2683 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
2688 torture_comment(tctx
, "Open the 2nd file handle on 2nd connection\n");
2689 fnum2
= smbcli_open(cli2
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2692 torture_result(tctx
, TORTURE_FAIL
, __location__
": unable to open %s", fname
);
2697 finfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2698 finfo0
.basic_info
.in
.file
.fnum
= fnum1
;
2704 pinfo0
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2705 pinfo0
.basic_info
.in
.file
.path
= fname
;
2714 /* get the initial times */
2715 GET_INFO_BOTH(finfo0
,pinfo0
);
2718 torture_comment(tctx
, "Do a write on the file handle\n");
2719 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2721 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2726 GET_INFO_BOTH(finfo1
,pinfo1
);
2727 COMPARE_WRITE_TIME_EQUAL(finfo1
, finfo0
);
2729 torture_comment(tctx
, "Set write time in the future on the 2nd file handle\n");
2730 SET_INFO_FILE_EX(finfo0
, time(NULL
) + 86400, cli2
->tree
, fnum2
);
2731 GET_INFO_BOTH(finfo2
,pinfo2
);
2732 COMPARE_WRITE_TIME_GREATER(finfo2
, finfo1
);
2734 torture_comment(tctx
, "Set write time in the past on the 2nd file handle\n");
2735 SET_INFO_FILE_EX(finfo0
, time(NULL
) - 86400, cli2
->tree
, fnum2
);
2736 GET_INFO_BOTH(finfo2
,pinfo2
);
2737 COMPARE_WRITE_TIME_LESS(finfo2
, finfo1
);
2739 /* make sure the 2 second delay from the first write are canceled */
2740 start
= timeval_current();
2741 end
= timeval_add(&start
, 10 * sec
, 0);
2742 while (!timeval_expired(&end
)) {
2744 /* get the times after the first write */
2745 GET_INFO_BOTH(finfo3
,pinfo3
);
2747 if (finfo3
.basic_info
.out
.write_time
> finfo2
.basic_info
.out
.write_time
) {
2748 double diff
= timeval_elapsed(&start
);
2749 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2755 smb_msleep(1 * msec
);
2758 GET_INFO_BOTH(finfo3
,pinfo3
);
2759 COMPARE_WRITE_TIME_EQUAL(finfo3
, finfo2
);
2760 if (finfo3
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
2761 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2764 /* sure any further write doesn't update the write time */
2765 start
= timeval_current();
2766 end
= timeval_add(&start
, 10 * sec
, 0);
2767 while (!timeval_expired(&end
)) {
2769 torture_comment(tctx
, "Do a write on the file handle\n");
2770 written
= smbcli_write(cli
->tree
, fnum1
, 0, "x", 0, 1);
2772 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2776 /* get the times after the write */
2777 GET_INFO_BOTH(finfo4
,pinfo4
);
2779 if (finfo4
.basic_info
.out
.write_time
> finfo3
.basic_info
.out
.write_time
) {
2780 double diff
= timeval_elapsed(&start
);
2781 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2787 smb_msleep(1 * msec
);
2790 GET_INFO_BOTH(finfo4
,pinfo4
);
2791 COMPARE_WRITE_TIME_EQUAL(finfo4
, finfo3
);
2792 if (finfo4
.basic_info
.out
.write_time
== finfo3
.basic_info
.out
.write_time
) {
2793 torture_comment(tctx
, "Server did not update write_time (correct)\n");
2797 smb_msleep(5 * msec
);
2799 GET_INFO_BOTH(finfo5
,pinfo5
);
2800 COMPARE_WRITE_TIME_EQUAL(finfo5
, finfo4
);
2803 * the close updates the write time to the time of the close
2804 * as the write time was set on the 2nd handle
2806 torture_comment(tctx
, "Close the file handle\n");
2807 smbcli_close(cli
->tree
, fnum1
);
2810 GET_INFO_PATH(pinfo6
);
2811 COMPARE_WRITE_TIME_GREATER(pinfo6
, pinfo5
);
2813 if (pinfo6
.basic_info
.out
.write_time
> pinfo5
.basic_info
.out
.write_time
) {
2814 torture_comment(tctx
, "Server updated the write_time on close (correct)\n");
2817 /* See what the second write handle thinks the time is ? */
2818 finfo5
.basic_info
.in
.file
.fnum
= fnum2
;
2819 GET_INFO_FILE2(finfo5
);
2820 COMPARE_WRITE_TIME_EQUAL(finfo5
, pinfo6
);
2822 /* See if we have lost the sticky write time on handle2 */
2823 smb_msleep(3 * msec
);
2824 torture_comment(tctx
, "Have we lost the sticky write time ?\n");
2826 /* Make sure any further normal write doesn't update the write time */
2827 start
= timeval_current();
2828 end
= timeval_add(&start
, 10 * sec
, 0);
2829 while (!timeval_expired(&end
)) {
2831 torture_comment(tctx
, "Do a write on the second file handle\n");
2832 written
= smbcli_write(cli2
->tree
, fnum2
, 0, "x", 0, 1);
2834 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2838 /* get the times after the write */
2839 GET_INFO_FILE2(finfo5
);
2840 GET_INFO_PATH(pinfo6
);
2842 if (finfo5
.basic_info
.out
.write_time
> pinfo6
.basic_info
.out
.write_time
) {
2843 double diff
= timeval_elapsed(&start
);
2844 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2850 smb_msleep(1 * msec
);
2853 /* What about a truncate write ? */
2854 start
= timeval_current();
2855 end
= timeval_add(&start
, 10 * sec
, 0);
2856 while (!timeval_expired(&end
)) {
2858 torture_comment(tctx
, "Do a truncate write on the second file handle\n");
2859 written
= smbcli_write(cli2
->tree
, fnum2
, 0, "x", 0, 0);
2861 torture_result(tctx
, TORTURE_FAIL
, __location__
": written gave %d - should have been 1", (int)written
);
2865 /* get the times after the write */
2866 GET_INFO_FILE2(finfo5
);
2867 GET_INFO_PATH(pinfo6
);
2869 if (finfo5
.basic_info
.out
.write_time
> pinfo6
.basic_info
.out
.write_time
) {
2870 double diff
= timeval_elapsed(&start
);
2871 torture_result(tctx
, TORTURE_FAIL
, "Server updated write_time after %.2f seconds "
2877 smb_msleep(1 * msec
);
2881 /* keep the 2nd handle open and rerun tests */
2888 * closing the 2nd handle will cause no write time update
2889 * as the write time was explicit set on this handle
2891 torture_comment(tctx
, "Close the 2nd file handle\n");
2892 smbcli_close(cli2
->tree
, fnum2
);
2895 GET_INFO_PATH(pinfo7
);
2896 COMPARE_WRITE_TIME_EQUAL(pinfo7
, pinfo6
);
2898 if (pinfo7
.basic_info
.out
.write_time
== pinfo6
.basic_info
.out
.write_time
) {
2899 torture_comment(tctx
, "Server did not update the write_time on close (correct)\n");
2904 smbcli_close(cli
->tree
, fnum1
);
2906 smbcli_close(cli2
->tree
, fnum2
);
2907 smbcli_unlink(cli
->tree
, fname
);
2908 smbcli_deltree(cli
->tree
, BASEDIR
);
2913 static bool test_delayed_write_update7(struct torture_context
*tctx
, struct smbcli_state
*cli
)
2915 union smb_open open_parms
;
2916 union smb_fileinfo finfo1
, finfo2
, finfo3
;
2917 const char *fname
= BASEDIR
"\\torture_file7.txt";
2921 TALLOC_CTX
*mem_ctx
;
2923 torture_comment(tctx
, "\nRunning test_delayed_write_update7 (timestamp resolution test)\n");
2925 mem_ctx
= talloc_init("test_delayed_write_update7");
2926 if (!mem_ctx
) return false;
2928 ZERO_STRUCT(finfo1
);
2929 ZERO_STRUCT(finfo2
);
2930 ZERO_STRUCT(finfo3
);
2931 ZERO_STRUCT(open_parms
);
2933 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
2935 /* Create the file. */
2936 fnum1
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
2938 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s", fname
);
2942 finfo1
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
2943 finfo1
.basic_info
.in
.file
.fnum
= fnum1
;
2947 /* Get the initial timestamps. */
2948 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo1
);
2950 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
2952 /* Set the pending write time to a value with ns. */
2953 SET_INFO_FILE_NS(finfo
, time(NULL
) + 86400, 103, cli
->tree
, fnum1
);
2955 /* Get the current pending write time by fnum. */
2956 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo2
);
2958 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
2960 /* Ensure the time is actually different. */
2961 if (finfo1
.basic_info
.out
.write_time
== finfo2
.basic_info
.out
.write_time
) {
2962 torture_result(tctx
, TORTURE_FAIL
,
2963 "setfileinfo time matches original fileinfo time");
2967 /* Get the current pending write time by path. */
2968 finfo3
.basic_info
.in
.file
.path
= fname
;
2969 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo3
);
2971 if (finfo2
.basic_info
.out
.write_time
!= finfo3
.basic_info
.out
.write_time
) {
2972 torture_result(tctx
, TORTURE_FAIL
,
2973 "qpathinfo time doens't match fileinfo time");
2977 /* Now close the file. Re-open and check that the write
2978 time is identical to the one we wrote. */
2980 smbcli_close(cli
->tree
, fnum1
);
2982 open_parms
.ntcreatex
.level
= RAW_OPEN_NTCREATEX
;
2983 open_parms
.ntcreatex
.in
.flags
= 0;
2984 open_parms
.ntcreatex
.in
.access_mask
= SEC_GENERIC_READ
;
2985 open_parms
.ntcreatex
.in
.file_attr
= 0;
2986 open_parms
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_DELETE
|
2987 NTCREATEX_SHARE_ACCESS_READ
|
2988 NTCREATEX_SHARE_ACCESS_WRITE
;
2989 open_parms
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
2990 open_parms
.ntcreatex
.in
.create_options
= 0;
2991 open_parms
.ntcreatex
.in
.fname
= fname
;
2993 status
= smb_raw_open(cli
->tree
, mem_ctx
, &open_parms
);
2994 talloc_free(mem_ctx
);
2996 if (!NT_STATUS_IS_OK(status
)) {
2997 torture_result(tctx
, TORTURE_FAIL
,
2998 "setfileinfo time matches original fileinfo time");
3002 fnum1
= open_parms
.ntcreatex
.out
.file
.fnum
;
3004 /* Check the returned time matches. */
3005 if (open_parms
.ntcreatex
.out
.write_time
!= finfo2
.basic_info
.out
.write_time
) {
3006 torture_result(tctx
, TORTURE_FAIL
,
3007 "final open time does not match set time");
3013 smbcli_close(cli
->tree
, fnum1
);
3015 smbcli_unlink(cli
->tree
, fname
);
3016 smbcli_deltree(cli
->tree
, BASEDIR
);
3021 Test if creating a file in a directory with an open handle updates the
3022 write timestamp (it should).
3024 static bool test_directory_update8(struct torture_context
*tctx
, struct smbcli_state
*cli
)
3026 union smb_fileinfo dir_info1
, dir_info2
;
3027 union smb_open open_parms
;
3028 const char *fname
= BASEDIR
"\\torture_file.txt";
3033 double used_delay
= torture_setting_int(tctx
, "writetimeupdatedelay", 2000000);
3034 int normal_delay
= 2000000;
3035 double sec
= ((double)used_delay
) / ((double)normal_delay
);
3036 int msec
= 1000 * sec
;
3037 TALLOC_CTX
*mem_ctx
= talloc_init("test_delayed_write_update8");
3039 if (!mem_ctx
) return false;
3041 torture_comment(tctx
, "\nRunning test directory write update\n");
3043 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
3045 /* Open a handle on the directory - and leave it open. */
3046 ZERO_STRUCT(open_parms
);
3047 open_parms
.ntcreatex
.level
= RAW_OPEN_NTCREATEX
;
3048 open_parms
.ntcreatex
.in
.flags
= 0;
3049 open_parms
.ntcreatex
.in
.access_mask
= SEC_RIGHTS_FILE_READ
;
3050 open_parms
.ntcreatex
.in
.file_attr
= 0;
3051 open_parms
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_DELETE
|
3052 NTCREATEX_SHARE_ACCESS_READ
|
3053 NTCREATEX_SHARE_ACCESS_WRITE
;
3054 open_parms
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
3055 open_parms
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
;
3056 open_parms
.ntcreatex
.in
.fname
= BASEDIR
;
3058 status
= smb_raw_open(cli
->tree
, mem_ctx
, &open_parms
);
3059 talloc_free(mem_ctx
);
3061 if (!NT_STATUS_IS_OK(status
)) {
3062 torture_result(tctx
, TORTURE_FAIL
,
3063 "failed to open directory handle");
3068 fnum1
= open_parms
.ntcreatex
.out
.file
.fnum
;
3070 /* Store the returned write time. */
3071 ZERO_STRUCT(dir_info1
);
3072 dir_info1
.basic_info
.out
.write_time
= open_parms
.ntcreatex
.out
.write_time
;
3074 torture_comment(tctx
, "Initial write time %s\n",
3075 nt_time_string(tctx
, dir_info1
.basic_info
.out
.write_time
));
3078 smb_msleep(3 * msec
);
3080 /* Now create a file within the directory. */
3081 fnum2
= smbcli_open(cli
->tree
, fname
, O_RDWR
|O_CREAT
, DENY_NONE
);
3083 torture_result(tctx
, TORTURE_FAIL
, "Failed to open %s", fname
);
3087 smbcli_close(cli
->tree
, fnum2
);
3089 /* Read the directory write time again. */
3090 ZERO_STRUCT(dir_info2
);
3091 dir_info2
.basic_info
.level
= RAW_FILEINFO_BASIC_INFO
;
3092 dir_info2
.basic_info
.in
.file
.fnum
= fnum1
;
3094 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &dir_info2
);
3096 torture_assert_ntstatus_ok(tctx
, status
, "fileinfo failed");
3098 /* Ensure it's been incremented. */
3099 COMPARE_WRITE_TIME_GREATER(dir_info2
, dir_info1
);
3101 torture_comment(tctx
, "Updated write time %s\n",
3102 nt_time_string(tctx
, dir_info2
.basic_info
.out
.write_time
));
3107 smbcli_close(cli
->tree
, fnum1
);
3108 smbcli_unlink(cli
->tree
, fname
);
3109 smbcli_deltree(cli
->tree
, BASEDIR
);
3115 testing of delayed update of write_time
3117 struct torture_suite
*torture_delay_write(void)
3119 struct torture_suite
*suite
= torture_suite_create(talloc_autofree_context(), "delaywrite");
3121 torture_suite_add_2smb_test(suite
, "finfo update on close", test_finfo_after_write
);
3122 torture_suite_add_1smb_test(suite
, "delayed update of write time", test_delayed_write_update
);
3123 torture_suite_add_1smb_test(suite
, "update of write time and SMBwrite truncate", test_delayed_write_update1
);
3124 torture_suite_add_1smb_test(suite
, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a
);
3125 torture_suite_add_1smb_test(suite
, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b
);
3126 torture_suite_add_1smb_test(suite
, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c
);
3127 torture_suite_add_2smb_test(suite
, "delayed update of write time using 2 connections", test_delayed_write_update2
);
3128 torture_suite_add_2smb_test(suite
, "delayed update of write time 3", test_delayed_write_update3
);
3129 torture_suite_add_2smb_test(suite
, "delayed update of write time 3a", test_delayed_write_update3a
);
3130 torture_suite_add_2smb_test(suite
, "delayed update of write time 3b", test_delayed_write_update3b
);
3131 torture_suite_add_2smb_test(suite
, "delayed update of write time 3c", test_delayed_write_update3c
);
3132 torture_suite_add_2smb_test(suite
, "delayed update of write time 4", test_delayed_write_update4
);
3133 torture_suite_add_2smb_test(suite
, "delayed update of write time 5", test_delayed_write_update5
);
3134 torture_suite_add_2smb_test(suite
, "delayed update of write time 5b", test_delayed_write_update5b
);
3135 torture_suite_add_2smb_test(suite
, "delayed update of write time 6", test_delayed_write_update6
);
3136 torture_suite_add_1smb_test(suite
, "timestamp resolution test", test_delayed_write_update7
);
3137 torture_suite_add_1smb_test(suite
, "timestamp resolution test", test_delayed_write_update7
);
3138 torture_suite_add_1smb_test(suite
, "directory timestamp update test", test_directory_update8
);