s3: don't replace the error message if already defined
[Samba/gebeck_regimport.git] / source4 / torture / basic / delaywrite.c
blob939827ec3ca20f20aabffb2ab3b7618eee99a2b2
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 if (!torture_setup_dir(cli, BASEDIR)) {
58 return false;
61 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
62 if (fnum1 == -1) {
63 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
64 return false;
67 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
68 finfo1.basic_info.in.file.fnum = fnum1;
69 finfo2 = finfo1;
71 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
73 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
75 torture_comment(tctx, "Initial write time %s\n",
76 nt_time_string(tctx, finfo1.basic_info.out.write_time));
78 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
80 if (written != 1) {
81 torture_result(tctx, TORTURE_FAIL,
82 "write failed - wrote %d bytes (%s)\n",
83 (int)written, __location__);
84 return false;
87 start = timeval_current();
88 end = timeval_add(&start, (120*sec), 0);
89 while (!timeval_expired(&end)) {
90 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
92 if (!NT_STATUS_IS_OK(status)) {
93 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
94 ret = false;
95 break;
97 torture_comment(tctx, "write time %s\n",
98 nt_time_string(tctx, finfo2.basic_info.out.write_time));
99 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
100 double diff = timeval_elapsed(&start);
101 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
102 torture_comment(tctx, "Server updated write_time after %.2f seconds"
103 "(1 sec == %.2f)(wrong!)\n",
104 diff, sec);
105 ret = false;
106 break;
109 torture_comment(tctx, "Server updated write_time after %.2f seconds"
110 "(1 sec == %.2f)(correct)\n",
111 diff, sec);
112 break;
114 fflush(stdout);
115 smb_msleep(1 * msec);
118 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
119 torture_result(tctx, TORTURE_FAIL,
120 "Server did not update write time (wrong!)");
121 ret = false;
125 if (fnum1 != -1)
126 smbcli_close(cli->tree, fnum1);
127 smbcli_unlink(cli->tree, fname);
128 smbcli_deltree(cli->tree, BASEDIR);
130 return ret;
133 static bool test_delayed_write_update1(struct torture_context *tctx, struct smbcli_state *cli)
135 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
136 const char *fname = BASEDIR "\\torture_file1.txt";
137 NTSTATUS status;
138 int fnum1 = -1;
139 bool ret = true;
140 ssize_t written;
141 struct timeval start;
142 struct timeval end;
143 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
144 int normal_delay = 2000000;
145 double sec = ((double)used_delay) / ((double)normal_delay);
146 int msec = 1000 * sec;
147 char buf[2048];
149 torture_comment(tctx, "\nRunning test_delayed_write_update1\n");
151 if (!torture_setup_dir(cli, BASEDIR)) {
152 return false;
155 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
156 if (fnum1 == -1) {
157 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
158 return false;
161 memset(buf, 'x', 2048);
162 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
164 /* 3 second delay to ensure we get past any 2 second time
165 granularity (older systems may have that) */
166 smb_msleep(3 * msec);
168 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
169 finfo1.all_info.in.file.fnum = fnum1;
170 finfo2 = finfo1;
171 finfo3 = finfo1;
172 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
173 pinfo4.all_info.in.file.path = fname;
175 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
177 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
179 torture_comment(tctx, "Initial write time %s\n",
180 nt_time_string(tctx, finfo1.all_info.out.write_time));
182 /* 3 second delay to ensure we get past any 2 second time
183 granularity (older systems may have that) */
184 smb_msleep(3 * msec);
186 /* Do a zero length SMBwrite call to truncate. */
187 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
189 if (written != 0) {
190 torture_result(tctx, TORTURE_FAIL,
191 "write failed - wrote %d bytes (%s)\n",
192 (int)written, __location__);
193 return false;
196 start = timeval_current();
197 end = timeval_add(&start, (120*sec), 0);
198 while (!timeval_expired(&end)) {
199 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
201 if (!NT_STATUS_IS_OK(status)) {
202 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
203 ret = false;
204 break;
207 if (finfo2.all_info.out.size != 1024) {
208 torture_result(tctx, TORTURE_FAIL,
209 "file not truncated, size = %u (should be 1024)",
210 (unsigned int)finfo2.all_info.out.size);
211 ret = false;
212 break;
215 torture_comment(tctx, "write time %s\n",
216 nt_time_string(tctx, finfo2.all_info.out.write_time));
217 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
218 double diff = timeval_elapsed(&start);
219 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
220 torture_comment(tctx, "After SMBwrite truncate "
221 "server updated write_time after %.2f seconds"
222 "(1 sec == %.2f)(wrong!)\n",
223 diff, sec);
224 ret = false;
225 break;
228 torture_comment(tctx, "After SMBwrite truncate "
229 "server updated write_time after %.2f seconds"
230 "(1 sec == %.2f)(correct)\n",
231 diff, sec);
232 break;
234 fflush(stdout);
235 smb_msleep(1 * msec);
238 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
239 torture_result(tctx, TORTURE_FAIL,
240 "Server did not update write time (wrong!)");
241 ret = false;
244 fflush(stdout);
245 smb_msleep(2 * msec);
247 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
248 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
250 if (written != 1) {
251 torture_result(tctx, TORTURE_FAIL,
252 "write failed - wrote %d bytes (%s)",
253 (int)written, __location__);
254 return false;
257 start = timeval_current();
258 end = timeval_add(&start, (10*sec), 0);
259 while (!timeval_expired(&end)) {
260 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
262 if (!NT_STATUS_IS_OK(status)) {
263 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
264 ret = false;
265 break;
268 if (finfo3.all_info.out.size != 1024) {
269 DEBUG(0, ("file not truncated, size = %u (should be 1024)\n",
270 (unsigned int)finfo3.all_info.out.size));
271 ret = false;
272 break;
275 torture_comment(tctx, "write time %s\n",
276 nt_time_string(tctx, finfo3.all_info.out.write_time));
277 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
278 double diff = timeval_elapsed(&start);
280 torture_comment(tctx, "server updated write_time after %.2f seconds"
281 "(1 sec == %.2f)(wrong)\n",
282 diff, sec);
283 break;
285 fflush(stdout);
286 smb_msleep(1 * msec);
289 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
290 torture_result(tctx, TORTURE_FAIL,
291 "Server updated write time (wrong!)");
292 ret = false;
295 fflush(stdout);
296 smb_msleep(2 * msec);
298 /* the close should trigger an write time update */
299 smbcli_close(cli->tree, fnum1);
300 fnum1 = -1;
302 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
303 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
305 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
306 torture_result(tctx, TORTURE_FAIL,
307 "Server did not update write time on close (wrong!)");
308 ret = false;
309 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
310 torture_comment(tctx, "Server updated write time on close (correct)\n");
313 if (fnum1 != -1)
314 smbcli_close(cli->tree, fnum1);
315 smbcli_unlink(cli->tree, fname);
316 smbcli_deltree(cli->tree, BASEDIR);
318 return ret;
321 /* Updating with a SMBwrite of zero length
322 * changes the write time immediately - even on expand. */
324 static bool test_delayed_write_update1a(struct torture_context *tctx, struct smbcli_state *cli)
326 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
327 const char *fname = BASEDIR "\\torture_file1a.txt";
328 NTSTATUS status;
329 int fnum1 = -1;
330 bool ret = true;
331 ssize_t written;
332 struct timeval start;
333 struct timeval end;
334 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
335 int normal_delay = 2000000;
336 double sec = ((double)used_delay) / ((double)normal_delay);
337 int msec = 1000 * sec;
338 char buf[2048];
340 torture_comment(tctx, "\nRunning test_delayed_write_update1a\n");
342 if (!torture_setup_dir(cli, BASEDIR)) {
343 return false;
346 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
347 if (fnum1 == -1) {
348 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
349 return false;
352 memset(buf, 'x', 2048);
353 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
355 /* 3 second delay to ensure we get past any 2 second time
356 granularity (older systems may have that) */
357 smb_msleep(3 * msec);
359 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
360 finfo1.all_info.in.file.fnum = fnum1;
361 finfo2 = finfo1;
362 finfo3 = finfo1;
363 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
364 pinfo4.all_info.in.file.path = fname;
366 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
368 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
370 torture_comment(tctx, "Initial write time %s\n",
371 nt_time_string(tctx, finfo1.all_info.out.write_time));
373 /* Do a zero length SMBwrite call to truncate. */
374 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
376 if (written != 0) {
377 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)",
378 (int)written, __location__);
379 return false;
382 start = timeval_current();
383 end = timeval_add(&start, (120*sec), 0);
384 while (!timeval_expired(&end)) {
385 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
387 if (!NT_STATUS_IS_OK(status)) {
388 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s",
389 nt_errstr(status));
390 ret = false;
391 break;
394 if (finfo2.all_info.out.size != 10240) {
395 torture_result(tctx, TORTURE_FAIL,
396 "file not truncated, size = %u (should be 10240)",
397 (unsigned int)finfo2.all_info.out.size);
398 ret = false;
399 break;
402 torture_comment(tctx, "write time %s\n",
403 nt_time_string(tctx, finfo2.all_info.out.write_time));
404 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
405 double diff = timeval_elapsed(&start);
406 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
407 torture_comment(tctx, "After SMBwrite truncate "
408 "server updated write_time after %.2f seconds"
409 "(1 sec == %.2f)(wrong!)\n",
410 diff, sec);
411 ret = false;
412 break;
415 torture_comment(tctx, "After SMBwrite truncate "
416 "server updated write_time after %.2f seconds"
417 "(1 sec == %.2f)(correct)\n",
418 diff, sec);
419 break;
421 fflush(stdout);
422 smb_msleep(1 * msec);
425 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
426 torture_result(tctx, TORTURE_FAIL,
427 "Server did not update write time (wrong!)");
428 ret = false;
431 fflush(stdout);
432 smb_msleep(2 * msec);
434 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
435 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
437 torture_assert_int_equal(tctx, written, 1,
438 "unexpected number of bytes written");
440 start = timeval_current();
441 end = timeval_add(&start, (10*sec), 0);
442 while (!timeval_expired(&end)) {
443 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
445 if (!NT_STATUS_IS_OK(status)) {
446 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s\n",
447 nt_errstr(status));
448 ret = false;
449 break;
452 if (finfo3.all_info.out.size != 10240) {
453 torture_result(tctx, TORTURE_FAIL,
454 "file not truncated, size = %u (should be 10240)",
455 (unsigned int)finfo3.all_info.out.size);
456 ret = false;
457 break;
460 torture_comment(tctx, "write time %s\n",
461 nt_time_string(tctx, finfo3.all_info.out.write_time));
462 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
463 double diff = timeval_elapsed(&start);
465 torture_comment(tctx, "server updated write_time after %.2f seconds"
466 "(1 sec == %.2f)(correct)\n",
467 diff, sec);
468 break;
470 fflush(stdout);
471 smb_msleep(1 * msec);
474 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
475 torture_result(tctx, TORTURE_FAIL,
476 "Server updated write time (wrong!)");
477 ret = false;
480 /* the close should trigger an write time update */
481 smbcli_close(cli->tree, fnum1);
482 fnum1 = -1;
484 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
485 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
487 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
488 torture_result(tctx, TORTURE_FAIL,
489 "Server did not update write time on close (wrong!)");
490 ret = false;
491 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
492 torture_comment(tctx, "Server updated write time on close (correct)\n");
495 if (fnum1 != -1)
496 smbcli_close(cli->tree, fnum1);
497 smbcli_unlink(cli->tree, fname);
498 smbcli_deltree(cli->tree, BASEDIR);
500 return ret;
503 /* Updating with a SET_FILE_END_OF_FILE_INFO
504 * changes the write time immediately - even on expand. */
506 static bool test_delayed_write_update1b(struct torture_context *tctx, struct smbcli_state *cli)
508 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
509 const char *fname = BASEDIR "\\torture_file1b.txt";
510 NTSTATUS status;
511 int fnum1 = -1;
512 bool ret = true;
513 ssize_t written;
514 struct timeval start;
515 struct timeval end;
516 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
517 int normal_delay = 2000000;
518 double sec = ((double)used_delay) / ((double)normal_delay);
519 int msec = 1000 * sec;
520 char buf[2048];
522 torture_comment(tctx, "\nRunning test_delayed_write_update1b\n");
524 if (!torture_setup_dir(cli, BASEDIR)) {
525 return false;
528 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
529 if (fnum1 == -1) {
530 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
531 return false;
534 memset(buf, 'x', 2048);
535 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
537 /* 3 second delay to ensure we get past any 2 second time
538 granularity (older systems may have that) */
539 smb_msleep(3 * msec);
541 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
542 finfo1.all_info.in.file.fnum = fnum1;
543 finfo2 = finfo1;
544 finfo3 = finfo1;
545 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
546 pinfo4.all_info.in.file.path = fname;
548 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
550 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
552 torture_comment(tctx, "Initial write time %s\n",
553 nt_time_string(tctx, finfo1.all_info.out.write_time));
555 /* Do a SET_END_OF_FILE_INFO call to truncate. */
556 status = smbcli_ftruncate(cli->tree, fnum1, (uint64_t)10240);
558 torture_assert_ntstatus_ok(tctx, status, "SET_END_OF_FILE failed");
560 start = timeval_current();
561 end = timeval_add(&start, (120*sec), 0);
562 while (!timeval_expired(&end)) {
563 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
565 if (!NT_STATUS_IS_OK(status)) {
566 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
567 ret = false;
568 break;
571 if (finfo2.all_info.out.size != 10240) {
572 torture_result(tctx, TORTURE_FAIL,
573 "file not truncated (size = %u, should be 10240)",
574 (unsigned int)finfo2.all_info.out.size );
575 ret = false;
576 break;
579 torture_comment(tctx, "write time %s\n",
580 nt_time_string(tctx, finfo2.all_info.out.write_time));
581 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
582 double diff = timeval_elapsed(&start);
583 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
584 torture_result(tctx, TORTURE_FAIL,
585 "After SET_END_OF_FILE truncate "
586 "server updated write_time after %.2f seconds"
587 "(1 sec == %.2f)(wrong!)",
588 diff, sec);
589 ret = false;
590 break;
593 torture_comment(tctx, "After SET_END_OF_FILE truncate "
594 "server updated write_time after %.2f seconds"
595 "(1 sec == %.2f)(correct)\n",
596 diff, sec);
597 break;
599 fflush(stdout);
600 smb_msleep(1 * msec);
603 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
604 torture_result(tctx, TORTURE_FAIL,
605 "Server did not update write time (wrong!)");
606 ret = false;
609 fflush(stdout);
610 smb_msleep(2 * msec);
612 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
613 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
615 torture_assert_int_equal(tctx, written, 1,
616 "unexpected number of bytes written");
618 start = timeval_current();
619 end = timeval_add(&start, (10*sec), 0);
620 while (!timeval_expired(&end)) {
621 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
623 if (!NT_STATUS_IS_OK(status)) {
624 torture_result(tctx, TORTURE_FAIL,
625 "fileinfo failed: %s", nt_errstr(status));
626 ret = false;
627 break;
630 if (finfo3.all_info.out.size != 10240) {
631 DEBUG(0, ("file not truncated (size = %u, should be 10240)\n",
632 (unsigned int)finfo3.all_info.out.size ));
633 ret = false;
634 break;
637 torture_comment(tctx, "write time %s\n",
638 nt_time_string(tctx, finfo3.all_info.out.write_time));
639 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
640 double diff = timeval_elapsed(&start);
642 torture_comment(tctx, "server updated write_time after %.2f seconds"
643 "(1 sec == %.2f)(correct)\n",
644 diff, sec);
645 break;
647 fflush(stdout);
648 smb_msleep(1 * msec);
651 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
652 torture_result(tctx, TORTURE_FAIL, "Server updated write time (wrong!)\n");
653 ret = false;
656 /* the close should trigger an write time update */
657 smbcli_close(cli->tree, fnum1);
658 fnum1 = -1;
660 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
661 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
663 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
664 torture_result(tctx, TORTURE_FAIL, "Server did not update write time on close (wrong!)\n");
665 ret = false;
666 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
667 torture_comment(tctx, "Server updated write time on close (correct)\n");
670 if (fnum1 != -1)
671 smbcli_close(cli->tree, fnum1);
672 smbcli_unlink(cli->tree, fname);
673 smbcli_deltree(cli->tree, BASEDIR);
675 return ret;
678 /* Updating with a SET_ALLOCATION_INFO (truncate) does so immediately. */
680 static bool test_delayed_write_update1c(struct torture_context *tctx, struct smbcli_state *cli)
682 union smb_setfileinfo parms;
683 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
684 const char *fname = BASEDIR "\\torture_file1c.txt";
685 NTSTATUS status;
686 int fnum1 = -1;
687 bool ret = true;
688 ssize_t written;
689 struct timeval start;
690 struct timeval end;
691 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
692 int normal_delay = 2000000;
693 double sec = ((double)used_delay) / ((double)normal_delay);
694 int msec = 1000 * sec;
695 char buf[2048];
697 torture_comment(tctx, "\nRunning test_delayed_write_update1c\n");
699 if (!torture_setup_dir(cli, BASEDIR)) {
700 return false;
703 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
704 if (fnum1 == -1) {
705 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
706 return false;
709 memset(buf, 'x', 2048);
710 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
712 /* 3 second delay to ensure we get past any 2 second time
713 granularity (older systems may have that) */
714 smb_msleep(3 * msec);
716 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
717 finfo1.all_info.in.file.fnum = fnum1;
718 finfo2 = finfo1;
719 finfo3 = finfo1;
720 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
721 pinfo4.all_info.in.file.path = fname;
723 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
725 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
727 torture_comment(tctx, "Initial write time %s\n",
728 nt_time_string(tctx, finfo1.all_info.out.write_time));
730 /* Do a SET_ALLOCATION_SIZE call to truncate. */
731 parms.allocation_info.level = RAW_SFILEINFO_ALLOCATION_INFO;
732 parms.allocation_info.in.file.fnum = fnum1;
733 parms.allocation_info.in.alloc_size = 0;
735 status = smb_raw_setfileinfo(cli->tree, &parms);
737 torture_assert_ntstatus_ok(tctx, status,
738 "RAW_SFILEINFO_ALLOCATION_INFO failed");
740 start = timeval_current();
741 end = timeval_add(&start, (120*sec), 0);
742 while (!timeval_expired(&end)) {
743 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
745 if (!NT_STATUS_IS_OK(status)) {
746 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s",
747 nt_errstr(status));
748 ret = false;
749 break;
752 if (finfo2.all_info.out.size != 0) {
753 torture_result(tctx, TORTURE_FAIL,
754 "file not truncated (size = %u, should be 10240)",
755 (unsigned int)finfo2.all_info.out.size);
756 ret = false;
757 break;
760 torture_comment(tctx, "write time %s\n",
761 nt_time_string(tctx, finfo2.all_info.out.write_time));
762 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
763 double diff = timeval_elapsed(&start);
764 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
765 torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
766 "server updated write_time after %.2f seconds"
767 "(1 sec == %.2f)(wrong!)\n",
768 diff, sec);
769 ret = false;
770 break;
773 torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
774 "server updated write_time after %.2f seconds"
775 "(1 sec == %.2f)(correct)\n",
776 diff, sec);
777 break;
779 fflush(stdout);
780 smb_msleep(1 * msec);
783 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
784 torture_result(tctx, TORTURE_FAIL,
785 "Server did not update write time (wrong!)");
786 ret = false;
789 fflush(stdout);
790 smb_msleep(2 * msec);
792 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
793 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
794 torture_assert_int_equal(tctx, written, 1,
795 "Unexpected number of bytes written");
797 start = timeval_current();
798 end = timeval_add(&start, (10*sec), 0);
799 while (!timeval_expired(&end)) {
800 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
802 if (!NT_STATUS_IS_OK(status)) {
803 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s",
804 nt_errstr(status));
805 ret = false;
806 break;
809 if (finfo3.all_info.out.size != 1) {
810 torture_result(tctx, TORTURE_FAIL, "file not expanded");
811 ret = false;
812 break;
815 torture_comment(tctx, "write time %s\n",
816 nt_time_string(tctx, finfo3.all_info.out.write_time));
817 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
818 double diff = timeval_elapsed(&start);
820 torture_comment(tctx, "server updated write_time after %.2f seconds"
821 "(1 sec == %.2f)(correct)\n",
822 diff, sec);
823 break;
825 fflush(stdout);
826 smb_msleep(1 * msec);
829 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
830 torture_result(tctx, TORTURE_FAIL,
831 "Server updated write time (wrong!)");
832 ret = false;
835 /* the close should trigger an write time update */
836 smbcli_close(cli->tree, fnum1);
837 fnum1 = -1;
839 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
840 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
842 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
843 torture_result(tctx, TORTURE_FAIL, "Server did not update write time on close (wrong!)\n");
844 ret = false;
845 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
846 torture_comment(tctx, "Server updated write time on close (correct)\n");
849 if (fnum1 != -1)
850 smbcli_close(cli->tree, fnum1);
851 smbcli_unlink(cli->tree, fname);
852 smbcli_deltree(cli->tree, BASEDIR);
854 return ret;
858 * Do as above, but using 2 connections.
861 static bool test_delayed_write_update2(struct torture_context *tctx, struct smbcli_state *cli,
862 struct smbcli_state *cli2)
864 union smb_fileinfo finfo1, finfo2;
865 const char *fname = BASEDIR "\\torture_file.txt";
866 NTSTATUS status;
867 int fnum1 = -1;
868 int fnum2 = -1;
869 bool ret = true;
870 ssize_t written;
871 struct timeval start;
872 struct timeval end;
873 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
874 int normal_delay = 2000000;
875 double sec = ((double)used_delay) / ((double)normal_delay);
876 int msec = 1000 * sec;
877 union smb_flush flsh;
879 torture_comment(tctx, "\nRunning test_delayed_write_update2\n");
881 if (!torture_setup_dir(cli, BASEDIR)) {
882 return false;
885 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
886 if (fnum1 == -1) {
887 torture_comment(tctx, "Failed to open %s\n", fname);
888 return false;
891 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
892 finfo1.basic_info.in.file.fnum = fnum1;
893 finfo2 = finfo1;
895 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
897 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
899 torture_comment(tctx, "Initial write time %s\n",
900 nt_time_string(tctx, finfo1.basic_info.out.write_time));
902 /* 3 second delay to ensure we get past any 2 second time
903 granularity (older systems may have that) */
904 smb_msleep(3 * msec);
907 /* Try using setfileinfo instead of write to update write time. */
908 union smb_setfileinfo sfinfo;
909 time_t t_set = time(NULL);
910 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
911 sfinfo.basic_info.in.file.fnum = fnum1;
912 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
913 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
915 /* I tried this with both + and - ve to see if it makes a different.
916 It doesn't - once the filetime is set via setfileinfo it stays that way. */
917 #if 1
918 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
919 #else
920 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
921 #endif
922 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
923 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
925 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
927 torture_assert_ntstatus_ok(tctx, status, "sfileinfo failed");
930 finfo2.basic_info.in.file.path = fname;
932 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
934 if (!NT_STATUS_IS_OK(status)) {
935 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
936 return false;
938 torture_comment(tctx, "write time %s\n",
939 nt_time_string(tctx, finfo2.basic_info.out.write_time));
941 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
942 torture_comment(tctx, "Server updated write_time (correct)\n");
943 } else {
944 torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n");
945 ret = false;
948 /* Now try a write to see if the write time gets reset. */
950 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
951 finfo1.basic_info.in.file.fnum = fnum1;
952 finfo2 = finfo1;
954 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
956 if (!NT_STATUS_IS_OK(status)) {
957 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
958 return false;
961 torture_comment(tctx, "Modified write time %s\n",
962 nt_time_string(tctx, finfo1.basic_info.out.write_time));
965 torture_comment(tctx, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
967 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
969 if (written != 10) {
970 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
971 (int)written, __location__);
972 return false;
975 /* Just to prove to tridge that the an smbflush has no effect on
976 the write time :-). The setfileinfo IS STICKY. JRA. */
978 torture_comment(tctx, "Doing flush after write\n");
980 flsh.flush.level = RAW_FLUSH_FLUSH;
981 flsh.flush.in.file.fnum = fnum1;
982 status = smb_raw_flush(cli->tree, &flsh);
983 if (!NT_STATUS_IS_OK(status)) {
984 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
985 return false;
988 /* Once the time was set using setfileinfo then it stays set - writes
989 don't have any effect. But make sure. */
990 start = timeval_current();
991 end = timeval_add(&start, (15*sec), 0);
992 while (!timeval_expired(&end)) {
993 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
995 if (!NT_STATUS_IS_OK(status)) {
996 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
997 ret = false;
998 break;
1000 torture_comment(tctx, "write time %s\n",
1001 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1002 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1003 double diff = timeval_elapsed(&start);
1004 torture_comment(tctx, "Server updated write_time after %.2f seconds"
1005 "(1sec == %.2f) (wrong!)\n",
1006 diff, sec);
1007 ret = false;
1008 break;
1010 fflush(stdout);
1011 smb_msleep(1 * msec);
1014 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1015 torture_comment(tctx, "Server did not update write time (correct)\n");
1018 fflush(stdout);
1019 smb_msleep(2 * msec);
1021 fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
1022 if (fnum2 == -1) {
1023 torture_comment(tctx, "Failed to open %s\n", fname);
1024 return false;
1027 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");
1029 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
1031 if (written != 10) {
1032 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
1033 (int)written, __location__);
1034 return false;
1037 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1039 if (!NT_STATUS_IS_OK(status)) {
1040 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1041 return false;
1043 torture_comment(tctx, "write time %s\n",
1044 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1045 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1046 torture_comment(tctx, "Server updated write_time (wrong!)\n");
1047 ret = false;
1050 torture_comment(tctx, "Closing the first fd to see if write time updated.\n");
1051 smbcli_close(cli->tree, fnum1);
1052 fnum1 = -1;
1054 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");
1056 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
1058 if (written != 10) {
1059 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
1060 (int)written, __location__);
1061 return false;
1064 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1065 finfo1.basic_info.in.file.fnum = fnum2;
1066 finfo2 = finfo1;
1067 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1069 if (!NT_STATUS_IS_OK(status)) {
1070 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1071 return false;
1073 torture_comment(tctx, "write time %s\n",
1074 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1075 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1076 torture_comment(tctx, "Server updated write_time (wrong!)\n");
1077 ret = false;
1080 /* Once the time was set using setfileinfo then it stays set - writes
1081 don't have any effect. But make sure. */
1082 start = timeval_current();
1083 end = timeval_add(&start, (15*sec), 0);
1084 while (!timeval_expired(&end)) {
1085 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1087 if (!NT_STATUS_IS_OK(status)) {
1088 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1089 ret = false;
1090 break;
1092 torture_comment(tctx, "write time %s\n",
1093 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1094 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1095 double diff = timeval_elapsed(&start);
1096 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1097 "(1sec == %.2f) (wrong!)\n",
1098 diff, sec);
1099 ret = false;
1100 break;
1102 fflush(stdout);
1103 smb_msleep(1 * msec);
1106 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1107 torture_comment(tctx, "Server did not update write time (correct)\n");
1110 torture_comment(tctx, "Closing second fd to see if write time updated.\n");
1112 smbcli_close(cli->tree, fnum2);
1113 fnum2 = -1;
1115 fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
1116 if (fnum1 == -1) {
1117 torture_comment(tctx, "Failed to open %s\n", fname);
1118 return false;
1121 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1122 finfo1.basic_info.in.file.fnum = fnum1;
1123 finfo2 = finfo1;
1125 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1127 if (!NT_STATUS_IS_OK(status)) {
1128 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1129 return false;
1132 torture_comment(tctx, "Second open initial write time %s\n",
1133 nt_time_string(tctx, finfo1.basic_info.out.write_time));
1135 smb_msleep(10 * msec);
1136 torture_comment(tctx, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
1138 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
1140 if (written != 10) {
1141 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
1142 (int)written, __location__);
1143 return false;
1146 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1147 finfo1.basic_info.in.file.fnum = fnum1;
1148 finfo2 = finfo1;
1149 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1151 if (!NT_STATUS_IS_OK(status)) {
1152 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1153 return false;
1155 torture_comment(tctx, "write time %s\n",
1156 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1157 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1158 torture_comment(tctx, "Server updated write_time (wrong!)\n");
1159 ret = false;
1162 /* Now the write time should be updated again */
1163 start = timeval_current();
1164 end = timeval_add(&start, (15*sec), 0);
1165 while (!timeval_expired(&end)) {
1166 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1168 if (!NT_STATUS_IS_OK(status)) {
1169 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1170 ret = false;
1171 break;
1173 torture_comment(tctx, "write time %s\n",
1174 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1175 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1176 double diff = timeval_elapsed(&start);
1177 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
1178 torture_comment(tctx, "Server updated write_time after %.2f seconds"
1179 "(1sec == %.2f) (wrong!)\n",
1180 diff, sec);
1181 ret = false;
1182 break;
1185 torture_comment(tctx, "Server updated write_time after %.2f seconds"
1186 "(1sec == %.2f) (correct)\n",
1187 diff, sec);
1188 break;
1190 fflush(stdout);
1191 smb_msleep(1*msec);
1194 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1195 torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n");
1196 ret = false;
1200 /* One more test to do. We should read the filetime via findfirst on the
1201 second connection to ensure it's the same. This is very easy for a Windows
1202 server but a bastard to get right on a POSIX server. JRA. */
1204 if (fnum1 != -1)
1205 smbcli_close(cli->tree, fnum1);
1206 smbcli_unlink(cli->tree, fname);
1207 smbcli_deltree(cli->tree, BASEDIR);
1209 return ret;
1213 /* Windows does obviously not update the stat info during a write call. I
1214 * *think* this is the problem causing a spurious Excel 2003 on XP error
1215 * message when saving a file. Excel does a setfileinfo, writes, and then does
1216 * a getpath(!)info. Or so... For Samba sometimes it displays an error message
1217 * that the file might have been changed in between. What i've been able to
1218 * trace down is that this happens if the getpathinfo after the write shows a
1219 * different last write time than the setfileinfo showed. This is really
1220 * nasty....
1223 static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli,
1224 struct smbcli_state *cli2)
1226 union smb_fileinfo finfo1, finfo2;
1227 const char *fname = BASEDIR "\\torture_file.txt";
1228 NTSTATUS status;
1229 int fnum1 = -1;
1230 int fnum2;
1231 bool ret = true;
1232 ssize_t written;
1233 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1234 int normal_delay = 2000000;
1235 double sec = ((double)used_delay) / ((double)normal_delay);
1236 int msec = 1000 * sec;
1238 torture_comment(tctx, "\nRunning test_finfo_after_write\n");
1240 if (!torture_setup_dir(cli, BASEDIR)) {
1241 return false;
1244 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1245 if (fnum1 == -1) {
1246 ret = false;
1247 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1248 goto done;
1251 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1252 finfo1.basic_info.in.file.fnum = fnum1;
1254 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1256 if (!NT_STATUS_IS_OK(status)) {
1257 ret = false;
1258 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1259 goto done;
1262 smb_msleep(1 * msec);
1264 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1266 if (written != 1) {
1267 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1268 ret = false;
1269 goto done;
1272 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
1273 if (fnum2 == -1) {
1274 torture_result(tctx, TORTURE_FAIL, __location__": failed to open 2nd time - %s",
1275 smbcli_errstr(cli2->tree));
1276 ret = false;
1277 goto done;
1280 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
1282 if (written != 1) {
1283 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1",
1284 (int)written);
1285 ret = false;
1286 goto done;
1289 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1290 finfo2.basic_info.in.file.path = fname;
1292 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
1294 if (!NT_STATUS_IS_OK(status)) {
1295 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s",
1296 nt_errstr(status));
1297 ret = false;
1298 goto done;
1301 if (finfo1.basic_info.out.create_time !=
1302 finfo2.basic_info.out.create_time) {
1303 torture_result(tctx, TORTURE_FAIL, __location__": create_time changed");
1304 ret = false;
1305 goto done;
1308 if (finfo1.basic_info.out.access_time !=
1309 finfo2.basic_info.out.access_time) {
1310 torture_result(tctx, TORTURE_FAIL, __location__": access_time changed");
1311 ret = false;
1312 goto done;
1315 if (finfo1.basic_info.out.write_time !=
1316 finfo2.basic_info.out.write_time) {
1317 torture_result(tctx, TORTURE_FAIL, __location__": write_time changed:\n"
1318 "write time conn 1 = %s, conn 2 = %s",
1319 nt_time_string(tctx, finfo1.basic_info.out.write_time),
1320 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1321 ret = false;
1322 goto done;
1325 if (finfo1.basic_info.out.change_time !=
1326 finfo2.basic_info.out.change_time) {
1327 torture_result(tctx, TORTURE_FAIL, __location__": change_time changed");
1328 ret = false;
1329 goto done;
1332 /* One of the two following calls updates the qpathinfo. */
1334 /* If you had skipped the smbcli_write on fnum2, it would
1335 * *not* have updated the stat on disk */
1337 smbcli_close(cli2->tree, fnum2);
1338 cli2 = NULL;
1340 /* This call is only for the people looking at ethereal :-) */
1341 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1342 finfo2.basic_info.in.file.path = fname;
1344 status = smb_raw_pathinfo(cli->tree, tctx, &finfo2);
1346 if (!NT_STATUS_IS_OK(status)) {
1347 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1348 ret = false;
1349 goto done;
1352 done:
1353 if (fnum1 != -1)
1354 smbcli_close(cli->tree, fnum1);
1355 smbcli_unlink(cli->tree, fname);
1356 smbcli_deltree(cli->tree, BASEDIR);
1358 return ret;
1361 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
1362 uint64_t r = 10*1000*1000; \
1363 NTTIME g = (given).basic_info.out.write_time; \
1364 NTTIME gr = (g / r) * r; \
1365 NTTIME c = (correct).basic_info.out.write_time; \
1366 NTTIME cr = (c / r) * r; \
1367 bool strict = torture_setting_bool(tctx, "strict mode", false); \
1368 bool err = false; \
1369 if (strict && (g cmp c)) { \
1370 err = true; \
1371 } else if ((g cmp c) && (gr cmp cr)) { \
1372 /* handle filesystem without high resolution timestamps */ \
1373 err = true; \
1375 if (err) { \
1376 torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \
1377 #given, nt_time_string(tctx, g), (unsigned long long)g, \
1378 #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \
1379 ret = false; \
1380 goto done; \
1382 } while (0)
1383 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
1384 COMPARE_WRITE_TIME_CMP(given,correct,!=)
1385 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
1386 COMPARE_WRITE_TIME_CMP(given,correct,<=)
1387 #define COMPARE_WRITE_TIME_LESS(given,correct) \
1388 COMPARE_WRITE_TIME_CMP(given,correct,>=)
1390 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
1391 NTTIME g = (given).basic_info.out.access_time; \
1392 NTTIME c = (correct).basic_info.out.access_time; \
1393 if (g cmp c) { \
1394 torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \
1395 #given, nt_time_string(tctx, g), \
1396 #cmp, #correct, nt_time_string(tctx, c)); \
1397 ret = false; \
1398 goto done; \
1400 } while (0)
1401 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
1402 COMPARE_ACCESS_TIME_CMP(given,correct,!=)
1404 #define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \
1405 COMPARE_ACCESS_TIME_EQUAL(given,correct); \
1406 COMPARE_WRITE_TIME_EQUAL(given,correct); \
1407 } while (0)
1409 #define GET_INFO_FILE(finfo) do { \
1410 NTSTATUS _status; \
1411 _status = smb_raw_fileinfo(cli->tree, tctx, &finfo); \
1412 if (!NT_STATUS_IS_OK(_status)) { \
1413 ret = false; \
1414 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1415 nt_errstr(_status)); \
1416 goto done; \
1418 torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1419 nt_time_string(tctx, finfo.basic_info.out.access_time), \
1420 nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1421 } while (0)
1422 #define GET_INFO_FILE2(finfo) do { \
1423 NTSTATUS _status; \
1424 _status = smb_raw_fileinfo(cli2->tree, tctx, &finfo); \
1425 if (!NT_STATUS_IS_OK(_status)) { \
1426 ret = false; \
1427 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1428 nt_errstr(_status)); \
1429 goto done; \
1431 torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1432 nt_time_string(tctx, finfo.basic_info.out.access_time), \
1433 nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1434 } while (0)
1435 #define GET_INFO_PATH(pinfo) do { \
1436 NTSTATUS _status; \
1437 _status = smb_raw_pathinfo(cli2->tree, tctx, &pinfo); \
1438 if (!NT_STATUS_IS_OK(_status)) { \
1439 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \
1440 nt_errstr(_status)); \
1441 ret = false; \
1442 goto done; \
1444 torture_comment(tctx, "pathinfo: Access(%s) Write(%s)\n", \
1445 nt_time_string(tctx, pinfo.basic_info.out.access_time), \
1446 nt_time_string(tctx, pinfo.basic_info.out.write_time)); \
1447 } while (0)
1448 #define GET_INFO_BOTH(finfo,pinfo) do { \
1449 GET_INFO_FILE(finfo); \
1450 GET_INFO_PATH(pinfo); \
1451 COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \
1452 } while (0)
1454 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
1455 NTSTATUS _status; \
1456 union smb_setfileinfo sfinfo; \
1457 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1458 sfinfo.basic_info.in.file.fnum = tfnum; \
1459 sfinfo.basic_info.in.create_time = 0; \
1460 sfinfo.basic_info.in.access_time = 0; \
1461 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1462 sfinfo.basic_info.in.change_time = 0; \
1463 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1464 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1465 if (!NT_STATUS_IS_OK(_status)) { \
1466 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1467 nt_errstr(_status)); \
1468 ret = false; \
1469 goto done; \
1471 } while (0)
1472 #define SET_INFO_FILE(finfo, wrtime) \
1473 SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
1475 #define SET_INFO_FILE_NS(finfo, wrtime, ns, tree, tfnum) do { \
1476 NTSTATUS _status; \
1477 union smb_setfileinfo sfinfo; \
1478 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1479 sfinfo.basic_info.in.file.fnum = tfnum; \
1480 sfinfo.basic_info.in.create_time = 0; \
1481 sfinfo.basic_info.in.access_time = 0; \
1482 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1483 sfinfo.basic_info.in.write_time += (ns); \
1484 sfinfo.basic_info.in.change_time = 0; \
1485 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1486 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1487 if (!NT_STATUS_IS_OK(_status)) { \
1488 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1489 nt_errstr(_status)); \
1490 ret = false; \
1491 goto done; \
1493 } while (0)
1495 static bool test_delayed_write_update3(struct torture_context *tctx,
1496 struct smbcli_state *cli,
1497 struct smbcli_state *cli2)
1499 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1500 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1501 const char *fname = BASEDIR "\\torture_file3.txt";
1502 int fnum1 = -1;
1503 bool ret = true;
1504 ssize_t written;
1505 struct timeval start;
1506 struct timeval end;
1507 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1508 int normal_delay = 2000000;
1509 double sec = ((double)used_delay) / ((double)normal_delay);
1510 int msec = 1000 * sec;
1512 torture_comment(tctx, "\nRunning test_delayed_write_update3\n");
1514 if (!torture_setup_dir(cli, BASEDIR)) {
1515 return false;
1518 torture_comment(tctx, "Open the file handle\n");
1519 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1520 if (fnum1 == -1) {
1521 ret = false;
1522 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1523 goto done;
1526 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1527 finfo0.basic_info.in.file.fnum = fnum1;
1528 finfo1 = finfo0;
1529 finfo2 = finfo0;
1530 finfo3 = finfo0;
1531 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1532 pinfo0.basic_info.in.file.path = fname;
1533 pinfo1 = pinfo0;
1534 pinfo2 = pinfo0;
1535 pinfo3 = pinfo0;
1536 pinfo4 = pinfo0;
1538 /* get the initial times */
1539 GET_INFO_BOTH(finfo0,pinfo0);
1542 * make sure the write time is updated 2 seconds later
1543 * calcuated from the first write
1544 * (but expect upto 5 seconds extra time for a busy server)
1546 start = timeval_current();
1547 end = timeval_add(&start, 7 * sec, 0);
1548 while (!timeval_expired(&end)) {
1549 /* do a write */
1550 torture_comment(tctx, "Do a write on the file handle\n");
1551 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1552 if (written != 1) {
1553 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1554 ret = false;
1555 goto done;
1557 /* get the times after the write */
1558 GET_INFO_FILE(finfo1);
1560 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1561 double diff = timeval_elapsed(&start);
1562 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
1563 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1564 "(1sec == %.2f) (wrong!)\n",
1565 diff, sec);
1566 ret = false;
1567 break;
1570 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1571 "(1sec == %.2f) (correct)\n",
1572 diff, sec);
1573 break;
1575 smb_msleep(0.5 * msec);
1578 GET_INFO_BOTH(finfo1,pinfo1);
1579 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1581 /* sure any further write doesn't update the write time */
1582 start = timeval_current();
1583 end = timeval_add(&start, 15 * sec, 0);
1584 while (!timeval_expired(&end)) {
1585 /* do a write */
1586 torture_comment(tctx, "Do a write on the file handle\n");
1587 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1588 if (written != 1) {
1589 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1590 ret = false;
1591 goto done;
1593 /* get the times after the write */
1594 GET_INFO_BOTH(finfo2,pinfo2);
1596 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1597 double diff = timeval_elapsed(&start);
1598 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1599 "(1sec == %.2f) (wrong!)\n",
1600 diff, sec);
1601 ret = false;
1602 break;
1604 smb_msleep(1 * msec);
1607 GET_INFO_BOTH(finfo2,pinfo2);
1608 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1609 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1610 torture_comment(tctx, "Server did not update write_time (correct)\n");
1613 /* sleep */
1614 smb_msleep(5 * msec);
1616 GET_INFO_BOTH(finfo3,pinfo3);
1617 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1620 * the close updates the write time to the time of the close
1621 * and not to the time of the last write!
1623 torture_comment(tctx, "Close the file handle\n");
1624 smbcli_close(cli->tree, fnum1);
1625 fnum1 = -1;
1627 GET_INFO_PATH(pinfo4);
1628 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1630 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1631 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1634 done:
1635 if (fnum1 != -1)
1636 smbcli_close(cli->tree, fnum1);
1637 smbcli_unlink(cli->tree, fname);
1638 smbcli_deltree(cli->tree, BASEDIR);
1640 return ret;
1644 * Show that a truncate write always updates the write time even
1645 * if an initial write has already updated the write time.
1648 static bool test_delayed_write_update3a(struct torture_context *tctx,
1649 struct smbcli_state *cli,
1650 struct smbcli_state *cli2)
1652 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1653 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1654 const char *fname = BASEDIR "\\torture_file3a.txt";
1655 int fnum1 = -1;
1656 bool ret = true;
1657 ssize_t written;
1658 int i;
1659 struct timeval start;
1660 struct timeval end;
1661 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1662 int normal_delay = 2000000;
1663 double sec = ((double)used_delay) / ((double)normal_delay);
1664 int msec = 1000 * sec;
1666 torture_comment(tctx, "\nRunning test_delayed_write_update3a\n");
1668 if (!torture_setup_dir(cli, BASEDIR)) {
1669 return false;
1672 torture_comment(tctx, "Open the file handle\n");
1673 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1674 if (fnum1 == -1) {
1675 ret = false;
1676 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1677 goto done;
1680 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1681 finfo0.basic_info.in.file.fnum = fnum1;
1682 finfo1 = finfo0;
1683 finfo2 = finfo0;
1684 finfo3 = finfo0;
1685 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1686 pinfo0.basic_info.in.file.path = fname;
1687 pinfo1 = pinfo0;
1688 pinfo2 = pinfo0;
1689 pinfo3 = pinfo0;
1690 pinfo4 = pinfo0;
1692 /* get the initial times */
1693 GET_INFO_BOTH(finfo0,pinfo0);
1696 * sleep some time, to demonstrate the handling of write times
1697 * doesn't depend on the time since the open
1699 smb_msleep(5 * msec);
1701 /* get the initial times */
1702 GET_INFO_BOTH(finfo1,pinfo1);
1703 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1706 * make sure the write time is updated 2 seconds later
1707 * calcuated from the first write
1708 * (but expect upto 5 seconds extra time for a busy server)
1710 start = timeval_current();
1711 end = timeval_add(&start, 7 * sec, 0);
1712 while (!timeval_expired(&end)) {
1713 /* do a write */
1714 torture_comment(tctx, "Do a write on the file handle\n");
1715 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1716 if (written != 1) {
1717 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1718 ret = false;
1719 goto done;
1721 /* get the times after the write */
1722 GET_INFO_FILE(finfo1);
1724 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1725 double diff = timeval_elapsed(&start);
1726 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
1727 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1728 "(1sec == %.2f) (wrong!)\n",
1729 diff, sec);
1730 ret = false;
1731 break;
1734 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1735 "(1sec == %.2f) (correct)\n",
1736 diff, sec);
1737 break;
1739 smb_msleep(0.5 * msec);
1742 GET_INFO_BOTH(finfo1,pinfo1);
1743 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1745 smb_msleep(3 * msec);
1748 * demonstrate that a truncate write always
1749 * updates the write time immediately
1751 for (i=0; i < 3; i++) {
1752 smb_msleep(2 * msec);
1753 /* do a write */
1754 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1755 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
1756 if (written != 0) {
1757 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1758 ret = false;
1759 goto done;
1761 /* get the times after the write */
1762 GET_INFO_BOTH(finfo2,pinfo2);
1763 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1764 finfo1 = finfo2;
1767 smb_msleep(3 * msec);
1769 /* sure any further write doesn't update the write time */
1770 start = timeval_current();
1771 end = timeval_add(&start, 15 * sec, 0);
1772 while (!timeval_expired(&end)) {
1773 /* do a write */
1774 torture_comment(tctx, "Do a write on the file handle\n");
1775 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1776 if (written != 1) {
1777 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1778 ret = false;
1779 goto done;
1781 /* get the times after the write */
1782 GET_INFO_BOTH(finfo2,pinfo2);
1784 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1785 double diff = timeval_elapsed(&start);
1786 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1787 "(1sec == %.2f) (wrong!)\n",
1788 diff, sec);
1789 ret = false;
1790 break;
1792 smb_msleep(1 * msec);
1795 GET_INFO_BOTH(finfo2,pinfo2);
1796 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1797 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1798 torture_comment(tctx, "Server did not update write_time (correct)\n");
1801 /* sleep */
1802 smb_msleep(3 * msec);
1804 /* get the initial times */
1805 GET_INFO_BOTH(finfo1,pinfo1);
1806 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
1809 * demonstrate that a truncate write always
1810 * updates the write time immediately
1812 for (i=0; i < 3; i++) {
1813 smb_msleep(2 * msec);
1814 /* do a write */
1815 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1816 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
1817 if (written != 0) {
1818 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1819 ret = false;
1820 goto done;
1822 /* get the times after the write */
1823 GET_INFO_BOTH(finfo2,pinfo2);
1824 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1825 finfo1 = finfo2;
1828 /* sleep */
1829 smb_msleep(3 * msec);
1831 GET_INFO_BOTH(finfo3,pinfo3);
1832 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1835 * the close doesn't update the write time
1837 torture_comment(tctx, "Close the file handle\n");
1838 smbcli_close(cli->tree, fnum1);
1839 fnum1 = -1;
1841 GET_INFO_PATH(pinfo4);
1842 COMPARE_WRITE_TIME_EQUAL(pinfo4, pinfo3);
1844 if (pinfo4.basic_info.out.write_time == pinfo3.basic_info.out.write_time) {
1845 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1848 done:
1849 if (fnum1 != -1)
1850 smbcli_close(cli->tree, fnum1);
1851 smbcli_unlink(cli->tree, fname);
1852 smbcli_deltree(cli->tree, BASEDIR);
1854 return ret;
1858 * Show a close after write updates the write timestamp to
1859 * the close time, not the last write time.
1862 static bool test_delayed_write_update3b(struct torture_context *tctx,
1863 struct smbcli_state *cli,
1864 struct smbcli_state *cli2)
1866 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1867 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1868 const char *fname = BASEDIR "\\torture_file3b.txt";
1869 int fnum1 = -1;
1870 bool ret = true;
1871 ssize_t written;
1872 struct timeval start;
1873 struct timeval end;
1874 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1875 int normal_delay = 2000000;
1876 double sec = ((double)used_delay) / ((double)normal_delay);
1877 int msec = 1000 * sec;
1879 torture_comment(tctx, "\nRunning test_delayed_write_update3b\n");
1881 if (!torture_setup_dir(cli, BASEDIR)) {
1882 return false;
1885 torture_comment(tctx, "Open the file handle\n");
1886 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1887 if (fnum1 == -1) {
1888 ret = false;
1889 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1890 goto done;
1893 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1894 finfo0.basic_info.in.file.fnum = fnum1;
1895 finfo1 = finfo0;
1896 finfo2 = finfo0;
1897 finfo3 = finfo0;
1898 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1899 pinfo0.basic_info.in.file.path = fname;
1900 pinfo1 = pinfo0;
1901 pinfo2 = pinfo0;
1902 pinfo3 = pinfo0;
1903 pinfo4 = pinfo0;
1905 /* get the initial times */
1906 GET_INFO_BOTH(finfo0,pinfo0);
1909 * sleep some time, to demonstrate the handling of write times
1910 * doesn't depend on the time since the open
1912 smb_msleep(5 * msec);
1914 /* get the initial times */
1915 GET_INFO_BOTH(finfo1,pinfo1);
1916 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1919 * make sure the write time is updated 2 seconds later
1920 * calcuated from the first write
1921 * (but expect upto 5 seconds extra time for a busy server)
1923 start = timeval_current();
1924 end = timeval_add(&start, 7 * sec, 0);
1925 while (!timeval_expired(&end)) {
1926 /* do a write */
1927 torture_comment(tctx, "Do a write on the file handle\n");
1928 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1929 if (written != 1) {
1930 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1931 ret = false;
1932 goto done;
1934 /* get the times after the write */
1935 GET_INFO_FILE(finfo1);
1937 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1938 double diff = timeval_elapsed(&start);
1939 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
1940 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1941 "(1sec == %.2f) (wrong!)\n",
1942 diff, sec);
1943 ret = false;
1944 break;
1947 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1948 "(1sec == %.2f) (correct)\n",
1949 diff, sec);
1950 break;
1952 smb_msleep(0.5 * msec);
1955 GET_INFO_BOTH(finfo1,pinfo1);
1956 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1958 /* sure any further write doesn't update the write time */
1959 start = timeval_current();
1960 end = timeval_add(&start, 15 * sec, 0);
1961 while (!timeval_expired(&end)) {
1962 /* do a write */
1963 torture_comment(tctx, "Do a write on the file handle\n");
1964 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1965 if (written != 1) {
1966 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1967 ret = false;
1968 goto done;
1970 /* get the times after the write */
1971 GET_INFO_BOTH(finfo2,pinfo2);
1973 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1974 double diff = timeval_elapsed(&start);
1975 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1976 "(1sec == %.2f) (wrong!)\n",
1977 diff, sec);
1978 ret = false;
1979 break;
1981 smb_msleep(1 * msec);
1984 GET_INFO_BOTH(finfo2,pinfo2);
1985 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1986 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1987 torture_comment(tctx, "Server did not update write_time (correct)\n");
1990 /* sleep */
1991 smb_msleep(5 * msec);
1993 GET_INFO_BOTH(finfo3,pinfo3);
1994 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1997 * the close updates the write time to the time of the close
1998 * and not to the time of the last write!
2000 torture_comment(tctx, "Close the file handle\n");
2001 smbcli_close(cli->tree, fnum1);
2002 fnum1 = -1;
2004 GET_INFO_PATH(pinfo4);
2005 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2007 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2008 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2011 done:
2012 if (fnum1 != -1)
2013 smbcli_close(cli->tree, fnum1);
2014 smbcli_unlink(cli->tree, fname);
2015 smbcli_deltree(cli->tree, BASEDIR);
2017 return ret;
2021 * Check that a write after a truncate write doesn't update
2022 * the timestamp, but a truncate write after a write does.
2023 * Also prove that a close after a truncate write updates the
2024 * timestamp to current, not the time of last write.
2027 static bool test_delayed_write_update3c(struct torture_context *tctx,
2028 struct smbcli_state *cli,
2029 struct smbcli_state *cli2)
2031 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
2032 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
2033 const char *fname = BASEDIR "\\torture_file3c.txt";
2034 int fnum1 = -1;
2035 bool ret = true;
2036 ssize_t written;
2037 int i;
2038 struct timeval start;
2039 struct timeval end;
2040 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2041 int normal_delay = 2000000;
2042 double sec = ((double)used_delay) / ((double)normal_delay);
2043 int msec = 1000 * sec;
2045 torture_comment(tctx, "\nRunning test_delayed_write_update3c\n");
2047 if (!torture_setup_dir(cli, BASEDIR)) {
2048 return false;
2051 torture_comment(tctx, "Open the file handle\n");
2052 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2053 if (fnum1 == -1) {
2054 ret = false;
2055 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2056 goto done;
2059 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2060 finfo0.basic_info.in.file.fnum = fnum1;
2061 finfo1 = finfo0;
2062 finfo2 = finfo0;
2063 finfo3 = finfo0;
2064 finfo4 = finfo0;
2065 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2066 pinfo0.basic_info.in.file.path = fname;
2067 pinfo1 = pinfo0;
2068 pinfo2 = pinfo0;
2069 pinfo3 = pinfo0;
2070 pinfo4 = pinfo0;
2071 pinfo5 = pinfo0;
2073 /* get the initial times */
2074 GET_INFO_BOTH(finfo0,pinfo0);
2077 * sleep some time, to demonstrate the handling of write times
2078 * doesn't depend on the time since the open
2080 smb_msleep(5 * msec);
2082 /* get the initial times */
2083 GET_INFO_BOTH(finfo1,pinfo1);
2084 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2087 * demonstrate that a truncate write always
2088 * updates the write time immediately
2090 for (i=0; i < 3; i++) {
2091 smb_msleep(2 * msec);
2092 /* do a write */
2093 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
2094 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2095 if (written != 0) {
2096 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2097 ret = false;
2098 goto done;
2100 /* get the times after the write */
2101 GET_INFO_BOTH(finfo2,pinfo2);
2102 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2103 finfo1 = finfo2;
2106 start = timeval_current();
2107 end = timeval_add(&start, 7 * sec, 0);
2108 while (!timeval_expired(&end)) {
2109 /* do a write */
2110 torture_comment(tctx, "Do a write on the file handle\n");
2111 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2112 if (written != 1) {
2113 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2114 ret = false;
2115 goto done;
2117 /* get the times after the write */
2118 GET_INFO_FILE(finfo2);
2120 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2121 double diff = timeval_elapsed(&start);
2122 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2123 "(1sec == %.2f) (wrong!)\n",
2124 diff, sec);
2125 ret = false;
2126 break;
2128 smb_msleep(1 * msec);
2131 GET_INFO_BOTH(finfo2,pinfo2);
2132 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2133 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2134 torture_comment(tctx, "Server did not update write_time (correct)\n");
2137 /* sleep */
2138 smb_msleep(5 * msec);
2140 /* get the initial times */
2141 GET_INFO_BOTH(finfo1,pinfo1);
2142 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
2145 * demonstrate that a truncate write always
2146 * updates the write time immediately
2148 for (i=0; i < 3; i++) {
2149 smb_msleep(2 * msec);
2150 /* do a write */
2151 torture_comment(tctx, "Do a truncate write [%d] on the file handle\n", i);
2152 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2153 if (written != 0) {
2154 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2155 ret = false;
2156 goto done;
2158 /* get the times after the write */
2159 GET_INFO_BOTH(finfo2,pinfo2);
2160 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2161 finfo1 = finfo2;
2164 /* sleep */
2165 smb_msleep(5 * msec);
2167 GET_INFO_BOTH(finfo2,pinfo2);
2168 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2170 /* sure any further write doesn't update the write time */
2171 start = timeval_current();
2172 end = timeval_add(&start, 15 * sec, 0);
2173 while (!timeval_expired(&end)) {
2174 /* do a write */
2175 torture_comment(tctx, "Do a write on the file handle\n");
2176 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2177 if (written != 1) {
2178 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2179 ret = false;
2180 goto done;
2182 /* get the times after the write */
2183 GET_INFO_BOTH(finfo2,pinfo2);
2185 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2186 double diff = timeval_elapsed(&start);
2187 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2188 "(1sec == %.2f) (wrong!)\n",
2189 diff, sec);
2190 ret = false;
2191 break;
2193 smb_msleep(1 * msec);
2196 GET_INFO_BOTH(finfo2,pinfo2);
2197 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2198 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2199 torture_comment(tctx, "Server did not update write_time (correct)\n");
2202 /* sleep */
2203 smb_msleep(5 * msec);
2205 GET_INFO_BOTH(finfo3,pinfo3);
2206 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2209 * the close updates the write time to the time of the close
2210 * and not to the time of the last write!
2212 torture_comment(tctx, "Close the file handle\n");
2213 smbcli_close(cli->tree, fnum1);
2214 fnum1 = -1;
2216 GET_INFO_PATH(pinfo4);
2217 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2219 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2220 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2223 done:
2224 if (fnum1 != -1)
2225 smbcli_close(cli->tree, fnum1);
2226 smbcli_unlink(cli->tree, fname);
2227 smbcli_deltree(cli->tree, BASEDIR);
2229 return ret;
2233 * Show only the first write updates the timestamp, and a close
2234 * after writes updates to current (I think this is the same
2235 * as test 3b. JRA).
2238 static bool test_delayed_write_update4(struct torture_context *tctx,
2239 struct smbcli_state *cli,
2240 struct smbcli_state *cli2)
2242 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
2243 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
2244 const char *fname = BASEDIR "\\torture_file4.txt";
2245 int fnum1 = -1;
2246 bool ret = true;
2247 ssize_t written;
2248 struct timeval start;
2249 struct timeval end;
2250 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2251 int normal_delay = 2000000;
2252 double sec = ((double)used_delay) / ((double)normal_delay);
2253 int msec = 1000 * sec;
2255 torture_comment(tctx, "\nRunning test_delayed_write_update4\n");
2257 if (!torture_setup_dir(cli, BASEDIR)) {
2258 return false;
2261 torture_comment(tctx, "Open the file handle\n");
2262 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2263 if (fnum1 == -1) {
2264 ret = false;
2265 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2266 goto done;
2269 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2270 finfo0.basic_info.in.file.fnum = fnum1;
2271 finfo1 = finfo0;
2272 finfo2 = finfo0;
2273 finfo3 = finfo0;
2274 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2275 pinfo0.basic_info.in.file.path = fname;
2276 pinfo1 = pinfo0;
2277 pinfo2 = pinfo0;
2278 pinfo3 = pinfo0;
2279 pinfo4 = pinfo0;
2281 /* get the initial times */
2282 GET_INFO_BOTH(finfo0,pinfo0);
2284 /* sleep a bit */
2285 smb_msleep(5 * msec);
2287 /* do a write */
2288 torture_comment(tctx, "Do a write on the file handle\n");
2289 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2290 if (written != 1) {
2291 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2292 ret = false;
2293 goto done;
2296 GET_INFO_BOTH(finfo1,pinfo1);
2297 COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
2300 * make sure the write time is updated 2 seconds later
2301 * calcuated from the first write
2302 * (but expect upto 3 seconds extra time for a busy server)
2304 start = timeval_current();
2305 end = timeval_add(&start, 5 * sec, 0);
2306 while (!timeval_expired(&end)) {
2307 /* get the times after the first write */
2308 GET_INFO_FILE(finfo1);
2310 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
2311 double diff = timeval_elapsed(&start);
2312 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
2313 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2314 "(1sec == %.2f) (wrong!)\n",
2315 diff, sec);
2316 ret = false;
2317 break;
2320 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2321 "(1sec == %.2f) (correct)\n",
2322 diff, sec);
2323 break;
2325 smb_msleep(0.5 * msec);
2328 GET_INFO_BOTH(finfo1,pinfo1);
2329 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
2331 /* sure any further write doesn't update the write time */
2332 start = timeval_current();
2333 end = timeval_add(&start, 15 * sec, 0);
2334 while (!timeval_expired(&end)) {
2335 /* do a write */
2336 torture_comment(tctx, "Do a write on the file handle\n");
2337 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2338 if (written != 1) {
2339 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2340 ret = false;
2341 goto done;
2343 /* get the times after the write */
2344 GET_INFO_BOTH(finfo2,pinfo2);
2346 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2347 double diff = timeval_elapsed(&start);
2348 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2349 "(1sec == %.2f) (wrong!)\n",
2350 diff, sec);
2351 ret = false;
2352 break;
2354 smb_msleep(1 * msec);
2357 GET_INFO_BOTH(finfo2,pinfo2);
2358 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2359 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2360 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
2363 /* sleep */
2364 smb_msleep(5 * msec);
2366 GET_INFO_BOTH(finfo3,pinfo3);
2367 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2370 * the close updates the write time to the time of the close
2371 * and not to the time of the last write!
2373 torture_comment(tctx, "Close the file handle\n");
2374 smbcli_close(cli->tree, fnum1);
2375 fnum1 = -1;
2377 GET_INFO_PATH(pinfo4);
2378 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2380 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2381 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2384 done:
2385 if (fnum1 != -1)
2386 smbcli_close(cli->tree, fnum1);
2387 smbcli_unlink(cli->tree, fname);
2388 smbcli_deltree(cli->tree, BASEDIR);
2390 return ret;
2394 * Show writes and closes have no effect on updating times once a SETWRITETIME is done.
2397 static bool test_delayed_write_update5(struct torture_context *tctx,
2398 struct smbcli_state *cli,
2399 struct smbcli_state *cli2)
2401 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2402 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2403 const char *fname = BASEDIR "\\torture_file5.txt";
2404 int fnum1 = -1;
2405 bool ret = true;
2406 ssize_t written;
2407 struct timeval start;
2408 struct timeval end;
2409 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2410 int normal_delay = 2000000;
2411 double sec = ((double)used_delay) / ((double)normal_delay);
2412 int msec = 1000 * sec;
2414 torture_comment(tctx, "\nRunning test_delayed_write_update5\n");
2416 if (!torture_setup_dir(cli, BASEDIR)) {
2417 return false;
2420 torture_comment(tctx, "Open the file handle\n");
2421 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2422 if (fnum1 == -1) {
2423 ret = false;
2424 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2425 goto done;
2428 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2429 finfo0.basic_info.in.file.fnum = fnum1;
2430 finfo1 = finfo0;
2431 finfo2 = finfo0;
2432 finfo3 = finfo0;
2433 finfo4 = finfo0;
2434 finfo5 = finfo0;
2435 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2436 pinfo0.basic_info.in.file.path = fname;
2437 pinfo1 = pinfo0;
2438 pinfo2 = pinfo0;
2439 pinfo3 = pinfo0;
2440 pinfo4 = pinfo0;
2441 pinfo5 = pinfo0;
2442 pinfo6 = pinfo0;
2444 /* get the initial times */
2445 GET_INFO_BOTH(finfo0,pinfo0);
2447 /* do a write */
2448 torture_comment(tctx, "Do a write on the file handle\n");
2449 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2450 if (written != 1) {
2451 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2452 ret = false;
2453 goto done;
2456 GET_INFO_BOTH(finfo1,pinfo1);
2457 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2459 torture_comment(tctx, "Set write time in the future on the file handle\n");
2460 SET_INFO_FILE(finfo0, time(NULL) + 86400);
2461 GET_INFO_BOTH(finfo2,pinfo2);
2462 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2464 torture_comment(tctx, "Set write time in the past on the file handle\n");
2465 SET_INFO_FILE(finfo0, time(NULL) - 86400);
2466 GET_INFO_BOTH(finfo2,pinfo2);
2467 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2469 /* make sure the 2 second delay from the first write are canceled */
2470 start = timeval_current();
2471 end = timeval_add(&start, 15 * sec, 0);
2472 while (!timeval_expired(&end)) {
2474 /* get the times after the first write */
2475 GET_INFO_BOTH(finfo3,pinfo3);
2477 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2478 double diff = timeval_elapsed(&start);
2479 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2480 "(1sec == %.2f) (wrong!)\n",
2481 diff, sec);
2482 ret = false;
2483 break;
2485 smb_msleep(1 * msec);
2488 GET_INFO_BOTH(finfo3,pinfo3);
2489 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2490 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2491 torture_comment(tctx, "Server did not update write_time (correct)\n");
2494 /* sure any further write doesn't update the write time */
2495 start = timeval_current();
2496 end = timeval_add(&start, 15 * sec, 0);
2497 while (!timeval_expired(&end)) {
2498 /* do a write */
2499 torture_comment(tctx, "Do a write on the file handle\n");
2500 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2501 if (written != 1) {
2502 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2503 ret = false;
2504 goto done;
2506 /* get the times after the write */
2507 GET_INFO_BOTH(finfo4,pinfo4);
2509 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2510 double diff = timeval_elapsed(&start);
2511 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2512 "(1sec == %.2f) (wrong!)\n",
2513 diff, sec);
2514 ret = false;
2515 break;
2517 smb_msleep(1 * msec);
2520 GET_INFO_BOTH(finfo4,pinfo4);
2521 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2522 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2523 torture_comment(tctx, "Server did not update write_time (correct)\n");
2526 /* sleep */
2527 smb_msleep(5 * msec);
2529 GET_INFO_BOTH(finfo5,pinfo5);
2530 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2533 * the close doesn't update the write time
2535 torture_comment(tctx, "Close the file handle\n");
2536 smbcli_close(cli->tree, fnum1);
2537 fnum1 = -1;
2539 GET_INFO_PATH(pinfo6);
2540 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2542 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2543 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2546 done:
2547 if (fnum1 != -1)
2548 smbcli_close(cli->tree, fnum1);
2549 smbcli_unlink(cli->tree, fname);
2550 smbcli_deltree(cli->tree, BASEDIR);
2552 return ret;
2556 * Show truncate writes and closes have no effect on updating times once a SETWRITETIME is done.
2559 static bool test_delayed_write_update5b(struct torture_context *tctx,
2560 struct smbcli_state *cli,
2561 struct smbcli_state *cli2)
2563 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2564 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2565 const char *fname = BASEDIR "\\torture_fileb.txt";
2566 int fnum1 = -1;
2567 bool ret = true;
2568 ssize_t written;
2569 struct timeval start;
2570 struct timeval end;
2571 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2572 int normal_delay = 2000000;
2573 double sec = ((double)used_delay) / ((double)normal_delay);
2574 int msec = 1000 * sec;
2576 torture_comment(tctx, "\nRunning test_delayed_write_update5b\n");
2578 if (!torture_setup_dir(cli, BASEDIR)) {
2579 return false;
2582 torture_comment(tctx, "Open the file handle\n");
2583 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2584 if (fnum1 == -1) {
2585 ret = false;
2586 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2587 goto done;
2590 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2591 finfo0.basic_info.in.file.fnum = fnum1;
2592 finfo1 = finfo0;
2593 finfo2 = finfo0;
2594 finfo3 = finfo0;
2595 finfo4 = finfo0;
2596 finfo5 = finfo0;
2597 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2598 pinfo0.basic_info.in.file.path = fname;
2599 pinfo1 = pinfo0;
2600 pinfo2 = pinfo0;
2601 pinfo3 = pinfo0;
2602 pinfo4 = pinfo0;
2603 pinfo5 = pinfo0;
2604 pinfo6 = pinfo0;
2606 /* get the initial times */
2607 GET_INFO_BOTH(finfo0,pinfo0);
2609 /* do a write */
2610 torture_comment(tctx, "Do a write on the file handle\n");
2611 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2612 if (written != 1) {
2613 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2614 ret = false;
2615 goto done;
2618 GET_INFO_BOTH(finfo1,pinfo1);
2619 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2621 torture_comment(tctx, "Set write time in the future on the file handle\n");
2622 SET_INFO_FILE(finfo0, time(NULL) + 86400);
2623 GET_INFO_BOTH(finfo2,pinfo2);
2624 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2626 torture_comment(tctx, "Set write time in the past on the file handle\n");
2627 SET_INFO_FILE(finfo0, time(NULL) - 86400);
2628 GET_INFO_BOTH(finfo2,pinfo2);
2629 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2631 /* make sure the 2 second delay from the first write are canceled */
2632 start = timeval_current();
2633 end = timeval_add(&start, 15 * sec, 0);
2634 while (!timeval_expired(&end)) {
2636 /* get the times after the first write */
2637 GET_INFO_BOTH(finfo3,pinfo3);
2639 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2640 double diff = timeval_elapsed(&start);
2641 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2642 "(1sec == %.2f) (wrong!)\n",
2643 diff, sec);
2644 ret = false;
2645 break;
2647 smb_msleep(1 * msec);
2650 GET_INFO_BOTH(finfo3,pinfo3);
2651 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2652 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2653 torture_comment(tctx, "Server did not update write_time (correct)\n");
2656 /* Do any further write (truncates) update the write time ? */
2657 start = timeval_current();
2658 end = timeval_add(&start, 15 * sec, 0);
2659 while (!timeval_expired(&end)) {
2660 /* do a write */
2661 torture_comment(tctx, "Do a truncate write on the file handle\n");
2662 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
2663 if (written != 0) {
2664 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2665 ret = false;
2666 goto done;
2668 /* get the times after the write */
2669 GET_INFO_BOTH(finfo4,pinfo4);
2671 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2672 double diff = timeval_elapsed(&start);
2673 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2674 "(1sec == %.2f) (wrong!)\n",
2675 diff, sec);
2676 ret = false;
2677 break;
2679 smb_msleep(1 * msec);
2682 GET_INFO_BOTH(finfo4,pinfo4);
2683 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2684 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2685 torture_comment(tctx, "Server did not update write_time (correct)\n");
2688 /* sleep */
2689 smb_msleep(5 * msec);
2691 GET_INFO_BOTH(finfo5,pinfo5);
2692 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2695 * the close doesn't update the write time
2697 torture_comment(tctx, "Close the file handle\n");
2698 smbcli_close(cli->tree, fnum1);
2699 fnum1 = -1;
2701 GET_INFO_PATH(pinfo6);
2702 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2704 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2705 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2708 done:
2709 if (fnum1 != -1)
2710 smbcli_close(cli->tree, fnum1);
2711 smbcli_unlink(cli->tree, fname);
2712 smbcli_deltree(cli->tree, BASEDIR);
2714 return ret;
2718 * Open 2 handles on a file. Write one one and then set the
2719 * WRITE TIME explicitly on the other. Ensure the write time
2720 * update is cancelled. Ensure the write time is updated to
2721 * the close time when the non-explicit set handle is closed.
2725 static bool test_delayed_write_update6(struct torture_context *tctx,
2726 struct smbcli_state *cli,
2727 struct smbcli_state *cli2)
2729 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2730 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
2731 const char *fname = BASEDIR "\\torture_file6.txt";
2732 int fnum1 = -1;
2733 int fnum2 = -1;
2734 bool ret = true;
2735 ssize_t written;
2736 struct timeval start;
2737 struct timeval end;
2738 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2739 int normal_delay = 2000000;
2740 double sec = ((double)used_delay) / ((double)normal_delay);
2741 int msec = 1000 * sec;
2742 bool first = true;
2744 torture_comment(tctx, "\nRunning test_delayed_write_update6\n");
2746 if (!torture_setup_dir(cli, BASEDIR)) {
2747 return false;
2749 again:
2750 torture_comment(tctx, "Open the file handle\n");
2751 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2752 if (fnum1 == -1) {
2753 ret = false;
2754 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2755 goto done;
2758 if (fnum2 == -1) {
2759 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
2760 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2761 if (fnum2 == -1) {
2762 ret = false;
2763 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2764 goto done;
2768 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2769 finfo0.basic_info.in.file.fnum = fnum1;
2770 finfo1 = finfo0;
2771 finfo2 = finfo0;
2772 finfo3 = finfo0;
2773 finfo4 = finfo0;
2774 finfo5 = finfo0;
2775 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2776 pinfo0.basic_info.in.file.path = fname;
2777 pinfo1 = pinfo0;
2778 pinfo2 = pinfo0;
2779 pinfo3 = pinfo0;
2780 pinfo4 = pinfo0;
2781 pinfo5 = pinfo0;
2782 pinfo6 = pinfo0;
2783 pinfo7 = pinfo0;
2785 /* get the initial times */
2786 GET_INFO_BOTH(finfo0,pinfo0);
2788 /* do a write */
2789 torture_comment(tctx, "Do a write on the file handle\n");
2790 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2791 if (written != 1) {
2792 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2793 ret = false;
2794 goto done;
2797 GET_INFO_BOTH(finfo1,pinfo1);
2798 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2800 torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
2801 SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
2802 GET_INFO_BOTH(finfo2,pinfo2);
2803 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2805 torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
2806 SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
2807 GET_INFO_BOTH(finfo2,pinfo2);
2808 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2810 /* make sure the 2 second delay from the first write are canceled */
2811 start = timeval_current();
2812 end = timeval_add(&start, 10 * sec, 0);
2813 while (!timeval_expired(&end)) {
2815 /* get the times after the first write */
2816 GET_INFO_BOTH(finfo3,pinfo3);
2818 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2819 double diff = timeval_elapsed(&start);
2820 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2821 "(1sec == %.2f) (wrong!)\n",
2822 diff, sec);
2823 ret = false;
2824 break;
2826 smb_msleep(1 * msec);
2829 GET_INFO_BOTH(finfo3,pinfo3);
2830 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2831 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2832 torture_comment(tctx, "Server did not update write_time (correct)\n");
2835 /* sure any further write doesn't update the write time */
2836 start = timeval_current();
2837 end = timeval_add(&start, 10 * sec, 0);
2838 while (!timeval_expired(&end)) {
2839 /* do a write */
2840 torture_comment(tctx, "Do a write on the file handle\n");
2841 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2842 if (written != 1) {
2843 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2844 ret = false;
2845 goto done;
2847 /* get the times after the write */
2848 GET_INFO_BOTH(finfo4,pinfo4);
2850 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2851 double diff = timeval_elapsed(&start);
2852 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2853 "(1sec == %.2f) (wrong!)\n",
2854 diff, sec);
2855 ret = false;
2856 break;
2858 smb_msleep(1 * msec);
2861 GET_INFO_BOTH(finfo4,pinfo4);
2862 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2863 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2864 torture_comment(tctx, "Server did not update write_time (correct)\n");
2867 /* sleep */
2868 smb_msleep(5 * msec);
2870 GET_INFO_BOTH(finfo5,pinfo5);
2871 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2874 * the close updates the write time to the time of the close
2875 * as the write time was set on the 2nd handle
2877 torture_comment(tctx, "Close the file handle\n");
2878 smbcli_close(cli->tree, fnum1);
2879 fnum1 = -1;
2881 GET_INFO_PATH(pinfo6);
2882 COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
2884 if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
2885 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2888 /* See what the second write handle thinks the time is ? */
2889 finfo5.basic_info.in.file.fnum = fnum2;
2890 GET_INFO_FILE2(finfo5);
2891 COMPARE_WRITE_TIME_EQUAL(finfo5, pinfo6);
2893 /* See if we have lost the sticky write time on handle2 */
2894 smb_msleep(3 * msec);
2895 torture_comment(tctx, "Have we lost the sticky write time ?\n");
2897 /* Make sure any further normal write doesn't update the write time */
2898 start = timeval_current();
2899 end = timeval_add(&start, 10 * sec, 0);
2900 while (!timeval_expired(&end)) {
2901 /* do a write */
2902 torture_comment(tctx, "Do a write on the second file handle\n");
2903 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
2904 if (written != 1) {
2905 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2906 ret = false;
2907 goto done;
2909 /* get the times after the write */
2910 GET_INFO_FILE2(finfo5);
2911 GET_INFO_PATH(pinfo6);
2913 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2914 double diff = timeval_elapsed(&start);
2915 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2916 "(1sec == %.2f) (wrong!)\n",
2917 diff, sec);
2918 ret = false;
2919 break;
2921 smb_msleep(1 * msec);
2924 /* What about a truncate write ? */
2925 start = timeval_current();
2926 end = timeval_add(&start, 10 * sec, 0);
2927 while (!timeval_expired(&end)) {
2928 /* do a write */
2929 torture_comment(tctx, "Do a truncate write on the second file handle\n");
2930 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 0);
2931 if (written != 0) {
2932 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2933 ret = false;
2934 goto done;
2936 /* get the times after the write */
2937 GET_INFO_FILE2(finfo5);
2938 GET_INFO_PATH(pinfo6);
2940 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2941 double diff = timeval_elapsed(&start);
2942 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2943 "(1sec == %.2f) (wrong!)\n",
2944 diff, sec);
2945 ret = false;
2946 break;
2948 smb_msleep(1 * msec);
2952 /* keep the 2nd handle open and rerun tests */
2953 if (first) {
2954 first = false;
2955 goto again;
2959 * closing the 2nd handle will cause no write time update
2960 * as the write time was explicit set on this handle
2962 torture_comment(tctx, "Close the 2nd file handle\n");
2963 smbcli_close(cli2->tree, fnum2);
2964 fnum2 = -1;
2966 GET_INFO_PATH(pinfo7);
2967 COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
2969 if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
2970 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2973 done:
2974 if (fnum1 != -1)
2975 smbcli_close(cli->tree, fnum1);
2976 if (fnum2 != -1)
2977 smbcli_close(cli2->tree, fnum2);
2978 smbcli_unlink(cli->tree, fname);
2979 smbcli_deltree(cli->tree, BASEDIR);
2981 return ret;
2984 static bool test_delayed_write_update7(struct torture_context *tctx, struct smbcli_state *cli)
2986 union smb_open open_parms;
2987 union smb_fileinfo finfo1, finfo2, finfo3;
2988 const char *fname = BASEDIR "\\torture_file7.txt";
2989 NTSTATUS status;
2990 int fnum1 = -1;
2991 bool ret = true;
2992 TALLOC_CTX *mem_ctx;
2994 torture_comment(tctx, "\nRunning test_delayed_write_update7 (timestamp resolution test)\n");
2996 mem_ctx = talloc_init("test_delayed_write_update7");
2997 if (!mem_ctx) return false;
2999 ZERO_STRUCT(finfo1);
3000 ZERO_STRUCT(finfo2);
3001 ZERO_STRUCT(finfo3);
3002 ZERO_STRUCT(open_parms);
3004 if (!torture_setup_dir(cli, BASEDIR)) {
3005 return false;
3008 /* Create the file. */
3009 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
3010 if (fnum1 == -1) {
3011 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
3012 return false;
3015 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
3016 finfo1.basic_info.in.file.fnum = fnum1;
3017 finfo2 = finfo1;
3018 finfo3 = finfo1;
3020 /* Get the initial timestamps. */
3021 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
3023 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
3025 /* Set the pending write time to a value with ns. */
3026 SET_INFO_FILE_NS(finfo, time(NULL) + 86400, 103, cli->tree, fnum1);
3028 /* Get the current pending write time by fnum. */
3029 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
3031 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
3033 /* Ensure the time is actually different. */
3034 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
3035 torture_result(tctx, TORTURE_FAIL,
3036 "setfileinfo time matches original fileinfo time");
3037 ret = false;
3040 /* Get the current pending write time by path. */
3041 finfo3.basic_info.in.file.path = fname;
3042 status = smb_raw_pathinfo(cli->tree, tctx, &finfo3);
3044 if (finfo2.basic_info.out.write_time != finfo3.basic_info.out.write_time) {
3045 torture_result(tctx, TORTURE_FAIL,
3046 "qpathinfo time doens't match fileinfo time");
3047 ret = false;
3050 /* Now close the file. Re-open and check that the write
3051 time is identical to the one we wrote. */
3053 smbcli_close(cli->tree, fnum1);
3055 open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
3056 open_parms.ntcreatex.in.flags = 0;
3057 open_parms.ntcreatex.in.access_mask = SEC_GENERIC_READ;
3058 open_parms.ntcreatex.in.file_attr = 0;
3059 open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE|
3060 NTCREATEX_SHARE_ACCESS_READ|
3061 NTCREATEX_SHARE_ACCESS_WRITE;
3062 open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
3063 open_parms.ntcreatex.in.create_options = 0;
3064 open_parms.ntcreatex.in.fname = fname;
3066 status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
3067 talloc_free(mem_ctx);
3069 if (!NT_STATUS_IS_OK(status)) {
3070 torture_result(tctx, TORTURE_FAIL,
3071 "setfileinfo time matches original fileinfo time");
3072 ret = false;
3075 fnum1 = open_parms.ntcreatex.out.file.fnum;
3077 /* Check the returned time matches. */
3078 if (open_parms.ntcreatex.out.write_time != finfo2.basic_info.out.write_time) {
3079 torture_result(tctx, TORTURE_FAIL,
3080 "final open time does not match set time");
3081 ret = false;
3084 done:
3086 smbcli_close(cli->tree, fnum1);
3088 smbcli_unlink(cli->tree, fname);
3089 smbcli_deltree(cli->tree, BASEDIR);
3090 return ret;
3094 testing of delayed update of write_time
3096 struct torture_suite *torture_delay_write(void)
3098 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "delaywrite");
3100 torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
3101 torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
3102 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate", test_delayed_write_update1);
3103 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a);
3104 torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b);
3105 torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c);
3106 torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
3107 torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
3108 torture_suite_add_2smb_test(suite, "delayed update of write time 3a", test_delayed_write_update3a);
3109 torture_suite_add_2smb_test(suite, "delayed update of write time 3b", test_delayed_write_update3b);
3110 torture_suite_add_2smb_test(suite, "delayed update of write time 3c", test_delayed_write_update3c);
3111 torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
3112 torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
3113 torture_suite_add_2smb_test(suite, "delayed update of write time 5b", test_delayed_write_update5b);
3114 torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
3115 torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7);
3116 torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7);
3118 return suite;