s4: allow python code to dump NTACL object as well
[Samba/ekacnet.git] / source4 / torture / basic / delaywrite.c
blob0c43c29cade006c6fbe84e19169f311e9ed336ce
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"
33 #define W2K8R2_TIMEDELAY_SECS 1
34 #define W2K3_TIMEDELAY_SECS 2
35 #define TIMEDELAY_SECS W2K3_TIMEDELAY_SECS
37 #define BASEDIR "\\delaywrite"
39 static bool test_delayed_write_update(struct torture_context *tctx, struct smbcli_state *cli)
41 union smb_fileinfo finfo1, finfo2;
42 const char *fname = BASEDIR "\\torture_file.txt";
43 NTSTATUS status;
44 int fnum1 = -1;
45 bool ret = true;
46 ssize_t written;
47 struct timeval start;
48 struct timeval end;
49 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
50 int normal_delay = 2000000;
51 double sec = ((double)used_delay) / ((double)normal_delay);
52 int msec = 1000 * sec;
54 torture_comment(tctx, "\nRunning test_delayed_write_update\n");
56 if (!torture_setup_dir(cli, BASEDIR)) {
57 return false;
60 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
61 if (fnum1 == -1) {
62 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
63 return false;
66 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
67 finfo1.basic_info.in.file.fnum = fnum1;
68 finfo2 = finfo1;
70 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
72 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
74 torture_comment(tctx, "Initial write time %s\n",
75 nt_time_string(tctx, finfo1.basic_info.out.write_time));
77 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
79 if (written != 1) {
80 torture_result(tctx, TORTURE_FAIL,
81 "write failed - wrote %d bytes (%s)\n",
82 (int)written, __location__);
83 return false;
86 start = timeval_current();
87 end = timeval_add(&start, (120*sec), 0);
88 while (!timeval_expired(&end)) {
89 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
91 if (!NT_STATUS_IS_OK(status)) {
92 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
93 ret = false;
94 break;
96 torture_comment(tctx, "write time %s\n",
97 nt_time_string(tctx, finfo2.basic_info.out.write_time));
98 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
99 double diff = timeval_elapsed(&start);
100 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
101 torture_comment(tctx, "Server updated write_time after %.2f seconds"
102 "(1 sec == %.2f)(wrong!)\n",
103 diff, sec);
104 ret = false;
105 break;
108 torture_comment(tctx, "Server updated write_time after %.2f seconds"
109 "(1 sec == %.2f)(correct)\n",
110 diff, sec);
111 break;
113 fflush(stdout);
114 msleep(1 * msec);
117 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
118 torture_result(tctx, TORTURE_FAIL,
119 "Server did not update write time (wrong!)");
120 ret = false;
124 if (fnum1 != -1)
125 smbcli_close(cli->tree, fnum1);
126 smbcli_unlink(cli->tree, fname);
127 smbcli_deltree(cli->tree, BASEDIR);
129 return ret;
132 static bool test_delayed_write_update1(struct torture_context *tctx, struct smbcli_state *cli)
134 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
135 const char *fname = BASEDIR "\\torture_file1.txt";
136 NTSTATUS status;
137 int fnum1 = -1;
138 bool ret = true;
139 ssize_t written;
140 struct timeval start;
141 struct timeval end;
142 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
143 int normal_delay = 2000000;
144 double sec = ((double)used_delay) / ((double)normal_delay);
145 int msec = 1000 * sec;
146 char buf[2048];
148 torture_comment(tctx, "\nRunning test_delayed_write_update1\n");
150 if (!torture_setup_dir(cli, BASEDIR)) {
151 return false;
154 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
155 if (fnum1 == -1) {
156 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
157 return false;
160 memset(buf, 'x', 2048);
161 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
163 /* 3 second delay to ensure we get past any 2 second time
164 granularity (older systems may have that) */
165 msleep(3 * msec);
167 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
168 finfo1.all_info.in.file.fnum = fnum1;
169 finfo2 = finfo1;
170 finfo3 = finfo1;
171 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
172 pinfo4.all_info.in.file.path = fname;
174 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
176 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
178 torture_comment(tctx, "Initial write time %s\n",
179 nt_time_string(tctx, finfo1.all_info.out.write_time));
181 /* 3 second delay to ensure we get past any 2 second time
182 granularity (older systems may have that) */
183 msleep(3 * msec);
185 /* Do a zero length SMBwrite call to truncate. */
186 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
188 if (written != 0) {
189 torture_result(tctx, TORTURE_FAIL,
190 "write failed - wrote %d bytes (%s)\n",
191 (int)written, __location__);
192 return false;
195 start = timeval_current();
196 end = timeval_add(&start, (120*sec), 0);
197 while (!timeval_expired(&end)) {
198 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
200 if (!NT_STATUS_IS_OK(status)) {
201 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
202 ret = false;
203 break;
206 if (finfo2.all_info.out.size != 1024) {
207 torture_result(tctx, TORTURE_FAIL,
208 "file not truncated, size = %u (should be 1024)",
209 (unsigned int)finfo2.all_info.out.size);
210 ret = false;
211 break;
214 torture_comment(tctx, "write time %s\n",
215 nt_time_string(tctx, finfo2.all_info.out.write_time));
216 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
217 double diff = timeval_elapsed(&start);
218 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
219 torture_comment(tctx, "After SMBwrite truncate "
220 "server updated write_time after %.2f seconds"
221 "(1 sec == %.2f)(wrong!)\n",
222 diff, sec);
223 ret = false;
224 break;
227 torture_comment(tctx, "After SMBwrite truncate "
228 "server updated write_time after %.2f seconds"
229 "(1 sec == %.2f)(correct)\n",
230 diff, sec);
231 break;
233 fflush(stdout);
234 msleep(1 * msec);
237 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
238 torture_result(tctx, TORTURE_FAIL,
239 "Server did not update write time (wrong!)");
240 ret = false;
243 fflush(stdout);
244 msleep(2 * msec);
246 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
247 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
249 if (written != 1) {
250 torture_result(tctx, TORTURE_FAIL,
251 "write failed - wrote %d bytes (%s)",
252 (int)written, __location__);
253 return false;
256 start = timeval_current();
257 end = timeval_add(&start, (10*sec), 0);
258 while (!timeval_expired(&end)) {
259 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
261 if (!NT_STATUS_IS_OK(status)) {
262 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
263 ret = false;
264 break;
267 if (finfo3.all_info.out.size != 1024) {
268 DEBUG(0, ("file not truncated, size = %u (should be 1024)\n",
269 (unsigned int)finfo3.all_info.out.size));
270 ret = false;
271 break;
274 torture_comment(tctx, "write time %s\n",
275 nt_time_string(tctx, finfo3.all_info.out.write_time));
276 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
277 double diff = timeval_elapsed(&start);
279 torture_comment(tctx, "server updated write_time after %.2f seconds"
280 "(1 sec == %.2f)(wrong)\n",
281 diff, sec);
282 break;
284 fflush(stdout);
285 msleep(1 * msec);
288 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
289 torture_result(tctx, TORTURE_FAIL,
290 "Server updated write time (wrong!)");
291 ret = false;
294 fflush(stdout);
295 msleep(2 * msec);
297 /* the close should trigger an write time update */
298 smbcli_close(cli->tree, fnum1);
299 fnum1 = -1;
301 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
302 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
304 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
305 torture_result(tctx, TORTURE_FAIL,
306 "Server did not update write time on close (wrong!)");
307 ret = false;
308 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
309 torture_comment(tctx, "Server updated write time on close (correct)\n");
312 if (fnum1 != -1)
313 smbcli_close(cli->tree, fnum1);
314 smbcli_unlink(cli->tree, fname);
315 smbcli_deltree(cli->tree, BASEDIR);
317 return ret;
320 /* Updating with a SMBwrite of zero length
321 * changes the write time immediately - even on expand. */
323 static bool test_delayed_write_update1a(struct torture_context *tctx, struct smbcli_state *cli)
325 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
326 const char *fname = BASEDIR "\\torture_file1a.txt";
327 NTSTATUS status;
328 int fnum1 = -1;
329 bool ret = true;
330 ssize_t written;
331 struct timeval start;
332 struct timeval end;
333 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
334 int normal_delay = 2000000;
335 double sec = ((double)used_delay) / ((double)normal_delay);
336 int msec = 1000 * sec;
337 char buf[2048];
339 torture_comment(tctx, "\nRunning test_delayed_write_update1a\n");
341 if (!torture_setup_dir(cli, BASEDIR)) {
342 return false;
345 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
346 if (fnum1 == -1) {
347 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
348 return false;
351 memset(buf, 'x', 2048);
352 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
354 /* 3 second delay to ensure we get past any 2 second time
355 granularity (older systems may have that) */
356 msleep(3 * msec);
358 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
359 finfo1.all_info.in.file.fnum = fnum1;
360 finfo2 = finfo1;
361 finfo3 = finfo1;
362 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
363 pinfo4.all_info.in.file.path = fname;
365 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
367 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
369 torture_comment(tctx, "Initial write time %s\n",
370 nt_time_string(tctx, finfo1.all_info.out.write_time));
372 /* Do a zero length SMBwrite call to truncate. */
373 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
375 if (written != 0) {
376 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)",
377 (int)written, __location__);
378 return false;
381 start = timeval_current();
382 end = timeval_add(&start, (120*sec), 0);
383 while (!timeval_expired(&end)) {
384 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
386 if (!NT_STATUS_IS_OK(status)) {
387 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s",
388 nt_errstr(status));
389 ret = false;
390 break;
393 if (finfo2.all_info.out.size != 10240) {
394 torture_result(tctx, TORTURE_FAIL,
395 "file not truncated, size = %u (should be 10240)",
396 (unsigned int)finfo2.all_info.out.size);
397 ret = false;
398 break;
401 torture_comment(tctx, "write time %s\n",
402 nt_time_string(tctx, finfo2.all_info.out.write_time));
403 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
404 double diff = timeval_elapsed(&start);
405 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
406 torture_comment(tctx, "After SMBwrite truncate "
407 "server updated write_time after %.2f seconds"
408 "(1 sec == %.2f)(wrong!)\n",
409 diff, sec);
410 ret = false;
411 break;
414 torture_comment(tctx, "After SMBwrite truncate "
415 "server updated write_time after %.2f seconds"
416 "(1 sec == %.2f)(correct)\n",
417 diff, sec);
418 break;
420 fflush(stdout);
421 msleep(1 * msec);
424 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
425 torture_result(tctx, TORTURE_FAIL,
426 "Server did not update write time (wrong!)");
427 ret = false;
430 fflush(stdout);
431 msleep(2 * msec);
433 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
434 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
436 torture_assert_int_equal(tctx, written, 1,
437 "unexpected number of bytes written");
439 start = timeval_current();
440 end = timeval_add(&start, (10*sec), 0);
441 while (!timeval_expired(&end)) {
442 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
444 if (!NT_STATUS_IS_OK(status)) {
445 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s\n",
446 nt_errstr(status));
447 ret = false;
448 break;
451 if (finfo3.all_info.out.size != 10240) {
452 torture_result(tctx, TORTURE_FAIL,
453 "file not truncated, size = %u (should be 10240)",
454 (unsigned int)finfo3.all_info.out.size);
455 ret = false;
456 break;
459 torture_comment(tctx, "write time %s\n",
460 nt_time_string(tctx, finfo3.all_info.out.write_time));
461 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
462 double diff = timeval_elapsed(&start);
464 torture_comment(tctx, "server updated write_time after %.2f seconds"
465 "(1 sec == %.2f)(correct)\n",
466 diff, sec);
467 break;
469 fflush(stdout);
470 msleep(1 * msec);
473 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
474 torture_result(tctx, TORTURE_FAIL,
475 "Server updated write time (wrong!)");
476 ret = false;
479 /* the close should trigger an write time update */
480 smbcli_close(cli->tree, fnum1);
481 fnum1 = -1;
483 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
484 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
486 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
487 torture_result(tctx, TORTURE_FAIL,
488 "Server did not update write time on close (wrong!)");
489 ret = false;
490 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
491 torture_comment(tctx, "Server updated write time on close (correct)\n");
494 if (fnum1 != -1)
495 smbcli_close(cli->tree, fnum1);
496 smbcli_unlink(cli->tree, fname);
497 smbcli_deltree(cli->tree, BASEDIR);
499 return ret;
502 /* Updating with a SET_FILE_END_OF_FILE_INFO
503 * changes the write time immediately - even on expand. */
505 static bool test_delayed_write_update1b(struct torture_context *tctx, struct smbcli_state *cli)
507 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
508 const char *fname = BASEDIR "\\torture_file1b.txt";
509 NTSTATUS status;
510 int fnum1 = -1;
511 bool ret = true;
512 ssize_t written;
513 struct timeval start;
514 struct timeval end;
515 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
516 int normal_delay = 2000000;
517 double sec = ((double)used_delay) / ((double)normal_delay);
518 int msec = 1000 * sec;
519 char buf[2048];
521 torture_comment(tctx, "\nRunning test_delayed_write_update1b\n");
523 if (!torture_setup_dir(cli, BASEDIR)) {
524 return false;
527 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
528 if (fnum1 == -1) {
529 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
530 return false;
533 memset(buf, 'x', 2048);
534 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
536 /* 3 second delay to ensure we get past any 2 second time
537 granularity (older systems may have that) */
538 msleep(3 * msec);
540 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
541 finfo1.all_info.in.file.fnum = fnum1;
542 finfo2 = finfo1;
543 finfo3 = finfo1;
544 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
545 pinfo4.all_info.in.file.path = fname;
547 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
549 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
551 torture_comment(tctx, "Initial write time %s\n",
552 nt_time_string(tctx, finfo1.all_info.out.write_time));
554 /* Do a SET_END_OF_FILE_INFO call to truncate. */
555 status = smbcli_ftruncate(cli->tree, fnum1, (uint64_t)10240);
557 torture_assert_ntstatus_ok(tctx, status, "SET_END_OF_FILE failed");
559 start = timeval_current();
560 end = timeval_add(&start, (120*sec), 0);
561 while (!timeval_expired(&end)) {
562 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
564 if (!NT_STATUS_IS_OK(status)) {
565 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
566 ret = false;
567 break;
570 if (finfo2.all_info.out.size != 10240) {
571 torture_result(tctx, TORTURE_FAIL,
572 "file not truncated (size = %u, should be 10240)",
573 (unsigned int)finfo2.all_info.out.size );
574 ret = false;
575 break;
578 torture_comment(tctx, "write time %s\n",
579 nt_time_string(tctx, finfo2.all_info.out.write_time));
580 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
581 double diff = timeval_elapsed(&start);
582 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
583 torture_result(tctx, TORTURE_FAIL,
584 "After SET_END_OF_FILE truncate "
585 "server updated write_time after %.2f seconds"
586 "(1 sec == %.2f)(wrong!)",
587 diff, sec);
588 ret = false;
589 break;
592 torture_comment(tctx, "After SET_END_OF_FILE truncate "
593 "server updated write_time after %.2f seconds"
594 "(1 sec == %.2f)(correct)\n",
595 diff, sec);
596 break;
598 fflush(stdout);
599 msleep(1 * msec);
602 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
603 torture_result(tctx, TORTURE_FAIL,
604 "Server did not update write time (wrong!)");
605 ret = false;
608 fflush(stdout);
609 msleep(2 * msec);
611 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
612 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
614 torture_assert_int_equal(tctx, written, 1,
615 "unexpected number of bytes written");
617 start = timeval_current();
618 end = timeval_add(&start, (10*sec), 0);
619 while (!timeval_expired(&end)) {
620 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
622 if (!NT_STATUS_IS_OK(status)) {
623 torture_result(tctx, TORTURE_FAIL,
624 "fileinfo failed: %s", nt_errstr(status));
625 ret = false;
626 break;
629 if (finfo3.all_info.out.size != 10240) {
630 DEBUG(0, ("file not truncated (size = %u, should be 10240)\n",
631 (unsigned int)finfo3.all_info.out.size ));
632 ret = false;
633 break;
636 torture_comment(tctx, "write time %s\n",
637 nt_time_string(tctx, finfo3.all_info.out.write_time));
638 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
639 double diff = timeval_elapsed(&start);
641 torture_comment(tctx, "server updated write_time after %.2f seconds"
642 "(1 sec == %.2f)(correct)\n",
643 diff, sec);
644 break;
646 fflush(stdout);
647 msleep(1 * msec);
650 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
651 torture_result(tctx, TORTURE_FAIL, "Server updated write time (wrong!)\n");
652 ret = false;
655 /* the close should trigger an write time update */
656 smbcli_close(cli->tree, fnum1);
657 fnum1 = -1;
659 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
660 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
662 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
663 torture_result(tctx, TORTURE_FAIL, "Server did not update write time on close (wrong!)\n");
664 ret = false;
665 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
666 torture_comment(tctx, "Server updated write time on close (correct)\n");
669 if (fnum1 != -1)
670 smbcli_close(cli->tree, fnum1);
671 smbcli_unlink(cli->tree, fname);
672 smbcli_deltree(cli->tree, BASEDIR);
674 return ret;
677 /* Updating with a SET_ALLOCATION_INFO (truncate) does so immediately. */
679 static bool test_delayed_write_update1c(struct torture_context *tctx, struct smbcli_state *cli)
681 union smb_setfileinfo parms;
682 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
683 const char *fname = BASEDIR "\\torture_file1c.txt";
684 NTSTATUS status;
685 int fnum1 = -1;
686 bool ret = true;
687 ssize_t written;
688 struct timeval start;
689 struct timeval end;
690 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
691 int normal_delay = 2000000;
692 double sec = ((double)used_delay) / ((double)normal_delay);
693 int msec = 1000 * sec;
694 char buf[2048];
696 torture_comment(tctx, "\nRunning test_delayed_write_update1c\n");
698 if (!torture_setup_dir(cli, BASEDIR)) {
699 return false;
702 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
703 if (fnum1 == -1) {
704 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
705 return false;
708 memset(buf, 'x', 2048);
709 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
711 /* 3 second delay to ensure we get past any 2 second time
712 granularity (older systems may have that) */
713 msleep(3 * msec);
715 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
716 finfo1.all_info.in.file.fnum = fnum1;
717 finfo2 = finfo1;
718 finfo3 = finfo1;
719 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
720 pinfo4.all_info.in.file.path = fname;
722 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
724 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
726 torture_comment(tctx, "Initial write time %s\n",
727 nt_time_string(tctx, finfo1.all_info.out.write_time));
729 /* Do a SET_ALLOCATION_SIZE call to truncate. */
730 parms.allocation_info.level = RAW_SFILEINFO_ALLOCATION_INFO;
731 parms.allocation_info.in.file.fnum = fnum1;
732 parms.allocation_info.in.alloc_size = 0;
734 status = smb_raw_setfileinfo(cli->tree, &parms);
736 torture_assert_ntstatus_ok(tctx, status,
737 "RAW_SFILEINFO_ALLOCATION_INFO failed");
739 start = timeval_current();
740 end = timeval_add(&start, (120*sec), 0);
741 while (!timeval_expired(&end)) {
742 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
744 if (!NT_STATUS_IS_OK(status)) {
745 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s",
746 nt_errstr(status));
747 ret = false;
748 break;
751 if (finfo2.all_info.out.size != 0) {
752 torture_result(tctx, TORTURE_FAIL,
753 "file not truncated (size = %u, should be 10240)",
754 (unsigned int)finfo2.all_info.out.size);
755 ret = false;
756 break;
759 torture_comment(tctx, "write time %s\n",
760 nt_time_string(tctx, finfo2.all_info.out.write_time));
761 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
762 double diff = timeval_elapsed(&start);
763 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
764 torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
765 "server updated write_time after %.2f seconds"
766 "(1 sec == %.2f)(wrong!)\n",
767 diff, sec);
768 ret = false;
769 break;
772 torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
773 "server updated write_time after %.2f seconds"
774 "(1 sec == %.2f)(correct)\n",
775 diff, sec);
776 break;
778 fflush(stdout);
779 msleep(1 * msec);
782 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
783 torture_result(tctx, TORTURE_FAIL,
784 "Server did not update write time (wrong!)");
785 ret = false;
788 fflush(stdout);
789 msleep(2 * msec);
791 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
792 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
793 torture_assert_int_equal(tctx, written, 1,
794 "Unexpected number of bytes written");
796 start = timeval_current();
797 end = timeval_add(&start, (10*sec), 0);
798 while (!timeval_expired(&end)) {
799 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
801 if (!NT_STATUS_IS_OK(status)) {
802 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s",
803 nt_errstr(status));
804 ret = false;
805 break;
808 if (finfo3.all_info.out.size != 1) {
809 torture_result(tctx, TORTURE_FAIL, "file not expanded");
810 ret = false;
811 break;
814 torture_comment(tctx, "write time %s\n",
815 nt_time_string(tctx, finfo3.all_info.out.write_time));
816 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
817 double diff = timeval_elapsed(&start);
819 torture_comment(tctx, "server updated write_time after %.2f seconds"
820 "(1 sec == %.2f)(correct)\n",
821 diff, sec);
822 break;
824 fflush(stdout);
825 msleep(1 * msec);
828 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
829 torture_result(tctx, TORTURE_FAIL,
830 "Server updated write time (wrong!)");
831 ret = false;
834 /* the close should trigger an write time update */
835 smbcli_close(cli->tree, fnum1);
836 fnum1 = -1;
838 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
839 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
841 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
842 torture_result(tctx, TORTURE_FAIL, "Server did not update write time on close (wrong!)\n");
843 ret = false;
844 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
845 torture_comment(tctx, "Server updated write time on close (correct)\n");
848 if (fnum1 != -1)
849 smbcli_close(cli->tree, fnum1);
850 smbcli_unlink(cli->tree, fname);
851 smbcli_deltree(cli->tree, BASEDIR);
853 return ret;
857 * Do as above, but using 2 connections.
860 static bool test_delayed_write_update2(struct torture_context *tctx, struct smbcli_state *cli,
861 struct smbcli_state *cli2)
863 union smb_fileinfo finfo1, finfo2;
864 const char *fname = BASEDIR "\\torture_file.txt";
865 NTSTATUS status;
866 int fnum1 = -1;
867 int fnum2 = -1;
868 bool ret = true;
869 ssize_t written;
870 struct timeval start;
871 struct timeval end;
872 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
873 int normal_delay = 2000000;
874 double sec = ((double)used_delay) / ((double)normal_delay);
875 int msec = 1000 * sec;
876 union smb_flush flsh;
878 torture_comment(tctx, "\nRunning test_delayed_write_update2\n");
880 if (!torture_setup_dir(cli, BASEDIR)) {
881 return false;
884 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
885 if (fnum1 == -1) {
886 torture_comment(tctx, "Failed to open %s\n", fname);
887 return false;
890 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
891 finfo1.basic_info.in.file.fnum = fnum1;
892 finfo2 = finfo1;
894 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
896 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
898 torture_comment(tctx, "Initial write time %s\n",
899 nt_time_string(tctx, finfo1.basic_info.out.write_time));
901 /* 3 second delay to ensure we get past any 2 second time
902 granularity (older systems may have that) */
903 msleep(3 * msec);
906 /* Try using setfileinfo instead of write to update write time. */
907 union smb_setfileinfo sfinfo;
908 time_t t_set = time(NULL);
909 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
910 sfinfo.basic_info.in.file.fnum = fnum1;
911 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
912 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
914 /* I tried this with both + and - ve to see if it makes a different.
915 It doesn't - once the filetime is set via setfileinfo it stays that way. */
916 #if 1
917 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
918 #else
919 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
920 #endif
921 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
922 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
924 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
926 torture_assert_ntstatus_ok(tctx, status, "sfileinfo failed");
929 finfo2.basic_info.in.file.path = fname;
931 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
933 if (!NT_STATUS_IS_OK(status)) {
934 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
935 return false;
937 torture_comment(tctx, "write time %s\n",
938 nt_time_string(tctx, finfo2.basic_info.out.write_time));
940 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
941 torture_comment(tctx, "Server updated write_time (correct)\n");
942 } else {
943 torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n");
944 ret = false;
947 /* Now try a write to see if the write time gets reset. */
949 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
950 finfo1.basic_info.in.file.fnum = fnum1;
951 finfo2 = finfo1;
953 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
955 if (!NT_STATUS_IS_OK(status)) {
956 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
957 return false;
960 torture_comment(tctx, "Modified write time %s\n",
961 nt_time_string(tctx, finfo1.basic_info.out.write_time));
964 torture_comment(tctx, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
966 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
968 if (written != 10) {
969 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
970 (int)written, __location__);
971 return false;
974 /* Just to prove to tridge that the an smbflush has no effect on
975 the write time :-). The setfileinfo IS STICKY. JRA. */
977 torture_comment(tctx, "Doing flush after write\n");
979 flsh.flush.level = RAW_FLUSH_FLUSH;
980 flsh.flush.in.file.fnum = fnum1;
981 status = smb_raw_flush(cli->tree, &flsh);
982 if (!NT_STATUS_IS_OK(status)) {
983 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
984 return false;
987 /* Once the time was set using setfileinfo then it stays set - writes
988 don't have any effect. But make sure. */
989 start = timeval_current();
990 end = timeval_add(&start, (15*sec), 0);
991 while (!timeval_expired(&end)) {
992 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
994 if (!NT_STATUS_IS_OK(status)) {
995 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
996 ret = false;
997 break;
999 torture_comment(tctx, "write time %s\n",
1000 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1001 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1002 double diff = timeval_elapsed(&start);
1003 torture_comment(tctx, "Server updated write_time after %.2f seconds"
1004 "(1sec == %.2f) (wrong!)\n",
1005 diff, sec);
1006 ret = false;
1007 break;
1009 fflush(stdout);
1010 msleep(1 * msec);
1013 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1014 torture_comment(tctx, "Server did not update write time (correct)\n");
1017 fflush(stdout);
1018 msleep(2 * msec);
1020 fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
1021 if (fnum2 == -1) {
1022 torture_comment(tctx, "Failed to open %s\n", fname);
1023 return false;
1026 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");
1028 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
1030 if (written != 10) {
1031 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
1032 (int)written, __location__);
1033 return false;
1036 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1038 if (!NT_STATUS_IS_OK(status)) {
1039 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1040 return false;
1042 torture_comment(tctx, "write time %s\n",
1043 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1044 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1045 torture_comment(tctx, "Server updated write_time (wrong!)\n");
1046 ret = false;
1049 torture_comment(tctx, "Closing the first fd to see if write time updated.\n");
1050 smbcli_close(cli->tree, fnum1);
1051 fnum1 = -1;
1053 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");
1055 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
1057 if (written != 10) {
1058 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
1059 (int)written, __location__);
1060 return false;
1063 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1064 finfo1.basic_info.in.file.fnum = fnum2;
1065 finfo2 = finfo1;
1066 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1068 if (!NT_STATUS_IS_OK(status)) {
1069 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1070 return false;
1072 torture_comment(tctx, "write time %s\n",
1073 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1074 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1075 torture_comment(tctx, "Server updated write_time (wrong!)\n");
1076 ret = false;
1079 /* Once the time was set using setfileinfo then it stays set - writes
1080 don't have any effect. But make sure. */
1081 start = timeval_current();
1082 end = timeval_add(&start, (15*sec), 0);
1083 while (!timeval_expired(&end)) {
1084 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1086 if (!NT_STATUS_IS_OK(status)) {
1087 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1088 ret = false;
1089 break;
1091 torture_comment(tctx, "write time %s\n",
1092 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1093 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1094 double diff = timeval_elapsed(&start);
1095 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1096 "(1sec == %.2f) (wrong!)\n",
1097 diff, sec);
1098 ret = false;
1099 break;
1101 fflush(stdout);
1102 msleep(1 * msec);
1105 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1106 torture_comment(tctx, "Server did not update write time (correct)\n");
1109 torture_comment(tctx, "Closing second fd to see if write time updated.\n");
1111 smbcli_close(cli->tree, fnum2);
1112 fnum2 = -1;
1114 fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
1115 if (fnum1 == -1) {
1116 torture_comment(tctx, "Failed to open %s\n", fname);
1117 return false;
1120 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1121 finfo1.basic_info.in.file.fnum = fnum1;
1122 finfo2 = finfo1;
1124 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1126 if (!NT_STATUS_IS_OK(status)) {
1127 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1128 return false;
1131 torture_comment(tctx, "Second open initial write time %s\n",
1132 nt_time_string(tctx, finfo1.basic_info.out.write_time));
1134 msleep(10 * msec);
1135 torture_comment(tctx, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
1137 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
1139 if (written != 10) {
1140 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
1141 (int)written, __location__);
1142 return false;
1145 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1146 finfo1.basic_info.in.file.fnum = fnum1;
1147 finfo2 = finfo1;
1148 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1150 if (!NT_STATUS_IS_OK(status)) {
1151 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1152 return false;
1154 torture_comment(tctx, "write time %s\n",
1155 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1156 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1157 torture_comment(tctx, "Server updated write_time (wrong!)\n");
1158 ret = false;
1161 /* Now the write time should be updated again */
1162 start = timeval_current();
1163 end = timeval_add(&start, (15*sec), 0);
1164 while (!timeval_expired(&end)) {
1165 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1167 if (!NT_STATUS_IS_OK(status)) {
1168 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1169 ret = false;
1170 break;
1172 torture_comment(tctx, "write time %s\n",
1173 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1174 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1175 double diff = timeval_elapsed(&start);
1176 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
1177 torture_comment(tctx, "Server updated write_time after %.2f seconds"
1178 "(1sec == %.2f) (wrong!)\n",
1179 diff, sec);
1180 ret = false;
1181 break;
1184 torture_comment(tctx, "Server updated write_time after %.2f seconds"
1185 "(1sec == %.2f) (correct)\n",
1186 diff, sec);
1187 break;
1189 fflush(stdout);
1190 msleep(1*msec);
1193 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1194 torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n");
1195 ret = false;
1199 /* One more test to do. We should read the filetime via findfirst on the
1200 second connection to ensure it's the same. This is very easy for a Windows
1201 server but a bastard to get right on a POSIX server. JRA. */
1203 if (fnum1 != -1)
1204 smbcli_close(cli->tree, fnum1);
1205 smbcli_unlink(cli->tree, fname);
1206 smbcli_deltree(cli->tree, BASEDIR);
1208 return ret;
1212 /* Windows does obviously not update the stat info during a write call. I
1213 * *think* this is the problem causing a spurious Excel 2003 on XP error
1214 * message when saving a file. Excel does a setfileinfo, writes, and then does
1215 * a getpath(!)info. Or so... For Samba sometimes it displays an error message
1216 * that the file might have been changed in between. What i've been able to
1217 * trace down is that this happens if the getpathinfo after the write shows a
1218 * different last write time than the setfileinfo showed. This is really
1219 * nasty....
1222 static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli,
1223 struct smbcli_state *cli2)
1225 union smb_fileinfo finfo1, finfo2;
1226 const char *fname = BASEDIR "\\torture_file.txt";
1227 NTSTATUS status;
1228 int fnum1 = -1;
1229 int fnum2;
1230 bool ret = true;
1231 ssize_t written;
1232 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1233 int normal_delay = 2000000;
1234 double sec = ((double)used_delay) / ((double)normal_delay);
1235 int msec = 1000 * sec;
1237 torture_comment(tctx, "\nRunning test_finfo_after_write\n");
1239 if (!torture_setup_dir(cli, BASEDIR)) {
1240 return false;
1243 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1244 if (fnum1 == -1) {
1245 ret = false;
1246 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1247 goto done;
1250 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1251 finfo1.basic_info.in.file.fnum = fnum1;
1253 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1255 if (!NT_STATUS_IS_OK(status)) {
1256 ret = false;
1257 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1258 goto done;
1261 msleep(1 * msec);
1263 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1265 if (written != 1) {
1266 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1267 ret = false;
1268 goto done;
1271 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
1272 if (fnum2 == -1) {
1273 torture_result(tctx, TORTURE_FAIL, __location__": failed to open 2nd time - %s",
1274 smbcli_errstr(cli2->tree));
1275 ret = false;
1276 goto done;
1279 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
1281 if (written != 1) {
1282 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1",
1283 (int)written);
1284 ret = false;
1285 goto done;
1288 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1289 finfo2.basic_info.in.file.path = fname;
1291 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
1293 if (!NT_STATUS_IS_OK(status)) {
1294 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s",
1295 nt_errstr(status));
1296 ret = false;
1297 goto done;
1300 if (finfo1.basic_info.out.create_time !=
1301 finfo2.basic_info.out.create_time) {
1302 torture_result(tctx, TORTURE_FAIL, __location__": create_time changed");
1303 ret = false;
1304 goto done;
1307 if (finfo1.basic_info.out.access_time !=
1308 finfo2.basic_info.out.access_time) {
1309 torture_result(tctx, TORTURE_FAIL, __location__": access_time changed");
1310 ret = false;
1311 goto done;
1314 if (finfo1.basic_info.out.write_time !=
1315 finfo2.basic_info.out.write_time) {
1316 torture_result(tctx, TORTURE_FAIL, __location__": write_time changed:\n"
1317 "write time conn 1 = %s, conn 2 = %s",
1318 nt_time_string(tctx, finfo1.basic_info.out.write_time),
1319 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1320 ret = false;
1321 goto done;
1324 if (finfo1.basic_info.out.change_time !=
1325 finfo2.basic_info.out.change_time) {
1326 torture_result(tctx, TORTURE_FAIL, __location__": change_time changed");
1327 ret = false;
1328 goto done;
1331 /* One of the two following calls updates the qpathinfo. */
1333 /* If you had skipped the smbcli_write on fnum2, it would
1334 * *not* have updated the stat on disk */
1336 smbcli_close(cli2->tree, fnum2);
1337 cli2 = NULL;
1339 /* This call is only for the people looking at ethereal :-) */
1340 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1341 finfo2.basic_info.in.file.path = fname;
1343 status = smb_raw_pathinfo(cli->tree, tctx, &finfo2);
1345 if (!NT_STATUS_IS_OK(status)) {
1346 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1347 ret = false;
1348 goto done;
1351 done:
1352 if (fnum1 != -1)
1353 smbcli_close(cli->tree, fnum1);
1354 smbcli_unlink(cli->tree, fname);
1355 smbcli_deltree(cli->tree, BASEDIR);
1357 return ret;
1360 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
1361 uint64_t r = 10*1000*1000; \
1362 NTTIME g = (given).basic_info.out.write_time; \
1363 NTTIME gr = (g / r) * r; \
1364 NTTIME c = (correct).basic_info.out.write_time; \
1365 NTTIME cr = (c / r) * r; \
1366 bool strict = torture_setting_bool(tctx, "strict mode", false); \
1367 bool err = false; \
1368 if (strict && (g cmp c)) { \
1369 err = true; \
1370 } else if ((g cmp c) && (gr cmp cr)) { \
1371 /* handle filesystem without high resolution timestamps */ \
1372 err = true; \
1374 if (err) { \
1375 torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \
1376 #given, nt_time_string(tctx, g), (unsigned long long)g, \
1377 #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \
1378 ret = false; \
1379 goto done; \
1381 } while (0)
1382 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
1383 COMPARE_WRITE_TIME_CMP(given,correct,!=)
1384 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
1385 COMPARE_WRITE_TIME_CMP(given,correct,<=)
1386 #define COMPARE_WRITE_TIME_LESS(given,correct) \
1387 COMPARE_WRITE_TIME_CMP(given,correct,>=)
1389 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
1390 NTTIME g = (given).basic_info.out.access_time; \
1391 NTTIME c = (correct).basic_info.out.access_time; \
1392 if (g cmp c) { \
1393 torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \
1394 #given, nt_time_string(tctx, g), \
1395 #cmp, #correct, nt_time_string(tctx, c)); \
1396 ret = false; \
1397 goto done; \
1399 } while (0)
1400 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
1401 COMPARE_ACCESS_TIME_CMP(given,correct,!=)
1403 #define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \
1404 COMPARE_ACCESS_TIME_EQUAL(given,correct); \
1405 COMPARE_WRITE_TIME_EQUAL(given,correct); \
1406 } while (0)
1408 #define GET_INFO_FILE(finfo) do { \
1409 NTSTATUS _status; \
1410 _status = smb_raw_fileinfo(cli->tree, tctx, &finfo); \
1411 if (!NT_STATUS_IS_OK(_status)) { \
1412 ret = false; \
1413 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1414 nt_errstr(_status)); \
1415 goto done; \
1417 torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1418 nt_time_string(tctx, finfo.basic_info.out.access_time), \
1419 nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1420 } while (0)
1421 #define GET_INFO_FILE2(finfo) do { \
1422 NTSTATUS _status; \
1423 _status = smb_raw_fileinfo(cli2->tree, tctx, &finfo); \
1424 if (!NT_STATUS_IS_OK(_status)) { \
1425 ret = false; \
1426 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1427 nt_errstr(_status)); \
1428 goto done; \
1430 torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1431 nt_time_string(tctx, finfo.basic_info.out.access_time), \
1432 nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1433 } while (0)
1434 #define GET_INFO_PATH(pinfo) do { \
1435 NTSTATUS _status; \
1436 _status = smb_raw_pathinfo(cli2->tree, tctx, &pinfo); \
1437 if (!NT_STATUS_IS_OK(_status)) { \
1438 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \
1439 nt_errstr(_status)); \
1440 ret = false; \
1441 goto done; \
1443 torture_comment(tctx, "pathinfo: Access(%s) Write(%s)\n", \
1444 nt_time_string(tctx, pinfo.basic_info.out.access_time), \
1445 nt_time_string(tctx, pinfo.basic_info.out.write_time)); \
1446 } while (0)
1447 #define GET_INFO_BOTH(finfo,pinfo) do { \
1448 GET_INFO_FILE(finfo); \
1449 GET_INFO_PATH(pinfo); \
1450 COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \
1451 } while (0)
1453 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
1454 NTSTATUS _status; \
1455 union smb_setfileinfo sfinfo; \
1456 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1457 sfinfo.basic_info.in.file.fnum = tfnum; \
1458 sfinfo.basic_info.in.create_time = 0; \
1459 sfinfo.basic_info.in.access_time = 0; \
1460 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1461 sfinfo.basic_info.in.change_time = 0; \
1462 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1463 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1464 if (!NT_STATUS_IS_OK(_status)) { \
1465 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1466 nt_errstr(_status)); \
1467 ret = false; \
1468 goto done; \
1470 } while (0)
1471 #define SET_INFO_FILE(finfo, wrtime) \
1472 SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
1474 #define SET_INFO_FILE_NS(finfo, wrtime, ns, tree, tfnum) do { \
1475 NTSTATUS _status; \
1476 union smb_setfileinfo sfinfo; \
1477 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1478 sfinfo.basic_info.in.file.fnum = tfnum; \
1479 sfinfo.basic_info.in.create_time = 0; \
1480 sfinfo.basic_info.in.access_time = 0; \
1481 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1482 sfinfo.basic_info.in.write_time += (ns); \
1483 sfinfo.basic_info.in.change_time = 0; \
1484 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1485 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1486 if (!NT_STATUS_IS_OK(_status)) { \
1487 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1488 nt_errstr(_status)); \
1489 ret = false; \
1490 goto done; \
1492 } while (0)
1494 static bool test_delayed_write_update3(struct torture_context *tctx,
1495 struct smbcli_state *cli,
1496 struct smbcli_state *cli2)
1498 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1499 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1500 const char *fname = BASEDIR "\\torture_file3.txt";
1501 int fnum1 = -1;
1502 bool ret = true;
1503 ssize_t written;
1504 struct timeval start;
1505 struct timeval end;
1506 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1507 int normal_delay = 2000000;
1508 double sec = ((double)used_delay) / ((double)normal_delay);
1509 int msec = 1000 * sec;
1511 torture_comment(tctx, "\nRunning test_delayed_write_update3\n");
1513 if (!torture_setup_dir(cli, BASEDIR)) {
1514 return false;
1517 torture_comment(tctx, "Open the file handle\n");
1518 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1519 if (fnum1 == -1) {
1520 ret = false;
1521 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1522 goto done;
1525 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1526 finfo0.basic_info.in.file.fnum = fnum1;
1527 finfo1 = finfo0;
1528 finfo2 = finfo0;
1529 finfo3 = finfo0;
1530 finfo4 = finfo0;
1531 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1532 pinfo0.basic_info.in.file.path = fname;
1533 pinfo1 = pinfo0;
1534 pinfo2 = pinfo0;
1535 pinfo3 = pinfo0;
1536 pinfo4 = pinfo0;
1537 pinfo5 = pinfo0;
1539 /* get the initial times */
1540 GET_INFO_BOTH(finfo0,pinfo0);
1543 * make sure the write time is updated 2 seconds later
1544 * calcuated from the first write
1545 * (but expect upto 5 seconds extra time for a busy server)
1547 start = timeval_current();
1548 end = timeval_add(&start, 7 * sec, 0);
1549 while (!timeval_expired(&end)) {
1550 /* do a write */
1551 torture_comment(tctx, "Do a write on the file handle\n");
1552 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1553 if (written != 1) {
1554 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1555 ret = false;
1556 goto done;
1558 /* get the times after the write */
1559 GET_INFO_FILE(finfo1);
1561 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1562 double diff = timeval_elapsed(&start);
1563 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
1564 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1565 "(1sec == %.2f) (wrong!)\n",
1566 diff, sec);
1567 ret = false;
1568 break;
1571 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1572 "(1sec == %.2f) (correct)\n",
1573 diff, sec);
1574 break;
1576 msleep(0.5 * msec);
1579 GET_INFO_BOTH(finfo1,pinfo1);
1580 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1582 /* sure any further write doesn't update the write time */
1583 start = timeval_current();
1584 end = timeval_add(&start, 15 * sec, 0);
1585 while (!timeval_expired(&end)) {
1586 /* do a write */
1587 torture_comment(tctx, "Do a write on the file handle\n");
1588 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1589 if (written != 1) {
1590 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1591 ret = false;
1592 goto done;
1594 /* get the times after the write */
1595 GET_INFO_BOTH(finfo2,pinfo2);
1597 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1598 double diff = timeval_elapsed(&start);
1599 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1600 "(1sec == %.2f) (wrong!)\n",
1601 diff, sec);
1602 ret = false;
1603 break;
1605 msleep(1 * msec);
1608 GET_INFO_BOTH(finfo2,pinfo2);
1609 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1610 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1611 torture_comment(tctx, "Server did not update write_time (correct)\n");
1614 /* sleep */
1615 msleep(5 * msec);
1617 GET_INFO_BOTH(finfo3,pinfo3);
1618 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1621 * the close updates the write time to the time of the close
1622 * and not to the time of the last write!
1624 torture_comment(tctx, "Close the file handle\n");
1625 smbcli_close(cli->tree, fnum1);
1626 fnum1 = -1;
1628 GET_INFO_PATH(pinfo4);
1629 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1631 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1632 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1635 done:
1636 if (fnum1 != -1)
1637 smbcli_close(cli->tree, fnum1);
1638 smbcli_unlink(cli->tree, fname);
1639 smbcli_deltree(cli->tree, BASEDIR);
1641 return ret;
1645 * Show that a truncate write always updates the write time even
1646 * if an initial write has already updated the write time.
1649 static bool test_delayed_write_update3a(struct torture_context *tctx,
1650 struct smbcli_state *cli,
1651 struct smbcli_state *cli2)
1653 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1654 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1655 const char *fname = BASEDIR "\\torture_file3a.txt";
1656 int fnum1 = -1;
1657 bool ret = true;
1658 ssize_t written;
1659 int i;
1660 struct timeval start;
1661 struct timeval end;
1662 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1663 int normal_delay = 2000000;
1664 double sec = ((double)used_delay) / ((double)normal_delay);
1665 int msec = 1000 * sec;
1667 torture_comment(tctx, "\nRunning test_delayed_write_update3a\n");
1669 if (!torture_setup_dir(cli, BASEDIR)) {
1670 return false;
1673 torture_comment(tctx, "Open the file handle\n");
1674 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1675 if (fnum1 == -1) {
1676 ret = false;
1677 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1678 goto done;
1681 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1682 finfo0.basic_info.in.file.fnum = fnum1;
1683 finfo1 = finfo0;
1684 finfo2 = finfo0;
1685 finfo3 = finfo0;
1686 finfo4 = finfo0;
1687 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1688 pinfo0.basic_info.in.file.path = fname;
1689 pinfo1 = pinfo0;
1690 pinfo2 = pinfo0;
1691 pinfo3 = pinfo0;
1692 pinfo4 = pinfo0;
1693 pinfo5 = pinfo0;
1695 /* get the initial times */
1696 GET_INFO_BOTH(finfo0,pinfo0);
1699 * sleep some time, to demonstrate the handling of write times
1700 * doesn't depend on the time since the open
1702 msleep(5 * msec);
1704 /* get the initial times */
1705 GET_INFO_BOTH(finfo1,pinfo1);
1706 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1709 * make sure the write time is updated 2 seconds later
1710 * calcuated from the first write
1711 * (but expect upto 5 seconds extra time for a busy server)
1713 start = timeval_current();
1714 end = timeval_add(&start, 7 * sec, 0);
1715 while (!timeval_expired(&end)) {
1716 /* do a write */
1717 torture_comment(tctx, "Do a write on the file handle\n");
1718 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1719 if (written != 1) {
1720 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1721 ret = false;
1722 goto done;
1724 /* get the times after the write */
1725 GET_INFO_FILE(finfo1);
1727 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1728 double diff = timeval_elapsed(&start);
1729 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
1730 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1731 "(1sec == %.2f) (wrong!)\n",
1732 diff, sec);
1733 ret = false;
1734 break;
1737 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1738 "(1sec == %.2f) (correct)\n",
1739 diff, sec);
1740 break;
1742 msleep(0.5 * msec);
1745 GET_INFO_BOTH(finfo1,pinfo1);
1746 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1748 msleep(3 * msec);
1751 * demonstrate that a truncate write always
1752 * updates the write time immediately
1754 for (i=0; i < 3; i++) {
1755 msleep(2 * msec);
1756 /* do a write */
1757 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1758 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
1759 if (written != 0) {
1760 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1761 ret = false;
1762 goto done;
1764 /* get the times after the write */
1765 GET_INFO_BOTH(finfo2,pinfo2);
1766 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1767 finfo1 = finfo2;
1770 msleep(3 * msec);
1772 /* sure any further write doesn't update the write time */
1773 start = timeval_current();
1774 end = timeval_add(&start, 15 * sec, 0);
1775 while (!timeval_expired(&end)) {
1776 /* do a write */
1777 torture_comment(tctx, "Do a write on the file handle\n");
1778 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1779 if (written != 1) {
1780 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1781 ret = false;
1782 goto done;
1784 /* get the times after the write */
1785 GET_INFO_BOTH(finfo2,pinfo2);
1787 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1788 double diff = timeval_elapsed(&start);
1789 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1790 "(1sec == %.2f) (wrong!)\n",
1791 diff, sec);
1792 ret = false;
1793 break;
1795 msleep(1 * msec);
1798 GET_INFO_BOTH(finfo2,pinfo2);
1799 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1800 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1801 torture_comment(tctx, "Server did not update write_time (correct)\n");
1804 /* sleep */
1805 msleep(3 * msec);
1807 /* get the initial times */
1808 GET_INFO_BOTH(finfo1,pinfo1);
1809 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
1812 * demonstrate that a truncate write always
1813 * updates the write time immediately
1815 for (i=0; i < 3; i++) {
1816 msleep(2 * msec);
1817 /* do a write */
1818 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1819 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
1820 if (written != 0) {
1821 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1822 ret = false;
1823 goto done;
1825 /* get the times after the write */
1826 GET_INFO_BOTH(finfo2,pinfo2);
1827 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1828 finfo1 = finfo2;
1831 /* sleep */
1832 msleep(3 * msec);
1834 GET_INFO_BOTH(finfo3,pinfo3);
1835 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1838 * the close doesn't update the write time
1840 torture_comment(tctx, "Close the file handle\n");
1841 smbcli_close(cli->tree, fnum1);
1842 fnum1 = -1;
1844 GET_INFO_PATH(pinfo4);
1845 COMPARE_WRITE_TIME_EQUAL(pinfo4, pinfo3);
1847 if (pinfo4.basic_info.out.write_time == pinfo3.basic_info.out.write_time) {
1848 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1851 done:
1852 if (fnum1 != -1)
1853 smbcli_close(cli->tree, fnum1);
1854 smbcli_unlink(cli->tree, fname);
1855 smbcli_deltree(cli->tree, BASEDIR);
1857 return ret;
1861 * Show a close after write updates the write timestamp to
1862 * the close time, not the last write time.
1865 static bool test_delayed_write_update3b(struct torture_context *tctx,
1866 struct smbcli_state *cli,
1867 struct smbcli_state *cli2)
1869 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1870 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1871 const char *fname = BASEDIR "\\torture_file3b.txt";
1872 int fnum1 = -1;
1873 bool ret = true;
1874 ssize_t written;
1875 struct timeval start;
1876 struct timeval end;
1877 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1878 int normal_delay = 2000000;
1879 double sec = ((double)used_delay) / ((double)normal_delay);
1880 int msec = 1000 * sec;
1882 torture_comment(tctx, "\nRunning test_delayed_write_update3b\n");
1884 if (!torture_setup_dir(cli, BASEDIR)) {
1885 return false;
1888 torture_comment(tctx, "Open the file handle\n");
1889 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1890 if (fnum1 == -1) {
1891 ret = false;
1892 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1893 goto done;
1896 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1897 finfo0.basic_info.in.file.fnum = fnum1;
1898 finfo1 = finfo0;
1899 finfo2 = finfo0;
1900 finfo3 = finfo0;
1901 finfo4 = finfo0;
1902 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1903 pinfo0.basic_info.in.file.path = fname;
1904 pinfo1 = pinfo0;
1905 pinfo2 = pinfo0;
1906 pinfo3 = pinfo0;
1907 pinfo4 = pinfo0;
1908 pinfo5 = pinfo0;
1910 /* get the initial times */
1911 GET_INFO_BOTH(finfo0,pinfo0);
1914 * sleep some time, to demonstrate the handling of write times
1915 * doesn't depend on the time since the open
1917 msleep(5 * msec);
1919 /* get the initial times */
1920 GET_INFO_BOTH(finfo1,pinfo1);
1921 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1924 * make sure the write time is updated 2 seconds later
1925 * calcuated from the first write
1926 * (but expect upto 5 seconds extra time for a busy server)
1928 start = timeval_current();
1929 end = timeval_add(&start, 7 * sec, 0);
1930 while (!timeval_expired(&end)) {
1931 /* do a write */
1932 torture_comment(tctx, "Do a write on the file handle\n");
1933 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1934 if (written != 1) {
1935 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1936 ret = false;
1937 goto done;
1939 /* get the times after the write */
1940 GET_INFO_FILE(finfo1);
1942 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1943 double diff = timeval_elapsed(&start);
1944 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
1945 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1946 "(1sec == %.2f) (wrong!)\n",
1947 diff, sec);
1948 ret = false;
1949 break;
1952 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1953 "(1sec == %.2f) (correct)\n",
1954 diff, sec);
1955 break;
1957 msleep(0.5 * msec);
1960 GET_INFO_BOTH(finfo1,pinfo1);
1961 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1963 /* sure any further write doesn't update the write time */
1964 start = timeval_current();
1965 end = timeval_add(&start, 15 * sec, 0);
1966 while (!timeval_expired(&end)) {
1967 /* do a write */
1968 torture_comment(tctx, "Do a write on the file handle\n");
1969 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1970 if (written != 1) {
1971 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1972 ret = false;
1973 goto done;
1975 /* get the times after the write */
1976 GET_INFO_BOTH(finfo2,pinfo2);
1978 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1979 double diff = timeval_elapsed(&start);
1980 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1981 "(1sec == %.2f) (wrong!)\n",
1982 diff, sec);
1983 ret = false;
1984 break;
1986 msleep(1 * msec);
1989 GET_INFO_BOTH(finfo2,pinfo2);
1990 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1991 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1992 torture_comment(tctx, "Server did not update write_time (correct)\n");
1995 /* sleep */
1996 msleep(5 * msec);
1998 GET_INFO_BOTH(finfo3,pinfo3);
1999 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2002 * the close updates the write time to the time of the close
2003 * and not to the time of the last write!
2005 torture_comment(tctx, "Close the file handle\n");
2006 smbcli_close(cli->tree, fnum1);
2007 fnum1 = -1;
2009 GET_INFO_PATH(pinfo4);
2010 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2012 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2013 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2016 done:
2017 if (fnum1 != -1)
2018 smbcli_close(cli->tree, fnum1);
2019 smbcli_unlink(cli->tree, fname);
2020 smbcli_deltree(cli->tree, BASEDIR);
2022 return ret;
2026 * Check that a write after a truncate write doesn't update
2027 * the timestamp, but a truncate write after a write does.
2028 * Also prove that a close after a truncate write updates the
2029 * timestamp to current, not the time of last write.
2032 static bool test_delayed_write_update3c(struct torture_context *tctx,
2033 struct smbcli_state *cli,
2034 struct smbcli_state *cli2)
2036 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
2037 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
2038 const char *fname = BASEDIR "\\torture_file3c.txt";
2039 int fnum1 = -1;
2040 bool ret = true;
2041 ssize_t written;
2042 int i;
2043 struct timeval start;
2044 struct timeval end;
2045 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2046 int normal_delay = 2000000;
2047 double sec = ((double)used_delay) / ((double)normal_delay);
2048 int msec = 1000 * sec;
2050 torture_comment(tctx, "\nRunning test_delayed_write_update3c\n");
2052 if (!torture_setup_dir(cli, BASEDIR)) {
2053 return false;
2056 torture_comment(tctx, "Open the file handle\n");
2057 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2058 if (fnum1 == -1) {
2059 ret = false;
2060 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2061 goto done;
2064 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2065 finfo0.basic_info.in.file.fnum = fnum1;
2066 finfo1 = finfo0;
2067 finfo2 = finfo0;
2068 finfo3 = finfo0;
2069 finfo4 = finfo0;
2070 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2071 pinfo0.basic_info.in.file.path = fname;
2072 pinfo1 = pinfo0;
2073 pinfo2 = pinfo0;
2074 pinfo3 = pinfo0;
2075 pinfo4 = pinfo0;
2076 pinfo5 = pinfo0;
2078 /* get the initial times */
2079 GET_INFO_BOTH(finfo0,pinfo0);
2082 * sleep some time, to demonstrate the handling of write times
2083 * doesn't depend on the time since the open
2085 msleep(5 * msec);
2087 /* get the initial times */
2088 GET_INFO_BOTH(finfo1,pinfo1);
2089 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2092 * demonstrate that a truncate write always
2093 * updates the write time immediately
2095 for (i=0; i < 3; i++) {
2096 msleep(2 * msec);
2097 /* do a write */
2098 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
2099 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2100 if (written != 0) {
2101 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2102 ret = false;
2103 goto done;
2105 /* get the times after the write */
2106 GET_INFO_BOTH(finfo2,pinfo2);
2107 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2108 finfo1 = finfo2;
2111 start = timeval_current();
2112 end = timeval_add(&start, 7 * sec, 0);
2113 while (!timeval_expired(&end)) {
2114 /* do a write */
2115 torture_comment(tctx, "Do a write on the file handle\n");
2116 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2117 if (written != 1) {
2118 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2119 ret = false;
2120 goto done;
2122 /* get the times after the write */
2123 GET_INFO_FILE(finfo2);
2125 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2126 double diff = timeval_elapsed(&start);
2127 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2128 "(1sec == %.2f) (wrong!)\n",
2129 diff, sec);
2130 ret = false;
2131 break;
2133 msleep(1 * msec);
2136 GET_INFO_BOTH(finfo2,pinfo2);
2137 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2138 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2139 torture_comment(tctx, "Server did not update write_time (correct)\n");
2142 /* sleep */
2143 msleep(5 * msec);
2145 /* get the initial times */
2146 GET_INFO_BOTH(finfo1,pinfo1);
2147 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
2150 * demonstrate that a truncate write always
2151 * updates the write time immediately
2153 for (i=0; i < 3; i++) {
2154 msleep(2 * msec);
2155 /* do a write */
2156 torture_comment(tctx, "Do a truncate write [%d] on the file handle\n", i);
2157 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2158 if (written != 0) {
2159 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2160 ret = false;
2161 goto done;
2163 /* get the times after the write */
2164 GET_INFO_BOTH(finfo2,pinfo2);
2165 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2166 finfo1 = finfo2;
2169 /* sleep */
2170 msleep(5 * msec);
2172 GET_INFO_BOTH(finfo2,pinfo2);
2173 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2175 /* sure any further write doesn't update the write time */
2176 start = timeval_current();
2177 end = timeval_add(&start, 15 * sec, 0);
2178 while (!timeval_expired(&end)) {
2179 /* do a write */
2180 torture_comment(tctx, "Do a write on the file handle\n");
2181 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2182 if (written != 1) {
2183 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2184 ret = false;
2185 goto done;
2187 /* get the times after the write */
2188 GET_INFO_BOTH(finfo2,pinfo2);
2190 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2191 double diff = timeval_elapsed(&start);
2192 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2193 "(1sec == %.2f) (wrong!)\n",
2194 diff, sec);
2195 ret = false;
2196 break;
2198 msleep(1 * msec);
2201 GET_INFO_BOTH(finfo2,pinfo2);
2202 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2203 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2204 torture_comment(tctx, "Server did not update write_time (correct)\n");
2207 /* sleep */
2208 msleep(5 * msec);
2210 GET_INFO_BOTH(finfo3,pinfo3);
2211 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2214 * the close updates the write time to the time of the close
2215 * and not to the time of the last write!
2217 torture_comment(tctx, "Close the file handle\n");
2218 smbcli_close(cli->tree, fnum1);
2219 fnum1 = -1;
2221 GET_INFO_PATH(pinfo4);
2222 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2224 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2225 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2228 done:
2229 if (fnum1 != -1)
2230 smbcli_close(cli->tree, fnum1);
2231 smbcli_unlink(cli->tree, fname);
2232 smbcli_deltree(cli->tree, BASEDIR);
2234 return ret;
2238 * Show only the first write updates the timestamp, and a close
2239 * after writes updates to current (I think this is the same
2240 * as test 3b. JRA).
2243 static bool test_delayed_write_update4(struct torture_context *tctx,
2244 struct smbcli_state *cli,
2245 struct smbcli_state *cli2)
2247 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
2248 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
2249 const char *fname = BASEDIR "\\torture_file4.txt";
2250 int fnum1 = -1;
2251 bool ret = true;
2252 ssize_t written;
2253 struct timeval start;
2254 struct timeval end;
2255 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2256 int normal_delay = 2000000;
2257 double sec = ((double)used_delay) / ((double)normal_delay);
2258 int msec = 1000 * sec;
2260 torture_comment(tctx, "\nRunning test_delayed_write_update4\n");
2262 if (!torture_setup_dir(cli, BASEDIR)) {
2263 return false;
2266 torture_comment(tctx, "Open the file handle\n");
2267 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2268 if (fnum1 == -1) {
2269 ret = false;
2270 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2271 goto done;
2274 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2275 finfo0.basic_info.in.file.fnum = fnum1;
2276 finfo1 = finfo0;
2277 finfo2 = finfo0;
2278 finfo3 = finfo0;
2279 finfo4 = finfo0;
2280 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2281 pinfo0.basic_info.in.file.path = fname;
2282 pinfo1 = pinfo0;
2283 pinfo2 = pinfo0;
2284 pinfo3 = pinfo0;
2285 pinfo4 = pinfo0;
2286 pinfo5 = pinfo0;
2288 /* get the initial times */
2289 GET_INFO_BOTH(finfo0,pinfo0);
2291 /* sleep a bit */
2292 msleep(5 * msec);
2294 /* do a write */
2295 torture_comment(tctx, "Do a write on the file handle\n");
2296 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2297 if (written != 1) {
2298 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2299 ret = false;
2300 goto done;
2303 GET_INFO_BOTH(finfo1,pinfo1);
2304 COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
2307 * make sure the write time is updated 2 seconds later
2308 * calcuated from the first write
2309 * (but expect upto 3 seconds extra time for a busy server)
2311 start = timeval_current();
2312 end = timeval_add(&start, 5 * sec, 0);
2313 while (!timeval_expired(&end)) {
2314 /* get the times after the first write */
2315 GET_INFO_FILE(finfo1);
2317 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
2318 double diff = timeval_elapsed(&start);
2319 if (diff < (TIMEDELAY_SECS * sec * 0.3)) { /* 0.3 to cope with vmware timing */
2320 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2321 "(1sec == %.2f) (wrong!)\n",
2322 diff, sec);
2323 ret = false;
2324 break;
2327 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2328 "(1sec == %.2f) (correct)\n",
2329 diff, sec);
2330 break;
2332 msleep(0.5 * msec);
2335 GET_INFO_BOTH(finfo1,pinfo1);
2336 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
2338 /* sure any further write doesn't update the write time */
2339 start = timeval_current();
2340 end = timeval_add(&start, 15 * sec, 0);
2341 while (!timeval_expired(&end)) {
2342 /* do a write */
2343 torture_comment(tctx, "Do a write on the file handle\n");
2344 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2345 if (written != 1) {
2346 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2347 ret = false;
2348 goto done;
2350 /* get the times after the write */
2351 GET_INFO_BOTH(finfo2,pinfo2);
2353 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2354 double diff = timeval_elapsed(&start);
2355 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2356 "(1sec == %.2f) (wrong!)\n",
2357 diff, sec);
2358 ret = false;
2359 break;
2361 msleep(1 * msec);
2364 GET_INFO_BOTH(finfo2,pinfo2);
2365 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2366 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2367 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
2370 /* sleep */
2371 msleep(5 * msec);
2373 GET_INFO_BOTH(finfo3,pinfo3);
2374 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2377 * the close updates the write time to the time of the close
2378 * and not to the time of the last write!
2380 torture_comment(tctx, "Close the file handle\n");
2381 smbcli_close(cli->tree, fnum1);
2382 fnum1 = -1;
2384 GET_INFO_PATH(pinfo4);
2385 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2387 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2388 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2391 done:
2392 if (fnum1 != -1)
2393 smbcli_close(cli->tree, fnum1);
2394 smbcli_unlink(cli->tree, fname);
2395 smbcli_deltree(cli->tree, BASEDIR);
2397 return ret;
2401 * Show writes and closes have no effect on updating times once a SETWRITETIME is done.
2404 static bool test_delayed_write_update5(struct torture_context *tctx,
2405 struct smbcli_state *cli,
2406 struct smbcli_state *cli2)
2408 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2409 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2410 const char *fname = BASEDIR "\\torture_file5.txt";
2411 int fnum1 = -1;
2412 bool ret = true;
2413 ssize_t written;
2414 struct timeval start;
2415 struct timeval end;
2416 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2417 int normal_delay = 2000000;
2418 double sec = ((double)used_delay) / ((double)normal_delay);
2419 int msec = 1000 * sec;
2421 torture_comment(tctx, "\nRunning test_delayed_write_update5\n");
2423 if (!torture_setup_dir(cli, BASEDIR)) {
2424 return false;
2427 torture_comment(tctx, "Open the file handle\n");
2428 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2429 if (fnum1 == -1) {
2430 ret = false;
2431 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2432 goto done;
2435 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2436 finfo0.basic_info.in.file.fnum = fnum1;
2437 finfo1 = finfo0;
2438 finfo2 = finfo0;
2439 finfo3 = finfo0;
2440 finfo4 = finfo0;
2441 finfo5 = finfo0;
2442 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2443 pinfo0.basic_info.in.file.path = fname;
2444 pinfo1 = pinfo0;
2445 pinfo2 = pinfo0;
2446 pinfo3 = pinfo0;
2447 pinfo4 = pinfo0;
2448 pinfo5 = pinfo0;
2449 pinfo6 = pinfo0;
2451 /* get the initial times */
2452 GET_INFO_BOTH(finfo0,pinfo0);
2454 /* do a write */
2455 torture_comment(tctx, "Do a write on the file handle\n");
2456 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2457 if (written != 1) {
2458 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2459 ret = false;
2460 goto done;
2463 GET_INFO_BOTH(finfo1,pinfo1);
2464 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2466 torture_comment(tctx, "Set write time in the future on the file handle\n");
2467 SET_INFO_FILE(finfo0, time(NULL) + 86400);
2468 GET_INFO_BOTH(finfo2,pinfo2);
2469 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2471 torture_comment(tctx, "Set write time in the past on the file handle\n");
2472 SET_INFO_FILE(finfo0, time(NULL) - 86400);
2473 GET_INFO_BOTH(finfo2,pinfo2);
2474 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2476 /* make sure the 2 second delay from the first write are canceled */
2477 start = timeval_current();
2478 end = timeval_add(&start, 15 * sec, 0);
2479 while (!timeval_expired(&end)) {
2481 /* get the times after the first write */
2482 GET_INFO_BOTH(finfo3,pinfo3);
2484 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2485 double diff = timeval_elapsed(&start);
2486 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2487 "(1sec == %.2f) (wrong!)\n",
2488 diff, sec);
2489 ret = false;
2490 break;
2492 msleep(1 * msec);
2495 GET_INFO_BOTH(finfo3,pinfo3);
2496 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2497 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2498 torture_comment(tctx, "Server did not update write_time (correct)\n");
2501 /* sure any further write doesn't update the write time */
2502 start = timeval_current();
2503 end = timeval_add(&start, 15 * sec, 0);
2504 while (!timeval_expired(&end)) {
2505 /* do a write */
2506 torture_comment(tctx, "Do a write on the file handle\n");
2507 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2508 if (written != 1) {
2509 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2510 ret = false;
2511 goto done;
2513 /* get the times after the write */
2514 GET_INFO_BOTH(finfo4,pinfo4);
2516 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2517 double diff = timeval_elapsed(&start);
2518 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2519 "(1sec == %.2f) (wrong!)\n",
2520 diff, sec);
2521 ret = false;
2522 break;
2524 msleep(1 * msec);
2527 GET_INFO_BOTH(finfo4,pinfo4);
2528 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2529 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2530 torture_comment(tctx, "Server did not update write_time (correct)\n");
2533 /* sleep */
2534 msleep(5 * msec);
2536 GET_INFO_BOTH(finfo5,pinfo5);
2537 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2540 * the close doesn't update the write time
2542 torture_comment(tctx, "Close the file handle\n");
2543 smbcli_close(cli->tree, fnum1);
2544 fnum1 = -1;
2546 GET_INFO_PATH(pinfo6);
2547 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2549 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2550 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2553 done:
2554 if (fnum1 != -1)
2555 smbcli_close(cli->tree, fnum1);
2556 smbcli_unlink(cli->tree, fname);
2557 smbcli_deltree(cli->tree, BASEDIR);
2559 return ret;
2563 * Show truncate writes and closes have no effect on updating times once a SETWRITETIME is done.
2566 static bool test_delayed_write_update5b(struct torture_context *tctx,
2567 struct smbcli_state *cli,
2568 struct smbcli_state *cli2)
2570 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2571 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2572 const char *fname = BASEDIR "\\torture_fileb.txt";
2573 int fnum1 = -1;
2574 bool ret = true;
2575 ssize_t written;
2576 struct timeval start;
2577 struct timeval end;
2578 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2579 int normal_delay = 2000000;
2580 double sec = ((double)used_delay) / ((double)normal_delay);
2581 int msec = 1000 * sec;
2583 torture_comment(tctx, "\nRunning test_delayed_write_update5b\n");
2585 if (!torture_setup_dir(cli, BASEDIR)) {
2586 return false;
2589 torture_comment(tctx, "Open the file handle\n");
2590 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2591 if (fnum1 == -1) {
2592 ret = false;
2593 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2594 goto done;
2597 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2598 finfo0.basic_info.in.file.fnum = fnum1;
2599 finfo1 = finfo0;
2600 finfo2 = finfo0;
2601 finfo3 = finfo0;
2602 finfo4 = finfo0;
2603 finfo5 = finfo0;
2604 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2605 pinfo0.basic_info.in.file.path = fname;
2606 pinfo1 = pinfo0;
2607 pinfo2 = pinfo0;
2608 pinfo3 = pinfo0;
2609 pinfo4 = pinfo0;
2610 pinfo5 = pinfo0;
2611 pinfo6 = pinfo0;
2613 /* get the initial times */
2614 GET_INFO_BOTH(finfo0,pinfo0);
2616 /* do a write */
2617 torture_comment(tctx, "Do a write on the file handle\n");
2618 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2619 if (written != 1) {
2620 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2621 ret = false;
2622 goto done;
2625 GET_INFO_BOTH(finfo1,pinfo1);
2626 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2628 torture_comment(tctx, "Set write time in the future on the file handle\n");
2629 SET_INFO_FILE(finfo0, time(NULL) + 86400);
2630 GET_INFO_BOTH(finfo2,pinfo2);
2631 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2633 torture_comment(tctx, "Set write time in the past on the file handle\n");
2634 SET_INFO_FILE(finfo0, time(NULL) - 86400);
2635 GET_INFO_BOTH(finfo2,pinfo2);
2636 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2638 /* make sure the 2 second delay from the first write are canceled */
2639 start = timeval_current();
2640 end = timeval_add(&start, 15 * sec, 0);
2641 while (!timeval_expired(&end)) {
2643 /* get the times after the first write */
2644 GET_INFO_BOTH(finfo3,pinfo3);
2646 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2647 double diff = timeval_elapsed(&start);
2648 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2649 "(1sec == %.2f) (wrong!)\n",
2650 diff, sec);
2651 ret = false;
2652 break;
2654 msleep(1 * msec);
2657 GET_INFO_BOTH(finfo3,pinfo3);
2658 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2659 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2660 torture_comment(tctx, "Server did not update write_time (correct)\n");
2663 /* Do any further write (truncates) update the write time ? */
2664 start = timeval_current();
2665 end = timeval_add(&start, 15 * sec, 0);
2666 while (!timeval_expired(&end)) {
2667 /* do a write */
2668 torture_comment(tctx, "Do a truncate write on the file handle\n");
2669 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
2670 if (written != 0) {
2671 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2672 ret = false;
2673 goto done;
2675 /* get the times after the write */
2676 GET_INFO_BOTH(finfo4,pinfo4);
2678 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2679 double diff = timeval_elapsed(&start);
2680 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2681 "(1sec == %.2f) (wrong!)\n",
2682 diff, sec);
2683 ret = false;
2684 break;
2686 msleep(1 * msec);
2689 GET_INFO_BOTH(finfo4,pinfo4);
2690 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2691 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2692 torture_comment(tctx, "Server did not update write_time (correct)\n");
2695 /* sleep */
2696 msleep(5 * msec);
2698 GET_INFO_BOTH(finfo5,pinfo5);
2699 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2702 * the close doesn't update the write time
2704 torture_comment(tctx, "Close the file handle\n");
2705 smbcli_close(cli->tree, fnum1);
2706 fnum1 = -1;
2708 GET_INFO_PATH(pinfo6);
2709 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2711 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2712 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2715 done:
2716 if (fnum1 != -1)
2717 smbcli_close(cli->tree, fnum1);
2718 smbcli_unlink(cli->tree, fname);
2719 smbcli_deltree(cli->tree, BASEDIR);
2721 return ret;
2725 * Open 2 handles on a file. Write one one and then set the
2726 * WRITE TIME explicitly on the other. Ensure the write time
2727 * update is cancelled. Ensure the write time is updated to
2728 * the close time when the non-explicit set handle is closed.
2732 static bool test_delayed_write_update6(struct torture_context *tctx,
2733 struct smbcli_state *cli,
2734 struct smbcli_state *cli2)
2736 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2737 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
2738 const char *fname = BASEDIR "\\torture_file6.txt";
2739 int fnum1 = -1;
2740 int fnum2 = -1;
2741 bool ret = true;
2742 ssize_t written;
2743 struct timeval start;
2744 struct timeval end;
2745 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2746 int normal_delay = 2000000;
2747 double sec = ((double)used_delay) / ((double)normal_delay);
2748 int msec = 1000 * sec;
2749 bool first = true;
2751 torture_comment(tctx, "\nRunning test_delayed_write_update6\n");
2753 if (!torture_setup_dir(cli, BASEDIR)) {
2754 return false;
2756 again:
2757 torture_comment(tctx, "Open the file handle\n");
2758 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2759 if (fnum1 == -1) {
2760 ret = false;
2761 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2762 goto done;
2765 if (fnum2 == -1) {
2766 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
2767 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2768 if (fnum2 == -1) {
2769 ret = false;
2770 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2771 goto done;
2775 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2776 finfo0.basic_info.in.file.fnum = fnum1;
2777 finfo1 = finfo0;
2778 finfo2 = finfo0;
2779 finfo3 = finfo0;
2780 finfo4 = finfo0;
2781 finfo5 = finfo0;
2782 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2783 pinfo0.basic_info.in.file.path = fname;
2784 pinfo1 = pinfo0;
2785 pinfo2 = pinfo0;
2786 pinfo3 = pinfo0;
2787 pinfo4 = pinfo0;
2788 pinfo5 = pinfo0;
2789 pinfo6 = pinfo0;
2790 pinfo7 = pinfo0;
2792 /* get the initial times */
2793 GET_INFO_BOTH(finfo0,pinfo0);
2795 /* do a write */
2796 torture_comment(tctx, "Do a write on the file handle\n");
2797 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2798 if (written != 1) {
2799 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2800 ret = false;
2801 goto done;
2804 GET_INFO_BOTH(finfo1,pinfo1);
2805 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2807 torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
2808 SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
2809 GET_INFO_BOTH(finfo2,pinfo2);
2810 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2812 torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
2813 SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
2814 GET_INFO_BOTH(finfo2,pinfo2);
2815 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2817 /* make sure the 2 second delay from the first write are canceled */
2818 start = timeval_current();
2819 end = timeval_add(&start, 10 * sec, 0);
2820 while (!timeval_expired(&end)) {
2822 /* get the times after the first write */
2823 GET_INFO_BOTH(finfo3,pinfo3);
2825 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2826 double diff = timeval_elapsed(&start);
2827 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2828 "(1sec == %.2f) (wrong!)\n",
2829 diff, sec);
2830 ret = false;
2831 break;
2833 msleep(1 * msec);
2836 GET_INFO_BOTH(finfo3,pinfo3);
2837 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2838 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2839 torture_comment(tctx, "Server did not update write_time (correct)\n");
2842 /* sure any further write doesn't update the write time */
2843 start = timeval_current();
2844 end = timeval_add(&start, 10 * sec, 0);
2845 while (!timeval_expired(&end)) {
2846 /* do a write */
2847 torture_comment(tctx, "Do a write on the file handle\n");
2848 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2849 if (written != 1) {
2850 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2851 ret = false;
2852 goto done;
2854 /* get the times after the write */
2855 GET_INFO_BOTH(finfo4,pinfo4);
2857 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2858 double diff = timeval_elapsed(&start);
2859 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2860 "(1sec == %.2f) (wrong!)\n",
2861 diff, sec);
2862 ret = false;
2863 break;
2865 msleep(1 * msec);
2868 GET_INFO_BOTH(finfo4,pinfo4);
2869 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2870 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2871 torture_comment(tctx, "Server did not update write_time (correct)\n");
2874 /* sleep */
2875 msleep(5 * msec);
2877 GET_INFO_BOTH(finfo5,pinfo5);
2878 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2881 * the close updates the write time to the time of the close
2882 * as the write time was set on the 2nd handle
2884 torture_comment(tctx, "Close the file handle\n");
2885 smbcli_close(cli->tree, fnum1);
2886 fnum1 = -1;
2888 GET_INFO_PATH(pinfo6);
2889 COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
2891 if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
2892 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2895 /* See what the second write handle thinks the time is ? */
2896 finfo5.basic_info.in.file.fnum = fnum2;
2897 GET_INFO_FILE2(finfo5);
2898 COMPARE_WRITE_TIME_EQUAL(finfo5, pinfo6);
2900 /* See if we have lost the sticky write time on handle2 */
2901 msleep(3 * msec);
2902 torture_comment(tctx, "Have we lost the sticky write time ?\n");
2904 /* Make sure any further normal write doesn't update the write time */
2905 start = timeval_current();
2906 end = timeval_add(&start, 10 * sec, 0);
2907 while (!timeval_expired(&end)) {
2908 /* do a write */
2909 torture_comment(tctx, "Do a write on the second file handle\n");
2910 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
2911 if (written != 1) {
2912 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2913 ret = false;
2914 goto done;
2916 /* get the times after the write */
2917 GET_INFO_FILE2(finfo5);
2918 GET_INFO_PATH(pinfo6);
2920 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2921 double diff = timeval_elapsed(&start);
2922 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2923 "(1sec == %.2f) (wrong!)\n",
2924 diff, sec);
2925 ret = false;
2926 break;
2928 msleep(1 * msec);
2931 /* What about a truncate write ? */
2932 start = timeval_current();
2933 end = timeval_add(&start, 10 * sec, 0);
2934 while (!timeval_expired(&end)) {
2935 /* do a write */
2936 torture_comment(tctx, "Do a truncate write on the second file handle\n");
2937 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 0);
2938 if (written != 0) {
2939 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2940 ret = false;
2941 goto done;
2943 /* get the times after the write */
2944 GET_INFO_FILE2(finfo5);
2945 GET_INFO_PATH(pinfo6);
2947 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2948 double diff = timeval_elapsed(&start);
2949 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2950 "(1sec == %.2f) (wrong!)\n",
2951 diff, sec);
2952 ret = false;
2953 break;
2955 msleep(1 * msec);
2959 /* keep the 2nd handle open and rerun tests */
2960 if (first) {
2961 first = false;
2962 goto again;
2966 * closing the 2nd handle will cause no write time update
2967 * as the write time was explicit set on this handle
2969 torture_comment(tctx, "Close the 2nd file handle\n");
2970 smbcli_close(cli2->tree, fnum2);
2971 fnum2 = -1;
2973 GET_INFO_PATH(pinfo7);
2974 COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
2976 if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
2977 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2980 done:
2981 if (fnum1 != -1)
2982 smbcli_close(cli->tree, fnum1);
2983 if (fnum2 != -1)
2984 smbcli_close(cli2->tree, fnum2);
2985 smbcli_unlink(cli->tree, fname);
2986 smbcli_deltree(cli->tree, BASEDIR);
2988 return ret;
2991 static bool test_delayed_write_update7(struct torture_context *tctx, struct smbcli_state *cli)
2993 union smb_open open_parms;
2994 union smb_fileinfo finfo1, finfo2, finfo3;
2995 const char *fname = BASEDIR "\\torture_file7.txt";
2996 NTSTATUS status;
2997 int fnum1 = -1;
2998 bool ret = true;
2999 TALLOC_CTX *mem_ctx;
3001 torture_comment(tctx, "\nRunning test_delayed_write_update7 (timestamp resolution test)\n");
3003 mem_ctx = talloc_init("test_delayed_write_update7");
3004 if (!mem_ctx) return false;
3006 ZERO_STRUCT(finfo1);
3007 ZERO_STRUCT(finfo2);
3008 ZERO_STRUCT(finfo3);
3009 ZERO_STRUCT(open_parms);
3011 if (!torture_setup_dir(cli, BASEDIR)) {
3012 return false;
3015 /* Create the file. */
3016 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
3017 if (fnum1 == -1) {
3018 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
3019 return false;
3022 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
3023 finfo1.basic_info.in.file.fnum = fnum1;
3024 finfo2 = finfo1;
3025 finfo3 = finfo1;
3027 /* Get the initial timestamps. */
3028 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
3030 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
3032 /* Set the pending write time to a value with ns. */
3033 SET_INFO_FILE_NS(finfo, time(NULL) + 86400, 103, cli->tree, fnum1);
3035 /* Get the current pending write time by fnum. */
3036 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
3038 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
3040 /* Ensure the time is actually different. */
3041 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
3042 torture_result(tctx, TORTURE_FAIL,
3043 "setfileinfo time matches original fileinfo time");
3044 ret = false;
3047 /* Get the current pending write time by path. */
3048 finfo3.basic_info.in.file.path = fname;
3049 status = smb_raw_pathinfo(cli->tree, tctx, &finfo3);
3051 if (finfo2.basic_info.out.write_time != finfo3.basic_info.out.write_time) {
3052 torture_result(tctx, TORTURE_FAIL,
3053 "qpathinfo time doens't match fileinfo time");
3054 ret = false;
3057 /* Now close the file. Re-open and check that the write
3058 time is identical to the one we wrote. */
3060 smbcli_close(cli->tree, fnum1);
3062 open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
3063 open_parms.ntcreatex.in.flags = 0;
3064 open_parms.ntcreatex.in.access_mask = SEC_GENERIC_READ;
3065 open_parms.ntcreatex.in.file_attr = 0;
3066 open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE|
3067 NTCREATEX_SHARE_ACCESS_READ|
3068 NTCREATEX_SHARE_ACCESS_WRITE;
3069 open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
3070 open_parms.ntcreatex.in.create_options = 0;
3071 open_parms.ntcreatex.in.fname = fname;
3073 status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
3074 talloc_free(mem_ctx);
3076 if (!NT_STATUS_IS_OK(status)) {
3077 torture_result(tctx, TORTURE_FAIL,
3078 "setfileinfo time matches original fileinfo time");
3079 ret = false;
3082 fnum1 = open_parms.ntcreatex.out.file.fnum;
3084 /* Check the returned time matches. */
3085 if (open_parms.ntcreatex.out.write_time != finfo2.basic_info.out.write_time) {
3086 torture_result(tctx, TORTURE_FAIL,
3087 "final open time does not match set time");
3088 ret = false;
3091 done:
3093 smbcli_close(cli->tree, fnum1);
3095 smbcli_unlink(cli->tree, fname);
3096 smbcli_deltree(cli->tree, BASEDIR);
3097 return ret;
3101 testing of delayed update of write_time
3103 struct torture_suite *torture_delay_write(void)
3105 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "DELAYWRITE");
3107 torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
3108 torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
3109 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate", test_delayed_write_update1);
3110 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a);
3111 torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b);
3112 torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c);
3113 torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
3114 torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
3115 torture_suite_add_2smb_test(suite, "delayed update of write time 3a", test_delayed_write_update3a);
3116 torture_suite_add_2smb_test(suite, "delayed update of write time 3b", test_delayed_write_update3b);
3117 torture_suite_add_2smb_test(suite, "delayed update of write time 3c", test_delayed_write_update3c);
3118 torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
3119 torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
3120 torture_suite_add_2smb_test(suite, "delayed update of write time 5b", test_delayed_write_update5b);
3121 torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
3122 torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7);
3123 torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7);
3125 return suite;