s4-torture: Fix some nonemtpy blank lines
[Samba/gebeck_regimport.git] / source4 / torture / basic / delaywrite.c
blob23a17d5a7a32896c2de76593f5f7cf53ae94d07e
1 /*
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/>.
24 #include "includes.h"
25 #include "torture/torture.h"
26 #include "libcli/raw/libcliraw.h"
27 #include "libcli/raw/raw_proto.h"
28 #include "system/time.h"
29 #include "system/filesys.h"
30 #include "libcli/libcli.h"
31 #include "torture/util.h"
32 #include "torture/basic/proto.h"
34 #define W2K8R2_TIMEDELAY_SECS 1
35 #define W2K3_TIMEDELAY_SECS 2
36 #define TIMEDELAY_SECS W2K3_TIMEDELAY_SECS
38 #define BASEDIR "\\delaywrite"
40 static bool test_delayed_write_update(struct torture_context *tctx, struct smbcli_state *cli)
42 union smb_fileinfo finfo1, finfo2;
43 const char *fname = BASEDIR "\\torture_file.txt";
44 NTSTATUS status;
45 int fnum1 = -1;
46 bool ret = true;
47 ssize_t written;
48 struct timeval start;
49 struct timeval end;
50 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
51 int normal_delay = 2000000;
52 double sec = ((double)used_delay) / ((double)normal_delay);
53 int msec = 1000 * sec;
55 torture_comment(tctx, "\nRunning test_delayed_write_update\n");
57 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
59 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
60 if (fnum1 == -1) {
61 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
62 return false;
65 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
66 finfo1.basic_info.in.file.fnum = fnum1;
67 finfo2 = finfo1;
69 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
71 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
73 torture_comment(tctx, "Initial write time %s\n",
74 nt_time_string(tctx, finfo1.basic_info.out.write_time));
76 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
78 if (written != 1) {
79 torture_result(tctx, TORTURE_FAIL,
80 "write failed - wrote %d bytes (%s)\n",
81 (int)written, __location__);
82 return false;
85 start = timeval_current();
86 end = timeval_add(&start, (120*sec), 0);
87 while (!timeval_expired(&end)) {
88 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
90 if (!NT_STATUS_IS_OK(status)) {
91 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
92 ret = false;
93 break;
95 torture_comment(tctx, "write time %s\n",
96 nt_time_string(tctx, finfo2.basic_info.out.write_time));
97 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
98 double diff = timeval_elapsed(&start);
99 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
100 torture_comment(tctx, "Server updated write_time after %.2f seconds"
101 "(1 sec == %.2f)(wrong!)\n",
102 diff, sec);
103 ret = false;
104 break;
107 torture_comment(tctx, "Server updated write_time after %.2f seconds"
108 "(1 sec == %.2f)(correct)\n",
109 diff, sec);
110 break;
112 fflush(stdout);
113 smb_msleep(1 * msec);
116 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
117 torture_result(tctx, TORTURE_FAIL,
118 "Server did not update write time (wrong!)");
119 ret = false;
123 if (fnum1 != -1)
124 smbcli_close(cli->tree, fnum1);
125 smbcli_unlink(cli->tree, fname);
126 smbcli_deltree(cli->tree, BASEDIR);
128 return ret;
131 static bool test_delayed_write_update1(struct torture_context *tctx, struct smbcli_state *cli)
133 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
134 const char *fname = BASEDIR "\\torture_file1.txt";
135 NTSTATUS status;
136 int fnum1 = -1;
137 bool ret = true;
138 ssize_t written;
139 struct timeval start;
140 struct timeval end;
141 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
142 int normal_delay = 2000000;
143 double sec = ((double)used_delay) / ((double)normal_delay);
144 int msec = 1000 * sec;
145 char buf[2048];
147 torture_comment(tctx, "\nRunning test_delayed_write_update1\n");
149 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
151 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
152 if (fnum1 == -1) {
153 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
154 return false;
157 memset(buf, 'x', 2048);
158 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
160 /* 3 second delay to ensure we get past any 2 second time
161 granularity (older systems may have that) */
162 smb_msleep(3 * msec);
164 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
165 finfo1.all_info.in.file.fnum = fnum1;
166 finfo2 = finfo1;
167 finfo3 = finfo1;
168 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
169 pinfo4.all_info.in.file.path = fname;
171 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
173 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
175 torture_comment(tctx, "Initial write time %s\n",
176 nt_time_string(tctx, finfo1.all_info.out.write_time));
178 /* 3 second delay to ensure we get past any 2 second time
179 granularity (older systems may have that) */
180 smb_msleep(3 * msec);
182 /* Do a zero length SMBwrite call to truncate. */
183 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
185 if (written != 0) {
186 torture_result(tctx, TORTURE_FAIL,
187 "write failed - wrote %d bytes (%s)\n",
188 (int)written, __location__);
189 return false;
192 start = timeval_current();
193 end = timeval_add(&start, (120*sec), 0);
194 while (!timeval_expired(&end)) {
195 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
197 if (!NT_STATUS_IS_OK(status)) {
198 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
199 ret = false;
200 break;
203 if (finfo2.all_info.out.size != 1024) {
204 torture_result(tctx, TORTURE_FAIL,
205 "file not truncated, size = %u (should be 1024)",
206 (unsigned int)finfo2.all_info.out.size);
207 ret = false;
208 break;
211 torture_comment(tctx, "write time %s\n",
212 nt_time_string(tctx, finfo2.all_info.out.write_time));
213 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
214 double diff = timeval_elapsed(&start);
215 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
216 torture_comment(tctx, "After SMBwrite truncate "
217 "server updated write_time after %.2f seconds"
218 "(1 sec == %.2f)(wrong!)\n",
219 diff, sec);
220 ret = false;
221 break;
224 torture_comment(tctx, "After SMBwrite truncate "
225 "server updated write_time after %.2f seconds"
226 "(1 sec == %.2f)(correct)\n",
227 diff, sec);
228 break;
230 fflush(stdout);
231 smb_msleep(1 * msec);
234 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
235 torture_result(tctx, TORTURE_FAIL,
236 "Server did not update write time (wrong!)");
237 ret = false;
240 fflush(stdout);
241 smb_msleep(2 * msec);
243 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
244 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
246 if (written != 1) {
247 torture_result(tctx, TORTURE_FAIL,
248 "write failed - wrote %d bytes (%s)",
249 (int)written, __location__);
250 return false;
253 start = timeval_current();
254 end = timeval_add(&start, (10*sec), 0);
255 while (!timeval_expired(&end)) {
256 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
258 if (!NT_STATUS_IS_OK(status)) {
259 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
260 ret = false;
261 break;
264 if (finfo3.all_info.out.size != 1024) {
265 DEBUG(0, ("file not truncated, size = %u (should be 1024)\n",
266 (unsigned int)finfo3.all_info.out.size));
267 ret = false;
268 break;
271 torture_comment(tctx, "write time %s\n",
272 nt_time_string(tctx, finfo3.all_info.out.write_time));
273 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
274 double diff = timeval_elapsed(&start);
276 torture_comment(tctx, "server updated write_time after %.2f seconds"
277 "(1 sec == %.2f)(wrong)\n",
278 diff, sec);
279 break;
281 fflush(stdout);
282 smb_msleep(1 * msec);
285 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
286 torture_result(tctx, TORTURE_FAIL,
287 "Server updated write time (wrong!)");
288 ret = false;
291 fflush(stdout);
292 smb_msleep(2 * msec);
294 /* the close should trigger an write time update */
295 smbcli_close(cli->tree, fnum1);
296 fnum1 = -1;
298 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
299 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
301 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
302 torture_result(tctx, TORTURE_FAIL,
303 "Server did not update write time on close (wrong!)");
304 ret = false;
305 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
306 torture_comment(tctx, "Server updated write time on close (correct)\n");
309 if (fnum1 != -1)
310 smbcli_close(cli->tree, fnum1);
311 smbcli_unlink(cli->tree, fname);
312 smbcli_deltree(cli->tree, BASEDIR);
314 return ret;
317 /* Updating with a SMBwrite of zero length
318 * changes the write time immediately - even on expand. */
320 static bool test_delayed_write_update1a(struct torture_context *tctx, struct smbcli_state *cli)
322 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
323 const char *fname = BASEDIR "\\torture_file1a.txt";
324 NTSTATUS status;
325 int fnum1 = -1;
326 bool ret = true;
327 ssize_t written;
328 struct timeval start;
329 struct timeval end;
330 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
331 int normal_delay = 2000000;
332 double sec = ((double)used_delay) / ((double)normal_delay);
333 int msec = 1000 * sec;
334 char buf[2048];
336 torture_comment(tctx, "\nRunning test_delayed_write_update1a\n");
338 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
340 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
341 if (fnum1 == -1) {
342 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
343 return false;
346 memset(buf, 'x', 2048);
347 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
349 /* 3 second delay to ensure we get past any 2 second time
350 granularity (older systems may have that) */
351 smb_msleep(3 * msec);
353 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
354 finfo1.all_info.in.file.fnum = fnum1;
355 finfo2 = finfo1;
356 finfo3 = finfo1;
357 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
358 pinfo4.all_info.in.file.path = fname;
360 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
362 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
364 torture_comment(tctx, "Initial write time %s\n",
365 nt_time_string(tctx, finfo1.all_info.out.write_time));
367 /* Do a zero length SMBwrite call to truncate. */
368 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
370 if (written != 0) {
371 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)",
372 (int)written, __location__);
373 return false;
376 start = timeval_current();
377 end = timeval_add(&start, (120*sec), 0);
378 while (!timeval_expired(&end)) {
379 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
381 if (!NT_STATUS_IS_OK(status)) {
382 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s",
383 nt_errstr(status));
384 ret = false;
385 break;
388 if (finfo2.all_info.out.size != 10240) {
389 torture_result(tctx, TORTURE_FAIL,
390 "file not truncated, size = %u (should be 10240)",
391 (unsigned int)finfo2.all_info.out.size);
392 ret = false;
393 break;
396 torture_comment(tctx, "write time %s\n",
397 nt_time_string(tctx, finfo2.all_info.out.write_time));
398 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
399 double diff = timeval_elapsed(&start);
400 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
401 torture_comment(tctx, "After SMBwrite truncate "
402 "server updated write_time after %.2f seconds"
403 "(1 sec == %.2f)(wrong!)\n",
404 diff, sec);
405 ret = false;
406 break;
409 torture_comment(tctx, "After SMBwrite truncate "
410 "server updated write_time after %.2f seconds"
411 "(1 sec == %.2f)(correct)\n",
412 diff, sec);
413 break;
415 fflush(stdout);
416 smb_msleep(1 * msec);
419 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
420 torture_result(tctx, TORTURE_FAIL,
421 "Server did not update write time (wrong!)");
422 ret = false;
425 fflush(stdout);
426 smb_msleep(2 * msec);
428 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
429 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
431 torture_assert_int_equal(tctx, written, 1,
432 "unexpected number of bytes written");
434 start = timeval_current();
435 end = timeval_add(&start, (10*sec), 0);
436 while (!timeval_expired(&end)) {
437 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
439 if (!NT_STATUS_IS_OK(status)) {
440 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s\n",
441 nt_errstr(status));
442 ret = false;
443 break;
446 if (finfo3.all_info.out.size != 10240) {
447 torture_result(tctx, TORTURE_FAIL,
448 "file not truncated, size = %u (should be 10240)",
449 (unsigned int)finfo3.all_info.out.size);
450 ret = false;
451 break;
454 torture_comment(tctx, "write time %s\n",
455 nt_time_string(tctx, finfo3.all_info.out.write_time));
456 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
457 double diff = timeval_elapsed(&start);
459 torture_comment(tctx, "server updated write_time after %.2f seconds"
460 "(1 sec == %.2f)(correct)\n",
461 diff, sec);
462 break;
464 fflush(stdout);
465 smb_msleep(1 * msec);
468 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
469 torture_result(tctx, TORTURE_FAIL,
470 "Server updated write time (wrong!)");
471 ret = false;
474 /* the close should trigger an write time update */
475 smbcli_close(cli->tree, fnum1);
476 fnum1 = -1;
478 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
479 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
481 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
482 torture_result(tctx, TORTURE_FAIL,
483 "Server did not update write time on close (wrong!)");
484 ret = false;
485 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
486 torture_comment(tctx, "Server updated write time on close (correct)\n");
489 if (fnum1 != -1)
490 smbcli_close(cli->tree, fnum1);
491 smbcli_unlink(cli->tree, fname);
492 smbcli_deltree(cli->tree, BASEDIR);
494 return ret;
497 /* Updating with a SET_FILE_END_OF_FILE_INFO
498 * changes the write time immediately - even on expand. */
500 static bool test_delayed_write_update1b(struct torture_context *tctx, struct smbcli_state *cli)
502 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
503 const char *fname = BASEDIR "\\torture_file1b.txt";
504 NTSTATUS status;
505 int fnum1 = -1;
506 bool ret = true;
507 ssize_t written;
508 struct timeval start;
509 struct timeval end;
510 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
511 int normal_delay = 2000000;
512 double sec = ((double)used_delay) / ((double)normal_delay);
513 int msec = 1000 * sec;
514 char buf[2048];
516 torture_comment(tctx, "\nRunning test_delayed_write_update1b\n");
518 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
520 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
521 if (fnum1 == -1) {
522 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
523 return false;
526 memset(buf, 'x', 2048);
527 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
529 /* 3 second delay to ensure we get past any 2 second time
530 granularity (older systems may have that) */
531 smb_msleep(3 * msec);
533 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
534 finfo1.all_info.in.file.fnum = fnum1;
535 finfo2 = finfo1;
536 finfo3 = finfo1;
537 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
538 pinfo4.all_info.in.file.path = fname;
540 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
542 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
544 torture_comment(tctx, "Initial write time %s\n",
545 nt_time_string(tctx, finfo1.all_info.out.write_time));
547 /* Do a SET_END_OF_FILE_INFO call to truncate. */
548 status = smbcli_ftruncate(cli->tree, fnum1, (uint64_t)10240);
550 torture_assert_ntstatus_ok(tctx, status, "SET_END_OF_FILE failed");
552 start = timeval_current();
553 end = timeval_add(&start, (120*sec), 0);
554 while (!timeval_expired(&end)) {
555 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
557 if (!NT_STATUS_IS_OK(status)) {
558 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
559 ret = false;
560 break;
563 if (finfo2.all_info.out.size != 10240) {
564 torture_result(tctx, TORTURE_FAIL,
565 "file not truncated (size = %u, should be 10240)",
566 (unsigned int)finfo2.all_info.out.size );
567 ret = false;
568 break;
571 torture_comment(tctx, "write time %s\n",
572 nt_time_string(tctx, finfo2.all_info.out.write_time));
573 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
574 double diff = timeval_elapsed(&start);
575 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
576 torture_result(tctx, TORTURE_FAIL,
577 "After SET_END_OF_FILE truncate "
578 "server updated write_time after %.2f seconds"
579 "(1 sec == %.2f)(wrong!)",
580 diff, sec);
581 ret = false;
582 break;
585 torture_comment(tctx, "After SET_END_OF_FILE truncate "
586 "server updated write_time after %.2f seconds"
587 "(1 sec == %.2f)(correct)\n",
588 diff, sec);
589 break;
591 fflush(stdout);
592 smb_msleep(1 * msec);
595 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
596 torture_result(tctx, TORTURE_FAIL,
597 "Server did not update write time (wrong!)");
598 ret = false;
601 fflush(stdout);
602 smb_msleep(2 * msec);
604 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
605 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
607 torture_assert_int_equal(tctx, written, 1,
608 "unexpected number of bytes written");
610 start = timeval_current();
611 end = timeval_add(&start, (10*sec), 0);
612 while (!timeval_expired(&end)) {
613 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
615 if (!NT_STATUS_IS_OK(status)) {
616 torture_result(tctx, TORTURE_FAIL,
617 "fileinfo failed: %s", nt_errstr(status));
618 ret = false;
619 break;
622 if (finfo3.all_info.out.size != 10240) {
623 DEBUG(0, ("file not truncated (size = %u, should be 10240)\n",
624 (unsigned int)finfo3.all_info.out.size ));
625 ret = false;
626 break;
629 torture_comment(tctx, "write time %s\n",
630 nt_time_string(tctx, finfo3.all_info.out.write_time));
631 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
632 double diff = timeval_elapsed(&start);
634 torture_comment(tctx, "server updated write_time after %.2f seconds"
635 "(1 sec == %.2f)(correct)\n",
636 diff, sec);
637 break;
639 fflush(stdout);
640 smb_msleep(1 * msec);
643 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
644 torture_result(tctx, TORTURE_FAIL, "Server updated write time (wrong!)\n");
645 ret = false;
648 /* the close should trigger an write time update */
649 smbcli_close(cli->tree, fnum1);
650 fnum1 = -1;
652 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
653 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
655 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
656 torture_result(tctx, TORTURE_FAIL, "Server did not update write time on close (wrong!)\n");
657 ret = false;
658 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
659 torture_comment(tctx, "Server updated write time on close (correct)\n");
662 if (fnum1 != -1)
663 smbcli_close(cli->tree, fnum1);
664 smbcli_unlink(cli->tree, fname);
665 smbcli_deltree(cli->tree, BASEDIR);
667 return ret;
670 /* Updating with a SET_ALLOCATION_INFO (truncate) does so immediately. */
672 static bool test_delayed_write_update1c(struct torture_context *tctx, struct smbcli_state *cli)
674 union smb_setfileinfo parms;
675 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
676 const char *fname = BASEDIR "\\torture_file1c.txt";
677 NTSTATUS status;
678 int fnum1 = -1;
679 bool ret = true;
680 ssize_t written;
681 struct timeval start;
682 struct timeval end;
683 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
684 int normal_delay = 2000000;
685 double sec = ((double)used_delay) / ((double)normal_delay);
686 int msec = 1000 * sec;
687 char buf[2048];
689 torture_comment(tctx, "\nRunning test_delayed_write_update1c\n");
691 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
693 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
694 if (fnum1 == -1) {
695 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
696 return false;
699 memset(buf, 'x', 2048);
700 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
702 /* 3 second delay to ensure we get past any 2 second time
703 granularity (older systems may have that) */
704 smb_msleep(3 * msec);
706 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
707 finfo1.all_info.in.file.fnum = fnum1;
708 finfo2 = finfo1;
709 finfo3 = finfo1;
710 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
711 pinfo4.all_info.in.file.path = fname;
713 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
715 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
717 torture_comment(tctx, "Initial write time %s\n",
718 nt_time_string(tctx, finfo1.all_info.out.write_time));
720 /* Do a SET_ALLOCATION_SIZE call to truncate. */
721 parms.allocation_info.level = RAW_SFILEINFO_ALLOCATION_INFO;
722 parms.allocation_info.in.file.fnum = fnum1;
723 parms.allocation_info.in.alloc_size = 0;
725 status = smb_raw_setfileinfo(cli->tree, &parms);
727 torture_assert_ntstatus_ok(tctx, status,
728 "RAW_SFILEINFO_ALLOCATION_INFO failed");
730 start = timeval_current();
731 end = timeval_add(&start, (120*sec), 0);
732 while (!timeval_expired(&end)) {
733 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
735 if (!NT_STATUS_IS_OK(status)) {
736 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s",
737 nt_errstr(status));
738 ret = false;
739 break;
742 if (finfo2.all_info.out.size != 0) {
743 torture_result(tctx, TORTURE_FAIL,
744 "file not truncated (size = %u, should be 10240)",
745 (unsigned int)finfo2.all_info.out.size);
746 ret = false;
747 break;
750 torture_comment(tctx, "write time %s\n",
751 nt_time_string(tctx, finfo2.all_info.out.write_time));
752 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
753 double diff = timeval_elapsed(&start);
754 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
755 torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
756 "server updated write_time after %.2f seconds"
757 "(1 sec == %.2f)(wrong!)\n",
758 diff, sec);
759 ret = false;
760 break;
763 torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
764 "server updated write_time after %.2f seconds"
765 "(1 sec == %.2f)(correct)\n",
766 diff, sec);
767 break;
769 fflush(stdout);
770 smb_msleep(1 * msec);
773 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
774 torture_result(tctx, TORTURE_FAIL,
775 "Server did not update write time (wrong!)");
776 ret = false;
779 fflush(stdout);
780 smb_msleep(2 * msec);
782 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
783 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
784 torture_assert_int_equal(tctx, written, 1,
785 "Unexpected number of bytes written");
787 start = timeval_current();
788 end = timeval_add(&start, (10*sec), 0);
789 while (!timeval_expired(&end)) {
790 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
792 if (!NT_STATUS_IS_OK(status)) {
793 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s",
794 nt_errstr(status));
795 ret = false;
796 break;
799 if (finfo3.all_info.out.size != 1) {
800 torture_result(tctx, TORTURE_FAIL, "file not expanded");
801 ret = false;
802 break;
805 torture_comment(tctx, "write time %s\n",
806 nt_time_string(tctx, finfo3.all_info.out.write_time));
807 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
808 double diff = timeval_elapsed(&start);
810 torture_comment(tctx, "server updated write_time after %.2f seconds"
811 "(1 sec == %.2f)(correct)\n",
812 diff, sec);
813 break;
815 fflush(stdout);
816 smb_msleep(1 * msec);
819 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
820 torture_result(tctx, TORTURE_FAIL,
821 "Server updated write time (wrong!)");
822 ret = false;
825 /* the close should trigger an write time update */
826 smbcli_close(cli->tree, fnum1);
827 fnum1 = -1;
829 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
830 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
832 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
833 torture_result(tctx, TORTURE_FAIL, "Server did not update write time on close (wrong!)\n");
834 ret = false;
835 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
836 torture_comment(tctx, "Server updated write time on close (correct)\n");
839 if (fnum1 != -1)
840 smbcli_close(cli->tree, fnum1);
841 smbcli_unlink(cli->tree, fname);
842 smbcli_deltree(cli->tree, BASEDIR);
844 return ret;
848 * Do as above, but using 2 connections.
851 static bool test_delayed_write_update2(struct torture_context *tctx, struct smbcli_state *cli,
852 struct smbcli_state *cli2)
854 union smb_fileinfo finfo1, finfo2;
855 const char *fname = BASEDIR "\\torture_file.txt";
856 NTSTATUS status;
857 int fnum1 = -1;
858 int fnum2 = -1;
859 bool ret = true;
860 ssize_t written;
861 struct timeval start;
862 struct timeval end;
863 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
864 int normal_delay = 2000000;
865 double sec = ((double)used_delay) / ((double)normal_delay);
866 int msec = 1000 * sec;
867 union smb_flush flsh;
869 torture_comment(tctx, "\nRunning test_delayed_write_update2\n");
871 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
873 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
874 if (fnum1 == -1) {
875 torture_comment(tctx, "Failed to open %s\n", fname);
876 return false;
879 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
880 finfo1.basic_info.in.file.fnum = fnum1;
881 finfo2 = finfo1;
883 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
885 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
887 torture_comment(tctx, "Initial write time %s\n",
888 nt_time_string(tctx, finfo1.basic_info.out.write_time));
890 /* 3 second delay to ensure we get past any 2 second time
891 granularity (older systems may have that) */
892 smb_msleep(3 * msec);
895 /* Try using setfileinfo instead of write to update write time. */
896 union smb_setfileinfo sfinfo;
897 time_t t_set = time(NULL);
898 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
899 sfinfo.basic_info.in.file.fnum = fnum1;
900 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
901 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
903 /* I tried this with both + and - ve to see if it makes a different.
904 It doesn't - once the filetime is set via setfileinfo it stays that way. */
905 #if 1
906 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
907 #else
908 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
909 #endif
910 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
911 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
913 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
915 torture_assert_ntstatus_ok(tctx, status, "sfileinfo failed");
918 finfo2.basic_info.in.file.path = fname;
920 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
922 if (!NT_STATUS_IS_OK(status)) {
923 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
924 return false;
926 torture_comment(tctx, "write time %s\n",
927 nt_time_string(tctx, finfo2.basic_info.out.write_time));
929 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
930 torture_comment(tctx, "Server updated write_time (correct)\n");
931 } else {
932 torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n");
933 ret = false;
936 /* Now try a write to see if the write time gets reset. */
938 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
939 finfo1.basic_info.in.file.fnum = fnum1;
940 finfo2 = finfo1;
942 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
944 if (!NT_STATUS_IS_OK(status)) {
945 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
946 return false;
949 torture_comment(tctx, "Modified write time %s\n",
950 nt_time_string(tctx, finfo1.basic_info.out.write_time));
953 torture_comment(tctx, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
955 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
957 if (written != 10) {
958 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
959 (int)written, __location__);
960 return false;
963 /* Just to prove to tridge that the an smbflush has no effect on
964 the write time :-). The setfileinfo IS STICKY. JRA. */
966 torture_comment(tctx, "Doing flush after write\n");
968 flsh.flush.level = RAW_FLUSH_FLUSH;
969 flsh.flush.in.file.fnum = fnum1;
970 status = smb_raw_flush(cli->tree, &flsh);
971 if (!NT_STATUS_IS_OK(status)) {
972 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
973 return false;
976 /* Once the time was set using setfileinfo then it stays set - writes
977 don't have any effect. But make sure. */
978 start = timeval_current();
979 end = timeval_add(&start, (15*sec), 0);
980 while (!timeval_expired(&end)) {
981 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
983 if (!NT_STATUS_IS_OK(status)) {
984 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
985 ret = false;
986 break;
988 torture_comment(tctx, "write time %s\n",
989 nt_time_string(tctx, finfo2.basic_info.out.write_time));
990 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
991 double diff = timeval_elapsed(&start);
992 torture_comment(tctx, "Server updated write_time after %.2f seconds"
993 "(1sec == %.2f) (wrong!)\n",
994 diff, sec);
995 ret = false;
996 break;
998 fflush(stdout);
999 smb_msleep(1 * msec);
1002 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1003 torture_comment(tctx, "Server did not update write time (correct)\n");
1006 fflush(stdout);
1007 smb_msleep(2 * msec);
1009 fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
1010 if (fnum2 == -1) {
1011 torture_comment(tctx, "Failed to open %s\n", fname);
1012 return false;
1015 torture_comment(tctx, "Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n");
1017 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
1019 if (written != 10) {
1020 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
1021 (int)written, __location__);
1022 return false;
1025 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1027 if (!NT_STATUS_IS_OK(status)) {
1028 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1029 return false;
1031 torture_comment(tctx, "write time %s\n",
1032 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1033 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1034 torture_comment(tctx, "Server updated write_time (wrong!)\n");
1035 ret = false;
1038 torture_comment(tctx, "Closing the first fd to see if write time updated.\n");
1039 smbcli_close(cli->tree, fnum1);
1040 fnum1 = -1;
1042 torture_comment(tctx, "Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n");
1044 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
1046 if (written != 10) {
1047 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
1048 (int)written, __location__);
1049 return false;
1052 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1053 finfo1.basic_info.in.file.fnum = fnum2;
1054 finfo2 = finfo1;
1055 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1057 if (!NT_STATUS_IS_OK(status)) {
1058 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1059 return false;
1061 torture_comment(tctx, "write time %s\n",
1062 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1063 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1064 torture_comment(tctx, "Server updated write_time (wrong!)\n");
1065 ret = false;
1068 /* Once the time was set using setfileinfo then it stays set - writes
1069 don't have any effect. But make sure. */
1070 start = timeval_current();
1071 end = timeval_add(&start, (15*sec), 0);
1072 while (!timeval_expired(&end)) {
1073 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1075 if (!NT_STATUS_IS_OK(status)) {
1076 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1077 ret = false;
1078 break;
1080 torture_comment(tctx, "write time %s\n",
1081 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1082 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1083 double diff = timeval_elapsed(&start);
1084 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1085 "(1sec == %.2f) (wrong!)\n",
1086 diff, sec);
1087 ret = false;
1088 break;
1090 fflush(stdout);
1091 smb_msleep(1 * msec);
1094 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1095 torture_comment(tctx, "Server did not update write time (correct)\n");
1098 torture_comment(tctx, "Closing second fd to see if write time updated.\n");
1100 smbcli_close(cli->tree, fnum2);
1101 fnum2 = -1;
1103 fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
1104 if (fnum1 == -1) {
1105 torture_comment(tctx, "Failed to open %s\n", fname);
1106 return false;
1109 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1110 finfo1.basic_info.in.file.fnum = fnum1;
1111 finfo2 = finfo1;
1113 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1115 if (!NT_STATUS_IS_OK(status)) {
1116 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1117 return false;
1120 torture_comment(tctx, "Second open initial write time %s\n",
1121 nt_time_string(tctx, finfo1.basic_info.out.write_time));
1123 smb_msleep(10 * msec);
1124 torture_comment(tctx, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
1126 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
1128 if (written != 10) {
1129 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
1130 (int)written, __location__);
1131 return false;
1134 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1135 finfo1.basic_info.in.file.fnum = fnum1;
1136 finfo2 = finfo1;
1137 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1139 if (!NT_STATUS_IS_OK(status)) {
1140 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1141 return false;
1143 torture_comment(tctx, "write time %s\n",
1144 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1145 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1146 torture_comment(tctx, "Server updated write_time (wrong!)\n");
1147 ret = false;
1150 /* Now the write time should be updated again */
1151 start = timeval_current();
1152 end = timeval_add(&start, (15*sec), 0);
1153 while (!timeval_expired(&end)) {
1154 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1156 if (!NT_STATUS_IS_OK(status)) {
1157 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1158 ret = false;
1159 break;
1161 torture_comment(tctx, "write time %s\n",
1162 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1163 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1164 double diff = timeval_elapsed(&start);
1165 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
1166 torture_comment(tctx, "Server updated write_time after %.2f seconds"
1167 "(1sec == %.2f) (wrong!)\n",
1168 diff, sec);
1169 ret = false;
1170 break;
1173 torture_comment(tctx, "Server updated write_time after %.2f seconds"
1174 "(1sec == %.2f) (correct)\n",
1175 diff, sec);
1176 break;
1178 fflush(stdout);
1179 smb_msleep(1*msec);
1182 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1183 torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n");
1184 ret = false;
1188 /* One more test to do. We should read the filetime via findfirst on the
1189 second connection to ensure it's the same. This is very easy for a Windows
1190 server but a bastard to get right on a POSIX server. JRA. */
1192 if (fnum1 != -1)
1193 smbcli_close(cli->tree, fnum1);
1194 smbcli_unlink(cli->tree, fname);
1195 smbcli_deltree(cli->tree, BASEDIR);
1197 return ret;
1201 /* Windows does obviously not update the stat info during a write call. I
1202 * *think* this is the problem causing a spurious Excel 2003 on XP error
1203 * message when saving a file. Excel does a setfileinfo, writes, and then does
1204 * a getpath(!)info. Or so... For Samba sometimes it displays an error message
1205 * that the file might have been changed in between. What i've been able to
1206 * trace down is that this happens if the getpathinfo after the write shows a
1207 * different last write time than the setfileinfo showed. This is really
1208 * nasty....
1211 static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli,
1212 struct smbcli_state *cli2)
1214 union smb_fileinfo finfo1, finfo2;
1215 const char *fname = BASEDIR "\\torture_file.txt";
1216 NTSTATUS status;
1217 int fnum1 = -1;
1218 int fnum2;
1219 bool ret = true;
1220 ssize_t written;
1221 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1222 int normal_delay = 2000000;
1223 double sec = ((double)used_delay) / ((double)normal_delay);
1224 int msec = 1000 * sec;
1226 torture_comment(tctx, "\nRunning test_finfo_after_write\n");
1228 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1230 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1231 if (fnum1 == -1) {
1232 ret = false;
1233 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1234 goto done;
1237 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1238 finfo1.basic_info.in.file.fnum = fnum1;
1240 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1242 if (!NT_STATUS_IS_OK(status)) {
1243 ret = false;
1244 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1245 goto done;
1248 smb_msleep(1 * msec);
1250 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1252 if (written != 1) {
1253 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1254 ret = false;
1255 goto done;
1258 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
1259 if (fnum2 == -1) {
1260 torture_result(tctx, TORTURE_FAIL, __location__": failed to open 2nd time - %s",
1261 smbcli_errstr(cli2->tree));
1262 ret = false;
1263 goto done;
1266 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
1268 if (written != 1) {
1269 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1",
1270 (int)written);
1271 ret = false;
1272 goto done;
1275 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1276 finfo2.basic_info.in.file.path = fname;
1278 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
1280 if (!NT_STATUS_IS_OK(status)) {
1281 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s",
1282 nt_errstr(status));
1283 ret = false;
1284 goto done;
1287 if (finfo1.basic_info.out.create_time !=
1288 finfo2.basic_info.out.create_time) {
1289 torture_result(tctx, TORTURE_FAIL, __location__": create_time changed");
1290 ret = false;
1291 goto done;
1294 if (finfo1.basic_info.out.access_time !=
1295 finfo2.basic_info.out.access_time) {
1296 torture_result(tctx, TORTURE_FAIL, __location__": access_time changed");
1297 ret = false;
1298 goto done;
1301 if (finfo1.basic_info.out.write_time !=
1302 finfo2.basic_info.out.write_time) {
1303 torture_result(tctx, TORTURE_FAIL, __location__": write_time changed:\n"
1304 "write time conn 1 = %s, conn 2 = %s",
1305 nt_time_string(tctx, finfo1.basic_info.out.write_time),
1306 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1307 ret = false;
1308 goto done;
1311 if (finfo1.basic_info.out.change_time !=
1312 finfo2.basic_info.out.change_time) {
1313 torture_result(tctx, TORTURE_FAIL, __location__": change_time changed");
1314 ret = false;
1315 goto done;
1318 /* One of the two following calls updates the qpathinfo. */
1320 /* If you had skipped the smbcli_write on fnum2, it would
1321 * *not* have updated the stat on disk */
1323 smbcli_close(cli2->tree, fnum2);
1324 cli2 = NULL;
1326 /* This call is only for the people looking at ethereal :-) */
1327 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1328 finfo2.basic_info.in.file.path = fname;
1330 status = smb_raw_pathinfo(cli->tree, tctx, &finfo2);
1332 if (!NT_STATUS_IS_OK(status)) {
1333 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1334 ret = false;
1335 goto done;
1338 done:
1339 if (fnum1 != -1)
1340 smbcli_close(cli->tree, fnum1);
1341 smbcli_unlink(cli->tree, fname);
1342 smbcli_deltree(cli->tree, BASEDIR);
1344 return ret;
1347 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
1348 uint64_t r = 10*1000*1000; \
1349 NTTIME g = (given).basic_info.out.write_time; \
1350 NTTIME gr = (g / r) * r; \
1351 NTTIME c = (correct).basic_info.out.write_time; \
1352 NTTIME cr = (c / r) * r; \
1353 bool strict = torture_setting_bool(tctx, "strict mode", false); \
1354 bool err = false; \
1355 if (strict && (g cmp c)) { \
1356 err = true; \
1357 } else if ((g cmp c) && (gr cmp cr)) { \
1358 /* handle filesystem without high resolution timestamps */ \
1359 err = true; \
1361 if (err) { \
1362 torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \
1363 #given, nt_time_string(tctx, g), (unsigned long long)g, \
1364 #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \
1365 ret = false; \
1366 goto done; \
1368 } while (0)
1369 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
1370 COMPARE_WRITE_TIME_CMP(given,correct,!=)
1371 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
1372 COMPARE_WRITE_TIME_CMP(given,correct,<=)
1373 #define COMPARE_WRITE_TIME_LESS(given,correct) \
1374 COMPARE_WRITE_TIME_CMP(given,correct,>=)
1376 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
1377 NTTIME g = (given).basic_info.out.access_time; \
1378 NTTIME c = (correct).basic_info.out.access_time; \
1379 if (g cmp c) { \
1380 torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \
1381 #given, nt_time_string(tctx, g), \
1382 #cmp, #correct, nt_time_string(tctx, c)); \
1383 ret = false; \
1384 goto done; \
1386 } while (0)
1387 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
1388 COMPARE_ACCESS_TIME_CMP(given,correct,!=)
1390 #define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \
1391 COMPARE_ACCESS_TIME_EQUAL(given,correct); \
1392 COMPARE_WRITE_TIME_EQUAL(given,correct); \
1393 } while (0)
1395 #define GET_INFO_FILE(finfo) do { \
1396 NTSTATUS _status; \
1397 _status = smb_raw_fileinfo(cli->tree, tctx, &finfo); \
1398 if (!NT_STATUS_IS_OK(_status)) { \
1399 ret = false; \
1400 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1401 nt_errstr(_status)); \
1402 goto done; \
1404 torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1405 nt_time_string(tctx, finfo.basic_info.out.access_time), \
1406 nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1407 } while (0)
1408 #define GET_INFO_FILE2(finfo) do { \
1409 NTSTATUS _status; \
1410 _status = smb_raw_fileinfo(cli2->tree, tctx, &finfo); \
1411 if (!NT_STATUS_IS_OK(_status)) { \
1412 ret = false; \
1413 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1414 nt_errstr(_status)); \
1415 goto done; \
1417 torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1418 nt_time_string(tctx, finfo.basic_info.out.access_time), \
1419 nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1420 } while (0)
1421 #define GET_INFO_PATH(pinfo) do { \
1422 NTSTATUS _status; \
1423 _status = smb_raw_pathinfo(cli2->tree, tctx, &pinfo); \
1424 if (!NT_STATUS_IS_OK(_status)) { \
1425 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \
1426 nt_errstr(_status)); \
1427 ret = false; \
1428 goto done; \
1430 torture_comment(tctx, "pathinfo: Access(%s) Write(%s)\n", \
1431 nt_time_string(tctx, pinfo.basic_info.out.access_time), \
1432 nt_time_string(tctx, pinfo.basic_info.out.write_time)); \
1433 } while (0)
1434 #define GET_INFO_BOTH(finfo,pinfo) do { \
1435 GET_INFO_FILE(finfo); \
1436 GET_INFO_PATH(pinfo); \
1437 COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \
1438 } while (0)
1440 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
1441 NTSTATUS _status; \
1442 union smb_setfileinfo sfinfo; \
1443 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1444 sfinfo.basic_info.in.file.fnum = tfnum; \
1445 sfinfo.basic_info.in.create_time = 0; \
1446 sfinfo.basic_info.in.access_time = 0; \
1447 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1448 sfinfo.basic_info.in.change_time = 0; \
1449 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1450 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1451 if (!NT_STATUS_IS_OK(_status)) { \
1452 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1453 nt_errstr(_status)); \
1454 ret = false; \
1455 goto done; \
1457 } while (0)
1458 #define SET_INFO_FILE(finfo, wrtime) \
1459 SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
1461 #define SET_INFO_FILE_NS(finfo, wrtime, ns, tree, tfnum) do { \
1462 NTSTATUS _status; \
1463 union smb_setfileinfo sfinfo; \
1464 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1465 sfinfo.basic_info.in.file.fnum = tfnum; \
1466 sfinfo.basic_info.in.create_time = 0; \
1467 sfinfo.basic_info.in.access_time = 0; \
1468 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1469 sfinfo.basic_info.in.write_time += (ns); \
1470 sfinfo.basic_info.in.change_time = 0; \
1471 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1472 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1473 if (!NT_STATUS_IS_OK(_status)) { \
1474 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1475 nt_errstr(_status)); \
1476 ret = false; \
1477 goto done; \
1479 } while (0)
1481 static bool test_delayed_write_update3(struct torture_context *tctx,
1482 struct smbcli_state *cli,
1483 struct smbcli_state *cli2)
1485 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1486 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1487 const char *fname = BASEDIR "\\torture_file3.txt";
1488 int fnum1 = -1;
1489 bool ret = true;
1490 ssize_t written;
1491 struct timeval start;
1492 struct timeval end;
1493 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1494 int normal_delay = 2000000;
1495 double sec = ((double)used_delay) / ((double)normal_delay);
1496 int msec = 1000 * sec;
1498 torture_comment(tctx, "\nRunning test_delayed_write_update3\n");
1500 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1502 torture_comment(tctx, "Open the file handle\n");
1503 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1504 if (fnum1 == -1) {
1505 ret = false;
1506 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1507 goto done;
1510 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1511 finfo0.basic_info.in.file.fnum = fnum1;
1512 finfo1 = finfo0;
1513 finfo2 = finfo0;
1514 finfo3 = finfo0;
1515 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1516 pinfo0.basic_info.in.file.path = fname;
1517 pinfo1 = pinfo0;
1518 pinfo2 = pinfo0;
1519 pinfo3 = pinfo0;
1520 pinfo4 = pinfo0;
1522 /* get the initial times */
1523 GET_INFO_BOTH(finfo0,pinfo0);
1526 * make sure the write time is updated 2 seconds later
1527 * calcuated from the first write
1528 * (but expect upto 5 seconds extra time for a busy server)
1530 start = timeval_current();
1531 end = timeval_add(&start, 7 * sec, 0);
1532 while (!timeval_expired(&end)) {
1533 /* do a write */
1534 torture_comment(tctx, "Do a write on the file handle\n");
1535 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1536 if (written != 1) {
1537 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1538 ret = false;
1539 goto done;
1541 /* get the times after the write */
1542 GET_INFO_FILE(finfo1);
1544 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1545 double diff = timeval_elapsed(&start);
1546 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
1547 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1548 "(1sec == %.2f) (wrong!)\n",
1549 diff, sec);
1550 ret = false;
1551 break;
1554 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1555 "(1sec == %.2f) (correct)\n",
1556 diff, sec);
1557 break;
1559 smb_msleep(0.5 * msec);
1562 GET_INFO_BOTH(finfo1,pinfo1);
1563 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1565 /* sure any further write doesn't update the write time */
1566 start = timeval_current();
1567 end = timeval_add(&start, 15 * sec, 0);
1568 while (!timeval_expired(&end)) {
1569 /* do a write */
1570 torture_comment(tctx, "Do a write on the file handle\n");
1571 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1572 if (written != 1) {
1573 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1574 ret = false;
1575 goto done;
1577 /* get the times after the write */
1578 GET_INFO_BOTH(finfo2,pinfo2);
1580 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1581 double diff = timeval_elapsed(&start);
1582 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1583 "(1sec == %.2f) (wrong!)\n",
1584 diff, sec);
1585 ret = false;
1586 break;
1588 smb_msleep(1 * msec);
1591 GET_INFO_BOTH(finfo2,pinfo2);
1592 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1593 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1594 torture_comment(tctx, "Server did not update write_time (correct)\n");
1597 /* sleep */
1598 smb_msleep(5 * msec);
1600 GET_INFO_BOTH(finfo3,pinfo3);
1601 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1604 * the close updates the write time to the time of the close
1605 * and not to the time of the last write!
1607 torture_comment(tctx, "Close the file handle\n");
1608 smbcli_close(cli->tree, fnum1);
1609 fnum1 = -1;
1611 GET_INFO_PATH(pinfo4);
1612 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1614 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1615 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1618 done:
1619 if (fnum1 != -1)
1620 smbcli_close(cli->tree, fnum1);
1621 smbcli_unlink(cli->tree, fname);
1622 smbcli_deltree(cli->tree, BASEDIR);
1624 return ret;
1628 * Show that a truncate write always updates the write time even
1629 * if an initial write has already updated the write time.
1632 static bool test_delayed_write_update3a(struct torture_context *tctx,
1633 struct smbcli_state *cli,
1634 struct smbcli_state *cli2)
1636 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1637 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1638 const char *fname = BASEDIR "\\torture_file3a.txt";
1639 int fnum1 = -1;
1640 bool ret = true;
1641 ssize_t written;
1642 int i;
1643 struct timeval start;
1644 struct timeval end;
1645 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1646 int normal_delay = 2000000;
1647 double sec = ((double)used_delay) / ((double)normal_delay);
1648 int msec = 1000 * sec;
1650 torture_comment(tctx, "\nRunning test_delayed_write_update3a\n");
1652 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1654 torture_comment(tctx, "Open the file handle\n");
1655 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1656 if (fnum1 == -1) {
1657 ret = false;
1658 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1659 goto done;
1662 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1663 finfo0.basic_info.in.file.fnum = fnum1;
1664 finfo1 = finfo0;
1665 finfo2 = finfo0;
1666 finfo3 = finfo0;
1667 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1668 pinfo0.basic_info.in.file.path = fname;
1669 pinfo1 = pinfo0;
1670 pinfo2 = pinfo0;
1671 pinfo3 = pinfo0;
1672 pinfo4 = pinfo0;
1674 /* get the initial times */
1675 GET_INFO_BOTH(finfo0,pinfo0);
1678 * sleep some time, to demonstrate the handling of write times
1679 * doesn't depend on the time since the open
1681 smb_msleep(5 * msec);
1683 /* get the initial times */
1684 GET_INFO_BOTH(finfo1,pinfo1);
1685 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1688 * make sure the write time is updated 2 seconds later
1689 * calcuated from the first write
1690 * (but expect upto 5 seconds extra time for a busy server)
1692 start = timeval_current();
1693 end = timeval_add(&start, 7 * sec, 0);
1694 while (!timeval_expired(&end)) {
1695 /* do a write */
1696 torture_comment(tctx, "Do a write on the file handle\n");
1697 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1698 if (written != 1) {
1699 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1700 ret = false;
1701 goto done;
1703 /* get the times after the write */
1704 GET_INFO_FILE(finfo1);
1706 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1707 double diff = timeval_elapsed(&start);
1708 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
1709 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1710 "(1sec == %.2f) (wrong!)\n",
1711 diff, sec);
1712 ret = false;
1713 break;
1716 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1717 "(1sec == %.2f) (correct)\n",
1718 diff, sec);
1719 break;
1721 smb_msleep(0.5 * msec);
1724 GET_INFO_BOTH(finfo1,pinfo1);
1725 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1727 smb_msleep(3 * msec);
1730 * demonstrate that a truncate write always
1731 * updates the write time immediately
1733 for (i=0; i < 3; i++) {
1734 smb_msleep(2 * msec);
1735 /* do a write */
1736 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1737 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
1738 if (written != 0) {
1739 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1740 ret = false;
1741 goto done;
1743 /* get the times after the write */
1744 GET_INFO_BOTH(finfo2,pinfo2);
1745 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1746 finfo1 = finfo2;
1749 smb_msleep(3 * msec);
1751 /* sure any further write doesn't update the write time */
1752 start = timeval_current();
1753 end = timeval_add(&start, 15 * sec, 0);
1754 while (!timeval_expired(&end)) {
1755 /* do a write */
1756 torture_comment(tctx, "Do a write on the file handle\n");
1757 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1758 if (written != 1) {
1759 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1760 ret = false;
1761 goto done;
1763 /* get the times after the write */
1764 GET_INFO_BOTH(finfo2,pinfo2);
1766 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1767 double diff = timeval_elapsed(&start);
1768 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1769 "(1sec == %.2f) (wrong!)\n",
1770 diff, sec);
1771 ret = false;
1772 break;
1774 smb_msleep(1 * msec);
1777 GET_INFO_BOTH(finfo2,pinfo2);
1778 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1779 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1780 torture_comment(tctx, "Server did not update write_time (correct)\n");
1783 /* sleep */
1784 smb_msleep(3 * msec);
1786 /* get the initial times */
1787 GET_INFO_BOTH(finfo1,pinfo1);
1788 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
1791 * demonstrate that a truncate write always
1792 * updates the write time immediately
1794 for (i=0; i < 3; i++) {
1795 smb_msleep(2 * msec);
1796 /* do a write */
1797 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1798 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
1799 if (written != 0) {
1800 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1801 ret = false;
1802 goto done;
1804 /* get the times after the write */
1805 GET_INFO_BOTH(finfo2,pinfo2);
1806 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1807 finfo1 = finfo2;
1810 /* sleep */
1811 smb_msleep(3 * msec);
1813 GET_INFO_BOTH(finfo3,pinfo3);
1814 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1817 * the close doesn't update the write time
1819 torture_comment(tctx, "Close the file handle\n");
1820 smbcli_close(cli->tree, fnum1);
1821 fnum1 = -1;
1823 GET_INFO_PATH(pinfo4);
1824 COMPARE_WRITE_TIME_EQUAL(pinfo4, pinfo3);
1826 if (pinfo4.basic_info.out.write_time == pinfo3.basic_info.out.write_time) {
1827 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1830 done:
1831 if (fnum1 != -1)
1832 smbcli_close(cli->tree, fnum1);
1833 smbcli_unlink(cli->tree, fname);
1834 smbcli_deltree(cli->tree, BASEDIR);
1836 return ret;
1840 * Show a close after write updates the write timestamp to
1841 * the close time, not the last write time.
1844 static bool test_delayed_write_update3b(struct torture_context *tctx,
1845 struct smbcli_state *cli,
1846 struct smbcli_state *cli2)
1848 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1849 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1850 const char *fname = BASEDIR "\\torture_file3b.txt";
1851 int fnum1 = -1;
1852 bool ret = true;
1853 ssize_t written;
1854 struct timeval start;
1855 struct timeval end;
1856 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1857 int normal_delay = 2000000;
1858 double sec = ((double)used_delay) / ((double)normal_delay);
1859 int msec = 1000 * sec;
1861 torture_comment(tctx, "\nRunning test_delayed_write_update3b\n");
1863 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1865 torture_comment(tctx, "Open the file handle\n");
1866 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1867 if (fnum1 == -1) {
1868 ret = false;
1869 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1870 goto done;
1873 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1874 finfo0.basic_info.in.file.fnum = fnum1;
1875 finfo1 = finfo0;
1876 finfo2 = finfo0;
1877 finfo3 = finfo0;
1878 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1879 pinfo0.basic_info.in.file.path = fname;
1880 pinfo1 = pinfo0;
1881 pinfo2 = pinfo0;
1882 pinfo3 = pinfo0;
1883 pinfo4 = pinfo0;
1885 /* get the initial times */
1886 GET_INFO_BOTH(finfo0,pinfo0);
1889 * sleep some time, to demonstrate the handling of write times
1890 * doesn't depend on the time since the open
1892 smb_msleep(5 * msec);
1894 /* get the initial times */
1895 GET_INFO_BOTH(finfo1,pinfo1);
1896 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1899 * make sure the write time is updated 2 seconds later
1900 * calcuated from the first write
1901 * (but expect upto 5 seconds extra time for a busy server)
1903 start = timeval_current();
1904 end = timeval_add(&start, 7 * sec, 0);
1905 while (!timeval_expired(&end)) {
1906 /* do a write */
1907 torture_comment(tctx, "Do a write on the file handle\n");
1908 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1909 if (written != 1) {
1910 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1911 ret = false;
1912 goto done;
1914 /* get the times after the write */
1915 GET_INFO_FILE(finfo1);
1917 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1918 double diff = timeval_elapsed(&start);
1919 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
1920 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1921 "(1sec == %.2f) (wrong!)\n",
1922 diff, sec);
1923 ret = false;
1924 break;
1927 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1928 "(1sec == %.2f) (correct)\n",
1929 diff, sec);
1930 break;
1932 smb_msleep(0.5 * msec);
1935 GET_INFO_BOTH(finfo1,pinfo1);
1936 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1938 /* sure any further write doesn't update the write time */
1939 start = timeval_current();
1940 end = timeval_add(&start, 15 * sec, 0);
1941 while (!timeval_expired(&end)) {
1942 /* do a write */
1943 torture_comment(tctx, "Do a write on the file handle\n");
1944 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1945 if (written != 1) {
1946 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1947 ret = false;
1948 goto done;
1950 /* get the times after the write */
1951 GET_INFO_BOTH(finfo2,pinfo2);
1953 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1954 double diff = timeval_elapsed(&start);
1955 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1956 "(1sec == %.2f) (wrong!)\n",
1957 diff, sec);
1958 ret = false;
1959 break;
1961 smb_msleep(1 * msec);
1964 GET_INFO_BOTH(finfo2,pinfo2);
1965 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1966 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1967 torture_comment(tctx, "Server did not update write_time (correct)\n");
1970 /* sleep */
1971 smb_msleep(5 * msec);
1973 GET_INFO_BOTH(finfo3,pinfo3);
1974 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1977 * the close updates the write time to the time of the close
1978 * and not to the time of the last write!
1980 torture_comment(tctx, "Close the file handle\n");
1981 smbcli_close(cli->tree, fnum1);
1982 fnum1 = -1;
1984 GET_INFO_PATH(pinfo4);
1985 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1987 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1988 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1991 done:
1992 if (fnum1 != -1)
1993 smbcli_close(cli->tree, fnum1);
1994 smbcli_unlink(cli->tree, fname);
1995 smbcli_deltree(cli->tree, BASEDIR);
1997 return ret;
2001 * Check that a write after a truncate write doesn't update
2002 * the timestamp, but a truncate write after a write does.
2003 * Also prove that a close after a truncate write updates the
2004 * timestamp to current, not the time of last write.
2007 static bool test_delayed_write_update3c(struct torture_context *tctx,
2008 struct smbcli_state *cli,
2009 struct smbcli_state *cli2)
2011 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
2012 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
2013 const char *fname = BASEDIR "\\torture_file3c.txt";
2014 int fnum1 = -1;
2015 bool ret = true;
2016 ssize_t written;
2017 int i;
2018 struct timeval start;
2019 struct timeval end;
2020 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2021 int normal_delay = 2000000;
2022 double sec = ((double)used_delay) / ((double)normal_delay);
2023 int msec = 1000 * sec;
2025 torture_comment(tctx, "\nRunning test_delayed_write_update3c\n");
2027 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2029 torture_comment(tctx, "Open the file handle\n");
2030 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2031 if (fnum1 == -1) {
2032 ret = false;
2033 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2034 goto done;
2037 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2038 finfo0.basic_info.in.file.fnum = fnum1;
2039 finfo1 = finfo0;
2040 finfo2 = finfo0;
2041 finfo3 = finfo0;
2042 finfo4 = finfo0;
2043 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2044 pinfo0.basic_info.in.file.path = fname;
2045 pinfo1 = pinfo0;
2046 pinfo2 = pinfo0;
2047 pinfo3 = pinfo0;
2048 pinfo4 = pinfo0;
2049 pinfo5 = pinfo0;
2051 /* get the initial times */
2052 GET_INFO_BOTH(finfo0,pinfo0);
2055 * sleep some time, to demonstrate the handling of write times
2056 * doesn't depend on the time since the open
2058 smb_msleep(5 * msec);
2060 /* get the initial times */
2061 GET_INFO_BOTH(finfo1,pinfo1);
2062 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2065 * demonstrate that a truncate write always
2066 * updates the write time immediately
2068 for (i=0; i < 3; i++) {
2069 smb_msleep(2 * msec);
2070 /* do a write */
2071 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
2072 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2073 if (written != 0) {
2074 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2075 ret = false;
2076 goto done;
2078 /* get the times after the write */
2079 GET_INFO_BOTH(finfo2,pinfo2);
2080 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2081 finfo1 = finfo2;
2084 start = timeval_current();
2085 end = timeval_add(&start, 7 * sec, 0);
2086 while (!timeval_expired(&end)) {
2087 /* do a write */
2088 torture_comment(tctx, "Do a write on the file handle\n");
2089 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2090 if (written != 1) {
2091 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2092 ret = false;
2093 goto done;
2095 /* get the times after the write */
2096 GET_INFO_FILE(finfo2);
2098 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2099 double diff = timeval_elapsed(&start);
2100 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2101 "(1sec == %.2f) (wrong!)\n",
2102 diff, sec);
2103 ret = false;
2104 break;
2106 smb_msleep(1 * msec);
2109 GET_INFO_BOTH(finfo2,pinfo2);
2110 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2111 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2112 torture_comment(tctx, "Server did not update write_time (correct)\n");
2115 /* sleep */
2116 smb_msleep(5 * msec);
2118 /* get the initial times */
2119 GET_INFO_BOTH(finfo1,pinfo1);
2120 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
2123 * demonstrate that a truncate write always
2124 * updates the write time immediately
2126 for (i=0; i < 3; i++) {
2127 smb_msleep(2 * msec);
2128 /* do a write */
2129 torture_comment(tctx, "Do a truncate write [%d] on the file handle\n", i);
2130 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2131 if (written != 0) {
2132 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2133 ret = false;
2134 goto done;
2136 /* get the times after the write */
2137 GET_INFO_BOTH(finfo2,pinfo2);
2138 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2139 finfo1 = finfo2;
2142 /* sleep */
2143 smb_msleep(5 * msec);
2145 GET_INFO_BOTH(finfo2,pinfo2);
2146 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2148 /* sure any further write doesn't update the write time */
2149 start = timeval_current();
2150 end = timeval_add(&start, 15 * sec, 0);
2151 while (!timeval_expired(&end)) {
2152 /* do a write */
2153 torture_comment(tctx, "Do a write on the file handle\n");
2154 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2155 if (written != 1) {
2156 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2157 ret = false;
2158 goto done;
2160 /* get the times after the write */
2161 GET_INFO_BOTH(finfo2,pinfo2);
2163 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2164 double diff = timeval_elapsed(&start);
2165 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2166 "(1sec == %.2f) (wrong!)\n",
2167 diff, sec);
2168 ret = false;
2169 break;
2171 smb_msleep(1 * msec);
2174 GET_INFO_BOTH(finfo2,pinfo2);
2175 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2176 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2177 torture_comment(tctx, "Server did not update write_time (correct)\n");
2180 /* sleep */
2181 smb_msleep(5 * msec);
2183 GET_INFO_BOTH(finfo3,pinfo3);
2184 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2187 * the close updates the write time to the time of the close
2188 * and not to the time of the last write!
2190 torture_comment(tctx, "Close the file handle\n");
2191 smbcli_close(cli->tree, fnum1);
2192 fnum1 = -1;
2194 GET_INFO_PATH(pinfo4);
2195 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2197 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2198 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2201 done:
2202 if (fnum1 != -1)
2203 smbcli_close(cli->tree, fnum1);
2204 smbcli_unlink(cli->tree, fname);
2205 smbcli_deltree(cli->tree, BASEDIR);
2207 return ret;
2211 * Show only the first write updates the timestamp, and a close
2212 * after writes updates to current (I think this is the same
2213 * as test 3b. JRA).
2216 static bool test_delayed_write_update4(struct torture_context *tctx,
2217 struct smbcli_state *cli,
2218 struct smbcli_state *cli2)
2220 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
2221 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
2222 const char *fname = BASEDIR "\\torture_file4.txt";
2223 int fnum1 = -1;
2224 bool ret = true;
2225 ssize_t written;
2226 struct timeval start;
2227 struct timeval end;
2228 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2229 int normal_delay = 2000000;
2230 double sec = ((double)used_delay) / ((double)normal_delay);
2231 int msec = 1000 * sec;
2233 torture_comment(tctx, "\nRunning test_delayed_write_update4\n");
2235 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2237 torture_comment(tctx, "Open the file handle\n");
2238 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2239 if (fnum1 == -1) {
2240 ret = false;
2241 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2242 goto done;
2245 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2246 finfo0.basic_info.in.file.fnum = fnum1;
2247 finfo1 = finfo0;
2248 finfo2 = finfo0;
2249 finfo3 = finfo0;
2250 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2251 pinfo0.basic_info.in.file.path = fname;
2252 pinfo1 = pinfo0;
2253 pinfo2 = pinfo0;
2254 pinfo3 = pinfo0;
2255 pinfo4 = pinfo0;
2257 /* get the initial times */
2258 GET_INFO_BOTH(finfo0,pinfo0);
2260 /* sleep a bit */
2261 smb_msleep(5 * msec);
2263 /* do a write */
2264 torture_comment(tctx, "Do a write on the file handle\n");
2265 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2266 if (written != 1) {
2267 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2268 ret = false;
2269 goto done;
2272 GET_INFO_BOTH(finfo1,pinfo1);
2273 COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
2276 * make sure the write time is updated 2 seconds later
2277 * calcuated from the first write
2278 * (but expect upto 3 seconds extra time for a busy server)
2280 start = timeval_current();
2281 end = timeval_add(&start, 5 * sec, 0);
2282 while (!timeval_expired(&end)) {
2283 /* get the times after the first write */
2284 GET_INFO_FILE(finfo1);
2286 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
2287 double diff = timeval_elapsed(&start);
2288 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
2289 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2290 "(1sec == %.2f) (wrong!)\n",
2291 diff, sec);
2292 ret = false;
2293 break;
2296 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2297 "(1sec == %.2f) (correct)\n",
2298 diff, sec);
2299 break;
2301 smb_msleep(0.5 * msec);
2304 GET_INFO_BOTH(finfo1,pinfo1);
2305 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
2307 /* sure any further write doesn't update the write time */
2308 start = timeval_current();
2309 end = timeval_add(&start, 15 * sec, 0);
2310 while (!timeval_expired(&end)) {
2311 /* do a write */
2312 torture_comment(tctx, "Do a write on the file handle\n");
2313 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2314 if (written != 1) {
2315 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2316 ret = false;
2317 goto done;
2319 /* get the times after the write */
2320 GET_INFO_BOTH(finfo2,pinfo2);
2322 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2323 double diff = timeval_elapsed(&start);
2324 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2325 "(1sec == %.2f) (wrong!)\n",
2326 diff, sec);
2327 ret = false;
2328 break;
2330 smb_msleep(1 * msec);
2333 GET_INFO_BOTH(finfo2,pinfo2);
2334 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2335 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2336 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
2339 /* sleep */
2340 smb_msleep(5 * msec);
2342 GET_INFO_BOTH(finfo3,pinfo3);
2343 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2346 * the close updates the write time to the time of the close
2347 * and not to the time of the last write!
2349 torture_comment(tctx, "Close the file handle\n");
2350 smbcli_close(cli->tree, fnum1);
2351 fnum1 = -1;
2353 GET_INFO_PATH(pinfo4);
2354 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2356 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2357 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2360 done:
2361 if (fnum1 != -1)
2362 smbcli_close(cli->tree, fnum1);
2363 smbcli_unlink(cli->tree, fname);
2364 smbcli_deltree(cli->tree, BASEDIR);
2366 return ret;
2370 * Show writes and closes have no effect on updating times once a SETWRITETIME is done.
2373 static bool test_delayed_write_update5(struct torture_context *tctx,
2374 struct smbcli_state *cli,
2375 struct smbcli_state *cli2)
2377 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2378 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2379 const char *fname = BASEDIR "\\torture_file5.txt";
2380 int fnum1 = -1;
2381 bool ret = true;
2382 ssize_t written;
2383 struct timeval start;
2384 struct timeval end;
2385 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2386 int normal_delay = 2000000;
2387 double sec = ((double)used_delay) / ((double)normal_delay);
2388 int msec = 1000 * sec;
2390 torture_comment(tctx, "\nRunning test_delayed_write_update5\n");
2392 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2394 torture_comment(tctx, "Open the file handle\n");
2395 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2396 if (fnum1 == -1) {
2397 ret = false;
2398 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2399 goto done;
2402 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2403 finfo0.basic_info.in.file.fnum = fnum1;
2404 finfo1 = finfo0;
2405 finfo2 = finfo0;
2406 finfo3 = finfo0;
2407 finfo4 = finfo0;
2408 finfo5 = finfo0;
2409 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2410 pinfo0.basic_info.in.file.path = fname;
2411 pinfo1 = pinfo0;
2412 pinfo2 = pinfo0;
2413 pinfo3 = pinfo0;
2414 pinfo4 = pinfo0;
2415 pinfo5 = pinfo0;
2416 pinfo6 = pinfo0;
2418 /* get the initial times */
2419 GET_INFO_BOTH(finfo0,pinfo0);
2421 /* do a write */
2422 torture_comment(tctx, "Do a write on the file handle\n");
2423 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2424 if (written != 1) {
2425 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2426 ret = false;
2427 goto done;
2430 GET_INFO_BOTH(finfo1,pinfo1);
2431 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2433 torture_comment(tctx, "Set write time in the future on the file handle\n");
2434 SET_INFO_FILE(finfo0, time(NULL) + 86400);
2435 GET_INFO_BOTH(finfo2,pinfo2);
2436 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2438 torture_comment(tctx, "Set write time in the past on the file handle\n");
2439 SET_INFO_FILE(finfo0, time(NULL) - 86400);
2440 GET_INFO_BOTH(finfo2,pinfo2);
2441 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2443 /* make sure the 2 second delay from the first write are canceled */
2444 start = timeval_current();
2445 end = timeval_add(&start, 15 * sec, 0);
2446 while (!timeval_expired(&end)) {
2448 /* get the times after the first write */
2449 GET_INFO_BOTH(finfo3,pinfo3);
2451 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2452 double diff = timeval_elapsed(&start);
2453 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2454 "(1sec == %.2f) (wrong!)\n",
2455 diff, sec);
2456 ret = false;
2457 break;
2459 smb_msleep(1 * msec);
2462 GET_INFO_BOTH(finfo3,pinfo3);
2463 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2464 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2465 torture_comment(tctx, "Server did not update write_time (correct)\n");
2468 /* sure any further write doesn't update the write time */
2469 start = timeval_current();
2470 end = timeval_add(&start, 15 * sec, 0);
2471 while (!timeval_expired(&end)) {
2472 /* do a write */
2473 torture_comment(tctx, "Do a write on the file handle\n");
2474 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2475 if (written != 1) {
2476 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2477 ret = false;
2478 goto done;
2480 /* get the times after the write */
2481 GET_INFO_BOTH(finfo4,pinfo4);
2483 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2484 double diff = timeval_elapsed(&start);
2485 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2486 "(1sec == %.2f) (wrong!)\n",
2487 diff, sec);
2488 ret = false;
2489 break;
2491 smb_msleep(1 * msec);
2494 GET_INFO_BOTH(finfo4,pinfo4);
2495 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2496 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2497 torture_comment(tctx, "Server did not update write_time (correct)\n");
2500 /* sleep */
2501 smb_msleep(5 * msec);
2503 GET_INFO_BOTH(finfo5,pinfo5);
2504 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2507 * the close doesn't update the write time
2509 torture_comment(tctx, "Close the file handle\n");
2510 smbcli_close(cli->tree, fnum1);
2511 fnum1 = -1;
2513 GET_INFO_PATH(pinfo6);
2514 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2516 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2517 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2520 done:
2521 if (fnum1 != -1)
2522 smbcli_close(cli->tree, fnum1);
2523 smbcli_unlink(cli->tree, fname);
2524 smbcli_deltree(cli->tree, BASEDIR);
2526 return ret;
2530 * Show truncate writes and closes have no effect on updating times once a SETWRITETIME is done.
2533 static bool test_delayed_write_update5b(struct torture_context *tctx,
2534 struct smbcli_state *cli,
2535 struct smbcli_state *cli2)
2537 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2538 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2539 const char *fname = BASEDIR "\\torture_fileb.txt";
2540 int fnum1 = -1;
2541 bool ret = true;
2542 ssize_t written;
2543 struct timeval start;
2544 struct timeval end;
2545 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2546 int normal_delay = 2000000;
2547 double sec = ((double)used_delay) / ((double)normal_delay);
2548 int msec = 1000 * sec;
2550 torture_comment(tctx, "\nRunning test_delayed_write_update5b\n");
2552 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2554 torture_comment(tctx, "Open the file handle\n");
2555 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2556 if (fnum1 == -1) {
2557 ret = false;
2558 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2559 goto done;
2562 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2563 finfo0.basic_info.in.file.fnum = fnum1;
2564 finfo1 = finfo0;
2565 finfo2 = finfo0;
2566 finfo3 = finfo0;
2567 finfo4 = finfo0;
2568 finfo5 = finfo0;
2569 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2570 pinfo0.basic_info.in.file.path = fname;
2571 pinfo1 = pinfo0;
2572 pinfo2 = pinfo0;
2573 pinfo3 = pinfo0;
2574 pinfo4 = pinfo0;
2575 pinfo5 = pinfo0;
2576 pinfo6 = pinfo0;
2578 /* get the initial times */
2579 GET_INFO_BOTH(finfo0,pinfo0);
2581 /* do a write */
2582 torture_comment(tctx, "Do a write on the file handle\n");
2583 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2584 if (written != 1) {
2585 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2586 ret = false;
2587 goto done;
2590 GET_INFO_BOTH(finfo1,pinfo1);
2591 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2593 torture_comment(tctx, "Set write time in the future on the file handle\n");
2594 SET_INFO_FILE(finfo0, time(NULL) + 86400);
2595 GET_INFO_BOTH(finfo2,pinfo2);
2596 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2598 torture_comment(tctx, "Set write time in the past on the file handle\n");
2599 SET_INFO_FILE(finfo0, time(NULL) - 86400);
2600 GET_INFO_BOTH(finfo2,pinfo2);
2601 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2603 /* make sure the 2 second delay from the first write are canceled */
2604 start = timeval_current();
2605 end = timeval_add(&start, 15 * sec, 0);
2606 while (!timeval_expired(&end)) {
2608 /* get the times after the first write */
2609 GET_INFO_BOTH(finfo3,pinfo3);
2611 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2612 double diff = timeval_elapsed(&start);
2613 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2614 "(1sec == %.2f) (wrong!)\n",
2615 diff, sec);
2616 ret = false;
2617 break;
2619 smb_msleep(1 * msec);
2622 GET_INFO_BOTH(finfo3,pinfo3);
2623 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2624 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2625 torture_comment(tctx, "Server did not update write_time (correct)\n");
2628 /* Do any further write (truncates) update the write time ? */
2629 start = timeval_current();
2630 end = timeval_add(&start, 15 * sec, 0);
2631 while (!timeval_expired(&end)) {
2632 /* do a write */
2633 torture_comment(tctx, "Do a truncate write on the file handle\n");
2634 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
2635 if (written != 0) {
2636 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2637 ret = false;
2638 goto done;
2640 /* get the times after the write */
2641 GET_INFO_BOTH(finfo4,pinfo4);
2643 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2644 double diff = timeval_elapsed(&start);
2645 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2646 "(1sec == %.2f) (wrong!)\n",
2647 diff, sec);
2648 ret = false;
2649 break;
2651 smb_msleep(1 * msec);
2654 GET_INFO_BOTH(finfo4,pinfo4);
2655 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2656 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2657 torture_comment(tctx, "Server did not update write_time (correct)\n");
2660 /* sleep */
2661 smb_msleep(5 * msec);
2663 GET_INFO_BOTH(finfo5,pinfo5);
2664 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2667 * the close doesn't update the write time
2669 torture_comment(tctx, "Close the file handle\n");
2670 smbcli_close(cli->tree, fnum1);
2671 fnum1 = -1;
2673 GET_INFO_PATH(pinfo6);
2674 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2676 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2677 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2680 done:
2681 if (fnum1 != -1)
2682 smbcli_close(cli->tree, fnum1);
2683 smbcli_unlink(cli->tree, fname);
2684 smbcli_deltree(cli->tree, BASEDIR);
2686 return ret;
2690 * Open 2 handles on a file. Write one one and then set the
2691 * WRITE TIME explicitly on the other. Ensure the write time
2692 * update is cancelled. Ensure the write time is updated to
2693 * the close time when the non-explicit set handle is closed.
2697 static bool test_delayed_write_update6(struct torture_context *tctx,
2698 struct smbcli_state *cli,
2699 struct smbcli_state *cli2)
2701 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2702 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
2703 const char *fname = BASEDIR "\\torture_file6.txt";
2704 int fnum1 = -1;
2705 int fnum2 = -1;
2706 bool ret = true;
2707 ssize_t written;
2708 struct timeval start;
2709 struct timeval end;
2710 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2711 int normal_delay = 2000000;
2712 double sec = ((double)used_delay) / ((double)normal_delay);
2713 int msec = 1000 * sec;
2714 bool first = true;
2716 torture_comment(tctx, "\nRunning test_delayed_write_update6\n");
2718 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2719 again:
2720 torture_comment(tctx, "Open the file handle\n");
2721 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2722 if (fnum1 == -1) {
2723 ret = false;
2724 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2725 goto done;
2728 if (fnum2 == -1) {
2729 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
2730 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2731 if (fnum2 == -1) {
2732 ret = false;
2733 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2734 goto done;
2738 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2739 finfo0.basic_info.in.file.fnum = fnum1;
2740 finfo1 = finfo0;
2741 finfo2 = finfo0;
2742 finfo3 = finfo0;
2743 finfo4 = finfo0;
2744 finfo5 = finfo0;
2745 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2746 pinfo0.basic_info.in.file.path = fname;
2747 pinfo1 = pinfo0;
2748 pinfo2 = pinfo0;
2749 pinfo3 = pinfo0;
2750 pinfo4 = pinfo0;
2751 pinfo5 = pinfo0;
2752 pinfo6 = pinfo0;
2753 pinfo7 = pinfo0;
2755 /* get the initial times */
2756 GET_INFO_BOTH(finfo0,pinfo0);
2758 /* do a write */
2759 torture_comment(tctx, "Do a write on the file handle\n");
2760 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2761 if (written != 1) {
2762 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2763 ret = false;
2764 goto done;
2767 GET_INFO_BOTH(finfo1,pinfo1);
2768 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2770 torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
2771 SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
2772 GET_INFO_BOTH(finfo2,pinfo2);
2773 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2775 torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
2776 SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
2777 GET_INFO_BOTH(finfo2,pinfo2);
2778 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2780 /* make sure the 2 second delay from the first write are canceled */
2781 start = timeval_current();
2782 end = timeval_add(&start, 10 * sec, 0);
2783 while (!timeval_expired(&end)) {
2785 /* get the times after the first write */
2786 GET_INFO_BOTH(finfo3,pinfo3);
2788 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2789 double diff = timeval_elapsed(&start);
2790 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2791 "(1sec == %.2f) (wrong!)\n",
2792 diff, sec);
2793 ret = false;
2794 break;
2796 smb_msleep(1 * msec);
2799 GET_INFO_BOTH(finfo3,pinfo3);
2800 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2801 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2802 torture_comment(tctx, "Server did not update write_time (correct)\n");
2805 /* sure any further write doesn't update the write time */
2806 start = timeval_current();
2807 end = timeval_add(&start, 10 * sec, 0);
2808 while (!timeval_expired(&end)) {
2809 /* do a write */
2810 torture_comment(tctx, "Do a write on the file handle\n");
2811 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2812 if (written != 1) {
2813 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2814 ret = false;
2815 goto done;
2817 /* get the times after the write */
2818 GET_INFO_BOTH(finfo4,pinfo4);
2820 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2821 double diff = timeval_elapsed(&start);
2822 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2823 "(1sec == %.2f) (wrong!)\n",
2824 diff, sec);
2825 ret = false;
2826 break;
2828 smb_msleep(1 * msec);
2831 GET_INFO_BOTH(finfo4,pinfo4);
2832 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2833 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2834 torture_comment(tctx, "Server did not update write_time (correct)\n");
2837 /* sleep */
2838 smb_msleep(5 * msec);
2840 GET_INFO_BOTH(finfo5,pinfo5);
2841 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2844 * the close updates the write time to the time of the close
2845 * as the write time was set on the 2nd handle
2847 torture_comment(tctx, "Close the file handle\n");
2848 smbcli_close(cli->tree, fnum1);
2849 fnum1 = -1;
2851 GET_INFO_PATH(pinfo6);
2852 COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
2854 if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
2855 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2858 /* See what the second write handle thinks the time is ? */
2859 finfo5.basic_info.in.file.fnum = fnum2;
2860 GET_INFO_FILE2(finfo5);
2861 COMPARE_WRITE_TIME_EQUAL(finfo5, pinfo6);
2863 /* See if we have lost the sticky write time on handle2 */
2864 smb_msleep(3 * msec);
2865 torture_comment(tctx, "Have we lost the sticky write time ?\n");
2867 /* Make sure any further normal write doesn't update the write time */
2868 start = timeval_current();
2869 end = timeval_add(&start, 10 * sec, 0);
2870 while (!timeval_expired(&end)) {
2871 /* do a write */
2872 torture_comment(tctx, "Do a write on the second file handle\n");
2873 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
2874 if (written != 1) {
2875 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2876 ret = false;
2877 goto done;
2879 /* get the times after the write */
2880 GET_INFO_FILE2(finfo5);
2881 GET_INFO_PATH(pinfo6);
2883 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2884 double diff = timeval_elapsed(&start);
2885 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2886 "(1sec == %.2f) (wrong!)\n",
2887 diff, sec);
2888 ret = false;
2889 break;
2891 smb_msleep(1 * msec);
2894 /* What about a truncate write ? */
2895 start = timeval_current();
2896 end = timeval_add(&start, 10 * sec, 0);
2897 while (!timeval_expired(&end)) {
2898 /* do a write */
2899 torture_comment(tctx, "Do a truncate write on the second file handle\n");
2900 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 0);
2901 if (written != 0) {
2902 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2903 ret = false;
2904 goto done;
2906 /* get the times after the write */
2907 GET_INFO_FILE2(finfo5);
2908 GET_INFO_PATH(pinfo6);
2910 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2911 double diff = timeval_elapsed(&start);
2912 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2913 "(1sec == %.2f) (wrong!)\n",
2914 diff, sec);
2915 ret = false;
2916 break;
2918 smb_msleep(1 * msec);
2922 /* keep the 2nd handle open and rerun tests */
2923 if (first) {
2924 first = false;
2925 goto again;
2929 * closing the 2nd handle will cause no write time update
2930 * as the write time was explicit set on this handle
2932 torture_comment(tctx, "Close the 2nd file handle\n");
2933 smbcli_close(cli2->tree, fnum2);
2934 fnum2 = -1;
2936 GET_INFO_PATH(pinfo7);
2937 COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
2939 if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
2940 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2943 done:
2944 if (fnum1 != -1)
2945 smbcli_close(cli->tree, fnum1);
2946 if (fnum2 != -1)
2947 smbcli_close(cli2->tree, fnum2);
2948 smbcli_unlink(cli->tree, fname);
2949 smbcli_deltree(cli->tree, BASEDIR);
2951 return ret;
2954 static bool test_delayed_write_update7(struct torture_context *tctx, struct smbcli_state *cli)
2956 union smb_open open_parms;
2957 union smb_fileinfo finfo1, finfo2, finfo3;
2958 const char *fname = BASEDIR "\\torture_file7.txt";
2959 NTSTATUS status;
2960 int fnum1 = -1;
2961 bool ret = true;
2962 TALLOC_CTX *mem_ctx;
2964 torture_comment(tctx, "\nRunning test_delayed_write_update7 (timestamp resolution test)\n");
2966 mem_ctx = talloc_init("test_delayed_write_update7");
2967 if (!mem_ctx) return false;
2969 ZERO_STRUCT(finfo1);
2970 ZERO_STRUCT(finfo2);
2971 ZERO_STRUCT(finfo3);
2972 ZERO_STRUCT(open_parms);
2974 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2976 /* Create the file. */
2977 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2978 if (fnum1 == -1) {
2979 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
2980 return false;
2983 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2984 finfo1.basic_info.in.file.fnum = fnum1;
2985 finfo2 = finfo1;
2986 finfo3 = finfo1;
2988 /* Get the initial timestamps. */
2989 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
2991 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
2993 /* Set the pending write time to a value with ns. */
2994 SET_INFO_FILE_NS(finfo, time(NULL) + 86400, 103, cli->tree, fnum1);
2996 /* Get the current pending write time by fnum. */
2997 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
2999 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
3001 /* Ensure the time is actually different. */
3002 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
3003 torture_result(tctx, TORTURE_FAIL,
3004 "setfileinfo time matches original fileinfo time");
3005 ret = false;
3008 /* Get the current pending write time by path. */
3009 finfo3.basic_info.in.file.path = fname;
3010 status = smb_raw_pathinfo(cli->tree, tctx, &finfo3);
3012 if (finfo2.basic_info.out.write_time != finfo3.basic_info.out.write_time) {
3013 torture_result(tctx, TORTURE_FAIL,
3014 "qpathinfo time doens't match fileinfo time");
3015 ret = false;
3018 /* Now close the file. Re-open and check that the write
3019 time is identical to the one we wrote. */
3021 smbcli_close(cli->tree, fnum1);
3023 open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
3024 open_parms.ntcreatex.in.flags = 0;
3025 open_parms.ntcreatex.in.access_mask = SEC_GENERIC_READ;
3026 open_parms.ntcreatex.in.file_attr = 0;
3027 open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE|
3028 NTCREATEX_SHARE_ACCESS_READ|
3029 NTCREATEX_SHARE_ACCESS_WRITE;
3030 open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
3031 open_parms.ntcreatex.in.create_options = 0;
3032 open_parms.ntcreatex.in.fname = fname;
3034 status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
3035 talloc_free(mem_ctx);
3037 if (!NT_STATUS_IS_OK(status)) {
3038 torture_result(tctx, TORTURE_FAIL,
3039 "setfileinfo time matches original fileinfo time");
3040 ret = false;
3043 fnum1 = open_parms.ntcreatex.out.file.fnum;
3045 /* Check the returned time matches. */
3046 if (open_parms.ntcreatex.out.write_time != finfo2.basic_info.out.write_time) {
3047 torture_result(tctx, TORTURE_FAIL,
3048 "final open time does not match set time");
3049 ret = false;
3052 done:
3054 smbcli_close(cli->tree, fnum1);
3056 smbcli_unlink(cli->tree, fname);
3057 smbcli_deltree(cli->tree, BASEDIR);
3058 return ret;
3062 testing of delayed update of write_time
3064 struct torture_suite *torture_delay_write(void)
3066 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "delaywrite");
3068 torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
3069 torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
3070 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate", test_delayed_write_update1);
3071 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a);
3072 torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b);
3073 torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c);
3074 torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
3075 torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
3076 torture_suite_add_2smb_test(suite, "delayed update of write time 3a", test_delayed_write_update3a);
3077 torture_suite_add_2smb_test(suite, "delayed update of write time 3b", test_delayed_write_update3b);
3078 torture_suite_add_2smb_test(suite, "delayed update of write time 3c", test_delayed_write_update3c);
3079 torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
3080 torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
3081 torture_suite_add_2smb_test(suite, "delayed update of write time 5b", test_delayed_write_update5b);
3082 torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
3083 torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7);
3084 torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7);
3086 return suite;