dns: The QCLASS is called IN, not IP
[Samba/gebeck_regimport.git] / source4 / torture / basic / delaywrite.c
blob8a1dc1ba9de072d5452d1ea6b75e698d0902a549
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, finfo4;
1500 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
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 finfo4 = finfo0;
1532 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1533 pinfo0.basic_info.in.file.path = fname;
1534 pinfo1 = pinfo0;
1535 pinfo2 = pinfo0;
1536 pinfo3 = pinfo0;
1537 pinfo4 = pinfo0;
1538 pinfo5 = pinfo0;
1540 /* get the initial times */
1541 GET_INFO_BOTH(finfo0,pinfo0);
1544 * make sure the write time is updated 2 seconds later
1545 * calcuated from the first write
1546 * (but expect upto 5 seconds extra time for a busy server)
1548 start = timeval_current();
1549 end = timeval_add(&start, 7 * sec, 0);
1550 while (!timeval_expired(&end)) {
1551 /* do a write */
1552 torture_comment(tctx, "Do a write on the file handle\n");
1553 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1554 if (written != 1) {
1555 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1556 ret = false;
1557 goto done;
1559 /* get the times after the write */
1560 GET_INFO_FILE(finfo1);
1562 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1563 double diff = timeval_elapsed(&start);
1564 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
1565 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1566 "(1sec == %.2f) (wrong!)\n",
1567 diff, sec);
1568 ret = false;
1569 break;
1572 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1573 "(1sec == %.2f) (correct)\n",
1574 diff, sec);
1575 break;
1577 smb_msleep(0.5 * msec);
1580 GET_INFO_BOTH(finfo1,pinfo1);
1581 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1583 /* sure any further write doesn't update the write time */
1584 start = timeval_current();
1585 end = timeval_add(&start, 15 * sec, 0);
1586 while (!timeval_expired(&end)) {
1587 /* do a write */
1588 torture_comment(tctx, "Do a write on the file handle\n");
1589 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1590 if (written != 1) {
1591 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1592 ret = false;
1593 goto done;
1595 /* get the times after the write */
1596 GET_INFO_BOTH(finfo2,pinfo2);
1598 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1599 double diff = timeval_elapsed(&start);
1600 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1601 "(1sec == %.2f) (wrong!)\n",
1602 diff, sec);
1603 ret = false;
1604 break;
1606 smb_msleep(1 * msec);
1609 GET_INFO_BOTH(finfo2,pinfo2);
1610 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1611 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1612 torture_comment(tctx, "Server did not update write_time (correct)\n");
1615 /* sleep */
1616 smb_msleep(5 * msec);
1618 GET_INFO_BOTH(finfo3,pinfo3);
1619 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1622 * the close updates the write time to the time of the close
1623 * and not to the time of the last write!
1625 torture_comment(tctx, "Close the file handle\n");
1626 smbcli_close(cli->tree, fnum1);
1627 fnum1 = -1;
1629 GET_INFO_PATH(pinfo4);
1630 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1632 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1633 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1636 done:
1637 if (fnum1 != -1)
1638 smbcli_close(cli->tree, fnum1);
1639 smbcli_unlink(cli->tree, fname);
1640 smbcli_deltree(cli->tree, BASEDIR);
1642 return ret;
1646 * Show that a truncate write always updates the write time even
1647 * if an initial write has already updated the write time.
1650 static bool test_delayed_write_update3a(struct torture_context *tctx,
1651 struct smbcli_state *cli,
1652 struct smbcli_state *cli2)
1654 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1655 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1656 const char *fname = BASEDIR "\\torture_file3a.txt";
1657 int fnum1 = -1;
1658 bool ret = true;
1659 ssize_t written;
1660 int i;
1661 struct timeval start;
1662 struct timeval end;
1663 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1664 int normal_delay = 2000000;
1665 double sec = ((double)used_delay) / ((double)normal_delay);
1666 int msec = 1000 * sec;
1668 torture_comment(tctx, "\nRunning test_delayed_write_update3a\n");
1670 if (!torture_setup_dir(cli, BASEDIR)) {
1671 return false;
1674 torture_comment(tctx, "Open the file handle\n");
1675 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1676 if (fnum1 == -1) {
1677 ret = false;
1678 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1679 goto done;
1682 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1683 finfo0.basic_info.in.file.fnum = fnum1;
1684 finfo1 = finfo0;
1685 finfo2 = finfo0;
1686 finfo3 = finfo0;
1687 finfo4 = finfo0;
1688 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1689 pinfo0.basic_info.in.file.path = fname;
1690 pinfo1 = pinfo0;
1691 pinfo2 = pinfo0;
1692 pinfo3 = pinfo0;
1693 pinfo4 = pinfo0;
1694 pinfo5 = pinfo0;
1696 /* get the initial times */
1697 GET_INFO_BOTH(finfo0,pinfo0);
1700 * sleep some time, to demonstrate the handling of write times
1701 * doesn't depend on the time since the open
1703 smb_msleep(5 * msec);
1705 /* get the initial times */
1706 GET_INFO_BOTH(finfo1,pinfo1);
1707 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1710 * make sure the write time is updated 2 seconds later
1711 * calcuated from the first write
1712 * (but expect upto 5 seconds extra time for a busy server)
1714 start = timeval_current();
1715 end = timeval_add(&start, 7 * sec, 0);
1716 while (!timeval_expired(&end)) {
1717 /* do a write */
1718 torture_comment(tctx, "Do a write on the file handle\n");
1719 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1720 if (written != 1) {
1721 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1722 ret = false;
1723 goto done;
1725 /* get the times after the write */
1726 GET_INFO_FILE(finfo1);
1728 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1729 double diff = timeval_elapsed(&start);
1730 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
1731 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1732 "(1sec == %.2f) (wrong!)\n",
1733 diff, sec);
1734 ret = false;
1735 break;
1738 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1739 "(1sec == %.2f) (correct)\n",
1740 diff, sec);
1741 break;
1743 smb_msleep(0.5 * msec);
1746 GET_INFO_BOTH(finfo1,pinfo1);
1747 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1749 smb_msleep(3 * msec);
1752 * demonstrate that a truncate write always
1753 * updates the write time immediately
1755 for (i=0; i < 3; i++) {
1756 smb_msleep(2 * msec);
1757 /* do a write */
1758 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1759 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
1760 if (written != 0) {
1761 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1762 ret = false;
1763 goto done;
1765 /* get the times after the write */
1766 GET_INFO_BOTH(finfo2,pinfo2);
1767 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1768 finfo1 = finfo2;
1771 smb_msleep(3 * msec);
1773 /* sure any further write doesn't update the write time */
1774 start = timeval_current();
1775 end = timeval_add(&start, 15 * sec, 0);
1776 while (!timeval_expired(&end)) {
1777 /* do a write */
1778 torture_comment(tctx, "Do a write on the file handle\n");
1779 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1780 if (written != 1) {
1781 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1782 ret = false;
1783 goto done;
1785 /* get the times after the write */
1786 GET_INFO_BOTH(finfo2,pinfo2);
1788 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1789 double diff = timeval_elapsed(&start);
1790 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1791 "(1sec == %.2f) (wrong!)\n",
1792 diff, sec);
1793 ret = false;
1794 break;
1796 smb_msleep(1 * msec);
1799 GET_INFO_BOTH(finfo2,pinfo2);
1800 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1801 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1802 torture_comment(tctx, "Server did not update write_time (correct)\n");
1805 /* sleep */
1806 smb_msleep(3 * msec);
1808 /* get the initial times */
1809 GET_INFO_BOTH(finfo1,pinfo1);
1810 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
1813 * demonstrate that a truncate write always
1814 * updates the write time immediately
1816 for (i=0; i < 3; i++) {
1817 smb_msleep(2 * msec);
1818 /* do a write */
1819 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1820 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
1821 if (written != 0) {
1822 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1823 ret = false;
1824 goto done;
1826 /* get the times after the write */
1827 GET_INFO_BOTH(finfo2,pinfo2);
1828 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1829 finfo1 = finfo2;
1832 /* sleep */
1833 smb_msleep(3 * msec);
1835 GET_INFO_BOTH(finfo3,pinfo3);
1836 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1839 * the close doesn't update the write time
1841 torture_comment(tctx, "Close the file handle\n");
1842 smbcli_close(cli->tree, fnum1);
1843 fnum1 = -1;
1845 GET_INFO_PATH(pinfo4);
1846 COMPARE_WRITE_TIME_EQUAL(pinfo4, pinfo3);
1848 if (pinfo4.basic_info.out.write_time == pinfo3.basic_info.out.write_time) {
1849 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1852 done:
1853 if (fnum1 != -1)
1854 smbcli_close(cli->tree, fnum1);
1855 smbcli_unlink(cli->tree, fname);
1856 smbcli_deltree(cli->tree, BASEDIR);
1858 return ret;
1862 * Show a close after write updates the write timestamp to
1863 * the close time, not the last write time.
1866 static bool test_delayed_write_update3b(struct torture_context *tctx,
1867 struct smbcli_state *cli,
1868 struct smbcli_state *cli2)
1870 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1871 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1872 const char *fname = BASEDIR "\\torture_file3b.txt";
1873 int fnum1 = -1;
1874 bool ret = true;
1875 ssize_t written;
1876 struct timeval start;
1877 struct timeval end;
1878 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1879 int normal_delay = 2000000;
1880 double sec = ((double)used_delay) / ((double)normal_delay);
1881 int msec = 1000 * sec;
1883 torture_comment(tctx, "\nRunning test_delayed_write_update3b\n");
1885 if (!torture_setup_dir(cli, BASEDIR)) {
1886 return false;
1889 torture_comment(tctx, "Open the file handle\n");
1890 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1891 if (fnum1 == -1) {
1892 ret = false;
1893 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1894 goto done;
1897 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1898 finfo0.basic_info.in.file.fnum = fnum1;
1899 finfo1 = finfo0;
1900 finfo2 = finfo0;
1901 finfo3 = finfo0;
1902 finfo4 = finfo0;
1903 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1904 pinfo0.basic_info.in.file.path = fname;
1905 pinfo1 = pinfo0;
1906 pinfo2 = pinfo0;
1907 pinfo3 = pinfo0;
1908 pinfo4 = pinfo0;
1909 pinfo5 = pinfo0;
1911 /* get the initial times */
1912 GET_INFO_BOTH(finfo0,pinfo0);
1915 * sleep some time, to demonstrate the handling of write times
1916 * doesn't depend on the time since the open
1918 smb_msleep(5 * msec);
1920 /* get the initial times */
1921 GET_INFO_BOTH(finfo1,pinfo1);
1922 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1925 * make sure the write time is updated 2 seconds later
1926 * calcuated from the first write
1927 * (but expect upto 5 seconds extra time for a busy server)
1929 start = timeval_current();
1930 end = timeval_add(&start, 7 * sec, 0);
1931 while (!timeval_expired(&end)) {
1932 /* do a write */
1933 torture_comment(tctx, "Do a write on the file handle\n");
1934 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1935 if (written != 1) {
1936 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1937 ret = false;
1938 goto done;
1940 /* get the times after the write */
1941 GET_INFO_FILE(finfo1);
1943 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1944 double diff = timeval_elapsed(&start);
1945 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
1946 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1947 "(1sec == %.2f) (wrong!)\n",
1948 diff, sec);
1949 ret = false;
1950 break;
1953 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1954 "(1sec == %.2f) (correct)\n",
1955 diff, sec);
1956 break;
1958 smb_msleep(0.5 * msec);
1961 GET_INFO_BOTH(finfo1,pinfo1);
1962 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1964 /* sure any further write doesn't update the write time */
1965 start = timeval_current();
1966 end = timeval_add(&start, 15 * sec, 0);
1967 while (!timeval_expired(&end)) {
1968 /* do a write */
1969 torture_comment(tctx, "Do a write on the file handle\n");
1970 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1971 if (written != 1) {
1972 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1973 ret = false;
1974 goto done;
1976 /* get the times after the write */
1977 GET_INFO_BOTH(finfo2,pinfo2);
1979 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1980 double diff = timeval_elapsed(&start);
1981 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1982 "(1sec == %.2f) (wrong!)\n",
1983 diff, sec);
1984 ret = false;
1985 break;
1987 smb_msleep(1 * msec);
1990 GET_INFO_BOTH(finfo2,pinfo2);
1991 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1992 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1993 torture_comment(tctx, "Server did not update write_time (correct)\n");
1996 /* sleep */
1997 smb_msleep(5 * msec);
1999 GET_INFO_BOTH(finfo3,pinfo3);
2000 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2003 * the close updates the write time to the time of the close
2004 * and not to the time of the last write!
2006 torture_comment(tctx, "Close the file handle\n");
2007 smbcli_close(cli->tree, fnum1);
2008 fnum1 = -1;
2010 GET_INFO_PATH(pinfo4);
2011 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2013 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2014 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2017 done:
2018 if (fnum1 != -1)
2019 smbcli_close(cli->tree, fnum1);
2020 smbcli_unlink(cli->tree, fname);
2021 smbcli_deltree(cli->tree, BASEDIR);
2023 return ret;
2027 * Check that a write after a truncate write doesn't update
2028 * the timestamp, but a truncate write after a write does.
2029 * Also prove that a close after a truncate write updates the
2030 * timestamp to current, not the time of last write.
2033 static bool test_delayed_write_update3c(struct torture_context *tctx,
2034 struct smbcli_state *cli,
2035 struct smbcli_state *cli2)
2037 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
2038 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
2039 const char *fname = BASEDIR "\\torture_file3c.txt";
2040 int fnum1 = -1;
2041 bool ret = true;
2042 ssize_t written;
2043 int i;
2044 struct timeval start;
2045 struct timeval end;
2046 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2047 int normal_delay = 2000000;
2048 double sec = ((double)used_delay) / ((double)normal_delay);
2049 int msec = 1000 * sec;
2051 torture_comment(tctx, "\nRunning test_delayed_write_update3c\n");
2053 if (!torture_setup_dir(cli, BASEDIR)) {
2054 return false;
2057 torture_comment(tctx, "Open the file handle\n");
2058 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2059 if (fnum1 == -1) {
2060 ret = false;
2061 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2062 goto done;
2065 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2066 finfo0.basic_info.in.file.fnum = fnum1;
2067 finfo1 = finfo0;
2068 finfo2 = finfo0;
2069 finfo3 = finfo0;
2070 finfo4 = finfo0;
2071 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2072 pinfo0.basic_info.in.file.path = fname;
2073 pinfo1 = pinfo0;
2074 pinfo2 = pinfo0;
2075 pinfo3 = pinfo0;
2076 pinfo4 = pinfo0;
2077 pinfo5 = pinfo0;
2079 /* get the initial times */
2080 GET_INFO_BOTH(finfo0,pinfo0);
2083 * sleep some time, to demonstrate the handling of write times
2084 * doesn't depend on the time since the open
2086 smb_msleep(5 * msec);
2088 /* get the initial times */
2089 GET_INFO_BOTH(finfo1,pinfo1);
2090 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2093 * demonstrate that a truncate write always
2094 * updates the write time immediately
2096 for (i=0; i < 3; i++) {
2097 smb_msleep(2 * msec);
2098 /* do a write */
2099 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
2100 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2101 if (written != 0) {
2102 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2103 ret = false;
2104 goto done;
2106 /* get the times after the write */
2107 GET_INFO_BOTH(finfo2,pinfo2);
2108 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2109 finfo1 = finfo2;
2112 start = timeval_current();
2113 end = timeval_add(&start, 7 * sec, 0);
2114 while (!timeval_expired(&end)) {
2115 /* do a write */
2116 torture_comment(tctx, "Do a write on the file handle\n");
2117 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2118 if (written != 1) {
2119 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2120 ret = false;
2121 goto done;
2123 /* get the times after the write */
2124 GET_INFO_FILE(finfo2);
2126 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2127 double diff = timeval_elapsed(&start);
2128 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2129 "(1sec == %.2f) (wrong!)\n",
2130 diff, sec);
2131 ret = false;
2132 break;
2134 smb_msleep(1 * msec);
2137 GET_INFO_BOTH(finfo2,pinfo2);
2138 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2139 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2140 torture_comment(tctx, "Server did not update write_time (correct)\n");
2143 /* sleep */
2144 smb_msleep(5 * msec);
2146 /* get the initial times */
2147 GET_INFO_BOTH(finfo1,pinfo1);
2148 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
2151 * demonstrate that a truncate write always
2152 * updates the write time immediately
2154 for (i=0; i < 3; i++) {
2155 smb_msleep(2 * msec);
2156 /* do a write */
2157 torture_comment(tctx, "Do a truncate write [%d] on the file handle\n", i);
2158 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2159 if (written != 0) {
2160 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2161 ret = false;
2162 goto done;
2164 /* get the times after the write */
2165 GET_INFO_BOTH(finfo2,pinfo2);
2166 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2167 finfo1 = finfo2;
2170 /* sleep */
2171 smb_msleep(5 * msec);
2173 GET_INFO_BOTH(finfo2,pinfo2);
2174 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2176 /* sure any further write doesn't update the write time */
2177 start = timeval_current();
2178 end = timeval_add(&start, 15 * sec, 0);
2179 while (!timeval_expired(&end)) {
2180 /* do a write */
2181 torture_comment(tctx, "Do a write on the file handle\n");
2182 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2183 if (written != 1) {
2184 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2185 ret = false;
2186 goto done;
2188 /* get the times after the write */
2189 GET_INFO_BOTH(finfo2,pinfo2);
2191 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2192 double diff = timeval_elapsed(&start);
2193 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2194 "(1sec == %.2f) (wrong!)\n",
2195 diff, sec);
2196 ret = false;
2197 break;
2199 smb_msleep(1 * msec);
2202 GET_INFO_BOTH(finfo2,pinfo2);
2203 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2204 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2205 torture_comment(tctx, "Server did not update write_time (correct)\n");
2208 /* sleep */
2209 smb_msleep(5 * msec);
2211 GET_INFO_BOTH(finfo3,pinfo3);
2212 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2215 * the close updates the write time to the time of the close
2216 * and not to the time of the last write!
2218 torture_comment(tctx, "Close the file handle\n");
2219 smbcli_close(cli->tree, fnum1);
2220 fnum1 = -1;
2222 GET_INFO_PATH(pinfo4);
2223 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2225 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2226 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2229 done:
2230 if (fnum1 != -1)
2231 smbcli_close(cli->tree, fnum1);
2232 smbcli_unlink(cli->tree, fname);
2233 smbcli_deltree(cli->tree, BASEDIR);
2235 return ret;
2239 * Show only the first write updates the timestamp, and a close
2240 * after writes updates to current (I think this is the same
2241 * as test 3b. JRA).
2244 static bool test_delayed_write_update4(struct torture_context *tctx,
2245 struct smbcli_state *cli,
2246 struct smbcli_state *cli2)
2248 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
2249 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
2250 const char *fname = BASEDIR "\\torture_file4.txt";
2251 int fnum1 = -1;
2252 bool ret = true;
2253 ssize_t written;
2254 struct timeval start;
2255 struct timeval end;
2256 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2257 int normal_delay = 2000000;
2258 double sec = ((double)used_delay) / ((double)normal_delay);
2259 int msec = 1000 * sec;
2261 torture_comment(tctx, "\nRunning test_delayed_write_update4\n");
2263 if (!torture_setup_dir(cli, BASEDIR)) {
2264 return false;
2267 torture_comment(tctx, "Open the file handle\n");
2268 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2269 if (fnum1 == -1) {
2270 ret = false;
2271 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2272 goto done;
2275 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2276 finfo0.basic_info.in.file.fnum = fnum1;
2277 finfo1 = finfo0;
2278 finfo2 = finfo0;
2279 finfo3 = finfo0;
2280 finfo4 = finfo0;
2281 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2282 pinfo0.basic_info.in.file.path = fname;
2283 pinfo1 = pinfo0;
2284 pinfo2 = pinfo0;
2285 pinfo3 = pinfo0;
2286 pinfo4 = pinfo0;
2287 pinfo5 = pinfo0;
2289 /* get the initial times */
2290 GET_INFO_BOTH(finfo0,pinfo0);
2292 /* sleep a bit */
2293 smb_msleep(5 * msec);
2295 /* do a write */
2296 torture_comment(tctx, "Do a write on the file handle\n");
2297 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2298 if (written != 1) {
2299 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2300 ret = false;
2301 goto done;
2304 GET_INFO_BOTH(finfo1,pinfo1);
2305 COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
2308 * make sure the write time is updated 2 seconds later
2309 * calcuated from the first write
2310 * (but expect upto 3 seconds extra time for a busy server)
2312 start = timeval_current();
2313 end = timeval_add(&start, 5 * sec, 0);
2314 while (!timeval_expired(&end)) {
2315 /* get the times after the first write */
2316 GET_INFO_FILE(finfo1);
2318 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
2319 double diff = timeval_elapsed(&start);
2320 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
2321 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2322 "(1sec == %.2f) (wrong!)\n",
2323 diff, sec);
2324 ret = false;
2325 break;
2328 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2329 "(1sec == %.2f) (correct)\n",
2330 diff, sec);
2331 break;
2333 smb_msleep(0.5 * msec);
2336 GET_INFO_BOTH(finfo1,pinfo1);
2337 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
2339 /* sure any further write doesn't update the write time */
2340 start = timeval_current();
2341 end = timeval_add(&start, 15 * sec, 0);
2342 while (!timeval_expired(&end)) {
2343 /* do a write */
2344 torture_comment(tctx, "Do a write on the file handle\n");
2345 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2346 if (written != 1) {
2347 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2348 ret = false;
2349 goto done;
2351 /* get the times after the write */
2352 GET_INFO_BOTH(finfo2,pinfo2);
2354 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2355 double diff = timeval_elapsed(&start);
2356 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2357 "(1sec == %.2f) (wrong!)\n",
2358 diff, sec);
2359 ret = false;
2360 break;
2362 smb_msleep(1 * msec);
2365 GET_INFO_BOTH(finfo2,pinfo2);
2366 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2367 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2368 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
2371 /* sleep */
2372 smb_msleep(5 * msec);
2374 GET_INFO_BOTH(finfo3,pinfo3);
2375 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2378 * the close updates the write time to the time of the close
2379 * and not to the time of the last write!
2381 torture_comment(tctx, "Close the file handle\n");
2382 smbcli_close(cli->tree, fnum1);
2383 fnum1 = -1;
2385 GET_INFO_PATH(pinfo4);
2386 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2388 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2389 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2392 done:
2393 if (fnum1 != -1)
2394 smbcli_close(cli->tree, fnum1);
2395 smbcli_unlink(cli->tree, fname);
2396 smbcli_deltree(cli->tree, BASEDIR);
2398 return ret;
2402 * Show writes and closes have no effect on updating times once a SETWRITETIME is done.
2405 static bool test_delayed_write_update5(struct torture_context *tctx,
2406 struct smbcli_state *cli,
2407 struct smbcli_state *cli2)
2409 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2410 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2411 const char *fname = BASEDIR "\\torture_file5.txt";
2412 int fnum1 = -1;
2413 bool ret = true;
2414 ssize_t written;
2415 struct timeval start;
2416 struct timeval end;
2417 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2418 int normal_delay = 2000000;
2419 double sec = ((double)used_delay) / ((double)normal_delay);
2420 int msec = 1000 * sec;
2422 torture_comment(tctx, "\nRunning test_delayed_write_update5\n");
2424 if (!torture_setup_dir(cli, BASEDIR)) {
2425 return false;
2428 torture_comment(tctx, "Open the file handle\n");
2429 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2430 if (fnum1 == -1) {
2431 ret = false;
2432 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2433 goto done;
2436 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2437 finfo0.basic_info.in.file.fnum = fnum1;
2438 finfo1 = finfo0;
2439 finfo2 = finfo0;
2440 finfo3 = finfo0;
2441 finfo4 = finfo0;
2442 finfo5 = finfo0;
2443 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2444 pinfo0.basic_info.in.file.path = fname;
2445 pinfo1 = pinfo0;
2446 pinfo2 = pinfo0;
2447 pinfo3 = pinfo0;
2448 pinfo4 = pinfo0;
2449 pinfo5 = pinfo0;
2450 pinfo6 = pinfo0;
2452 /* get the initial times */
2453 GET_INFO_BOTH(finfo0,pinfo0);
2455 /* do a write */
2456 torture_comment(tctx, "Do a write on the file handle\n");
2457 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2458 if (written != 1) {
2459 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2460 ret = false;
2461 goto done;
2464 GET_INFO_BOTH(finfo1,pinfo1);
2465 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2467 torture_comment(tctx, "Set write time in the future on the file handle\n");
2468 SET_INFO_FILE(finfo0, time(NULL) + 86400);
2469 GET_INFO_BOTH(finfo2,pinfo2);
2470 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2472 torture_comment(tctx, "Set write time in the past on the file handle\n");
2473 SET_INFO_FILE(finfo0, time(NULL) - 86400);
2474 GET_INFO_BOTH(finfo2,pinfo2);
2475 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2477 /* make sure the 2 second delay from the first write are canceled */
2478 start = timeval_current();
2479 end = timeval_add(&start, 15 * sec, 0);
2480 while (!timeval_expired(&end)) {
2482 /* get the times after the first write */
2483 GET_INFO_BOTH(finfo3,pinfo3);
2485 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2486 double diff = timeval_elapsed(&start);
2487 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2488 "(1sec == %.2f) (wrong!)\n",
2489 diff, sec);
2490 ret = false;
2491 break;
2493 smb_msleep(1 * msec);
2496 GET_INFO_BOTH(finfo3,pinfo3);
2497 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2498 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2499 torture_comment(tctx, "Server did not update write_time (correct)\n");
2502 /* sure any further write doesn't update the write time */
2503 start = timeval_current();
2504 end = timeval_add(&start, 15 * sec, 0);
2505 while (!timeval_expired(&end)) {
2506 /* do a write */
2507 torture_comment(tctx, "Do a write on the file handle\n");
2508 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2509 if (written != 1) {
2510 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2511 ret = false;
2512 goto done;
2514 /* get the times after the write */
2515 GET_INFO_BOTH(finfo4,pinfo4);
2517 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2518 double diff = timeval_elapsed(&start);
2519 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2520 "(1sec == %.2f) (wrong!)\n",
2521 diff, sec);
2522 ret = false;
2523 break;
2525 smb_msleep(1 * msec);
2528 GET_INFO_BOTH(finfo4,pinfo4);
2529 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2530 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2531 torture_comment(tctx, "Server did not update write_time (correct)\n");
2534 /* sleep */
2535 smb_msleep(5 * msec);
2537 GET_INFO_BOTH(finfo5,pinfo5);
2538 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2541 * the close doesn't update the write time
2543 torture_comment(tctx, "Close the file handle\n");
2544 smbcli_close(cli->tree, fnum1);
2545 fnum1 = -1;
2547 GET_INFO_PATH(pinfo6);
2548 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2550 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2551 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2554 done:
2555 if (fnum1 != -1)
2556 smbcli_close(cli->tree, fnum1);
2557 smbcli_unlink(cli->tree, fname);
2558 smbcli_deltree(cli->tree, BASEDIR);
2560 return ret;
2564 * Show truncate writes and closes have no effect on updating times once a SETWRITETIME is done.
2567 static bool test_delayed_write_update5b(struct torture_context *tctx,
2568 struct smbcli_state *cli,
2569 struct smbcli_state *cli2)
2571 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2572 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2573 const char *fname = BASEDIR "\\torture_fileb.txt";
2574 int fnum1 = -1;
2575 bool ret = true;
2576 ssize_t written;
2577 struct timeval start;
2578 struct timeval end;
2579 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2580 int normal_delay = 2000000;
2581 double sec = ((double)used_delay) / ((double)normal_delay);
2582 int msec = 1000 * sec;
2584 torture_comment(tctx, "\nRunning test_delayed_write_update5b\n");
2586 if (!torture_setup_dir(cli, BASEDIR)) {
2587 return false;
2590 torture_comment(tctx, "Open the file handle\n");
2591 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2592 if (fnum1 == -1) {
2593 ret = false;
2594 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2595 goto done;
2598 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2599 finfo0.basic_info.in.file.fnum = fnum1;
2600 finfo1 = finfo0;
2601 finfo2 = finfo0;
2602 finfo3 = finfo0;
2603 finfo4 = finfo0;
2604 finfo5 = finfo0;
2605 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2606 pinfo0.basic_info.in.file.path = fname;
2607 pinfo1 = pinfo0;
2608 pinfo2 = pinfo0;
2609 pinfo3 = pinfo0;
2610 pinfo4 = pinfo0;
2611 pinfo5 = pinfo0;
2612 pinfo6 = pinfo0;
2614 /* get the initial times */
2615 GET_INFO_BOTH(finfo0,pinfo0);
2617 /* do a write */
2618 torture_comment(tctx, "Do a write on the file handle\n");
2619 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2620 if (written != 1) {
2621 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2622 ret = false;
2623 goto done;
2626 GET_INFO_BOTH(finfo1,pinfo1);
2627 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2629 torture_comment(tctx, "Set write time in the future on the file handle\n");
2630 SET_INFO_FILE(finfo0, time(NULL) + 86400);
2631 GET_INFO_BOTH(finfo2,pinfo2);
2632 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2634 torture_comment(tctx, "Set write time in the past on the file handle\n");
2635 SET_INFO_FILE(finfo0, time(NULL) - 86400);
2636 GET_INFO_BOTH(finfo2,pinfo2);
2637 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2639 /* make sure the 2 second delay from the first write are canceled */
2640 start = timeval_current();
2641 end = timeval_add(&start, 15 * sec, 0);
2642 while (!timeval_expired(&end)) {
2644 /* get the times after the first write */
2645 GET_INFO_BOTH(finfo3,pinfo3);
2647 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2648 double diff = timeval_elapsed(&start);
2649 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2650 "(1sec == %.2f) (wrong!)\n",
2651 diff, sec);
2652 ret = false;
2653 break;
2655 smb_msleep(1 * msec);
2658 GET_INFO_BOTH(finfo3,pinfo3);
2659 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2660 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2661 torture_comment(tctx, "Server did not update write_time (correct)\n");
2664 /* Do any further write (truncates) update the write time ? */
2665 start = timeval_current();
2666 end = timeval_add(&start, 15 * sec, 0);
2667 while (!timeval_expired(&end)) {
2668 /* do a write */
2669 torture_comment(tctx, "Do a truncate write on the file handle\n");
2670 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
2671 if (written != 0) {
2672 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2673 ret = false;
2674 goto done;
2676 /* get the times after the write */
2677 GET_INFO_BOTH(finfo4,pinfo4);
2679 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2680 double diff = timeval_elapsed(&start);
2681 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2682 "(1sec == %.2f) (wrong!)\n",
2683 diff, sec);
2684 ret = false;
2685 break;
2687 smb_msleep(1 * msec);
2690 GET_INFO_BOTH(finfo4,pinfo4);
2691 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2692 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2693 torture_comment(tctx, "Server did not update write_time (correct)\n");
2696 /* sleep */
2697 smb_msleep(5 * msec);
2699 GET_INFO_BOTH(finfo5,pinfo5);
2700 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2703 * the close doesn't update the write time
2705 torture_comment(tctx, "Close the file handle\n");
2706 smbcli_close(cli->tree, fnum1);
2707 fnum1 = -1;
2709 GET_INFO_PATH(pinfo6);
2710 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2712 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2713 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2716 done:
2717 if (fnum1 != -1)
2718 smbcli_close(cli->tree, fnum1);
2719 smbcli_unlink(cli->tree, fname);
2720 smbcli_deltree(cli->tree, BASEDIR);
2722 return ret;
2726 * Open 2 handles on a file. Write one one and then set the
2727 * WRITE TIME explicitly on the other. Ensure the write time
2728 * update is cancelled. Ensure the write time is updated to
2729 * the close time when the non-explicit set handle is closed.
2733 static bool test_delayed_write_update6(struct torture_context *tctx,
2734 struct smbcli_state *cli,
2735 struct smbcli_state *cli2)
2737 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2738 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
2739 const char *fname = BASEDIR "\\torture_file6.txt";
2740 int fnum1 = -1;
2741 int fnum2 = -1;
2742 bool ret = true;
2743 ssize_t written;
2744 struct timeval start;
2745 struct timeval end;
2746 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2747 int normal_delay = 2000000;
2748 double sec = ((double)used_delay) / ((double)normal_delay);
2749 int msec = 1000 * sec;
2750 bool first = true;
2752 torture_comment(tctx, "\nRunning test_delayed_write_update6\n");
2754 if (!torture_setup_dir(cli, BASEDIR)) {
2755 return false;
2757 again:
2758 torture_comment(tctx, "Open the file handle\n");
2759 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2760 if (fnum1 == -1) {
2761 ret = false;
2762 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2763 goto done;
2766 if (fnum2 == -1) {
2767 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
2768 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2769 if (fnum2 == -1) {
2770 ret = false;
2771 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2772 goto done;
2776 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2777 finfo0.basic_info.in.file.fnum = fnum1;
2778 finfo1 = finfo0;
2779 finfo2 = finfo0;
2780 finfo3 = finfo0;
2781 finfo4 = finfo0;
2782 finfo5 = finfo0;
2783 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2784 pinfo0.basic_info.in.file.path = fname;
2785 pinfo1 = pinfo0;
2786 pinfo2 = pinfo0;
2787 pinfo3 = pinfo0;
2788 pinfo4 = pinfo0;
2789 pinfo5 = pinfo0;
2790 pinfo6 = pinfo0;
2791 pinfo7 = pinfo0;
2793 /* get the initial times */
2794 GET_INFO_BOTH(finfo0,pinfo0);
2796 /* do a write */
2797 torture_comment(tctx, "Do a write on the file handle\n");
2798 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2799 if (written != 1) {
2800 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2801 ret = false;
2802 goto done;
2805 GET_INFO_BOTH(finfo1,pinfo1);
2806 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2808 torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
2809 SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
2810 GET_INFO_BOTH(finfo2,pinfo2);
2811 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2813 torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
2814 SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
2815 GET_INFO_BOTH(finfo2,pinfo2);
2816 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2818 /* make sure the 2 second delay from the first write are canceled */
2819 start = timeval_current();
2820 end = timeval_add(&start, 10 * sec, 0);
2821 while (!timeval_expired(&end)) {
2823 /* get the times after the first write */
2824 GET_INFO_BOTH(finfo3,pinfo3);
2826 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2827 double diff = timeval_elapsed(&start);
2828 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2829 "(1sec == %.2f) (wrong!)\n",
2830 diff, sec);
2831 ret = false;
2832 break;
2834 smb_msleep(1 * msec);
2837 GET_INFO_BOTH(finfo3,pinfo3);
2838 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2839 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2840 torture_comment(tctx, "Server did not update write_time (correct)\n");
2843 /* sure any further write doesn't update the write time */
2844 start = timeval_current();
2845 end = timeval_add(&start, 10 * sec, 0);
2846 while (!timeval_expired(&end)) {
2847 /* do a write */
2848 torture_comment(tctx, "Do a write on the file handle\n");
2849 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2850 if (written != 1) {
2851 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2852 ret = false;
2853 goto done;
2855 /* get the times after the write */
2856 GET_INFO_BOTH(finfo4,pinfo4);
2858 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2859 double diff = timeval_elapsed(&start);
2860 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2861 "(1sec == %.2f) (wrong!)\n",
2862 diff, sec);
2863 ret = false;
2864 break;
2866 smb_msleep(1 * msec);
2869 GET_INFO_BOTH(finfo4,pinfo4);
2870 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2871 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2872 torture_comment(tctx, "Server did not update write_time (correct)\n");
2875 /* sleep */
2876 smb_msleep(5 * msec);
2878 GET_INFO_BOTH(finfo5,pinfo5);
2879 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2882 * the close updates the write time to the time of the close
2883 * as the write time was set on the 2nd handle
2885 torture_comment(tctx, "Close the file handle\n");
2886 smbcli_close(cli->tree, fnum1);
2887 fnum1 = -1;
2889 GET_INFO_PATH(pinfo6);
2890 COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
2892 if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
2893 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2896 /* See what the second write handle thinks the time is ? */
2897 finfo5.basic_info.in.file.fnum = fnum2;
2898 GET_INFO_FILE2(finfo5);
2899 COMPARE_WRITE_TIME_EQUAL(finfo5, pinfo6);
2901 /* See if we have lost the sticky write time on handle2 */
2902 smb_msleep(3 * msec);
2903 torture_comment(tctx, "Have we lost the sticky write time ?\n");
2905 /* Make sure any further normal write doesn't update the write time */
2906 start = timeval_current();
2907 end = timeval_add(&start, 10 * sec, 0);
2908 while (!timeval_expired(&end)) {
2909 /* do a write */
2910 torture_comment(tctx, "Do a write on the second file handle\n");
2911 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
2912 if (written != 1) {
2913 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2914 ret = false;
2915 goto done;
2917 /* get the times after the write */
2918 GET_INFO_FILE2(finfo5);
2919 GET_INFO_PATH(pinfo6);
2921 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2922 double diff = timeval_elapsed(&start);
2923 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2924 "(1sec == %.2f) (wrong!)\n",
2925 diff, sec);
2926 ret = false;
2927 break;
2929 smb_msleep(1 * msec);
2932 /* What about a truncate write ? */
2933 start = timeval_current();
2934 end = timeval_add(&start, 10 * sec, 0);
2935 while (!timeval_expired(&end)) {
2936 /* do a write */
2937 torture_comment(tctx, "Do a truncate write on the second file handle\n");
2938 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 0);
2939 if (written != 0) {
2940 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2941 ret = false;
2942 goto done;
2944 /* get the times after the write */
2945 GET_INFO_FILE2(finfo5);
2946 GET_INFO_PATH(pinfo6);
2948 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2949 double diff = timeval_elapsed(&start);
2950 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2951 "(1sec == %.2f) (wrong!)\n",
2952 diff, sec);
2953 ret = false;
2954 break;
2956 smb_msleep(1 * msec);
2960 /* keep the 2nd handle open and rerun tests */
2961 if (first) {
2962 first = false;
2963 goto again;
2967 * closing the 2nd handle will cause no write time update
2968 * as the write time was explicit set on this handle
2970 torture_comment(tctx, "Close the 2nd file handle\n");
2971 smbcli_close(cli2->tree, fnum2);
2972 fnum2 = -1;
2974 GET_INFO_PATH(pinfo7);
2975 COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
2977 if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
2978 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2981 done:
2982 if (fnum1 != -1)
2983 smbcli_close(cli->tree, fnum1);
2984 if (fnum2 != -1)
2985 smbcli_close(cli2->tree, fnum2);
2986 smbcli_unlink(cli->tree, fname);
2987 smbcli_deltree(cli->tree, BASEDIR);
2989 return ret;
2992 static bool test_delayed_write_update7(struct torture_context *tctx, struct smbcli_state *cli)
2994 union smb_open open_parms;
2995 union smb_fileinfo finfo1, finfo2, finfo3;
2996 const char *fname = BASEDIR "\\torture_file7.txt";
2997 NTSTATUS status;
2998 int fnum1 = -1;
2999 bool ret = true;
3000 TALLOC_CTX *mem_ctx;
3002 torture_comment(tctx, "\nRunning test_delayed_write_update7 (timestamp resolution test)\n");
3004 mem_ctx = talloc_init("test_delayed_write_update7");
3005 if (!mem_ctx) return false;
3007 ZERO_STRUCT(finfo1);
3008 ZERO_STRUCT(finfo2);
3009 ZERO_STRUCT(finfo3);
3010 ZERO_STRUCT(open_parms);
3012 if (!torture_setup_dir(cli, BASEDIR)) {
3013 return false;
3016 /* Create the file. */
3017 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
3018 if (fnum1 == -1) {
3019 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
3020 return false;
3023 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
3024 finfo1.basic_info.in.file.fnum = fnum1;
3025 finfo2 = finfo1;
3026 finfo3 = finfo1;
3028 /* Get the initial timestamps. */
3029 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
3031 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
3033 /* Set the pending write time to a value with ns. */
3034 SET_INFO_FILE_NS(finfo, time(NULL) + 86400, 103, cli->tree, fnum1);
3036 /* Get the current pending write time by fnum. */
3037 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
3039 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
3041 /* Ensure the time is actually different. */
3042 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
3043 torture_result(tctx, TORTURE_FAIL,
3044 "setfileinfo time matches original fileinfo time");
3045 ret = false;
3048 /* Get the current pending write time by path. */
3049 finfo3.basic_info.in.file.path = fname;
3050 status = smb_raw_pathinfo(cli->tree, tctx, &finfo3);
3052 if (finfo2.basic_info.out.write_time != finfo3.basic_info.out.write_time) {
3053 torture_result(tctx, TORTURE_FAIL,
3054 "qpathinfo time doens't match fileinfo time");
3055 ret = false;
3058 /* Now close the file. Re-open and check that the write
3059 time is identical to the one we wrote. */
3061 smbcli_close(cli->tree, fnum1);
3063 open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
3064 open_parms.ntcreatex.in.flags = 0;
3065 open_parms.ntcreatex.in.access_mask = SEC_GENERIC_READ;
3066 open_parms.ntcreatex.in.file_attr = 0;
3067 open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE|
3068 NTCREATEX_SHARE_ACCESS_READ|
3069 NTCREATEX_SHARE_ACCESS_WRITE;
3070 open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
3071 open_parms.ntcreatex.in.create_options = 0;
3072 open_parms.ntcreatex.in.fname = fname;
3074 status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
3075 talloc_free(mem_ctx);
3077 if (!NT_STATUS_IS_OK(status)) {
3078 torture_result(tctx, TORTURE_FAIL,
3079 "setfileinfo time matches original fileinfo time");
3080 ret = false;
3083 fnum1 = open_parms.ntcreatex.out.file.fnum;
3085 /* Check the returned time matches. */
3086 if (open_parms.ntcreatex.out.write_time != finfo2.basic_info.out.write_time) {
3087 torture_result(tctx, TORTURE_FAIL,
3088 "final open time does not match set time");
3089 ret = false;
3092 done:
3094 smbcli_close(cli->tree, fnum1);
3096 smbcli_unlink(cli->tree, fname);
3097 smbcli_deltree(cli->tree, BASEDIR);
3098 return ret;
3102 testing of delayed update of write_time
3104 struct torture_suite *torture_delay_write(void)
3106 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "delaywrite");
3108 torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
3109 torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
3110 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate", test_delayed_write_update1);
3111 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a);
3112 torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b);
3113 torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c);
3114 torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
3115 torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
3116 torture_suite_add_2smb_test(suite, "delayed update of write time 3a", test_delayed_write_update3a);
3117 torture_suite_add_2smb_test(suite, "delayed update of write time 3b", test_delayed_write_update3b);
3118 torture_suite_add_2smb_test(suite, "delayed update of write time 3c", test_delayed_write_update3c);
3119 torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
3120 torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
3121 torture_suite_add_2smb_test(suite, "delayed update of write time 5b", test_delayed_write_update5b);
3122 torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
3123 torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7);
3124 torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7);
3126 return suite;