smbd: Make tf a field of its own in smbd_smb2_send_oplock_break
[Samba.git] / source4 / torture / basic / delaywrite.c
blob12d95e21109c461f6b2f79fe5de7c7852d117f21
1 /*
2 Unix SMB/CIFS implementation.
4 test suite for delayed write update
6 Copyright (C) Volker Lendecke 2004
7 Copyright (C) Andrew Tridgell 2004
8 Copyright (C) Jeremy Allison 2004
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "torture/torture.h"
26 #include "libcli/raw/libcliraw.h"
27 #include "libcli/raw/raw_proto.h"
28 #include "system/time.h"
29 #include "system/filesys.h"
30 #include "libcli/libcli.h"
31 #include "torture/util.h"
32 #include "torture/basic/proto.h"
34 #define BASEDIR "\\delaywrite"
36 static bool test_delayed_write_update(struct torture_context *tctx, struct smbcli_state *cli)
38 union smb_fileinfo finfo1, finfo2;
39 const char *fname = BASEDIR "\\torture_file.txt";
40 NTSTATUS status;
41 int fnum1 = -1;
42 bool ret = true;
43 ssize_t written;
44 struct timeval start;
45 struct timeval end;
46 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
47 int normal_delay = 2000000;
48 double sec = ((double)used_delay) / ((double)normal_delay);
49 int msec = 1000 * sec;
51 torture_comment(tctx, "\nRunning test_delayed_write_update\n");
53 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
55 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
56 if (fnum1 == -1) {
57 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
58 return false;
61 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
62 finfo1.basic_info.in.file.fnum = fnum1;
63 finfo2 = finfo1;
65 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
67 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
69 torture_comment(tctx, "Initial write time %s\n",
70 nt_time_string(tctx, finfo1.basic_info.out.write_time));
72 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
74 if (written != 1) {
75 torture_result(tctx, TORTURE_FAIL,
76 "write failed - wrote %d bytes (%s)\n",
77 (int)written, __location__);
78 return false;
81 start = timeval_current();
82 end = timeval_add(&start, (120 * sec), 0);
83 while (!timeval_expired(&end)) {
84 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
86 if (!NT_STATUS_IS_OK(status)) {
87 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
88 ret = false;
89 break;
91 torture_comment(tctx, "write time %s\n",
92 nt_time_string(tctx, finfo2.basic_info.out.write_time));
93 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
94 double diff = timeval_elapsed(&start);
95 if (diff < (used_delay / (double)1000000)) {
96 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
97 "(expected > %.2f) (wrong!)\n",
98 diff, used_delay / (double)1000000);
99 ret = false;
100 break;
103 torture_comment(tctx, "Server updated write_time after %.2f seconds (correct)\n",
104 diff);
105 break;
107 fflush(stdout);
108 smb_msleep(1 * msec);
111 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
112 torture_result(tctx, TORTURE_FAIL,
113 "Server did not update write time (wrong!)");
114 ret = false;
118 if (fnum1 != -1)
119 smbcli_close(cli->tree, fnum1);
120 smbcli_unlink(cli->tree, fname);
121 smbcli_deltree(cli->tree, BASEDIR);
123 return ret;
126 static bool test_delayed_write_update1(struct torture_context *tctx, struct smbcli_state *cli)
128 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
129 const char *fname = BASEDIR "\\torture_file1.txt";
130 NTSTATUS status;
131 int fnum1 = -1;
132 bool ret = true;
133 ssize_t written;
134 struct timeval start;
135 struct timeval end;
136 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
137 int normal_delay = 2000000;
138 double sec = ((double)used_delay) / ((double)normal_delay);
139 int msec = 1000 * sec;
140 char buf[2048];
142 torture_comment(tctx, "\nRunning test_delayed_write_update1\n");
144 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
146 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
147 if (fnum1 == -1) {
148 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
149 return false;
152 memset(buf, 'x', 2048);
153 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
155 /* 3 second delay to ensure we get past any 2 second time
156 granularity (older systems may have that) */
157 smb_msleep(3 * msec);
159 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
160 finfo1.all_info.in.file.fnum = fnum1;
161 finfo2 = finfo1;
162 finfo3 = finfo1;
163 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
164 pinfo4.all_info.in.file.path = fname;
166 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
168 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
170 torture_comment(tctx, "Initial write time %s\n",
171 nt_time_string(tctx, finfo1.all_info.out.write_time));
173 /* 3 second delay to ensure we get past any 2 second time
174 granularity (older systems may have that) */
175 smb_msleep(3 * msec);
177 /* Do a zero length SMBwrite call to truncate. */
178 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
180 if (written != 0) {
181 torture_result(tctx, TORTURE_FAIL,
182 "write failed - wrote %d bytes (%s)\n",
183 (int)written, __location__);
184 return false;
187 start = timeval_current();
188 end = timeval_add(&start, (120 * sec), 0);
189 while (!timeval_expired(&end)) {
190 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
192 if (!NT_STATUS_IS_OK(status)) {
193 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
194 ret = false;
195 break;
198 if (finfo2.all_info.out.size != 1024) {
199 torture_result(tctx, TORTURE_FAIL,
200 "file not truncated, size = %u (should be 1024)",
201 (unsigned int)finfo2.all_info.out.size);
202 ret = false;
203 break;
206 torture_comment(tctx, "write time %s\n",
207 nt_time_string(tctx, finfo2.all_info.out.write_time));
208 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
209 double diff = timeval_elapsed(&start);
210 if (diff > (0.25 * (used_delay / (double)1000000))) {
211 torture_result(tctx, TORTURE_FAIL, "After SMBwrite truncate "
212 "server updated write_time after %.2f seconds"
213 "(write time update dealy == %.2f)(wrong!)\n",
214 diff, used_delay / (double)1000000);
215 ret = false;
216 break;
219 torture_comment(tctx, "After SMBwrite truncate "
220 "server updated write_time after %.2f seconds"
221 "(1 sec == %.2f)(correct)\n",
222 diff, used_delay / (double)1000000);
223 break;
225 fflush(stdout);
226 smb_msleep(1 * msec);
229 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
230 torture_result(tctx, TORTURE_FAIL,
231 "Server did not update write time (wrong!)");
232 ret = false;
235 fflush(stdout);
236 smb_msleep(2 * msec);
238 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
239 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
241 if (written != 1) {
242 torture_result(tctx, TORTURE_FAIL,
243 "write failed - wrote %d bytes (%s)",
244 (int)written, __location__);
245 return false;
248 start = timeval_current();
249 end = timeval_add(&start, (10*sec), 0);
250 while (!timeval_expired(&end)) {
251 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
253 if (!NT_STATUS_IS_OK(status)) {
254 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
255 ret = false;
256 break;
259 if (finfo3.all_info.out.size != 1024) {
260 DEBUG(0, ("file not truncated, size = %u (should be 1024)\n",
261 (unsigned int)finfo3.all_info.out.size));
262 ret = false;
263 break;
266 torture_comment(tctx, "write time %s\n",
267 nt_time_string(tctx, finfo3.all_info.out.write_time));
268 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
269 double diff = timeval_elapsed(&start);
271 torture_comment(tctx, "server updated write_time after %.2f seconds"
272 "(wrong)\n",
273 diff);
274 break;
276 fflush(stdout);
277 smb_msleep(1 * msec);
280 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
281 torture_result(tctx, TORTURE_FAIL,
282 "Server updated write time (wrong!)");
283 ret = false;
286 fflush(stdout);
287 smb_msleep(2 * msec);
289 /* the close should trigger an write time update */
290 smbcli_close(cli->tree, fnum1);
291 fnum1 = -1;
293 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
294 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
296 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
297 torture_result(tctx, TORTURE_FAIL,
298 "Server did not update write time on close (wrong!)");
299 ret = false;
300 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
301 torture_comment(tctx, "Server updated write time on close (correct)\n");
304 if (fnum1 != -1)
305 smbcli_close(cli->tree, fnum1);
306 smbcli_unlink(cli->tree, fname);
307 smbcli_deltree(cli->tree, BASEDIR);
309 return ret;
312 /* Updating with a SMBwrite of zero length
313 * changes the write time immediately - even on expand. */
315 static bool test_delayed_write_update1a(struct torture_context *tctx, struct smbcli_state *cli)
317 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
318 const char *fname = BASEDIR "\\torture_file1a.txt";
319 NTSTATUS status;
320 int fnum1 = -1;
321 bool ret = true;
322 ssize_t written;
323 struct timeval start;
324 struct timeval end;
325 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
326 int normal_delay = 2000000;
327 double sec = ((double)used_delay) / ((double)normal_delay);
328 int msec = 1000 * sec;
329 char buf[2048];
331 torture_comment(tctx, "\nRunning test_delayed_write_update1a\n");
333 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
335 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
336 if (fnum1 == -1) {
337 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
338 return false;
341 memset(buf, 'x', 2048);
342 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
344 /* 3 second delay to ensure we get past any 2 second time
345 granularity (older systems may have that) */
346 smb_msleep(3 * msec);
348 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
349 finfo1.all_info.in.file.fnum = fnum1;
350 finfo2 = finfo1;
351 finfo3 = finfo1;
352 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
353 pinfo4.all_info.in.file.path = fname;
355 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
357 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
359 torture_comment(tctx, "Initial write time %s\n",
360 nt_time_string(tctx, finfo1.all_info.out.write_time));
362 /* Do a zero length SMBwrite call to truncate. */
363 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
365 if (written != 0) {
366 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)",
367 (int)written, __location__);
368 return false;
371 start = timeval_current();
372 end = timeval_add(&start, (120*sec), 0);
373 while (!timeval_expired(&end)) {
374 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
376 if (!NT_STATUS_IS_OK(status)) {
377 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s",
378 nt_errstr(status));
379 ret = false;
380 break;
383 if (finfo2.all_info.out.size != 10240) {
384 torture_result(tctx, TORTURE_FAIL,
385 "file not truncated, size = %u (should be 10240)",
386 (unsigned int)finfo2.all_info.out.size);
387 ret = false;
388 break;
391 torture_comment(tctx, "write time %s\n",
392 nt_time_string(tctx, finfo2.all_info.out.write_time));
393 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
394 double diff = timeval_elapsed(&start);
395 if (diff > (0.25 * (used_delay / (double)1000000))) {
396 torture_result(tctx, TORTURE_FAIL, "After SMBwrite truncate "
397 "server updated write_time after %.2f seconds"
398 "(write time update delay == %.2f)(wrong!)\n",
399 diff, used_delay / (double)1000000);
400 ret = false;
401 break;
404 torture_comment(tctx, "After SMBwrite truncate "
405 "server updated write_time after %.2f seconds"
406 "(write time update delay == %.2f)(correct)\n",
407 diff, used_delay / (double)1000000);
408 break;
410 fflush(stdout);
411 smb_msleep(1 * msec);
414 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
415 torture_result(tctx, TORTURE_FAIL,
416 "Server did not update write time (wrong!)");
417 ret = false;
420 fflush(stdout);
421 smb_msleep(2 * msec);
423 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
424 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
426 torture_assert_int_equal(tctx, written, 1,
427 "unexpected number of bytes written");
429 start = timeval_current();
430 end = timeval_add(&start, (10*sec), 0);
431 while (!timeval_expired(&end)) {
432 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
434 if (!NT_STATUS_IS_OK(status)) {
435 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s\n",
436 nt_errstr(status));
437 ret = false;
438 break;
441 if (finfo3.all_info.out.size != 10240) {
442 torture_result(tctx, TORTURE_FAIL,
443 "file not truncated, size = %u (should be 10240)",
444 (unsigned int)finfo3.all_info.out.size);
445 ret = false;
446 break;
449 torture_comment(tctx, "write time %s\n",
450 nt_time_string(tctx, finfo3.all_info.out.write_time));
451 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
452 double diff = timeval_elapsed(&start);
454 torture_result(tctx, TORTURE_FAIL, "server updated write_time after %.2f seconds"
455 "(write time update delay == %.2f)(correct)\n",
456 diff, used_delay / (double)1000000);
457 break;
459 fflush(stdout);
460 smb_msleep(1 * msec);
463 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
464 torture_result(tctx, TORTURE_FAIL,
465 "Server updated write time (wrong!)");
466 ret = false;
469 /* the close should trigger an write time update */
470 smbcli_close(cli->tree, fnum1);
471 fnum1 = -1;
473 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
474 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
476 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
477 torture_result(tctx, TORTURE_FAIL,
478 "Server did not update write time on close (wrong!)");
479 ret = false;
480 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
481 torture_comment(tctx, "Server updated write time on close (correct)\n");
484 if (fnum1 != -1)
485 smbcli_close(cli->tree, fnum1);
486 smbcli_unlink(cli->tree, fname);
487 smbcli_deltree(cli->tree, BASEDIR);
489 return ret;
492 /* Updating with a SET_FILE_END_OF_FILE_INFO
493 * changes the write time immediately - even on expand. */
495 static bool test_delayed_write_update1b(struct torture_context *tctx, struct smbcli_state *cli)
497 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
498 const char *fname = BASEDIR "\\torture_file1b.txt";
499 NTSTATUS status;
500 int fnum1 = -1;
501 bool ret = true;
502 ssize_t written;
503 struct timeval start;
504 struct timeval end;
505 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
506 int normal_delay = 2000000;
507 double sec = ((double)used_delay) / ((double)normal_delay);
508 int msec = 1000 * sec;
509 char buf[2048];
511 torture_comment(tctx, "\nRunning test_delayed_write_update1b\n");
513 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
515 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
516 if (fnum1 == -1) {
517 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
518 return false;
521 memset(buf, 'x', 2048);
522 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
524 /* 3 second delay to ensure we get past any 2 second time
525 granularity (older systems may have that) */
526 smb_msleep(3 * msec);
528 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
529 finfo1.all_info.in.file.fnum = fnum1;
530 finfo2 = finfo1;
531 finfo3 = finfo1;
532 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
533 pinfo4.all_info.in.file.path = fname;
535 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
537 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
539 torture_comment(tctx, "Initial write time %s\n",
540 nt_time_string(tctx, finfo1.all_info.out.write_time));
542 /* Do a SET_END_OF_FILE_INFO call to truncate. */
543 status = smbcli_ftruncate(cli->tree, fnum1, (uint64_t)10240);
545 torture_assert_ntstatus_ok(tctx, status, "SET_END_OF_FILE failed");
547 start = timeval_current();
548 end = timeval_add(&start, (120*sec), 0);
549 while (!timeval_expired(&end)) {
550 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
552 if (!NT_STATUS_IS_OK(status)) {
553 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
554 ret = false;
555 break;
558 if (finfo2.all_info.out.size != 10240) {
559 torture_result(tctx, TORTURE_FAIL,
560 "file not truncated (size = %u, should be 10240)",
561 (unsigned int)finfo2.all_info.out.size );
562 ret = false;
563 break;
566 torture_comment(tctx, "write time %s\n",
567 nt_time_string(tctx, finfo2.all_info.out.write_time));
568 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
569 double diff = timeval_elapsed(&start);
570 if (diff > (0.25 * (used_delay / (double)1000000))) {
571 torture_result(tctx, TORTURE_FAIL,
572 "After SET_END_OF_FILE truncate "
573 "server updated write_time after %.2f seconds"
574 "(write time update delay == %.2f)(wrong!)",
575 diff, used_delay / (double)1000000);
576 ret = false;
577 break;
580 torture_comment(tctx, "After SET_END_OF_FILE truncate "
581 "server updated write_time after %.2f seconds"
582 "(write time update delay == %.2f)(correct)\n",
583 diff, used_delay / (double)1000000);
584 break;
586 fflush(stdout);
587 smb_msleep(1 * msec);
590 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
591 torture_result(tctx, TORTURE_FAIL,
592 "Server did not update write time (wrong!)");
593 ret = false;
596 fflush(stdout);
597 smb_msleep(2 * msec);
599 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
600 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
602 torture_assert_int_equal(tctx, written, 1,
603 "unexpected number of bytes written");
605 start = timeval_current();
606 end = timeval_add(&start, (10*sec), 0);
607 while (!timeval_expired(&end)) {
608 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
610 if (!NT_STATUS_IS_OK(status)) {
611 torture_result(tctx, TORTURE_FAIL,
612 "fileinfo failed: %s", nt_errstr(status));
613 ret = false;
614 break;
617 if (finfo3.all_info.out.size != 10240) {
618 DEBUG(0, ("file not truncated (size = %u, should be 10240)\n",
619 (unsigned int)finfo3.all_info.out.size ));
620 ret = false;
621 break;
624 torture_comment(tctx, "write time %s\n",
625 nt_time_string(tctx, finfo3.all_info.out.write_time));
626 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
627 double diff = timeval_elapsed(&start);
629 torture_comment(tctx, "server updated write_time after %.2f seconds"
630 "(write time update delay == %.2f)(correct)\n",
631 diff, used_delay / (double)1000000);
632 break;
634 fflush(stdout);
635 smb_msleep(1 * msec);
638 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
639 torture_result(tctx, TORTURE_FAIL, "Server updated write time (wrong!)\n");
640 ret = false;
643 /* the close should trigger an write time update */
644 smbcli_close(cli->tree, fnum1);
645 fnum1 = -1;
647 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
648 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
650 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
651 torture_result(tctx, TORTURE_FAIL, "Server did not update write time on close (wrong!)\n");
652 ret = false;
653 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
654 torture_comment(tctx, "Server updated write time on close (correct)\n");
657 if (fnum1 != -1)
658 smbcli_close(cli->tree, fnum1);
659 smbcli_unlink(cli->tree, fname);
660 smbcli_deltree(cli->tree, BASEDIR);
662 return ret;
665 /* Updating with a SET_ALLOCATION_INFO (truncate) does so immediately. */
667 static bool test_delayed_write_update1c(struct torture_context *tctx, struct smbcli_state *cli)
669 union smb_setfileinfo parms;
670 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
671 const char *fname = BASEDIR "\\torture_file1c.txt";
672 NTSTATUS status;
673 int fnum1 = -1;
674 bool ret = true;
675 ssize_t written;
676 struct timeval start;
677 struct timeval end;
678 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
679 int normal_delay = 2000000;
680 double sec = ((double)used_delay) / ((double)normal_delay);
681 int msec = 1000 * sec;
682 char buf[2048];
684 torture_comment(tctx, "\nRunning test_delayed_write_update1c\n");
686 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
688 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
689 if (fnum1 == -1) {
690 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
691 return false;
694 memset(buf, 'x', 2048);
695 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
697 /* 3 second delay to ensure we get past any 2 second time
698 granularity (older systems may have that) */
699 smb_msleep(3 * msec);
701 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
702 finfo1.all_info.in.file.fnum = fnum1;
703 finfo2 = finfo1;
704 finfo3 = finfo1;
705 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
706 pinfo4.all_info.in.file.path = fname;
708 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
710 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
712 torture_comment(tctx, "Initial write time %s\n",
713 nt_time_string(tctx, finfo1.all_info.out.write_time));
715 /* Do a SET_ALLOCATION_SIZE call to truncate. */
716 parms.allocation_info.level = RAW_SFILEINFO_ALLOCATION_INFO;
717 parms.allocation_info.in.file.fnum = fnum1;
718 parms.allocation_info.in.alloc_size = 0;
720 status = smb_raw_setfileinfo(cli->tree, &parms);
722 torture_assert_ntstatus_ok(tctx, status,
723 "RAW_SFILEINFO_ALLOCATION_INFO failed");
725 start = timeval_current();
726 end = timeval_add(&start, (120*sec), 0);
727 while (!timeval_expired(&end)) {
728 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
730 if (!NT_STATUS_IS_OK(status)) {
731 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s",
732 nt_errstr(status));
733 ret = false;
734 break;
737 if (finfo2.all_info.out.size != 0) {
738 torture_result(tctx, TORTURE_FAIL,
739 "file not truncated (size = %u, should be 10240)",
740 (unsigned int)finfo2.all_info.out.size);
741 ret = false;
742 break;
745 torture_comment(tctx, "write time %s\n",
746 nt_time_string(tctx, finfo2.all_info.out.write_time));
747 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
748 double diff = timeval_elapsed(&start);
749 if (diff > (0.25 * (used_delay / (double)1000000))) {
750 torture_result(tctx, TORTURE_FAIL, "After SET_ALLOCATION_INFO truncate "
751 "server updated write_time after %.2f seconds"
752 "(write time update delay == %.2f)(wrong!)\n",
753 diff, used_delay / (double)1000000);
754 ret = false;
755 break;
758 torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
759 "server updated write_time after %.2f seconds"
760 "(write time update delay == %.2f)(correct)\n",
761 diff, used_delay / (double)1000000);
762 break;
764 fflush(stdout);
765 smb_msleep(1 * msec);
768 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
769 torture_result(tctx, TORTURE_FAIL,
770 "Server did not update write time (wrong!)");
771 ret = false;
774 fflush(stdout);
775 smb_msleep(2 * msec);
777 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
778 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
779 torture_assert_int_equal(tctx, written, 1,
780 "Unexpected number of bytes written");
782 start = timeval_current();
783 end = timeval_add(&start, (10*sec), 0);
784 while (!timeval_expired(&end)) {
785 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
787 if (!NT_STATUS_IS_OK(status)) {
788 torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s",
789 nt_errstr(status));
790 ret = false;
791 break;
794 if (finfo3.all_info.out.size != 1) {
795 torture_result(tctx, TORTURE_FAIL, "file not expanded");
796 ret = false;
797 break;
800 torture_comment(tctx, "write time %s\n",
801 nt_time_string(tctx, finfo3.all_info.out.write_time));
802 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
803 double diff = timeval_elapsed(&start);
805 torture_comment(tctx, "server updated write_time after %.2f seconds"
806 "(write time update delay == %.2f)(wrong)\n",
807 diff, used_delay / (double)1000000);
808 break;
810 fflush(stdout);
811 smb_msleep(1 * msec);
814 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
815 torture_result(tctx, TORTURE_FAIL,
816 "Server updated write time (wrong!)");
817 ret = false;
820 /* the close should trigger an write time update */
821 smbcli_close(cli->tree, fnum1);
822 fnum1 = -1;
824 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
825 torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
827 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
828 torture_result(tctx, TORTURE_FAIL, "Server did not update write time on close (wrong!)\n");
829 ret = false;
830 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
831 torture_comment(tctx, "Server updated write time on close (correct)\n");
834 if (fnum1 != -1)
835 smbcli_close(cli->tree, fnum1);
836 smbcli_unlink(cli->tree, fname);
837 smbcli_deltree(cli->tree, BASEDIR);
839 return ret;
843 * Do as above, but using 2 connections.
846 static bool test_delayed_write_update2(struct torture_context *tctx, struct smbcli_state *cli,
847 struct smbcli_state *cli2)
849 union smb_fileinfo finfo1, finfo2;
850 const char *fname = BASEDIR "\\torture_file.txt";
851 NTSTATUS status;
852 int fnum1 = -1;
853 int fnum2 = -1;
854 bool ret = true;
855 ssize_t written;
856 struct timeval start;
857 struct timeval end;
858 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
859 int normal_delay = 2000000;
860 double sec = ((double)used_delay) / ((double)normal_delay);
861 int msec = 1000 * sec;
862 union smb_flush flsh;
864 torture_comment(tctx, "\nRunning test_delayed_write_update2\n");
866 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
868 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
869 if (fnum1 == -1) {
870 torture_comment(tctx, "Failed to open %s\n", fname);
871 return false;
874 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
875 finfo1.basic_info.in.file.fnum = fnum1;
876 finfo2 = finfo1;
878 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
880 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
882 torture_comment(tctx, "Initial write time %s\n",
883 nt_time_string(tctx, finfo1.basic_info.out.write_time));
885 /* 3 second delay to ensure we get past any 2 second time
886 granularity (older systems may have that) */
887 smb_msleep(3 * msec);
890 /* Try using setfileinfo instead of write to update write time. */
891 union smb_setfileinfo sfinfo;
892 time_t t_set = time(NULL);
893 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
894 sfinfo.basic_info.in.file.fnum = fnum1;
895 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
896 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
898 /* I tried this with both + and - ve to see if it makes a different.
899 It doesn't - once the filetime is set via setfileinfo it stays that way. */
900 #if 1
901 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
902 #else
903 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
904 #endif
905 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
906 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
908 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
910 torture_assert_ntstatus_ok(tctx, status, "sfileinfo failed");
913 finfo2.basic_info.in.file.path = fname;
915 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
917 if (!NT_STATUS_IS_OK(status)) {
918 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
919 return false;
921 torture_comment(tctx, "write time %s\n",
922 nt_time_string(tctx, finfo2.basic_info.out.write_time));
924 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
925 torture_comment(tctx, "Server updated write_time (correct)\n");
926 } else {
927 torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n");
928 ret = false;
931 /* Now try a write to see if the write time gets reset. */
933 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
934 finfo1.basic_info.in.file.fnum = fnum1;
935 finfo2 = finfo1;
937 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
939 if (!NT_STATUS_IS_OK(status)) {
940 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
941 return false;
944 torture_comment(tctx, "Modified write time %s\n",
945 nt_time_string(tctx, finfo1.basic_info.out.write_time));
948 torture_comment(tctx, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
950 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
952 if (written != 10) {
953 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n",
954 (int)written, __location__);
955 return false;
958 /* Just to prove to tridge that the an smbflush has no effect on
959 the write time :-). The setfileinfo IS STICKY. JRA. */
961 torture_comment(tctx, "Doing flush after write\n");
963 flsh.flush.level = RAW_FLUSH_FLUSH;
964 flsh.flush.in.file.fnum = fnum1;
965 status = smb_raw_flush(cli->tree, &flsh);
966 if (!NT_STATUS_IS_OK(status)) {
967 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
968 return false;
971 /* Once the time was set using setfileinfo then it stays set - writes
972 don't have any effect. But make sure. */
973 start = timeval_current();
974 end = timeval_add(&start, (15*sec), 0);
975 while (!timeval_expired(&end)) {
976 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
978 if (!NT_STATUS_IS_OK(status)) {
979 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
980 ret = false;
981 break;
983 torture_comment(tctx, "write time %s\n",
984 nt_time_string(tctx, finfo2.basic_info.out.write_time));
985 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
986 double diff = timeval_elapsed(&start);
987 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
988 "(wrong!)\n",
989 diff);
990 ret = false;
991 break;
993 fflush(stdout);
994 smb_msleep(1 * msec);
997 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
998 torture_comment(tctx, "Server did not update write time (correct)\n");
1001 fflush(stdout);
1002 smb_msleep(2 * msec);
1004 fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
1005 if (fnum2 == -1) {
1006 torture_result(tctx, TORTURE_FAIL, "Failed to open %s\n", fname);
1007 return false;
1010 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");
1012 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
1014 if (written != 10) {
1015 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n",
1016 (int)written, __location__);
1017 return false;
1020 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1022 if (!NT_STATUS_IS_OK(status)) {
1023 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1024 return false;
1026 torture_comment(tctx, "write time %s\n",
1027 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1028 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1029 torture_result(tctx, TORTURE_FAIL, "Server updated write_time (wrong!)\n");
1030 ret = false;
1033 torture_comment(tctx, "Closing the first fd to see if write time updated.\n");
1034 smbcli_close(cli->tree, fnum1);
1035 fnum1 = -1;
1037 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");
1039 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
1041 if (written != 10) {
1042 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n",
1043 (int)written, __location__);
1044 return false;
1047 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1048 finfo1.basic_info.in.file.fnum = fnum2;
1049 finfo2 = finfo1;
1050 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1052 if (!NT_STATUS_IS_OK(status)) {
1053 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1054 return false;
1056 torture_comment(tctx, "write time %s\n",
1057 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1058 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1059 torture_result(tctx, TORTURE_FAIL, "Server updated write_time (wrong!)\n");
1060 ret = false;
1063 /* Once the time was set using setfileinfo then it stays set - writes
1064 don't have any effect. But make sure. */
1065 start = timeval_current();
1066 end = timeval_add(&start, (15*sec), 0);
1067 while (!timeval_expired(&end)) {
1068 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1070 if (!NT_STATUS_IS_OK(status)) {
1071 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1072 ret = false;
1073 break;
1075 torture_comment(tctx, "write time %s\n",
1076 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1077 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1078 double diff = timeval_elapsed(&start);
1079 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1080 "(wrong!)\n",
1081 diff);
1082 ret = false;
1083 break;
1085 fflush(stdout);
1086 smb_msleep(1 * msec);
1089 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1090 torture_comment(tctx, "Server did not update write time (correct)\n");
1093 torture_comment(tctx, "Closing second fd to see if write time updated.\n");
1095 smbcli_close(cli->tree, fnum2);
1096 fnum2 = -1;
1098 fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
1099 if (fnum1 == -1) {
1100 torture_comment(tctx, "Failed to open %s\n", fname);
1101 return false;
1104 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1105 finfo1.basic_info.in.file.fnum = fnum1;
1106 finfo2 = finfo1;
1108 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1110 if (!NT_STATUS_IS_OK(status)) {
1111 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1112 return false;
1115 torture_comment(tctx, "Second open initial write time %s\n",
1116 nt_time_string(tctx, finfo1.basic_info.out.write_time));
1118 smb_msleep(10 * msec);
1119 torture_comment(tctx, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
1121 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
1123 if (written != 10) {
1124 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)\n",
1125 (int)written, __location__);
1126 return false;
1129 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1130 finfo1.basic_info.in.file.fnum = fnum1;
1131 finfo2 = finfo1;
1132 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1134 if (!NT_STATUS_IS_OK(status)) {
1135 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1136 return false;
1138 torture_comment(tctx, "write time %s\n",
1139 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1140 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1141 torture_result(tctx, TORTURE_FAIL, "Server updated write_time (wrong!)\n");
1142 ret = false;
1145 /* Now the write time should be updated again */
1146 start = timeval_current();
1147 end = timeval_add(&start, (15*sec), 0);
1148 while (!timeval_expired(&end)) {
1149 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1151 if (!NT_STATUS_IS_OK(status)) {
1152 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1153 ret = false;
1154 break;
1156 torture_comment(tctx, "write time %s\n",
1157 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1158 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1159 double diff = timeval_elapsed(&start);
1160 if (diff < (used_delay / (double)1000000)) {
1161 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
1162 "(expected > %.2f) (wrong!)\n",
1163 diff, used_delay / (double)1000000);
1164 ret = false;
1165 break;
1168 torture_comment(tctx, "Server updated write_time after %.2f seconds"
1169 "(correct)\n",
1170 diff);
1171 break;
1173 fflush(stdout);
1174 smb_msleep(1*msec);
1177 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1178 torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n");
1179 ret = false;
1183 /* One more test to do. We should read the filetime via findfirst on the
1184 second connection to ensure it's the same. This is very easy for a Windows
1185 server but a bastard to get right on a POSIX server. JRA. */
1187 if (fnum1 != -1)
1188 smbcli_close(cli->tree, fnum1);
1189 smbcli_unlink(cli->tree, fname);
1190 smbcli_deltree(cli->tree, BASEDIR);
1192 return ret;
1196 /* Windows does obviously not update the stat info during a write call. I
1197 * *think* this is the problem causing a spurious Excel 2003 on XP error
1198 * message when saving a file. Excel does a setfileinfo, writes, and then does
1199 * a getpath(!)info. Or so... For Samba sometimes it displays an error message
1200 * that the file might have been changed in between. What i've been able to
1201 * trace down is that this happens if the getpathinfo after the write shows a
1202 * different last write time than the setfileinfo showed. This is really
1203 * nasty....
1206 static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli,
1207 struct smbcli_state *cli2)
1209 union smb_fileinfo finfo1, finfo2;
1210 const char *fname = BASEDIR "\\torture_file.txt";
1211 NTSTATUS status;
1212 int fnum1 = -1;
1213 int fnum2;
1214 bool ret = true;
1215 ssize_t written;
1216 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1217 int normal_delay = 2000000;
1218 double sec = ((double)used_delay) / ((double)normal_delay);
1219 int msec = 1000 * sec;
1221 torture_comment(tctx, "\nRunning test_finfo_after_write\n");
1223 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1225 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1226 if (fnum1 == -1) {
1227 ret = false;
1228 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1229 goto done;
1232 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1233 finfo1.basic_info.in.file.fnum = fnum1;
1235 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1237 if (!NT_STATUS_IS_OK(status)) {
1238 ret = false;
1239 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1240 goto done;
1243 smb_msleep(1 * msec);
1245 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1247 if (written != 1) {
1248 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1249 ret = false;
1250 goto done;
1253 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
1254 if (fnum2 == -1) {
1255 torture_result(tctx, TORTURE_FAIL, __location__": failed to open 2nd time - %s",
1256 smbcli_errstr(cli2->tree));
1257 ret = false;
1258 goto done;
1261 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
1263 if (written != 1) {
1264 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1",
1265 (int)written);
1266 ret = false;
1267 goto done;
1270 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1271 finfo2.basic_info.in.file.path = fname;
1273 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
1275 if (!NT_STATUS_IS_OK(status)) {
1276 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s",
1277 nt_errstr(status));
1278 ret = false;
1279 goto done;
1282 if (finfo1.basic_info.out.create_time !=
1283 finfo2.basic_info.out.create_time) {
1284 torture_result(tctx, TORTURE_FAIL, __location__": create_time changed");
1285 ret = false;
1286 goto done;
1289 if (finfo1.basic_info.out.access_time !=
1290 finfo2.basic_info.out.access_time) {
1291 torture_result(tctx, TORTURE_FAIL, __location__": access_time changed");
1292 ret = false;
1293 goto done;
1296 if (finfo1.basic_info.out.write_time !=
1297 finfo2.basic_info.out.write_time) {
1298 torture_result(tctx, TORTURE_FAIL, __location__": write_time changed:\n"
1299 "write time conn 1 = %s, conn 2 = %s",
1300 nt_time_string(tctx, finfo1.basic_info.out.write_time),
1301 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1302 ret = false;
1303 goto done;
1306 if (finfo1.basic_info.out.change_time !=
1307 finfo2.basic_info.out.change_time) {
1308 torture_result(tctx, TORTURE_FAIL, __location__": change_time changed");
1309 ret = false;
1310 goto done;
1313 /* One of the two following calls updates the qpathinfo. */
1315 /* If you had skipped the smbcli_write on fnum2, it would
1316 * *not* have updated the stat on disk */
1318 smbcli_close(cli2->tree, fnum2);
1319 cli2 = NULL;
1321 /* This call is only for the people looking at ethereal :-) */
1322 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1323 finfo2.basic_info.in.file.path = fname;
1325 status = smb_raw_pathinfo(cli->tree, tctx, &finfo2);
1327 if (!NT_STATUS_IS_OK(status)) {
1328 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1329 ret = false;
1330 goto done;
1333 done:
1334 if (fnum1 != -1)
1335 smbcli_close(cli->tree, fnum1);
1336 smbcli_unlink(cli->tree, fname);
1337 smbcli_deltree(cli->tree, BASEDIR);
1339 return ret;
1342 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
1343 uint64_t r = 10*1000*1000; \
1344 NTTIME g = (given).basic_info.out.write_time; \
1345 NTTIME gr = (g / r) * r; \
1346 NTTIME c = (correct).basic_info.out.write_time; \
1347 NTTIME cr = (c / r) * r; \
1348 bool strict = torture_setting_bool(tctx, "strict mode", false); \
1349 bool err = false; \
1350 if (strict && (g cmp c)) { \
1351 err = true; \
1352 } else if ((g cmp c) && (gr cmp cr)) { \
1353 /* handle filesystem without high resolution timestamps */ \
1354 err = true; \
1356 if (err) { \
1357 torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \
1358 #given, nt_time_string(tctx, g), (unsigned long long)g, \
1359 #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \
1360 ret = false; \
1361 goto done; \
1363 } while (0)
1364 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
1365 COMPARE_WRITE_TIME_CMP(given,correct,!=)
1366 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
1367 COMPARE_WRITE_TIME_CMP(given,correct,<=)
1368 #define COMPARE_WRITE_TIME_LESS(given,correct) \
1369 COMPARE_WRITE_TIME_CMP(given,correct,>=)
1371 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
1372 NTTIME g = (given).basic_info.out.access_time; \
1373 NTTIME c = (correct).basic_info.out.access_time; \
1374 if (g cmp c) { \
1375 torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \
1376 #given, nt_time_string(tctx, g), \
1377 #cmp, #correct, nt_time_string(tctx, c)); \
1378 ret = false; \
1379 goto done; \
1381 } while (0)
1382 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
1383 COMPARE_ACCESS_TIME_CMP(given,correct,!=)
1385 #define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \
1386 COMPARE_ACCESS_TIME_EQUAL(given,correct); \
1387 COMPARE_WRITE_TIME_EQUAL(given,correct); \
1388 } while (0)
1390 #define GET_INFO_FILE(finfo) do { \
1391 NTSTATUS _status; \
1392 _status = smb_raw_fileinfo(cli->tree, tctx, &finfo); \
1393 if (!NT_STATUS_IS_OK(_status)) { \
1394 ret = false; \
1395 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1396 nt_errstr(_status)); \
1397 goto done; \
1399 torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1400 nt_time_string(tctx, finfo.basic_info.out.access_time), \
1401 nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1402 } while (0)
1403 #define GET_INFO_FILE2(finfo) do { \
1404 NTSTATUS _status; \
1405 _status = smb_raw_fileinfo(cli2->tree, tctx, &finfo); \
1406 if (!NT_STATUS_IS_OK(_status)) { \
1407 ret = false; \
1408 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1409 nt_errstr(_status)); \
1410 goto done; \
1412 torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1413 nt_time_string(tctx, finfo.basic_info.out.access_time), \
1414 nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1415 } while (0)
1416 #define GET_INFO_PATH(pinfo) do { \
1417 NTSTATUS _status; \
1418 _status = smb_raw_pathinfo(cli2->tree, tctx, &pinfo); \
1419 if (!NT_STATUS_IS_OK(_status)) { \
1420 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \
1421 nt_errstr(_status)); \
1422 ret = false; \
1423 goto done; \
1425 torture_comment(tctx, "pathinfo: Access(%s) Write(%s)\n", \
1426 nt_time_string(tctx, pinfo.basic_info.out.access_time), \
1427 nt_time_string(tctx, pinfo.basic_info.out.write_time)); \
1428 } while (0)
1429 #define GET_INFO_BOTH(finfo,pinfo) do { \
1430 GET_INFO_FILE(finfo); \
1431 GET_INFO_PATH(pinfo); \
1432 COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \
1433 } while (0)
1435 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
1436 NTSTATUS _status; \
1437 union smb_setfileinfo sfinfo; \
1438 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1439 sfinfo.basic_info.in.file.fnum = tfnum; \
1440 sfinfo.basic_info.in.create_time = 0; \
1441 sfinfo.basic_info.in.access_time = 0; \
1442 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1443 sfinfo.basic_info.in.change_time = 0; \
1444 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1445 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1446 if (!NT_STATUS_IS_OK(_status)) { \
1447 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1448 nt_errstr(_status)); \
1449 ret = false; \
1450 goto done; \
1452 } while (0)
1453 #define SET_INFO_FILE(finfo, wrtime) \
1454 SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
1456 #define SET_INFO_FILE_NS(finfo, wrtime, ns, tree, tfnum) do { \
1457 NTSTATUS _status; \
1458 union smb_setfileinfo sfinfo; \
1459 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1460 sfinfo.basic_info.in.file.fnum = tfnum; \
1461 sfinfo.basic_info.in.create_time = 0; \
1462 sfinfo.basic_info.in.access_time = 0; \
1463 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1464 sfinfo.basic_info.in.write_time += (ns); \
1465 sfinfo.basic_info.in.change_time = 0; \
1466 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1467 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1468 if (!NT_STATUS_IS_OK(_status)) { \
1469 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1470 nt_errstr(_status)); \
1471 ret = false; \
1472 goto done; \
1474 } while (0)
1476 static bool test_delayed_write_update3(struct torture_context *tctx,
1477 struct smbcli_state *cli,
1478 struct smbcli_state *cli2)
1480 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1481 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1482 const char *fname = BASEDIR "\\torture_file3.txt";
1483 int fnum1 = -1;
1484 bool ret = true;
1485 ssize_t written;
1486 struct timeval start;
1487 struct timeval end;
1488 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1489 int normal_delay = 2000000;
1490 double sec = ((double)used_delay) / ((double)normal_delay);
1491 int msec = 1000 * sec;
1493 torture_comment(tctx, "\nRunning test_delayed_write_update3\n");
1495 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1497 torture_comment(tctx, "Open the file handle\n");
1498 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1499 if (fnum1 == -1) {
1500 ret = false;
1501 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1502 goto done;
1505 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1506 finfo0.basic_info.in.file.fnum = fnum1;
1507 finfo1 = finfo0;
1508 finfo2 = finfo0;
1509 finfo3 = finfo0;
1510 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1511 pinfo0.basic_info.in.file.path = fname;
1512 pinfo1 = pinfo0;
1513 pinfo2 = pinfo0;
1514 pinfo3 = pinfo0;
1515 pinfo4 = pinfo0;
1517 /* get the initial times */
1518 GET_INFO_BOTH(finfo0,pinfo0);
1521 * make sure the write time is updated 2 seconds later
1522 * calcuated from the first write
1523 * (but expect upto 5 seconds extra time for a busy server)
1525 start = timeval_current();
1526 end = timeval_add(&start, 7 * sec, 0);
1527 while (!timeval_expired(&end)) {
1528 /* do a write */
1529 torture_comment(tctx, "Do a write on the file handle\n");
1530 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1531 if (written != 1) {
1532 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1533 ret = false;
1534 goto done;
1536 /* get the times after the write */
1537 GET_INFO_FILE(finfo1);
1539 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1540 double diff = timeval_elapsed(&start);
1541 if (diff < (used_delay / (double)1000000)) {
1542 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1543 "(write time update delay == %.2f) (wrong!)\n",
1544 diff, used_delay / (double)1000000);
1545 ret = false;
1546 break;
1549 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1550 "(correct)\n",
1551 diff);
1552 break;
1554 smb_msleep(0.5 * msec);
1557 GET_INFO_BOTH(finfo1,pinfo1);
1558 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1560 /* sure any further write doesn't update the write time */
1561 start = timeval_current();
1562 end = timeval_add(&start, 15 * sec, 0);
1563 while (!timeval_expired(&end)) {
1564 /* do a write */
1565 torture_comment(tctx, "Do a write on the file handle\n");
1566 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1567 if (written != 1) {
1568 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1569 ret = false;
1570 goto done;
1572 /* get the times after the write */
1573 GET_INFO_BOTH(finfo2,pinfo2);
1575 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1576 double diff = timeval_elapsed(&start);
1577 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1578 "(wrong!)\n",
1579 diff);
1580 ret = false;
1581 break;
1583 smb_msleep(1 * msec);
1586 GET_INFO_BOTH(finfo2,pinfo2);
1587 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1588 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1589 torture_comment(tctx, "Server did not update write_time (correct)\n");
1592 /* sleep */
1593 smb_msleep(5 * msec);
1595 GET_INFO_BOTH(finfo3,pinfo3);
1596 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1599 * the close updates the write time to the time of the close
1600 * and not to the time of the last write!
1602 torture_comment(tctx, "Close the file handle\n");
1603 smbcli_close(cli->tree, fnum1);
1604 fnum1 = -1;
1606 GET_INFO_PATH(pinfo4);
1607 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1609 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1610 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1613 done:
1614 if (fnum1 != -1)
1615 smbcli_close(cli->tree, fnum1);
1616 smbcli_unlink(cli->tree, fname);
1617 smbcli_deltree(cli->tree, BASEDIR);
1619 return ret;
1623 * Show that a truncate write always updates the write time even
1624 * if an initial write has already updated the write time.
1627 static bool test_delayed_write_update3a(struct torture_context *tctx,
1628 struct smbcli_state *cli,
1629 struct smbcli_state *cli2)
1631 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1632 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1633 const char *fname = BASEDIR "\\torture_file3a.txt";
1634 int fnum1 = -1;
1635 bool ret = true;
1636 ssize_t written;
1637 int i;
1638 struct timeval start;
1639 struct timeval end;
1640 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1641 int normal_delay = 2000000;
1642 double sec = ((double)used_delay) / ((double)normal_delay);
1643 int msec = 1000 * sec;
1645 torture_comment(tctx, "\nRunning test_delayed_write_update3a\n");
1647 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1649 torture_comment(tctx, "Open the file handle\n");
1650 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1651 if (fnum1 == -1) {
1652 ret = false;
1653 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1654 goto done;
1657 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1658 finfo0.basic_info.in.file.fnum = fnum1;
1659 finfo1 = finfo0;
1660 finfo2 = finfo0;
1661 finfo3 = finfo0;
1662 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1663 pinfo0.basic_info.in.file.path = fname;
1664 pinfo1 = pinfo0;
1665 pinfo2 = pinfo0;
1666 pinfo3 = pinfo0;
1667 pinfo4 = pinfo0;
1669 /* get the initial times */
1670 GET_INFO_BOTH(finfo0,pinfo0);
1673 * sleep some time, to demonstrate the handling of write times
1674 * doesn't depend on the time since the open
1676 smb_msleep(5 * msec);
1678 /* get the initial times */
1679 GET_INFO_BOTH(finfo1,pinfo1);
1680 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1683 * make sure the write time is updated 2 seconds later
1684 * calcuated from the first write
1685 * (but expect upto 5 seconds extra time for a busy server)
1687 start = timeval_current();
1688 end = timeval_add(&start, 7 * sec, 0);
1689 while (!timeval_expired(&end)) {
1690 /* do a write */
1691 torture_comment(tctx, "Do a write on the file handle\n");
1692 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1693 if (written != 1) {
1694 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1695 ret = false;
1696 goto done;
1698 /* get the times after the write */
1699 GET_INFO_FILE(finfo1);
1701 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1702 double diff = timeval_elapsed(&start);
1703 if (diff < (used_delay / (double)1000000)) {
1704 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1705 "(1sec == %.2f) (wrong!)\n",
1706 diff, sec);
1707 ret = false;
1708 break;
1711 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1712 "(correct)\n",
1713 diff);
1714 break;
1716 smb_msleep(0.5 * msec);
1719 GET_INFO_BOTH(finfo1,pinfo1);
1720 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1722 smb_msleep(3 * msec);
1725 * demonstrate that a truncate write always
1726 * updates the write time immediately
1728 for (i=0; i < 3; i++) {
1729 smb_msleep(2 * msec);
1730 /* do a write */
1731 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1732 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
1733 if (written != 0) {
1734 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1735 ret = false;
1736 goto done;
1738 /* get the times after the write */
1739 GET_INFO_BOTH(finfo2,pinfo2);
1740 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1741 finfo1 = finfo2;
1744 smb_msleep(3 * msec);
1746 /* sure any further write doesn't update the write time */
1747 start = timeval_current();
1748 end = timeval_add(&start, 15 * sec, 0);
1749 while (!timeval_expired(&end)) {
1750 /* do a write */
1751 torture_comment(tctx, "Do a write on the file handle\n");
1752 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1753 if (written != 1) {
1754 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1755 ret = false;
1756 goto done;
1758 /* get the times after the write */
1759 GET_INFO_BOTH(finfo2,pinfo2);
1761 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1762 double diff = timeval_elapsed(&start);
1763 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1764 "(wrong!)\n",
1765 diff);
1766 ret = false;
1767 break;
1769 smb_msleep(1 * msec);
1772 GET_INFO_BOTH(finfo2,pinfo2);
1773 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1774 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1775 torture_comment(tctx, "Server did not update write_time (correct)\n");
1778 /* sleep */
1779 smb_msleep(3 * msec);
1781 /* get the initial times */
1782 GET_INFO_BOTH(finfo1,pinfo1);
1783 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
1786 * demonstrate that a truncate write always
1787 * updates the write time immediately
1789 for (i=0; i < 3; i++) {
1790 smb_msleep(2 * msec);
1791 /* do a write */
1792 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1793 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
1794 if (written != 0) {
1795 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1796 ret = false;
1797 goto done;
1799 /* get the times after the write */
1800 GET_INFO_BOTH(finfo2,pinfo2);
1801 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1802 finfo1 = finfo2;
1805 /* sleep */
1806 smb_msleep(3 * msec);
1808 GET_INFO_BOTH(finfo3,pinfo3);
1809 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1812 * the close doesn't update the write time
1814 torture_comment(tctx, "Close the file handle\n");
1815 smbcli_close(cli->tree, fnum1);
1816 fnum1 = -1;
1818 GET_INFO_PATH(pinfo4);
1819 COMPARE_WRITE_TIME_EQUAL(pinfo4, pinfo3);
1821 if (pinfo4.basic_info.out.write_time == pinfo3.basic_info.out.write_time) {
1822 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1825 done:
1826 if (fnum1 != -1)
1827 smbcli_close(cli->tree, fnum1);
1828 smbcli_unlink(cli->tree, fname);
1829 smbcli_deltree(cli->tree, BASEDIR);
1831 return ret;
1835 * Show a close after write updates the write timestamp to
1836 * the close time, not the last write time.
1839 static bool test_delayed_write_update3b(struct torture_context *tctx,
1840 struct smbcli_state *cli,
1841 struct smbcli_state *cli2)
1843 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1844 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1845 const char *fname = BASEDIR "\\torture_file3b.txt";
1846 int fnum1 = -1;
1847 bool ret = true;
1848 ssize_t written;
1849 struct timeval start;
1850 struct timeval end;
1851 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1852 int normal_delay = 2000000;
1853 double sec = ((double)used_delay) / ((double)normal_delay);
1854 int msec = 1000 * sec;
1856 torture_comment(tctx, "\nRunning test_delayed_write_update3b\n");
1858 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1860 torture_comment(tctx, "Open the file handle\n");
1861 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1862 if (fnum1 == -1) {
1863 ret = false;
1864 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1865 goto done;
1868 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1869 finfo0.basic_info.in.file.fnum = fnum1;
1870 finfo1 = finfo0;
1871 finfo2 = finfo0;
1872 finfo3 = finfo0;
1873 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1874 pinfo0.basic_info.in.file.path = fname;
1875 pinfo1 = pinfo0;
1876 pinfo2 = pinfo0;
1877 pinfo3 = pinfo0;
1878 pinfo4 = pinfo0;
1880 /* get the initial times */
1881 GET_INFO_BOTH(finfo0,pinfo0);
1884 * sleep some time, to demonstrate the handling of write times
1885 * doesn't depend on the time since the open
1887 smb_msleep(5 * msec);
1889 /* get the initial times */
1890 GET_INFO_BOTH(finfo1,pinfo1);
1891 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1894 * make sure the write time is updated 2 seconds later
1895 * calcuated from the first write
1896 * (but expect upto 5 seconds extra time for a busy server)
1898 start = timeval_current();
1899 end = timeval_add(&start, 7 * sec, 0);
1900 while (!timeval_expired(&end)) {
1901 /* do a write */
1902 torture_comment(tctx, "Do a write on the file handle\n");
1903 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1904 if (written != 1) {
1905 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1906 ret = false;
1907 goto done;
1909 /* get the times after the write */
1910 GET_INFO_FILE(finfo1);
1912 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1913 double diff = timeval_elapsed(&start);
1914 if (diff < (used_delay / (double)1000000)) {
1915 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
1916 "(expected > %.2f) (wrong!)\n",
1917 diff, used_delay / (double)1000000);
1918 ret = false;
1919 break;
1922 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1923 "(write time update delay == %.2f) (correct)\n",
1924 diff, used_delay / (double)1000000);
1925 break;
1927 smb_msleep(0.5 * msec);
1930 GET_INFO_BOTH(finfo1,pinfo1);
1931 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1933 /* sure any further write doesn't update the write time */
1934 start = timeval_current();
1935 end = timeval_add(&start, 15 * sec, 0);
1936 while (!timeval_expired(&end)) {
1937 /* do a write */
1938 torture_comment(tctx, "Do a write on the file handle\n");
1939 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1940 if (written != 1) {
1941 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1942 ret = false;
1943 goto done;
1945 /* get the times after the write */
1946 GET_INFO_BOTH(finfo2,pinfo2);
1948 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1949 double diff = timeval_elapsed(&start);
1950 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1951 "(wrong!)\n",
1952 diff);
1953 ret = false;
1954 break;
1956 smb_msleep(1 * msec);
1959 GET_INFO_BOTH(finfo2,pinfo2);
1960 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1961 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1962 torture_comment(tctx, "Server did not update write_time (correct)\n");
1965 /* sleep */
1966 smb_msleep(5 * msec);
1968 GET_INFO_BOTH(finfo3,pinfo3);
1969 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1972 * the close updates the write time to the time of the close
1973 * and not to the time of the last write!
1975 torture_comment(tctx, "Close the file handle\n");
1976 smbcli_close(cli->tree, fnum1);
1977 fnum1 = -1;
1979 GET_INFO_PATH(pinfo4);
1980 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1982 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1983 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1986 done:
1987 if (fnum1 != -1)
1988 smbcli_close(cli->tree, fnum1);
1989 smbcli_unlink(cli->tree, fname);
1990 smbcli_deltree(cli->tree, BASEDIR);
1992 return ret;
1996 * Check that a write after a truncate write doesn't update
1997 * the timestamp, but a truncate write after a write does.
1998 * Also prove that a close after a truncate write updates the
1999 * timestamp to current, not the time of last write.
2002 static bool test_delayed_write_update3c(struct torture_context *tctx,
2003 struct smbcli_state *cli,
2004 struct smbcli_state *cli2)
2006 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
2007 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
2008 const char *fname = BASEDIR "\\torture_file3c.txt";
2009 int fnum1 = -1;
2010 bool ret = true;
2011 ssize_t written;
2012 int i;
2013 struct timeval start;
2014 struct timeval end;
2015 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2016 int normal_delay = 2000000;
2017 double sec = ((double)used_delay) / ((double)normal_delay);
2018 int msec = 1000 * sec;
2020 torture_comment(tctx, "\nRunning test_delayed_write_update3c\n");
2022 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2024 torture_comment(tctx, "Open the file handle\n");
2025 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2026 if (fnum1 == -1) {
2027 ret = false;
2028 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2029 goto done;
2032 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2033 finfo0.basic_info.in.file.fnum = fnum1;
2034 finfo1 = finfo0;
2035 finfo2 = finfo0;
2036 finfo3 = finfo0;
2037 finfo4 = finfo0;
2038 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2039 pinfo0.basic_info.in.file.path = fname;
2040 pinfo1 = pinfo0;
2041 pinfo2 = pinfo0;
2042 pinfo3 = pinfo0;
2043 pinfo4 = pinfo0;
2044 pinfo5 = pinfo0;
2046 /* get the initial times */
2047 GET_INFO_BOTH(finfo0,pinfo0);
2050 * sleep some time, to demonstrate the handling of write times
2051 * doesn't depend on the time since the open
2053 smb_msleep(5 * msec);
2055 /* get the initial times */
2056 GET_INFO_BOTH(finfo1,pinfo1);
2057 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2060 * demonstrate that a truncate write always
2061 * updates the write time immediately
2063 for (i=0; i < 3; i++) {
2064 smb_msleep(2 * msec);
2065 /* do a write */
2066 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
2067 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2068 if (written != 0) {
2069 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2070 ret = false;
2071 goto done;
2073 /* get the times after the write */
2074 GET_INFO_BOTH(finfo2,pinfo2);
2075 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2076 finfo1 = finfo2;
2079 start = timeval_current();
2080 end = timeval_add(&start, 7 * sec, 0);
2081 while (!timeval_expired(&end)) {
2082 /* do a write */
2083 torture_comment(tctx, "Do a write on the file handle\n");
2084 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2085 if (written != 1) {
2086 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2087 ret = false;
2088 goto done;
2090 /* get the times after the write */
2091 GET_INFO_FILE(finfo2);
2093 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2094 double diff = timeval_elapsed(&start);
2095 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2096 "(wrong!)\n",
2097 diff);
2098 ret = false;
2099 break;
2101 smb_msleep(1 * msec);
2104 GET_INFO_BOTH(finfo2,pinfo2);
2105 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2106 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2107 torture_comment(tctx, "Server did not update write_time (correct)\n");
2110 /* sleep */
2111 smb_msleep(5 * msec);
2113 /* get the initial times */
2114 GET_INFO_BOTH(finfo1,pinfo1);
2115 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
2118 * demonstrate that a truncate write always
2119 * updates the write time immediately
2121 for (i=0; i < 3; i++) {
2122 smb_msleep(2 * msec);
2123 /* do a write */
2124 torture_comment(tctx, "Do a truncate write [%d] on the file handle\n", i);
2125 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2126 if (written != 0) {
2127 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2128 ret = false;
2129 goto done;
2131 /* get the times after the write */
2132 GET_INFO_BOTH(finfo2,pinfo2);
2133 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2134 finfo1 = finfo2;
2137 /* sleep */
2138 smb_msleep(5 * msec);
2140 GET_INFO_BOTH(finfo2,pinfo2);
2141 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2143 /* sure any further write doesn't update the write time */
2144 start = timeval_current();
2145 end = timeval_add(&start, 15 * sec, 0);
2146 while (!timeval_expired(&end)) {
2147 /* do a write */
2148 torture_comment(tctx, "Do a write on the file handle\n");
2149 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2150 if (written != 1) {
2151 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2152 ret = false;
2153 goto done;
2155 /* get the times after the write */
2156 GET_INFO_BOTH(finfo2,pinfo2);
2158 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2159 double diff = timeval_elapsed(&start);
2160 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2161 "(wrong!)\n",
2162 diff);
2163 ret = false;
2164 break;
2166 smb_msleep(1 * msec);
2169 GET_INFO_BOTH(finfo2,pinfo2);
2170 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2171 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2172 torture_comment(tctx, "Server did not update write_time (correct)\n");
2175 /* sleep */
2176 smb_msleep(5 * msec);
2178 GET_INFO_BOTH(finfo3,pinfo3);
2179 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2182 * the close updates the write time to the time of the close
2183 * and not to the time of the last write!
2185 torture_comment(tctx, "Close the file handle\n");
2186 smbcli_close(cli->tree, fnum1);
2187 fnum1 = -1;
2189 GET_INFO_PATH(pinfo4);
2190 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2192 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2193 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2196 done:
2197 if (fnum1 != -1)
2198 smbcli_close(cli->tree, fnum1);
2199 smbcli_unlink(cli->tree, fname);
2200 smbcli_deltree(cli->tree, BASEDIR);
2202 return ret;
2206 * Show only the first write updates the timestamp, and a close
2207 * after writes updates to current (I think this is the same
2208 * as test 3b. JRA).
2211 static bool test_delayed_write_update4(struct torture_context *tctx,
2212 struct smbcli_state *cli,
2213 struct smbcli_state *cli2)
2215 union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
2216 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
2217 const char *fname = BASEDIR "\\torture_file4.txt";
2218 int fnum1 = -1;
2219 bool ret = true;
2220 ssize_t written;
2221 struct timeval start;
2222 struct timeval end;
2223 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2224 int normal_delay = 2000000;
2225 double sec = ((double)used_delay) / ((double)normal_delay);
2226 int msec = 1000 * sec;
2228 torture_comment(tctx, "\nRunning test_delayed_write_update4\n");
2230 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2232 torture_comment(tctx, "Open the file handle\n");
2233 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2234 if (fnum1 == -1) {
2235 ret = false;
2236 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2237 goto done;
2240 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2241 finfo0.basic_info.in.file.fnum = fnum1;
2242 finfo1 = finfo0;
2243 finfo2 = finfo0;
2244 finfo3 = finfo0;
2245 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2246 pinfo0.basic_info.in.file.path = fname;
2247 pinfo1 = pinfo0;
2248 pinfo2 = pinfo0;
2249 pinfo3 = pinfo0;
2250 pinfo4 = pinfo0;
2252 /* get the initial times */
2253 GET_INFO_BOTH(finfo0,pinfo0);
2255 /* sleep a bit */
2256 smb_msleep(5 * msec);
2258 /* do a write */
2259 torture_comment(tctx, "Do a write on the file handle\n");
2260 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2261 if (written != 1) {
2262 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2263 ret = false;
2264 goto done;
2267 GET_INFO_BOTH(finfo1,pinfo1);
2268 COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
2271 * make sure the write time is updated 2 seconds later
2272 * calcuated from the first write
2273 * (but expect upto 3 seconds extra time for a busy server)
2275 start = timeval_current();
2276 end = timeval_add(&start, 5 * sec, 0);
2277 while (!timeval_expired(&end)) {
2278 /* get the times after the first write */
2279 GET_INFO_FILE(finfo1);
2281 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
2282 double diff = timeval_elapsed(&start);
2283 if (diff < (used_delay / (double)1000000)) {
2284 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
2285 "(expected > %.2f) (wrong!)\n",
2286 diff, used_delay / (double)1000000);
2287 ret = false;
2288 break;
2291 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2292 "(write time update delay == %.2f) (correct)\n",
2293 diff, used_delay / (double)1000000);
2294 break;
2296 smb_msleep(0.5 * msec);
2299 GET_INFO_BOTH(finfo1,pinfo1);
2300 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
2302 /* sure any further write doesn't update the write time */
2303 start = timeval_current();
2304 end = timeval_add(&start, 15 * sec, 0);
2305 while (!timeval_expired(&end)) {
2306 /* do a write */
2307 torture_comment(tctx, "Do a write on the file handle\n");
2308 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2309 if (written != 1) {
2310 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2311 ret = false;
2312 goto done;
2314 /* get the times after the write */
2315 GET_INFO_BOTH(finfo2,pinfo2);
2317 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2318 double diff = timeval_elapsed(&start);
2319 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2320 "(wrong!)\n",
2321 diff);
2322 ret = false;
2323 break;
2325 smb_msleep(1 * msec);
2328 GET_INFO_BOTH(finfo2,pinfo2);
2329 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2330 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2331 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
2334 /* sleep */
2335 smb_msleep(5 * msec);
2337 GET_INFO_BOTH(finfo3,pinfo3);
2338 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2341 * the close updates the write time to the time of the close
2342 * and not to the time of the last write!
2344 torture_comment(tctx, "Close the file handle\n");
2345 smbcli_close(cli->tree, fnum1);
2346 fnum1 = -1;
2348 GET_INFO_PATH(pinfo4);
2349 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2351 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2352 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2355 done:
2356 if (fnum1 != -1)
2357 smbcli_close(cli->tree, fnum1);
2358 smbcli_unlink(cli->tree, fname);
2359 smbcli_deltree(cli->tree, BASEDIR);
2361 return ret;
2365 * Show writes and closes have no effect on updating times once a SETWRITETIME is done.
2368 static bool test_delayed_write_update5(struct torture_context *tctx,
2369 struct smbcli_state *cli,
2370 struct smbcli_state *cli2)
2372 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2373 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2374 const char *fname = BASEDIR "\\torture_file5.txt";
2375 int fnum1 = -1;
2376 bool ret = true;
2377 ssize_t written;
2378 struct timeval start;
2379 struct timeval end;
2380 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2381 int normal_delay = 2000000;
2382 double sec = ((double)used_delay) / ((double)normal_delay);
2383 int msec = 1000 * sec;
2385 torture_comment(tctx, "\nRunning test_delayed_write_update5\n");
2387 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2389 torture_comment(tctx, "Open the file handle\n");
2390 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2391 if (fnum1 == -1) {
2392 ret = false;
2393 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2394 goto done;
2397 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2398 finfo0.basic_info.in.file.fnum = fnum1;
2399 finfo1 = finfo0;
2400 finfo2 = finfo0;
2401 finfo3 = finfo0;
2402 finfo4 = finfo0;
2403 finfo5 = finfo0;
2404 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2405 pinfo0.basic_info.in.file.path = fname;
2406 pinfo1 = pinfo0;
2407 pinfo2 = pinfo0;
2408 pinfo3 = pinfo0;
2409 pinfo4 = pinfo0;
2410 pinfo5 = pinfo0;
2411 pinfo6 = pinfo0;
2413 /* get the initial times */
2414 GET_INFO_BOTH(finfo0,pinfo0);
2416 /* do a write */
2417 torture_comment(tctx, "Do a write on the file handle\n");
2418 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2419 if (written != 1) {
2420 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2421 ret = false;
2422 goto done;
2425 GET_INFO_BOTH(finfo1,pinfo1);
2426 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2428 torture_comment(tctx, "Set write time in the future on the file handle\n");
2429 SET_INFO_FILE(finfo0, time(NULL) + 86400);
2430 GET_INFO_BOTH(finfo2,pinfo2);
2431 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2433 torture_comment(tctx, "Set write time in the past on the file handle\n");
2434 SET_INFO_FILE(finfo0, time(NULL) - 86400);
2435 GET_INFO_BOTH(finfo2,pinfo2);
2436 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2438 /* make sure the 2 second delay from the first write are canceled */
2439 start = timeval_current();
2440 end = timeval_add(&start, 15 * sec, 0);
2441 while (!timeval_expired(&end)) {
2443 /* get the times after the first write */
2444 GET_INFO_BOTH(finfo3,pinfo3);
2446 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2447 double diff = timeval_elapsed(&start);
2448 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2449 "(wrong!)\n",
2450 diff);
2451 ret = false;
2452 break;
2454 smb_msleep(1 * msec);
2457 GET_INFO_BOTH(finfo3,pinfo3);
2458 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2459 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2460 torture_comment(tctx, "Server did not update write_time (correct)\n");
2463 /* sure any further write doesn't update the write time */
2464 start = timeval_current();
2465 end = timeval_add(&start, 15 * sec, 0);
2466 while (!timeval_expired(&end)) {
2467 /* do a write */
2468 torture_comment(tctx, "Do a write on the file handle\n");
2469 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2470 if (written != 1) {
2471 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2472 ret = false;
2473 goto done;
2475 /* get the times after the write */
2476 GET_INFO_BOTH(finfo4,pinfo4);
2478 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2479 double diff = timeval_elapsed(&start);
2480 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2481 "(wrong!)\n",
2482 diff);
2483 ret = false;
2484 break;
2486 smb_msleep(1 * msec);
2489 GET_INFO_BOTH(finfo4,pinfo4);
2490 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2491 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2492 torture_comment(tctx, "Server did not update write_time (correct)\n");
2495 /* sleep */
2496 smb_msleep(5 * msec);
2498 GET_INFO_BOTH(finfo5,pinfo5);
2499 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2502 * the close doesn't update the write time
2504 torture_comment(tctx, "Close the file handle\n");
2505 smbcli_close(cli->tree, fnum1);
2506 fnum1 = -1;
2508 GET_INFO_PATH(pinfo6);
2509 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2511 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2512 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2515 done:
2516 if (fnum1 != -1)
2517 smbcli_close(cli->tree, fnum1);
2518 smbcli_unlink(cli->tree, fname);
2519 smbcli_deltree(cli->tree, BASEDIR);
2521 return ret;
2525 * Show truncate writes and closes have no effect on updating times once a SETWRITETIME is done.
2528 static bool test_delayed_write_update5b(struct torture_context *tctx,
2529 struct smbcli_state *cli,
2530 struct smbcli_state *cli2)
2532 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2533 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2534 const char *fname = BASEDIR "\\torture_fileb.txt";
2535 int fnum1 = -1;
2536 bool ret = true;
2537 ssize_t written;
2538 struct timeval start;
2539 struct timeval end;
2540 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2541 int normal_delay = 2000000;
2542 double sec = ((double)used_delay) / ((double)normal_delay);
2543 int msec = 1000 * sec;
2545 torture_comment(tctx, "\nRunning test_delayed_write_update5b\n");
2547 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2549 torture_comment(tctx, "Open the file handle\n");
2550 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2551 if (fnum1 == -1) {
2552 ret = false;
2553 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2554 goto done;
2557 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2558 finfo0.basic_info.in.file.fnum = fnum1;
2559 finfo1 = finfo0;
2560 finfo2 = finfo0;
2561 finfo3 = finfo0;
2562 finfo4 = finfo0;
2563 finfo5 = finfo0;
2564 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2565 pinfo0.basic_info.in.file.path = fname;
2566 pinfo1 = pinfo0;
2567 pinfo2 = pinfo0;
2568 pinfo3 = pinfo0;
2569 pinfo4 = pinfo0;
2570 pinfo5 = pinfo0;
2571 pinfo6 = pinfo0;
2573 /* get the initial times */
2574 GET_INFO_BOTH(finfo0,pinfo0);
2576 /* do a write */
2577 torture_comment(tctx, "Do a write on the file handle\n");
2578 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2579 if (written != 1) {
2580 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2581 ret = false;
2582 goto done;
2585 GET_INFO_BOTH(finfo1,pinfo1);
2586 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2588 torture_comment(tctx, "Set write time in the future on the file handle\n");
2589 SET_INFO_FILE(finfo0, time(NULL) + 86400);
2590 GET_INFO_BOTH(finfo2,pinfo2);
2591 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2593 torture_comment(tctx, "Set write time in the past on the file handle\n");
2594 SET_INFO_FILE(finfo0, time(NULL) - 86400);
2595 GET_INFO_BOTH(finfo2,pinfo2);
2596 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2598 /* make sure the 2 second delay from the first write are canceled */
2599 start = timeval_current();
2600 end = timeval_add(&start, 15 * sec, 0);
2601 while (!timeval_expired(&end)) {
2603 /* get the times after the first write */
2604 GET_INFO_BOTH(finfo3,pinfo3);
2606 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2607 double diff = timeval_elapsed(&start);
2608 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2609 "(wrong!)\n",
2610 diff);
2611 ret = false;
2612 break;
2614 smb_msleep(1 * msec);
2617 GET_INFO_BOTH(finfo3,pinfo3);
2618 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2619 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2620 torture_comment(tctx, "Server did not update write_time (correct)\n");
2623 /* Do any further write (truncates) update the write time ? */
2624 start = timeval_current();
2625 end = timeval_add(&start, 15 * sec, 0);
2626 while (!timeval_expired(&end)) {
2627 /* do a write */
2628 torture_comment(tctx, "Do a truncate write on the file handle\n");
2629 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
2630 if (written != 0) {
2631 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2632 ret = false;
2633 goto done;
2635 /* get the times after the write */
2636 GET_INFO_BOTH(finfo4,pinfo4);
2638 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2639 double diff = timeval_elapsed(&start);
2640 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2641 "(wrong!)\n",
2642 diff);
2643 ret = false;
2644 break;
2646 smb_msleep(1 * msec);
2649 GET_INFO_BOTH(finfo4,pinfo4);
2650 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2651 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2652 torture_comment(tctx, "Server did not update write_time (correct)\n");
2655 /* sleep */
2656 smb_msleep(5 * msec);
2658 GET_INFO_BOTH(finfo5,pinfo5);
2659 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2662 * the close doesn't update the write time
2664 torture_comment(tctx, "Close the file handle\n");
2665 smbcli_close(cli->tree, fnum1);
2666 fnum1 = -1;
2668 GET_INFO_PATH(pinfo6);
2669 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2671 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2672 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2675 done:
2676 if (fnum1 != -1)
2677 smbcli_close(cli->tree, fnum1);
2678 smbcli_unlink(cli->tree, fname);
2679 smbcli_deltree(cli->tree, BASEDIR);
2681 return ret;
2685 * Open 2 handles on a file. Write one one and then set the
2686 * WRITE TIME explicitly on the other. Ensure the write time
2687 * update is cancelled. Ensure the write time is updated to
2688 * the close time when the non-explicit set handle is closed.
2692 static bool test_delayed_write_update6(struct torture_context *tctx,
2693 struct smbcli_state *cli,
2694 struct smbcli_state *cli2)
2696 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2697 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
2698 const char *fname = BASEDIR "\\torture_file6.txt";
2699 int fnum1 = -1;
2700 int fnum2 = -1;
2701 bool ret = true;
2702 ssize_t written;
2703 struct timeval start;
2704 struct timeval end;
2705 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2706 int normal_delay = 2000000;
2707 double sec = ((double)used_delay) / ((double)normal_delay);
2708 int msec = 1000 * sec;
2709 bool first = true;
2711 torture_comment(tctx, "\nRunning test_delayed_write_update6\n");
2713 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2714 again:
2715 torture_comment(tctx, "Open the file handle\n");
2716 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2717 if (fnum1 == -1) {
2718 ret = false;
2719 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2720 goto done;
2723 if (fnum2 == -1) {
2724 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
2725 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2726 if (fnum2 == -1) {
2727 ret = false;
2728 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2729 goto done;
2733 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2734 finfo0.basic_info.in.file.fnum = fnum1;
2735 finfo1 = finfo0;
2736 finfo2 = finfo0;
2737 finfo3 = finfo0;
2738 finfo4 = finfo0;
2739 finfo5 = finfo0;
2740 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2741 pinfo0.basic_info.in.file.path = fname;
2742 pinfo1 = pinfo0;
2743 pinfo2 = pinfo0;
2744 pinfo3 = pinfo0;
2745 pinfo4 = pinfo0;
2746 pinfo5 = pinfo0;
2747 pinfo6 = pinfo0;
2748 pinfo7 = pinfo0;
2750 /* get the initial times */
2751 GET_INFO_BOTH(finfo0,pinfo0);
2753 /* do a write */
2754 torture_comment(tctx, "Do a write on the file handle\n");
2755 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2756 if (written != 1) {
2757 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2758 ret = false;
2759 goto done;
2762 GET_INFO_BOTH(finfo1,pinfo1);
2763 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2765 torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
2766 SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
2767 GET_INFO_BOTH(finfo2,pinfo2);
2768 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2770 torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
2771 SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
2772 GET_INFO_BOTH(finfo2,pinfo2);
2773 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2775 /* make sure the 2 second delay from the first write are canceled */
2776 start = timeval_current();
2777 end = timeval_add(&start, 10 * sec, 0);
2778 while (!timeval_expired(&end)) {
2780 /* get the times after the first write */
2781 GET_INFO_BOTH(finfo3,pinfo3);
2783 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2784 double diff = timeval_elapsed(&start);
2785 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2786 "(wrong!)\n",
2787 diff);
2788 ret = false;
2789 break;
2791 smb_msleep(1 * msec);
2794 GET_INFO_BOTH(finfo3,pinfo3);
2795 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2796 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2797 torture_comment(tctx, "Server did not update write_time (correct)\n");
2800 /* sure any further write doesn't update the write time */
2801 start = timeval_current();
2802 end = timeval_add(&start, 10 * sec, 0);
2803 while (!timeval_expired(&end)) {
2804 /* do a write */
2805 torture_comment(tctx, "Do a write on the file handle\n");
2806 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2807 if (written != 1) {
2808 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2809 ret = false;
2810 goto done;
2812 /* get the times after the write */
2813 GET_INFO_BOTH(finfo4,pinfo4);
2815 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2816 double diff = timeval_elapsed(&start);
2817 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2818 "(wrong!)\n",
2819 diff);
2820 ret = false;
2821 break;
2823 smb_msleep(1 * msec);
2826 GET_INFO_BOTH(finfo4,pinfo4);
2827 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2828 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2829 torture_comment(tctx, "Server did not update write_time (correct)\n");
2832 /* sleep */
2833 smb_msleep(5 * msec);
2835 GET_INFO_BOTH(finfo5,pinfo5);
2836 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2839 * the close updates the write time to the time of the close
2840 * as the write time was set on the 2nd handle
2842 torture_comment(tctx, "Close the file handle\n");
2843 smbcli_close(cli->tree, fnum1);
2844 fnum1 = -1;
2846 GET_INFO_PATH(pinfo6);
2847 COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
2849 if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
2850 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2853 /* See what the second write handle thinks the time is ? */
2854 finfo5.basic_info.in.file.fnum = fnum2;
2855 GET_INFO_FILE2(finfo5);
2856 COMPARE_WRITE_TIME_EQUAL(finfo5, pinfo6);
2858 /* See if we have lost the sticky write time on handle2 */
2859 smb_msleep(3 * msec);
2860 torture_comment(tctx, "Have we lost the sticky write time ?\n");
2862 /* Make sure any further normal write doesn't update the write time */
2863 start = timeval_current();
2864 end = timeval_add(&start, 10 * sec, 0);
2865 while (!timeval_expired(&end)) {
2866 /* do a write */
2867 torture_comment(tctx, "Do a write on the second file handle\n");
2868 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
2869 if (written != 1) {
2870 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2871 ret = false;
2872 goto done;
2874 /* get the times after the write */
2875 GET_INFO_FILE2(finfo5);
2876 GET_INFO_PATH(pinfo6);
2878 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2879 double diff = timeval_elapsed(&start);
2880 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2881 "(wrong!)\n",
2882 diff);
2883 ret = false;
2884 break;
2886 smb_msleep(1 * msec);
2889 /* What about a truncate write ? */
2890 start = timeval_current();
2891 end = timeval_add(&start, 10 * sec, 0);
2892 while (!timeval_expired(&end)) {
2893 /* do a write */
2894 torture_comment(tctx, "Do a truncate write on the second file handle\n");
2895 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 0);
2896 if (written != 0) {
2897 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2898 ret = false;
2899 goto done;
2901 /* get the times after the write */
2902 GET_INFO_FILE2(finfo5);
2903 GET_INFO_PATH(pinfo6);
2905 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2906 double diff = timeval_elapsed(&start);
2907 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2908 "(wrong!)\n",
2909 diff);
2910 ret = false;
2911 break;
2913 smb_msleep(1 * msec);
2917 /* keep the 2nd handle open and rerun tests */
2918 if (first) {
2919 first = false;
2920 goto again;
2924 * closing the 2nd handle will cause no write time update
2925 * as the write time was explicit set on this handle
2927 torture_comment(tctx, "Close the 2nd file handle\n");
2928 smbcli_close(cli2->tree, fnum2);
2929 fnum2 = -1;
2931 GET_INFO_PATH(pinfo7);
2932 COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
2934 if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
2935 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2938 done:
2939 if (fnum1 != -1)
2940 smbcli_close(cli->tree, fnum1);
2941 if (fnum2 != -1)
2942 smbcli_close(cli2->tree, fnum2);
2943 smbcli_unlink(cli->tree, fname);
2944 smbcli_deltree(cli->tree, BASEDIR);
2946 return ret;
2949 static bool test_delayed_write_update7(struct torture_context *tctx, struct smbcli_state *cli)
2951 union smb_open open_parms;
2952 union smb_fileinfo finfo1, finfo2, finfo3;
2953 const char *fname = BASEDIR "\\torture_file7.txt";
2954 NTSTATUS status;
2955 int fnum1 = -1;
2956 bool ret = true;
2957 TALLOC_CTX *mem_ctx;
2959 torture_comment(tctx, "\nRunning test_delayed_write_update7 (timestamp resolution test)\n");
2961 mem_ctx = talloc_init("test_delayed_write_update7");
2962 if (!mem_ctx) return false;
2964 ZERO_STRUCT(finfo1);
2965 ZERO_STRUCT(finfo2);
2966 ZERO_STRUCT(finfo3);
2967 ZERO_STRUCT(open_parms);
2969 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2971 /* Create the file. */
2972 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2973 if (fnum1 == -1) {
2974 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
2975 return false;
2978 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2979 finfo1.basic_info.in.file.fnum = fnum1;
2980 finfo2 = finfo1;
2981 finfo3 = finfo1;
2983 /* Get the initial timestamps. */
2984 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
2986 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
2988 /* Set the pending write time to a value with ns. */
2989 SET_INFO_FILE_NS(finfo, time(NULL) + 86400, 103, cli->tree, fnum1);
2991 /* Get the current pending write time by fnum. */
2992 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
2994 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
2996 /* Ensure the time is actually different. */
2997 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2998 torture_result(tctx, TORTURE_FAIL,
2999 "setfileinfo time matches original fileinfo time");
3000 ret = false;
3003 /* Get the current pending write time by path. */
3004 finfo3.basic_info.in.file.path = fname;
3005 status = smb_raw_pathinfo(cli->tree, tctx, &finfo3);
3007 if (finfo2.basic_info.out.write_time != finfo3.basic_info.out.write_time) {
3008 torture_result(tctx, TORTURE_FAIL,
3009 "qpathinfo time doens't match fileinfo time");
3010 ret = false;
3013 /* Now close the file. Re-open and check that the write
3014 time is identical to the one we wrote. */
3016 smbcli_close(cli->tree, fnum1);
3018 open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
3019 open_parms.ntcreatex.in.flags = 0;
3020 open_parms.ntcreatex.in.access_mask = SEC_GENERIC_READ;
3021 open_parms.ntcreatex.in.file_attr = 0;
3022 open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE|
3023 NTCREATEX_SHARE_ACCESS_READ|
3024 NTCREATEX_SHARE_ACCESS_WRITE;
3025 open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
3026 open_parms.ntcreatex.in.create_options = 0;
3027 open_parms.ntcreatex.in.fname = fname;
3029 status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
3030 talloc_free(mem_ctx);
3032 if (!NT_STATUS_IS_OK(status)) {
3033 torture_result(tctx, TORTURE_FAIL,
3034 "setfileinfo time matches original fileinfo time");
3035 ret = false;
3038 fnum1 = open_parms.ntcreatex.out.file.fnum;
3040 /* Check the returned time matches. */
3041 if (open_parms.ntcreatex.out.write_time != finfo2.basic_info.out.write_time) {
3042 torture_result(tctx, TORTURE_FAIL,
3043 "final open time does not match set time");
3044 ret = false;
3047 done:
3049 smbcli_close(cli->tree, fnum1);
3051 smbcli_unlink(cli->tree, fname);
3052 smbcli_deltree(cli->tree, BASEDIR);
3053 return ret;
3057 Test if creating a file in a directory with an open handle updates the
3058 write timestamp (it should).
3060 static bool test_directory_update8(struct torture_context *tctx, struct smbcli_state *cli)
3062 union smb_fileinfo dir_info1, dir_info2;
3063 union smb_open open_parms;
3064 const char *fname = BASEDIR "\\torture_file.txt";
3065 NTSTATUS status;
3066 int fnum1 = -1;
3067 int fnum2 = -1;
3068 bool ret = true;
3069 double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
3070 int normal_delay = 2000000;
3071 double sec = ((double)used_delay) / ((double)normal_delay);
3072 int msec = 1000 * sec;
3073 TALLOC_CTX *mem_ctx = talloc_init("test_delayed_write_update8");
3075 if (!mem_ctx) return false;
3077 torture_comment(tctx, "\nRunning test directory write update\n");
3079 torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
3081 /* Open a handle on the directory - and leave it open. */
3082 ZERO_STRUCT(open_parms);
3083 open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
3084 open_parms.ntcreatex.in.flags = 0;
3085 open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ;
3086 open_parms.ntcreatex.in.file_attr = 0;
3087 open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE|
3088 NTCREATEX_SHARE_ACCESS_READ|
3089 NTCREATEX_SHARE_ACCESS_WRITE;
3090 open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
3091 open_parms.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
3092 open_parms.ntcreatex.in.fname = BASEDIR;
3094 status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
3095 talloc_free(mem_ctx);
3097 if (!NT_STATUS_IS_OK(status)) {
3098 torture_result(tctx, TORTURE_FAIL,
3099 "failed to open directory handle");
3100 ret = false;
3101 goto done;
3104 fnum1 = open_parms.ntcreatex.out.file.fnum;
3106 /* Store the returned write time. */
3107 ZERO_STRUCT(dir_info1);
3108 dir_info1.basic_info.out.write_time = open_parms.ntcreatex.out.write_time;
3110 torture_comment(tctx, "Initial write time %s\n",
3111 nt_time_string(tctx, dir_info1.basic_info.out.write_time));
3113 /* sleep */
3114 smb_msleep(3 * msec);
3116 /* Now create a file within the directory. */
3117 fnum2 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
3118 if (fnum2 == -1) {
3119 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
3120 ret = false;
3121 goto done;
3123 smbcli_close(cli->tree, fnum2);
3125 /* Read the directory write time again. */
3126 ZERO_STRUCT(dir_info2);
3127 dir_info2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
3128 dir_info2.basic_info.in.file.fnum = fnum1;
3130 status = smb_raw_fileinfo(cli->tree, tctx, &dir_info2);
3132 torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
3134 /* Ensure it's been incremented. */
3135 COMPARE_WRITE_TIME_GREATER(dir_info2, dir_info1);
3137 torture_comment(tctx, "Updated write time %s\n",
3138 nt_time_string(tctx, dir_info2.basic_info.out.write_time));
3140 done:
3142 if (fnum1 != -1)
3143 smbcli_close(cli->tree, fnum1);
3144 smbcli_unlink(cli->tree, fname);
3145 smbcli_deltree(cli->tree, BASEDIR);
3147 return ret;
3151 testing of delayed update of write_time
3153 struct torture_suite *torture_delay_write(void)
3155 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "delaywrite");
3157 torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
3158 torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
3159 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate", test_delayed_write_update1);
3160 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a);
3161 torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b);
3162 torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c);
3163 torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
3164 torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
3165 torture_suite_add_2smb_test(suite, "delayed update of write time 3a", test_delayed_write_update3a);
3166 torture_suite_add_2smb_test(suite, "delayed update of write time 3b", test_delayed_write_update3b);
3167 torture_suite_add_2smb_test(suite, "delayed update of write time 3c", test_delayed_write_update3c);
3168 torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
3169 torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
3170 torture_suite_add_2smb_test(suite, "delayed update of write time 5b", test_delayed_write_update5b);
3171 torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
3172 torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7);
3173 torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7);
3174 torture_suite_add_1smb_test(suite, "directory timestamp update test", test_directory_update8);
3176 return suite;