BASE-DELAYWRITE: test behavior of SMBwrite truncate, writeX, SMBwrite truncate and...
[Samba.git] / source4 / torture / basic / delaywrite.c
blob4f6d4fda0ca87a5de3d43a7e136d4dd74ad21ed5
1 /*
2 Unix SMB/CIFS implementation.
4 test suite for delayed write update
6 Copyright (C) Volker Lendecke 2004
7 Copyright (C) Andrew Tridgell 2004
8 Copyright (C) Jeremy Allison 2004
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "torture/torture.h"
26 #include "libcli/raw/libcliraw.h"
27 #include "libcli/raw/raw_proto.h"
28 #include "system/time.h"
29 #include "system/filesys.h"
30 #include "libcli/libcli.h"
31 #include "torture/util.h"
33 #define BASEDIR "\\delaywrite"
35 static bool test_delayed_write_update(struct torture_context *tctx, struct smbcli_state *cli)
37 union smb_fileinfo finfo1, finfo2;
38 const char *fname = BASEDIR "\\torture_file.txt";
39 NTSTATUS status;
40 int fnum1 = -1;
41 bool ret = true;
42 ssize_t written;
43 struct timeval start;
44 struct timeval end;
45 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
46 int normal_delay = 2000000;
47 double sec = ((double)used_delay) / ((double)normal_delay);
48 int msec = 1000 * sec;
50 if (!torture_setup_dir(cli, BASEDIR)) {
51 return false;
54 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
55 if (fnum1 == -1) {
56 torture_comment(tctx, "Failed to open %s\n", fname);
57 return false;
60 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
61 finfo1.basic_info.in.file.fnum = fnum1;
62 finfo2 = finfo1;
64 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
66 if (!NT_STATUS_IS_OK(status)) {
67 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
68 return false;
71 torture_comment(tctx, "Initial write time %s\n",
72 nt_time_string(tctx, finfo1.basic_info.out.write_time));
74 /* 3 second delay to ensure we get past any 2 second time
75 granularity (older systems may have that) */
76 msleep(3 * msec);
78 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
80 if (written != 1) {
81 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
82 (int)written, __location__);
83 return false;
86 start = timeval_current();
87 end = timeval_add(&start, (120*sec), 0);
88 while (!timeval_expired(&end)) {
89 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
91 if (!NT_STATUS_IS_OK(status)) {
92 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
93 ret = false;
94 break;
96 torture_comment(tctx, "write time %s\n",
97 nt_time_string(tctx, finfo2.basic_info.out.write_time));
98 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
99 double diff = timeval_elapsed(&start);
100 if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
101 torture_comment(tctx, "Server updated write_time after %.2f seconds"
102 "(1 sec == %.2f)(wrong!)\n",
103 diff, sec);
104 ret = false;
105 break;
108 torture_comment(tctx, "Server updated write_time after %.2f seconds"
109 "(1 sec == %.2f)(correct)\n",
110 diff, sec);
111 break;
113 fflush(stdout);
114 msleep(1 * msec);
117 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
118 torture_comment(tctx, "Server did not update write time (wrong!)\n");
119 ret = false;
123 if (fnum1 != -1)
124 smbcli_close(cli->tree, fnum1);
125 smbcli_unlink(cli->tree, fname);
126 smbcli_deltree(cli->tree, BASEDIR);
128 return ret;
131 static bool test_delayed_write_update1(struct torture_context *tctx, struct smbcli_state *cli)
133 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
134 const char *fname = BASEDIR "\\torture_file1.txt";
135 NTSTATUS status;
136 int fnum1 = -1;
137 bool ret = true;
138 ssize_t written;
139 struct timeval start;
140 struct timeval end;
141 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
142 int normal_delay = 2000000;
143 double sec = ((double)used_delay) / ((double)normal_delay);
144 int msec = 1000 * sec;
145 char buf[2048];
147 if (!torture_setup_dir(cli, BASEDIR)) {
148 return false;
151 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
152 if (fnum1 == -1) {
153 torture_comment(tctx, "Failed to open %s\n", fname);
154 return false;
157 memset(buf, 'x', 2048);
158 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
160 /* 3 second delay to ensure we get past any 2 second time
161 granularity (older systems may have that) */
162 msleep(3 * msec);
164 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
165 finfo1.all_info.in.file.fnum = fnum1;
166 finfo2 = finfo1;
167 finfo3 = finfo1;
168 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
169 pinfo4.all_info.in.file.path = fname;
171 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
173 if (!NT_STATUS_IS_OK(status)) {
174 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
175 return false;
178 torture_comment(tctx, "Initial write time %s\n",
179 nt_time_string(tctx, finfo1.all_info.out.write_time));
181 /* Do a zero length SMBwrite call to truncate. */
182 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
184 if (written != 0) {
185 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
186 (int)written, __location__);
187 return false;
190 start = timeval_current();
191 end = timeval_add(&start, (120*sec), 0);
192 while (!timeval_expired(&end)) {
193 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
195 if (!NT_STATUS_IS_OK(status)) {
196 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
197 ret = false;
198 break;
201 if (finfo2.all_info.out.size != 1024) {
202 DEBUG(0, ("file not truncated\n"));
203 ret = false;
204 break;
207 torture_comment(tctx, "write time %s\n",
208 nt_time_string(tctx, finfo2.all_info.out.write_time));
209 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
210 double diff = timeval_elapsed(&start);
211 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
212 torture_comment(tctx, "After SMBwrite truncate "
213 "server updated write_time after %.2f seconds"
214 "(1 sec == %.2f)(wrong!)\n",
215 diff, sec);
216 ret = false;
217 break;
220 torture_comment(tctx, "After SMBwrite truncate "
221 "server updated write_time after %.2f seconds"
222 "(1 sec == %.2f)(correct)\n",
223 diff, sec);
224 break;
226 fflush(stdout);
227 msleep(1 * msec);
230 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
231 torture_comment(tctx, "Server did not update write time (wrong!)\n");
232 ret = false;
235 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
236 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
238 if (written != 1) {
239 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
240 (int)written, __location__);
241 return false;
244 start = timeval_current();
245 end = timeval_add(&start, (10*sec), 0);
246 while (!timeval_expired(&end)) {
247 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
249 if (!NT_STATUS_IS_OK(status)) {
250 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
251 ret = false;
252 break;
255 if (finfo3.all_info.out.size != 1024) {
256 DEBUG(0, ("file not truncated\n"));
257 ret = false;
258 break;
261 torture_comment(tctx, "write time %s\n",
262 nt_time_string(tctx, finfo3.all_info.out.write_time));
263 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
264 double diff = timeval_elapsed(&start);
266 torture_comment(tctx, "server updated write_time after %.2f seconds"
267 "(1 sec == %.2f)(correct)\n",
268 diff, sec);
269 break;
271 fflush(stdout);
272 msleep(1 * msec);
275 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
276 torture_comment(tctx, "Server updated write time (wrong!)\n");
277 ret = false;
280 /* the close should trigger an write time update */
281 smbcli_close(cli->tree, fnum1);
282 fnum1 = -1;
284 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
285 if (!NT_STATUS_IS_OK(status)) {
286 DEBUG(0, ("pathinfo failed: %s\n", nt_errstr(status)));
287 return false;
290 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
291 torture_comment(tctx, "Server did not update write time on close (wrong!)\n");
292 ret = false;
293 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
294 torture_comment(tctx, "Server updated write time on close (correct)\n");
297 if (fnum1 != -1)
298 smbcli_close(cli->tree, fnum1);
299 smbcli_unlink(cli->tree, fname);
300 smbcli_deltree(cli->tree, BASEDIR);
302 return ret;
305 /* Updating with a SMBwrite of zero length
306 * changes the write time immediately - even on expand. */
308 static bool test_delayed_write_update1a(struct torture_context *tctx, struct smbcli_state *cli)
310 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
311 const char *fname = BASEDIR "\\torture_file1a.txt";
312 NTSTATUS status;
313 int fnum1 = -1;
314 bool ret = true;
315 ssize_t written;
316 struct timeval start;
317 struct timeval end;
318 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
319 int normal_delay = 2000000;
320 double sec = ((double)used_delay) / ((double)normal_delay);
321 int msec = 1000 * sec;
322 char buf[2048];
324 if (!torture_setup_dir(cli, BASEDIR)) {
325 return false;
328 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
329 if (fnum1 == -1) {
330 torture_comment(tctx, "Failed to open %s\n", fname);
331 return false;
334 memset(buf, 'x', 2048);
335 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
337 /* 3 second delay to ensure we get past any 2 second time
338 granularity (older systems may have that) */
339 msleep(3 * msec);
341 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
342 finfo1.all_info.in.file.fnum = fnum1;
343 finfo2 = finfo1;
344 finfo3 = finfo1;
345 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
346 pinfo4.all_info.in.file.path = fname;
348 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
350 if (!NT_STATUS_IS_OK(status)) {
351 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
352 return false;
355 torture_comment(tctx, "Initial write time %s\n",
356 nt_time_string(tctx, finfo1.all_info.out.write_time));
358 /* Do a zero length SMBwrite call to truncate. */
359 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
361 if (written != 0) {
362 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
363 (int)written, __location__);
364 return false;
367 start = timeval_current();
368 end = timeval_add(&start, (120*sec), 0);
369 while (!timeval_expired(&end)) {
370 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
372 if (!NT_STATUS_IS_OK(status)) {
373 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
374 ret = false;
375 break;
378 if (finfo2.all_info.out.size != 10240) {
379 DEBUG(0, ("file not truncated\n"));
380 ret = false;
381 break;
384 torture_comment(tctx, "write time %s\n",
385 nt_time_string(tctx, finfo2.all_info.out.write_time));
386 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
387 double diff = timeval_elapsed(&start);
388 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
389 torture_comment(tctx, "After SMBwrite truncate "
390 "server updated write_time after %.2f seconds"
391 "(1 sec == %.2f)(wrong!)\n",
392 diff, sec);
393 ret = false;
394 break;
397 torture_comment(tctx, "After SMBwrite truncate "
398 "server updated write_time after %.2f seconds"
399 "(1 sec == %.2f)(correct)\n",
400 diff, sec);
401 break;
403 fflush(stdout);
404 msleep(1 * msec);
407 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
408 torture_comment(tctx, "Server did not update write time (wrong!)\n");
409 ret = false;
412 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
413 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
415 if (written != 1) {
416 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
417 (int)written, __location__);
418 return false;
421 start = timeval_current();
422 end = timeval_add(&start, (10*sec), 0);
423 while (!timeval_expired(&end)) {
424 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
426 if (!NT_STATUS_IS_OK(status)) {
427 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
428 ret = false;
429 break;
432 if (finfo3.all_info.out.size != 10240) {
433 DEBUG(0, ("file not truncated\n"));
434 ret = false;
435 break;
438 torture_comment(tctx, "write time %s\n",
439 nt_time_string(tctx, finfo3.all_info.out.write_time));
440 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
441 double diff = timeval_elapsed(&start);
443 torture_comment(tctx, "server updated write_time after %.2f seconds"
444 "(1 sec == %.2f)(correct)\n",
445 diff, sec);
446 break;
448 fflush(stdout);
449 msleep(1 * msec);
452 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
453 torture_comment(tctx, "Server updated write time (wrong!)\n");
454 ret = false;
457 /* the close should trigger an write time update */
458 smbcli_close(cli->tree, fnum1);
459 fnum1 = -1;
461 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
462 if (!NT_STATUS_IS_OK(status)) {
463 DEBUG(0, ("pathinfo failed: %s\n", nt_errstr(status)));
464 return false;
467 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
468 torture_comment(tctx, "Server did not update write time on close (wrong!)\n");
469 ret = false;
470 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
471 torture_comment(tctx, "Server updated write time on close (correct)\n");
474 if (fnum1 != -1)
475 smbcli_close(cli->tree, fnum1);
476 smbcli_unlink(cli->tree, fname);
477 smbcli_deltree(cli->tree, BASEDIR);
479 return ret;
482 /* Updating with a SET_FILE_END_OF_FILE_INFO
483 * changes the write time immediately - even on expand. */
485 static bool test_delayed_write_update1b(struct torture_context *tctx, struct smbcli_state *cli)
487 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
488 const char *fname = BASEDIR "\\torture_file1b.txt";
489 NTSTATUS status;
490 int fnum1 = -1;
491 bool ret = true;
492 ssize_t written;
493 struct timeval start;
494 struct timeval end;
495 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
496 int normal_delay = 2000000;
497 double sec = ((double)used_delay) / ((double)normal_delay);
498 int msec = 1000 * sec;
499 char buf[2048];
501 if (!torture_setup_dir(cli, BASEDIR)) {
502 return false;
505 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
506 if (fnum1 == -1) {
507 torture_comment(tctx, "Failed to open %s\n", fname);
508 return false;
511 memset(buf, 'x', 2048);
512 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
514 /* 3 second delay to ensure we get past any 2 second time
515 granularity (older systems may have that) */
516 msleep(3 * msec);
518 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
519 finfo1.all_info.in.file.fnum = fnum1;
520 finfo2 = finfo1;
521 finfo3 = finfo1;
522 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
523 pinfo4.all_info.in.file.path = fname;
525 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
527 if (!NT_STATUS_IS_OK(status)) {
528 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
529 return false;
532 torture_comment(tctx, "Initial write time %s\n",
533 nt_time_string(tctx, finfo1.all_info.out.write_time));
535 /* Do a SET_END_OF_FILE_INFO call to truncate. */
536 status = smbcli_ftruncate(cli->tree, fnum1, (uint64_t)10240);
538 if (!NT_STATUS_IS_OK(status)) {
539 torture_comment(tctx, "SET_END_OF_FILE failed (%s)\n",
540 nt_errstr(status));
541 return false;
544 start = timeval_current();
545 end = timeval_add(&start, (120*sec), 0);
546 while (!timeval_expired(&end)) {
547 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
549 if (!NT_STATUS_IS_OK(status)) {
550 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
551 ret = false;
552 break;
555 if (finfo2.all_info.out.size != 10240) {
556 DEBUG(0, ("file not truncated\n"));
557 ret = false;
558 break;
561 torture_comment(tctx, "write time %s\n",
562 nt_time_string(tctx, finfo2.all_info.out.write_time));
563 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
564 double diff = timeval_elapsed(&start);
565 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
566 torture_comment(tctx, "After SET_END_OF_FILE truncate "
567 "server updated write_time after %.2f seconds"
568 "(1 sec == %.2f)(wrong!)\n",
569 diff, sec);
570 ret = false;
571 break;
574 torture_comment(tctx, "After SET_END_OF_FILE truncate "
575 "server updated write_time after %.2f seconds"
576 "(1 sec == %.2f)(correct)\n",
577 diff, sec);
578 break;
580 fflush(stdout);
581 msleep(1 * msec);
584 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
585 torture_comment(tctx, "Server did not update write time (wrong!)\n");
586 ret = false;
589 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
590 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
592 if (written != 1) {
593 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
594 (int)written, __location__);
595 return false;
598 start = timeval_current();
599 end = timeval_add(&start, (10*sec), 0);
600 while (!timeval_expired(&end)) {
601 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
603 if (!NT_STATUS_IS_OK(status)) {
604 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
605 ret = false;
606 break;
609 if (finfo3.all_info.out.size != 10240) {
610 DEBUG(0, ("file not truncated\n"));
611 ret = false;
612 break;
615 torture_comment(tctx, "write time %s\n",
616 nt_time_string(tctx, finfo3.all_info.out.write_time));
617 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
618 double diff = timeval_elapsed(&start);
620 torture_comment(tctx, "server updated write_time after %.2f seconds"
621 "(1 sec == %.2f)(correct)\n",
622 diff, sec);
623 break;
625 fflush(stdout);
626 msleep(1 * msec);
629 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
630 torture_comment(tctx, "Server updated write time (wrong!)\n");
631 ret = false;
634 /* the close should trigger an write time update */
635 smbcli_close(cli->tree, fnum1);
636 fnum1 = -1;
638 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
639 if (!NT_STATUS_IS_OK(status)) {
640 DEBUG(0, ("pathinfo failed: %s\n", nt_errstr(status)));
641 return false;
644 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
645 torture_comment(tctx, "Server did not update write time on close (wrong!)\n");
646 ret = false;
647 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
648 torture_comment(tctx, "Server updated write time on close (correct)\n");
651 if (fnum1 != -1)
652 smbcli_close(cli->tree, fnum1);
653 smbcli_unlink(cli->tree, fname);
654 smbcli_deltree(cli->tree, BASEDIR);
656 return ret;
659 /* Updating with a SET_ALLOCATION_INFO (truncate) does so immediately. */
661 static bool test_delayed_write_update1c(struct torture_context *tctx, struct smbcli_state *cli)
663 union smb_setfileinfo parms;
664 union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
665 const char *fname = BASEDIR "\\torture_file1c.txt";
666 NTSTATUS status;
667 int fnum1 = -1;
668 bool ret = true;
669 ssize_t written;
670 struct timeval start;
671 struct timeval end;
672 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
673 int normal_delay = 2000000;
674 double sec = ((double)used_delay) / ((double)normal_delay);
675 int msec = 1000 * sec;
676 char buf[2048];
678 if (!torture_setup_dir(cli, BASEDIR)) {
679 return false;
682 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
683 if (fnum1 == -1) {
684 torture_comment(tctx, "Failed to open %s\n", fname);
685 return false;
688 memset(buf, 'x', 2048);
689 written = smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
691 /* 3 second delay to ensure we get past any 2 second time
692 granularity (older systems may have that) */
693 msleep(3 * msec);
695 finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
696 finfo1.all_info.in.file.fnum = fnum1;
697 finfo2 = finfo1;
698 finfo3 = finfo1;
699 pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
700 pinfo4.all_info.in.file.path = fname;
702 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
704 if (!NT_STATUS_IS_OK(status)) {
705 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
706 return false;
709 torture_comment(tctx, "Initial write time %s\n",
710 nt_time_string(tctx, finfo1.all_info.out.write_time));
712 /* Do a SET_ALLOCATION_SIZE call to truncate. */
713 parms.allocation_info.level = RAW_SFILEINFO_ALLOCATION_INFO;
714 parms.allocation_info.in.file.fnum = fnum1;
715 parms.allocation_info.in.alloc_size = 0;
717 status = smb_raw_setfileinfo(cli->tree, &parms);
719 if (!NT_STATUS_IS_OK(status)) {
720 torture_comment(tctx, "RAW_SFILEINFO_ALLOCATION_INFO failed (%s)\n",
721 nt_errstr(status));
722 return false;
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 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
732 ret = false;
733 break;
736 if (finfo2.all_info.out.size != 0) {
737 DEBUG(0, ("file not truncated\n"));
738 ret = false;
739 break;
742 torture_comment(tctx, "write time %s\n",
743 nt_time_string(tctx, finfo2.all_info.out.write_time));
744 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
745 double diff = timeval_elapsed(&start);
746 if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
747 torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
748 "server updated write_time after %.2f seconds"
749 "(1 sec == %.2f)(wrong!)\n",
750 diff, sec);
751 ret = false;
752 break;
755 torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
756 "server updated write_time after %.2f seconds"
757 "(1 sec == %.2f)(correct)\n",
758 diff, sec);
759 break;
761 fflush(stdout);
762 msleep(1 * msec);
765 if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
766 torture_comment(tctx, "Server did not update write time (wrong!)\n");
767 ret = false;
770 /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
771 written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
773 if (written != 1) {
774 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
775 (int)written, __location__);
776 return false;
779 start = timeval_current();
780 end = timeval_add(&start, (10*sec), 0);
781 while (!timeval_expired(&end)) {
782 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
784 if (!NT_STATUS_IS_OK(status)) {
785 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
786 ret = false;
787 break;
790 if (finfo3.all_info.out.size != 1) {
791 DEBUG(0, ("file not expanded\n"));
792 ret = false;
793 break;
796 torture_comment(tctx, "write time %s\n",
797 nt_time_string(tctx, finfo3.all_info.out.write_time));
798 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
799 double diff = timeval_elapsed(&start);
801 torture_comment(tctx, "server updated write_time after %.2f seconds"
802 "(1 sec == %.2f)(correct)\n",
803 diff, sec);
804 break;
806 fflush(stdout);
807 msleep(1 * msec);
810 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
811 torture_comment(tctx, "Server updated write time (wrong!)\n");
812 ret = false;
815 /* the close should trigger an write time update */
816 smbcli_close(cli->tree, fnum1);
817 fnum1 = -1;
819 status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
820 if (!NT_STATUS_IS_OK(status)) {
821 DEBUG(0, ("pathinfo failed: %s\n", nt_errstr(status)));
822 return false;
825 if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
826 torture_comment(tctx, "Server did not update write time on close (wrong!)\n");
827 ret = false;
828 } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
829 torture_comment(tctx, "Server updated write time on close (correct)\n");
832 if (fnum1 != -1)
833 smbcli_close(cli->tree, fnum1);
834 smbcli_unlink(cli->tree, fname);
835 smbcli_deltree(cli->tree, BASEDIR);
837 return ret;
841 * Do as above, but using 2 connections.
844 static bool test_delayed_write_update2(struct torture_context *tctx, struct smbcli_state *cli,
845 struct smbcli_state *cli2)
847 union smb_fileinfo finfo1, finfo2;
848 const char *fname = BASEDIR "\\torture_file.txt";
849 NTSTATUS status;
850 int fnum1 = -1;
851 int fnum2 = -1;
852 bool ret = true;
853 ssize_t written;
854 struct timeval start;
855 struct timeval end;
856 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
857 int normal_delay = 2000000;
858 double sec = ((double)used_delay) / ((double)normal_delay);
859 int msec = 1000 * sec;
860 union smb_flush flsh;
862 if (!torture_setup_dir(cli, BASEDIR)) {
863 return false;
866 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
867 if (fnum1 == -1) {
868 torture_comment(tctx, "Failed to open %s\n", fname);
869 return false;
872 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
873 finfo1.basic_info.in.file.fnum = fnum1;
874 finfo2 = finfo1;
876 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
878 if (!NT_STATUS_IS_OK(status)) {
879 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
880 return false;
883 torture_comment(tctx, "Initial write time %s\n",
884 nt_time_string(tctx, finfo1.basic_info.out.write_time));
886 /* 3 second delay to ensure we get past any 2 second time
887 granularity (older systems may have that) */
888 msleep(3 * msec);
891 /* Try using setfileinfo instead of write to update write time. */
892 union smb_setfileinfo sfinfo;
893 time_t t_set = time(NULL);
894 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
895 sfinfo.basic_info.in.file.fnum = fnum1;
896 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
897 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
899 /* I tried this with both + and - ve to see if it makes a different.
900 It doesn't - once the filetime is set via setfileinfo it stays that way. */
901 #if 1
902 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
903 #else
904 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
905 #endif
906 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
907 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
909 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
911 if (!NT_STATUS_IS_OK(status)) {
912 DEBUG(0, ("sfileinfo failed: %s\n", nt_errstr(status)));
913 return false;
917 finfo2.basic_info.in.file.path = fname;
919 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
921 if (!NT_STATUS_IS_OK(status)) {
922 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
923 return false;
925 torture_comment(tctx, "write time %s\n",
926 nt_time_string(tctx, finfo2.basic_info.out.write_time));
928 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
929 torture_comment(tctx, "Server updated write_time (correct)\n");
930 } else {
931 torture_comment(tctx, "Server did not update write time (wrong!)\n");
932 ret = false;
935 /* Now try a write to see if the write time gets reset. */
937 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
938 finfo1.basic_info.in.file.fnum = fnum1;
939 finfo2 = finfo1;
941 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
943 if (!NT_STATUS_IS_OK(status)) {
944 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
945 return false;
948 torture_comment(tctx, "Modified write time %s\n",
949 nt_time_string(tctx, finfo1.basic_info.out.write_time));
952 torture_comment(tctx, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
954 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
956 if (written != 10) {
957 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
958 (int)written, __location__);
959 return false;
962 /* Just to prove to tridge that the an smbflush has no effect on
963 the write time :-). The setfileinfo IS STICKY. JRA. */
965 torture_comment(tctx, "Doing flush after write\n");
967 flsh.flush.level = RAW_FLUSH_FLUSH;
968 flsh.flush.in.file.fnum = fnum1;
969 status = smb_raw_flush(cli->tree, &flsh);
970 if (!NT_STATUS_IS_OK(status)) {
971 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
972 return false;
975 /* Once the time was set using setfileinfo then it stays set - writes
976 don't have any effect. But make sure. */
977 start = timeval_current();
978 end = timeval_add(&start, (15*sec), 0);
979 while (!timeval_expired(&end)) {
980 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
982 if (!NT_STATUS_IS_OK(status)) {
983 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
984 ret = false;
985 break;
987 torture_comment(tctx, "write time %s\n",
988 nt_time_string(tctx, finfo2.basic_info.out.write_time));
989 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
990 double diff = timeval_elapsed(&start);
991 torture_comment(tctx, "Server updated write_time after %.2f seconds"
992 "(1sec == %.2f) (wrong!)\n",
993 diff, sec);
994 ret = false;
995 break;
997 fflush(stdout);
998 msleep(1 * msec);
1001 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1002 torture_comment(tctx, "Server did not update write time (correct)\n");
1005 fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
1006 if (fnum2 == -1) {
1007 torture_comment(tctx, "Failed to open %s\n", fname);
1008 return false;
1011 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");
1013 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
1015 if (written != 10) {
1016 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
1017 (int)written, __location__);
1018 return false;
1021 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1023 if (!NT_STATUS_IS_OK(status)) {
1024 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1025 return false;
1027 torture_comment(tctx, "write time %s\n",
1028 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1029 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1030 torture_comment(tctx, "Server updated write_time (wrong!)\n");
1031 ret = false;
1034 torture_comment(tctx, "Closing the first fd to see if write time updated.\n");
1035 smbcli_close(cli->tree, fnum1);
1036 fnum1 = -1;
1038 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");
1040 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
1042 if (written != 10) {
1043 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
1044 (int)written, __location__);
1045 return false;
1048 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1049 finfo1.basic_info.in.file.fnum = fnum2;
1050 finfo2 = finfo1;
1051 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1053 if (!NT_STATUS_IS_OK(status)) {
1054 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1055 return false;
1057 torture_comment(tctx, "write time %s\n",
1058 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1059 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1060 torture_comment(tctx, "Server updated write_time (wrong!)\n");
1061 ret = false;
1064 /* Once the time was set using setfileinfo then it stays set - writes
1065 don't have any effect. But make sure. */
1066 start = timeval_current();
1067 end = timeval_add(&start, (15*sec), 0);
1068 while (!timeval_expired(&end)) {
1069 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1071 if (!NT_STATUS_IS_OK(status)) {
1072 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1073 ret = false;
1074 break;
1076 torture_comment(tctx, "write time %s\n",
1077 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1078 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1079 double diff = timeval_elapsed(&start);
1080 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1081 "(1sec == %.2f) (wrong!)\n",
1082 diff, sec);
1083 ret = false;
1084 break;
1086 fflush(stdout);
1087 msleep(1 * msec);
1090 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1091 torture_comment(tctx, "Server did not update write time (correct)\n");
1094 torture_comment(tctx, "Closing second fd to see if write time updated.\n");
1096 smbcli_close(cli->tree, fnum2);
1097 fnum2 = -1;
1099 fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
1100 if (fnum1 == -1) {
1101 torture_comment(tctx, "Failed to open %s\n", fname);
1102 return false;
1105 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1106 finfo1.basic_info.in.file.fnum = fnum1;
1107 finfo2 = finfo1;
1109 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1111 if (!NT_STATUS_IS_OK(status)) {
1112 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1113 return false;
1116 torture_comment(tctx, "Second open initial write time %s\n",
1117 nt_time_string(tctx, finfo1.basic_info.out.write_time));
1119 msleep(10 * msec);
1120 torture_comment(tctx, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
1122 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
1124 if (written != 10) {
1125 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
1126 (int)written, __location__);
1127 return false;
1130 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1131 finfo1.basic_info.in.file.fnum = fnum1;
1132 finfo2 = finfo1;
1133 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1135 if (!NT_STATUS_IS_OK(status)) {
1136 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1137 return false;
1139 torture_comment(tctx, "write time %s\n",
1140 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1141 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1142 torture_comment(tctx, "Server updated write_time (wrong!)\n");
1143 ret = false;
1146 /* Now the write time should be updated again */
1147 start = timeval_current();
1148 end = timeval_add(&start, (15*sec), 0);
1149 while (!timeval_expired(&end)) {
1150 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1152 if (!NT_STATUS_IS_OK(status)) {
1153 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1154 ret = false;
1155 break;
1157 torture_comment(tctx, "write time %s\n",
1158 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1159 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1160 double diff = timeval_elapsed(&start);
1161 if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
1162 torture_comment(tctx, "Server updated write_time after %.2f seconds"
1163 "(1sec == %.2f) (wrong!)\n",
1164 diff, sec);
1165 ret = false;
1166 break;
1169 torture_comment(tctx, "Server updated write_time after %.2f seconds"
1170 "(1sec == %.2f) (correct)\n",
1171 diff, sec);
1172 break;
1174 fflush(stdout);
1175 msleep(1*msec);
1178 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1179 torture_comment(tctx, "Server did not update write time (wrong!)\n");
1180 ret = false;
1184 /* One more test to do. We should read the filetime via findfirst on the
1185 second connection to ensure it's the same. This is very easy for a Windows
1186 server but a bastard to get right on a POSIX server. JRA. */
1188 if (fnum1 != -1)
1189 smbcli_close(cli->tree, fnum1);
1190 smbcli_unlink(cli->tree, fname);
1191 smbcli_deltree(cli->tree, BASEDIR);
1193 return ret;
1197 /* Windows does obviously not update the stat info during a write call. I
1198 * *think* this is the problem causing a spurious Excel 2003 on XP error
1199 * message when saving a file. Excel does a setfileinfo, writes, and then does
1200 * a getpath(!)info. Or so... For Samba sometimes it displays an error message
1201 * that the file might have been changed in between. What i've been able to
1202 * trace down is that this happens if the getpathinfo after the write shows a
1203 * different last write time than the setfileinfo showed. This is really
1204 * nasty....
1207 static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli,
1208 struct smbcli_state *cli2)
1210 union smb_fileinfo finfo1, finfo2;
1211 const char *fname = BASEDIR "\\torture_file.txt";
1212 NTSTATUS status;
1213 int fnum1 = -1;
1214 int fnum2;
1215 bool ret = true;
1216 ssize_t written;
1217 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1218 int normal_delay = 2000000;
1219 double sec = ((double)used_delay) / ((double)normal_delay);
1220 int msec = 1000 * sec;
1222 if (!torture_setup_dir(cli, BASEDIR)) {
1223 return false;
1226 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1227 if (fnum1 == -1) {
1228 ret = false;
1229 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1230 goto done;
1233 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1234 finfo1.basic_info.in.file.fnum = fnum1;
1236 status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1238 if (!NT_STATUS_IS_OK(status)) {
1239 ret = false;
1240 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1241 goto done;
1244 msleep(1 * msec);
1246 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1248 if (written != 1) {
1249 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1250 ret = false;
1251 goto done;
1254 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
1255 if (fnum2 == -1) {
1256 torture_result(tctx, TORTURE_FAIL, __location__": failed to open 2nd time - %s",
1257 smbcli_errstr(cli2->tree));
1258 ret = false;
1259 goto done;
1262 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
1264 if (written != 1) {
1265 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1",
1266 (int)written);
1267 ret = false;
1268 goto done;
1271 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1272 finfo2.basic_info.in.file.path = fname;
1274 status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
1276 if (!NT_STATUS_IS_OK(status)) {
1277 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s",
1278 nt_errstr(status));
1279 ret = false;
1280 goto done;
1283 if (finfo1.basic_info.out.create_time !=
1284 finfo2.basic_info.out.create_time) {
1285 torture_result(tctx, TORTURE_FAIL, __location__": create_time changed");
1286 ret = false;
1287 goto done;
1290 if (finfo1.basic_info.out.access_time !=
1291 finfo2.basic_info.out.access_time) {
1292 torture_result(tctx, TORTURE_FAIL, __location__": access_time changed");
1293 ret = false;
1294 goto done;
1297 if (finfo1.basic_info.out.write_time !=
1298 finfo2.basic_info.out.write_time) {
1299 torture_result(tctx, TORTURE_FAIL, __location__": write_time changed:\n"
1300 "write time conn 1 = %s, conn 2 = %s",
1301 nt_time_string(tctx, finfo1.basic_info.out.write_time),
1302 nt_time_string(tctx, finfo2.basic_info.out.write_time));
1303 ret = false;
1304 goto done;
1307 if (finfo1.basic_info.out.change_time !=
1308 finfo2.basic_info.out.change_time) {
1309 torture_result(tctx, TORTURE_FAIL, __location__": change_time changed");
1310 ret = false;
1311 goto done;
1314 /* One of the two following calls updates the qpathinfo. */
1316 /* If you had skipped the smbcli_write on fnum2, it would
1317 * *not* have updated the stat on disk */
1319 smbcli_close(cli2->tree, fnum2);
1320 cli2 = NULL;
1322 /* This call is only for the people looking at ethereal :-) */
1323 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1324 finfo2.basic_info.in.file.path = fname;
1326 status = smb_raw_pathinfo(cli->tree, tctx, &finfo2);
1328 if (!NT_STATUS_IS_OK(status)) {
1329 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1330 ret = false;
1331 goto done;
1334 done:
1335 if (fnum1 != -1)
1336 smbcli_close(cli->tree, fnum1);
1337 smbcli_unlink(cli->tree, fname);
1338 smbcli_deltree(cli->tree, BASEDIR);
1340 return ret;
1343 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
1344 uint64_t r = 10*1000*1000; \
1345 NTTIME g = (given).basic_info.out.write_time; \
1346 NTTIME gr = (g / r) * r; \
1347 NTTIME c = (correct).basic_info.out.write_time; \
1348 NTTIME cr = (c / r) * r; \
1349 bool strict = torture_setting_bool(tctx, "strict mode", false); \
1350 bool err = false; \
1351 if (strict && (g cmp c)) { \
1352 err = true; \
1353 } else if ((g cmp c) && (gr cmp cr)) { \
1354 /* handle filesystem without high resolution timestamps */ \
1355 err = true; \
1357 if (err) { \
1358 torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \
1359 #given, nt_time_string(tctx, g), (unsigned long long)g, \
1360 #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \
1361 ret = false; \
1362 goto done; \
1364 } while (0)
1365 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
1366 COMPARE_WRITE_TIME_CMP(given,correct,!=)
1367 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
1368 COMPARE_WRITE_TIME_CMP(given,correct,<=)
1369 #define COMPARE_WRITE_TIME_LESS(given,correct) \
1370 COMPARE_WRITE_TIME_CMP(given,correct,>=)
1372 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
1373 NTTIME g = (given).basic_info.out.access_time; \
1374 NTTIME c = (correct).basic_info.out.access_time; \
1375 if (g cmp c) { \
1376 torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \
1377 #given, nt_time_string(tctx, g), \
1378 #cmp, #correct, nt_time_string(tctx, c)); \
1379 ret = false; \
1380 goto done; \
1382 } while (0)
1383 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
1384 COMPARE_ACCESS_TIME_CMP(given,correct,!=)
1386 #define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \
1387 COMPARE_ACCESS_TIME_EQUAL(given,correct); \
1388 COMPARE_WRITE_TIME_EQUAL(given,correct); \
1389 } while (0)
1391 #define GET_INFO_FILE(finfo) do { \
1392 NTSTATUS _status; \
1393 _status = smb_raw_fileinfo(cli->tree, tctx, &finfo); \
1394 if (!NT_STATUS_IS_OK(_status)) { \
1395 ret = false; \
1396 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1397 nt_errstr(_status)); \
1398 goto done; \
1400 torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1401 nt_time_string(tctx, finfo.basic_info.out.access_time), \
1402 nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1403 } while (0)
1404 #define GET_INFO_PATH(pinfo) do { \
1405 NTSTATUS _status; \
1406 _status = smb_raw_pathinfo(cli2->tree, tctx, &pinfo); \
1407 if (!NT_STATUS_IS_OK(_status)) { \
1408 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \
1409 nt_errstr(_status)); \
1410 ret = false; \
1411 goto done; \
1413 torture_comment(tctx, "pathinfo: Access(%s) Write(%s)\n", \
1414 nt_time_string(tctx, pinfo.basic_info.out.access_time), \
1415 nt_time_string(tctx, pinfo.basic_info.out.write_time)); \
1416 } while (0)
1417 #define GET_INFO_BOTH(finfo,pinfo) do { \
1418 GET_INFO_FILE(finfo); \
1419 GET_INFO_PATH(pinfo); \
1420 COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \
1421 } while (0)
1423 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
1424 NTSTATUS _status; \
1425 union smb_setfileinfo sfinfo; \
1426 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1427 sfinfo.basic_info.in.file.fnum = tfnum; \
1428 sfinfo.basic_info.in.create_time = 0; \
1429 sfinfo.basic_info.in.access_time = 0; \
1430 unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1431 sfinfo.basic_info.in.change_time = 0; \
1432 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1433 _status = smb_raw_setfileinfo(tree, &sfinfo); \
1434 if (!NT_STATUS_IS_OK(_status)) { \
1435 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1436 nt_errstr(_status)); \
1437 ret = false; \
1438 goto done; \
1440 } while (0)
1441 #define SET_INFO_FILE(finfo, wrtime) \
1442 SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
1444 static bool test_delayed_write_update3(struct torture_context *tctx,
1445 struct smbcli_state *cli,
1446 struct smbcli_state *cli2)
1448 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1449 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1450 const char *fname = BASEDIR "\\torture_file.txt";
1451 int fnum1 = -1;
1452 bool ret = true;
1453 ssize_t written;
1454 struct timeval start;
1455 struct timeval end;
1456 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1457 int normal_delay = 2000000;
1458 double sec = ((double)used_delay) / ((double)normal_delay);
1459 int msec = 1000 * sec;
1461 if (!torture_setup_dir(cli, BASEDIR)) {
1462 return false;
1465 torture_comment(tctx, "Open the file handle\n");
1466 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1467 if (fnum1 == -1) {
1468 ret = false;
1469 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1470 goto done;
1473 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1474 finfo0.basic_info.in.file.fnum = fnum1;
1475 finfo1 = finfo0;
1476 finfo2 = finfo0;
1477 finfo3 = finfo0;
1478 finfo4 = finfo0;
1479 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1480 pinfo0.basic_info.in.file.path = fname;
1481 pinfo1 = pinfo0;
1482 pinfo2 = pinfo0;
1483 pinfo3 = pinfo0;
1484 pinfo4 = pinfo0;
1485 pinfo5 = pinfo0;
1487 /* get the initial times */
1488 GET_INFO_BOTH(finfo0,pinfo0);
1491 * make sure the write time is updated 2 seconds later
1492 * calcuated from the first write
1493 * (but expect upto 5 seconds extra time for a busy server)
1495 start = timeval_current();
1496 end = timeval_add(&start, 7 * sec, 0);
1497 while (!timeval_expired(&end)) {
1498 /* do a write */
1499 torture_comment(tctx, "Do a write on the file handle\n");
1500 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1501 if (written != 1) {
1502 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1503 ret = false;
1504 goto done;
1506 /* get the times after the write */
1507 GET_INFO_FILE(finfo1);
1509 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1510 double diff = timeval_elapsed(&start);
1511 if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
1512 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1513 "(1sec == %.2f) (wrong!)\n",
1514 diff, sec);
1515 ret = false;
1516 break;
1519 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1520 "(1sec == %.2f) (correct)\n",
1521 diff, sec);
1522 break;
1524 msleep(0.5 * msec);
1527 GET_INFO_BOTH(finfo1,pinfo1);
1528 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1530 /* sure any further write doesn't update the write time */
1531 start = timeval_current();
1532 end = timeval_add(&start, 15 * sec, 0);
1533 while (!timeval_expired(&end)) {
1534 /* do a write */
1535 torture_comment(tctx, "Do a write on the file handle\n");
1536 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1537 if (written != 1) {
1538 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1539 ret = false;
1540 goto done;
1542 /* get the times after the write */
1543 GET_INFO_BOTH(finfo2,pinfo2);
1545 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1546 double diff = timeval_elapsed(&start);
1547 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1548 "(1sec == %.2f) (wrong!)\n",
1549 diff, sec);
1550 ret = false;
1551 break;
1553 msleep(2 * msec);
1556 GET_INFO_BOTH(finfo2,pinfo2);
1557 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1558 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1559 torture_comment(tctx, "Server did not update write_time (correct)\n");
1562 /* sleep */
1563 msleep(5 * msec);
1565 GET_INFO_BOTH(finfo3,pinfo3);
1566 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1569 * the close updates the write time to the time of the close
1570 * and not to the time of the last write!
1572 torture_comment(tctx, "Close the file handle\n");
1573 smbcli_close(cli->tree, fnum1);
1574 fnum1 = -1;
1576 GET_INFO_PATH(pinfo4);
1577 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1579 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1580 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1583 done:
1584 if (fnum1 != -1)
1585 smbcli_close(cli->tree, fnum1);
1586 smbcli_unlink(cli->tree, fname);
1587 smbcli_deltree(cli->tree, BASEDIR);
1589 return ret;
1592 static bool test_delayed_write_update3a(struct torture_context *tctx,
1593 struct smbcli_state *cli,
1594 struct smbcli_state *cli2)
1596 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1597 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1598 const char *fname = BASEDIR "\\torture_file.txt";
1599 int fnum1 = -1;
1600 bool ret = true;
1601 ssize_t written;
1602 int i;
1603 struct timeval start;
1604 struct timeval end;
1605 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1606 int normal_delay = 2000000;
1607 double sec = ((double)used_delay) / ((double)normal_delay);
1608 int msec = 1000 * sec;
1610 if (!torture_setup_dir(cli, BASEDIR)) {
1611 return false;
1614 torture_comment(tctx, "Open the file handle\n");
1615 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1616 if (fnum1 == -1) {
1617 ret = false;
1618 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1619 goto done;
1622 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1623 finfo0.basic_info.in.file.fnum = fnum1;
1624 finfo1 = finfo0;
1625 finfo2 = finfo0;
1626 finfo3 = finfo0;
1627 finfo4 = finfo0;
1628 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1629 pinfo0.basic_info.in.file.path = fname;
1630 pinfo1 = pinfo0;
1631 pinfo2 = pinfo0;
1632 pinfo3 = pinfo0;
1633 pinfo4 = pinfo0;
1634 pinfo5 = pinfo0;
1636 /* get the initial times */
1637 GET_INFO_BOTH(finfo0,pinfo0);
1640 * sleep some time, to demonstrate the handling of write times
1641 * doesn't depend on the time since the open
1643 msleep(5 * msec);
1645 /* get the initial times */
1646 GET_INFO_BOTH(finfo1,pinfo1);
1647 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1650 * make sure the write time is updated 2 seconds later
1651 * calcuated from the first write
1652 * (but expect upto 5 seconds extra time for a busy server)
1654 start = timeval_current();
1655 end = timeval_add(&start, 7 * sec, 0);
1656 while (!timeval_expired(&end)) {
1657 /* do a write */
1658 torture_comment(tctx, "Do a write on the file handle\n");
1659 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1660 if (written != 1) {
1661 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1662 ret = false;
1663 goto done;
1665 /* get the times after the write */
1666 GET_INFO_FILE(finfo1);
1668 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1669 double diff = timeval_elapsed(&start);
1670 if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
1671 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1672 "(1sec == %.2f) (wrong!)\n",
1673 diff, sec);
1674 ret = false;
1675 break;
1678 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1679 "(1sec == %.2f) (correct)\n",
1680 diff, sec);
1681 break;
1683 msleep(0.5 * msec);
1686 GET_INFO_BOTH(finfo1,pinfo1);
1687 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1690 * demonstrate that a truncate write always
1691 * updates the write time immediately
1693 for (i=0; i < 3; i++) {
1694 msleep(1 * msec);
1695 /* do a write */
1696 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1697 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
1698 if (written != 0) {
1699 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1700 ret = false;
1701 goto done;
1703 /* get the times after the write */
1704 GET_INFO_BOTH(finfo2,pinfo2);
1705 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1706 finfo1 = finfo2;
1709 /* sure any further write doesn't update the write time */
1710 start = timeval_current();
1711 end = timeval_add(&start, 15 * sec, 0);
1712 while (!timeval_expired(&end)) {
1713 /* do a write */
1714 torture_comment(tctx, "Do a write on the file handle\n");
1715 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1716 if (written != 1) {
1717 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1718 ret = false;
1719 goto done;
1721 /* get the times after the write */
1722 GET_INFO_BOTH(finfo2,pinfo2);
1724 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1725 double diff = timeval_elapsed(&start);
1726 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1727 "(1sec == %.2f) (wrong!)\n",
1728 diff, sec);
1729 ret = false;
1730 break;
1732 msleep(2 * msec);
1735 GET_INFO_BOTH(finfo2,pinfo2);
1736 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1737 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1738 torture_comment(tctx, "Server did not update write_time (correct)\n");
1741 /* sleep */
1742 msleep(5 * msec);
1744 /* get the initial times */
1745 GET_INFO_BOTH(finfo1,pinfo1);
1746 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
1749 * demonstrate that a truncate write always
1750 * updates the write time immediately
1752 for (i=0; i < 3; i++) {
1753 msleep(1 * msec);
1754 /* do a write */
1755 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1756 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
1757 if (written != 0) {
1758 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1759 ret = false;
1760 goto done;
1762 /* get the times after the write */
1763 GET_INFO_BOTH(finfo2,pinfo2);
1764 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1765 finfo1 = finfo2;
1768 /* sleep */
1769 msleep(5 * msec);
1771 GET_INFO_BOTH(finfo3,pinfo3);
1772 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1775 * the close doesn't update the write time
1777 torture_comment(tctx, "Close the file handle\n");
1778 smbcli_close(cli->tree, fnum1);
1779 fnum1 = -1;
1781 GET_INFO_PATH(pinfo4);
1782 COMPARE_WRITE_TIME_EQUAL(pinfo4, pinfo3);
1784 if (pinfo4.basic_info.out.write_time == pinfo3.basic_info.out.write_time) {
1785 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1788 done:
1789 if (fnum1 != -1)
1790 smbcli_close(cli->tree, fnum1);
1791 smbcli_unlink(cli->tree, fname);
1792 smbcli_deltree(cli->tree, BASEDIR);
1794 return ret;
1797 static bool test_delayed_write_update3b(struct torture_context *tctx,
1798 struct smbcli_state *cli,
1799 struct smbcli_state *cli2)
1801 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1802 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1803 const char *fname = BASEDIR "\\torture_file.txt";
1804 int fnum1 = -1;
1805 bool ret = true;
1806 ssize_t written;
1807 struct timeval start;
1808 struct timeval end;
1809 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1810 int normal_delay = 2000000;
1811 double sec = ((double)used_delay) / ((double)normal_delay);
1812 int msec = 1000 * sec;
1814 if (!torture_setup_dir(cli, BASEDIR)) {
1815 return false;
1818 torture_comment(tctx, "Open the file handle\n");
1819 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1820 if (fnum1 == -1) {
1821 ret = false;
1822 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1823 goto done;
1826 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1827 finfo0.basic_info.in.file.fnum = fnum1;
1828 finfo1 = finfo0;
1829 finfo2 = finfo0;
1830 finfo3 = finfo0;
1831 finfo4 = finfo0;
1832 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1833 pinfo0.basic_info.in.file.path = fname;
1834 pinfo1 = pinfo0;
1835 pinfo2 = pinfo0;
1836 pinfo3 = pinfo0;
1837 pinfo4 = pinfo0;
1838 pinfo5 = pinfo0;
1840 /* get the initial times */
1841 GET_INFO_BOTH(finfo0,pinfo0);
1844 * sleep some time, to demonstrate the handling of write times
1845 * doesn't depend on the time since the open
1847 msleep(5 * msec);
1849 /* get the initial times */
1850 GET_INFO_BOTH(finfo1,pinfo1);
1851 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1854 * make sure the write time is updated 2 seconds later
1855 * calcuated from the first write
1856 * (but expect upto 5 seconds extra time for a busy server)
1858 start = timeval_current();
1859 end = timeval_add(&start, 7 * sec, 0);
1860 while (!timeval_expired(&end)) {
1861 /* do a write */
1862 torture_comment(tctx, "Do a write on the file handle\n");
1863 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1864 if (written != 1) {
1865 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1866 ret = false;
1867 goto done;
1869 /* get the times after the write */
1870 GET_INFO_FILE(finfo1);
1872 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1873 double diff = timeval_elapsed(&start);
1874 if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
1875 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1876 "(1sec == %.2f) (wrong!)\n",
1877 diff, sec);
1878 ret = false;
1879 break;
1882 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1883 "(1sec == %.2f) (correct)\n",
1884 diff, sec);
1885 break;
1887 msleep(0.5 * msec);
1890 GET_INFO_BOTH(finfo1,pinfo1);
1891 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1893 /* sure any further write doesn't update the write time */
1894 start = timeval_current();
1895 end = timeval_add(&start, 15 * sec, 0);
1896 while (!timeval_expired(&end)) {
1897 /* do a write */
1898 torture_comment(tctx, "Do a write on the file handle\n");
1899 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1900 if (written != 1) {
1901 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1902 ret = false;
1903 goto done;
1905 /* get the times after the write */
1906 GET_INFO_BOTH(finfo2,pinfo2);
1908 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1909 double diff = timeval_elapsed(&start);
1910 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1911 "(1sec == %.2f) (wrong!)\n",
1912 diff, sec);
1913 ret = false;
1914 break;
1916 msleep(2 * msec);
1919 GET_INFO_BOTH(finfo2,pinfo2);
1920 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1921 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1922 torture_comment(tctx, "Server did not update write_time (correct)\n");
1925 /* sleep */
1926 msleep(5 * msec);
1928 GET_INFO_BOTH(finfo3,pinfo3);
1929 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1932 * the close updates the write time to the time of the close
1933 * and not to the time of the last write!
1935 torture_comment(tctx, "Close the file handle\n");
1936 smbcli_close(cli->tree, fnum1);
1937 fnum1 = -1;
1939 GET_INFO_PATH(pinfo4);
1940 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1942 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1943 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1946 done:
1947 if (fnum1 != -1)
1948 smbcli_close(cli->tree, fnum1);
1949 smbcli_unlink(cli->tree, fname);
1950 smbcli_deltree(cli->tree, BASEDIR);
1952 return ret;
1955 static bool test_delayed_write_update3c(struct torture_context *tctx,
1956 struct smbcli_state *cli,
1957 struct smbcli_state *cli2)
1959 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1960 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1961 const char *fname = BASEDIR "\\torture_file.txt";
1962 int fnum1 = -1;
1963 bool ret = true;
1964 ssize_t written;
1965 int i;
1966 struct timeval start;
1967 struct timeval end;
1968 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1969 int normal_delay = 2000000;
1970 double sec = ((double)used_delay) / ((double)normal_delay);
1971 int msec = 1000 * sec;
1973 if (!torture_setup_dir(cli, BASEDIR)) {
1974 return false;
1977 torture_comment(tctx, "Open the file handle\n");
1978 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1979 if (fnum1 == -1) {
1980 ret = false;
1981 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1982 goto done;
1985 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1986 finfo0.basic_info.in.file.fnum = fnum1;
1987 finfo1 = finfo0;
1988 finfo2 = finfo0;
1989 finfo3 = finfo0;
1990 finfo4 = finfo0;
1991 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1992 pinfo0.basic_info.in.file.path = fname;
1993 pinfo1 = pinfo0;
1994 pinfo2 = pinfo0;
1995 pinfo3 = pinfo0;
1996 pinfo4 = pinfo0;
1997 pinfo5 = pinfo0;
1999 /* get the initial times */
2000 GET_INFO_BOTH(finfo0,pinfo0);
2003 * sleep some time, to demonstrate the handling of write times
2004 * doesn't depend on the time since the open
2006 msleep(5 * msec);
2008 /* get the initial times */
2009 GET_INFO_BOTH(finfo1,pinfo1);
2010 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2013 * demonstrate that a truncate write always
2014 * updates the write time immediately
2016 for (i=0; i < 3; i++) {
2017 msleep(1 * msec);
2018 /* do a write */
2019 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
2020 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2021 if (written != 0) {
2022 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2023 ret = false;
2024 goto done;
2026 /* get the times after the write */
2027 GET_INFO_BOTH(finfo2,pinfo2);
2028 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2029 finfo1 = finfo2;
2032 start = timeval_current();
2033 end = timeval_add(&start, 7 * sec, 0);
2034 while (!timeval_expired(&end)) {
2035 /* do a write */
2036 torture_comment(tctx, "Do a write on the file handle\n");
2037 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2038 if (written != 1) {
2039 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2040 ret = false;
2041 goto done;
2043 /* get the times after the write */
2044 GET_INFO_FILE(finfo2);
2046 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2047 double diff = timeval_elapsed(&start);
2048 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2049 "(1sec == %.2f) (wrong!)\n",
2050 diff, sec);
2051 ret = false;
2052 break;
2054 msleep(2 * msec);
2057 GET_INFO_BOTH(finfo2,pinfo2);
2058 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2059 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2060 torture_comment(tctx, "Server did not update write_time (correct)\n");
2063 /* sleep */
2064 msleep(5 * msec);
2066 /* get the initial times */
2067 GET_INFO_BOTH(finfo1,pinfo1);
2068 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
2071 * demonstrate that a truncate write always
2072 * updates the write time immediately
2074 for (i=0; i < 3; i++) {
2075 msleep(1 * msec);
2076 /* do a write */
2077 torture_comment(tctx, "Do a truncate write [%d] on the file handle\n", i);
2078 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2079 if (written != 0) {
2080 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2081 ret = false;
2082 goto done;
2084 /* get the times after the write */
2085 GET_INFO_BOTH(finfo2,pinfo2);
2086 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2087 finfo1 = finfo2;
2090 /* sleep */
2091 msleep(5 * msec);
2093 GET_INFO_BOTH(finfo2,pinfo2);
2094 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2096 /* sure any further write doesn't update the write time */
2097 start = timeval_current();
2098 end = timeval_add(&start, 15 * sec, 0);
2099 while (!timeval_expired(&end)) {
2100 /* do a write */
2101 torture_comment(tctx, "Do a write on the file handle\n");
2102 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2103 if (written != 1) {
2104 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2105 ret = false;
2106 goto done;
2108 /* get the times after the write */
2109 GET_INFO_BOTH(finfo2,pinfo2);
2111 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2112 double diff = timeval_elapsed(&start);
2113 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2114 "(1sec == %.2f) (wrong!)\n",
2115 diff, sec);
2116 ret = false;
2117 break;
2119 msleep(2 * msec);
2122 GET_INFO_BOTH(finfo2,pinfo2);
2123 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2124 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2125 torture_comment(tctx, "Server did not update write_time (correct)\n");
2128 /* sleep */
2129 msleep(5 * msec);
2131 GET_INFO_BOTH(finfo3,pinfo3);
2132 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2135 * the close updates the write time to the time of the close
2136 * and not to the time of the last write!
2138 torture_comment(tctx, "Close the file handle\n");
2139 smbcli_close(cli->tree, fnum1);
2140 fnum1 = -1;
2142 GET_INFO_PATH(pinfo4);
2143 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2145 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2146 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2149 done:
2150 if (fnum1 != -1)
2151 smbcli_close(cli->tree, fnum1);
2152 smbcli_unlink(cli->tree, fname);
2153 smbcli_deltree(cli->tree, BASEDIR);
2155 return ret;
2158 static bool test_delayed_write_update4(struct torture_context *tctx,
2159 struct smbcli_state *cli,
2160 struct smbcli_state *cli2)
2162 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
2163 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
2164 const char *fname = BASEDIR "\\torture_file.txt";
2165 int fnum1 = -1;
2166 bool ret = true;
2167 ssize_t written;
2168 struct timeval start;
2169 struct timeval end;
2170 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2171 int normal_delay = 2000000;
2172 double sec = ((double)used_delay) / ((double)normal_delay);
2173 int msec = 1000 * sec;
2175 if (!torture_setup_dir(cli, BASEDIR)) {
2176 return false;
2179 torture_comment(tctx, "Open the file handle\n");
2180 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2181 if (fnum1 == -1) {
2182 ret = false;
2183 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2184 goto done;
2187 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2188 finfo0.basic_info.in.file.fnum = fnum1;
2189 finfo1 = finfo0;
2190 finfo2 = finfo0;
2191 finfo3 = finfo0;
2192 finfo4 = finfo0;
2193 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2194 pinfo0.basic_info.in.file.path = fname;
2195 pinfo1 = pinfo0;
2196 pinfo2 = pinfo0;
2197 pinfo3 = pinfo0;
2198 pinfo4 = pinfo0;
2199 pinfo5 = pinfo0;
2201 /* get the initial times */
2202 GET_INFO_BOTH(finfo0,pinfo0);
2204 /* sleep a bit */
2205 msleep(5 * msec);
2207 /* do a write */
2208 torture_comment(tctx, "Do a write on the file handle\n");
2209 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2210 if (written != 1) {
2211 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2212 ret = false;
2213 goto done;
2216 GET_INFO_BOTH(finfo1,pinfo1);
2217 COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
2220 * make sure the write time is updated 2 seconds later
2221 * calcuated from the first write
2222 * (but expect upto 3 seconds extra time for a busy server)
2224 start = timeval_current();
2225 end = timeval_add(&start, 5 * sec, 0);
2226 while (!timeval_expired(&end)) {
2227 /* get the times after the first write */
2228 GET_INFO_FILE(finfo1);
2230 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
2231 double diff = timeval_elapsed(&start);
2232 if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
2233 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2234 "(1sec == %.2f) (wrong!)\n",
2235 diff, sec);
2236 ret = false;
2237 break;
2240 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2241 "(1sec == %.2f) (correct)\n",
2242 diff, sec);
2243 break;
2245 msleep(0.5 * msec);
2248 GET_INFO_BOTH(finfo1,pinfo1);
2249 COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
2251 /* sure any further write doesn't update the write time */
2252 start = timeval_current();
2253 end = timeval_add(&start, 15 * sec, 0);
2254 while (!timeval_expired(&end)) {
2255 /* do a write */
2256 torture_comment(tctx, "Do a write on the file handle\n");
2257 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2258 if (written != 1) {
2259 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2260 ret = false;
2261 goto done;
2263 /* get the times after the write */
2264 GET_INFO_BOTH(finfo2,pinfo2);
2266 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2267 double diff = timeval_elapsed(&start);
2268 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2269 "(1sec == %.2f) (wrong!)\n",
2270 diff, sec);
2271 ret = false;
2272 break;
2274 msleep(2 * msec);
2277 GET_INFO_BOTH(finfo2,pinfo2);
2278 COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2279 if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2280 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
2283 /* sleep */
2284 msleep(5 * msec);
2286 GET_INFO_BOTH(finfo3,pinfo3);
2287 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2290 * the close updates the write time to the time of the close
2291 * and not to the time of the last write!
2293 torture_comment(tctx, "Close the file handle\n");
2294 smbcli_close(cli->tree, fnum1);
2295 fnum1 = -1;
2297 GET_INFO_PATH(pinfo4);
2298 COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2300 if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2301 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2304 done:
2305 if (fnum1 != -1)
2306 smbcli_close(cli->tree, fnum1);
2307 smbcli_unlink(cli->tree, fname);
2308 smbcli_deltree(cli->tree, BASEDIR);
2310 return ret;
2313 static bool test_delayed_write_update5(struct torture_context *tctx,
2314 struct smbcli_state *cli,
2315 struct smbcli_state *cli2)
2317 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2318 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2319 const char *fname = BASEDIR "\\torture_file.txt";
2320 int fnum1 = -1;
2321 bool ret = true;
2322 ssize_t written;
2323 struct timeval start;
2324 struct timeval end;
2325 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2326 int normal_delay = 2000000;
2327 double sec = ((double)used_delay) / ((double)normal_delay);
2328 int msec = 1000 * sec;
2330 if (!torture_setup_dir(cli, BASEDIR)) {
2331 return false;
2334 torture_comment(tctx, "Open the file handle\n");
2335 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2336 if (fnum1 == -1) {
2337 ret = false;
2338 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2339 goto done;
2342 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2343 finfo0.basic_info.in.file.fnum = fnum1;
2344 finfo1 = finfo0;
2345 finfo2 = finfo0;
2346 finfo3 = finfo0;
2347 finfo4 = finfo0;
2348 finfo5 = finfo0;
2349 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2350 pinfo0.basic_info.in.file.path = fname;
2351 pinfo1 = pinfo0;
2352 pinfo2 = pinfo0;
2353 pinfo3 = pinfo0;
2354 pinfo4 = pinfo0;
2355 pinfo5 = pinfo0;
2356 pinfo6 = pinfo0;
2358 /* get the initial times */
2359 GET_INFO_BOTH(finfo0,pinfo0);
2361 /* do a write */
2362 torture_comment(tctx, "Do a write on the file handle\n");
2363 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2364 if (written != 1) {
2365 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2366 ret = false;
2367 goto done;
2370 GET_INFO_BOTH(finfo1,pinfo1);
2371 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2373 torture_comment(tctx, "Set write time in the future on the file handle\n");
2374 SET_INFO_FILE(finfo0, time(NULL) + 86400);
2375 GET_INFO_BOTH(finfo2,pinfo2);
2376 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2378 torture_comment(tctx, "Set write time in the past on the file handle\n");
2379 SET_INFO_FILE(finfo0, time(NULL) - 86400);
2380 GET_INFO_BOTH(finfo2,pinfo2);
2381 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2383 /* make sure the 2 second delay from the first write are canceled */
2384 start = timeval_current();
2385 end = timeval_add(&start, 15 * sec, 0);
2386 while (!timeval_expired(&end)) {
2388 /* get the times after the first write */
2389 GET_INFO_BOTH(finfo3,pinfo3);
2391 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2392 double diff = timeval_elapsed(&start);
2393 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2394 "(1sec == %.2f) (wrong!)\n",
2395 diff, sec);
2396 ret = false;
2397 break;
2399 msleep(2 * msec);
2402 GET_INFO_BOTH(finfo3,pinfo3);
2403 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2404 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2405 torture_comment(tctx, "Server did not update write_time (correct)\n");
2408 /* sure any further write doesn't update the write time */
2409 start = timeval_current();
2410 end = timeval_add(&start, 15 * sec, 0);
2411 while (!timeval_expired(&end)) {
2412 /* do a write */
2413 torture_comment(tctx, "Do a write on the file handle\n");
2414 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2415 if (written != 1) {
2416 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2417 ret = false;
2418 goto done;
2420 /* get the times after the write */
2421 GET_INFO_BOTH(finfo4,pinfo4);
2423 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2424 double diff = timeval_elapsed(&start);
2425 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2426 "(1sec == %.2f) (wrong!)\n",
2427 diff, sec);
2428 ret = false;
2429 break;
2431 msleep(2 * msec);
2434 GET_INFO_BOTH(finfo4,pinfo4);
2435 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2436 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2437 torture_comment(tctx, "Server did not update write_time (correct)\n");
2440 /* sleep */
2441 msleep(5 * msec);
2443 GET_INFO_BOTH(finfo5,pinfo5);
2444 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2447 * the close doesn't update the write time
2449 torture_comment(tctx, "Close the file handle\n");
2450 smbcli_close(cli->tree, fnum1);
2451 fnum1 = -1;
2453 GET_INFO_PATH(pinfo6);
2454 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2456 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2457 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2460 done:
2461 if (fnum1 != -1)
2462 smbcli_close(cli->tree, fnum1);
2463 smbcli_unlink(cli->tree, fname);
2464 smbcli_deltree(cli->tree, BASEDIR);
2466 return ret;
2469 static bool test_delayed_write_update5b(struct torture_context *tctx,
2470 struct smbcli_state *cli,
2471 struct smbcli_state *cli2)
2473 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2474 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2475 const char *fname = BASEDIR "\\torture_file.txt";
2476 int fnum1 = -1;
2477 bool ret = true;
2478 ssize_t written;
2479 struct timeval start;
2480 struct timeval end;
2481 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2482 int normal_delay = 2000000;
2483 double sec = ((double)used_delay) / ((double)normal_delay);
2484 int msec = 1000 * sec;
2486 if (!torture_setup_dir(cli, BASEDIR)) {
2487 return false;
2490 torture_comment(tctx, "Open the file handle\n");
2491 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2492 if (fnum1 == -1) {
2493 ret = false;
2494 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2495 goto done;
2498 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2499 finfo0.basic_info.in.file.fnum = fnum1;
2500 finfo1 = finfo0;
2501 finfo2 = finfo0;
2502 finfo3 = finfo0;
2503 finfo4 = finfo0;
2504 finfo5 = finfo0;
2505 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2506 pinfo0.basic_info.in.file.path = fname;
2507 pinfo1 = pinfo0;
2508 pinfo2 = pinfo0;
2509 pinfo3 = pinfo0;
2510 pinfo4 = pinfo0;
2511 pinfo5 = pinfo0;
2512 pinfo6 = pinfo0;
2514 /* get the initial times */
2515 GET_INFO_BOTH(finfo0,pinfo0);
2517 /* do a write */
2518 torture_comment(tctx, "Do a write on the file handle\n");
2519 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2520 if (written != 1) {
2521 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2522 ret = false;
2523 goto done;
2526 GET_INFO_BOTH(finfo1,pinfo1);
2527 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2529 torture_comment(tctx, "Set write time in the future on the file handle\n");
2530 SET_INFO_FILE(finfo0, time(NULL) + 86400);
2531 GET_INFO_BOTH(finfo2,pinfo2);
2532 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2534 torture_comment(tctx, "Set write time in the past on the file handle\n");
2535 SET_INFO_FILE(finfo0, time(NULL) - 86400);
2536 GET_INFO_BOTH(finfo2,pinfo2);
2537 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2539 /* make sure the 2 second delay from the first write are canceled */
2540 start = timeval_current();
2541 end = timeval_add(&start, 15 * sec, 0);
2542 while (!timeval_expired(&end)) {
2544 /* get the times after the first write */
2545 GET_INFO_BOTH(finfo3,pinfo3);
2547 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2548 double diff = timeval_elapsed(&start);
2549 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2550 "(1sec == %.2f) (wrong!)\n",
2551 diff, sec);
2552 ret = false;
2553 break;
2555 msleep(2 * msec);
2558 GET_INFO_BOTH(finfo3,pinfo3);
2559 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2560 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2561 torture_comment(tctx, "Server did not update write_time (correct)\n");
2564 /* sure any further write (truncates) update the write time */
2565 start = timeval_current();
2566 end = timeval_add(&start, 15 * sec, 0);
2567 while (!timeval_expired(&end)) {
2568 /* do a write */
2569 torture_comment(tctx, "Do a truncate write on the file handle\n");
2570 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 0);
2571 if (written != 0) {
2572 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2573 ret = false;
2574 goto done;
2576 /* get the times after the write */
2577 GET_INFO_BOTH(finfo4,pinfo4);
2579 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2580 double diff = timeval_elapsed(&start);
2581 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2582 "(1sec == %.2f) (wrong!)\n",
2583 diff, sec);
2584 ret = false;
2585 break;
2587 msleep(2 * msec);
2590 GET_INFO_BOTH(finfo4,pinfo4);
2591 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2592 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2593 torture_comment(tctx, "Server did not update write_time (correct)\n");
2596 /* sleep */
2597 msleep(5 * msec);
2599 GET_INFO_BOTH(finfo5,pinfo5);
2600 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2603 * the close doesn't update the write time
2605 torture_comment(tctx, "Close the file handle\n");
2606 smbcli_close(cli->tree, fnum1);
2607 fnum1 = -1;
2609 GET_INFO_PATH(pinfo6);
2610 COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2612 if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2613 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2616 done:
2617 if (fnum1 != -1)
2618 smbcli_close(cli->tree, fnum1);
2619 smbcli_unlink(cli->tree, fname);
2620 smbcli_deltree(cli->tree, BASEDIR);
2622 return ret;
2625 static bool test_delayed_write_update6(struct torture_context *tctx,
2626 struct smbcli_state *cli,
2627 struct smbcli_state *cli2)
2629 union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2630 union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
2631 const char *fname = BASEDIR "\\torture_file.txt";
2632 int fnum1 = -1;
2633 int fnum2 = -1;
2634 bool ret = true;
2635 ssize_t written;
2636 struct timeval start;
2637 struct timeval end;
2638 int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2639 int normal_delay = 2000000;
2640 double sec = ((double)used_delay) / ((double)normal_delay);
2641 int msec = 1000 * sec;
2642 bool first = true;
2644 if (!torture_setup_dir(cli, BASEDIR)) {
2645 return false;
2647 again:
2648 torture_comment(tctx, "Open the file handle\n");
2649 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2650 if (fnum1 == -1) {
2651 ret = false;
2652 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2653 goto done;
2656 if (fnum2 == -1) {
2657 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
2658 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2659 if (fnum2 == -1) {
2660 ret = false;
2661 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2662 goto done;
2666 finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2667 finfo0.basic_info.in.file.fnum = fnum1;
2668 finfo1 = finfo0;
2669 finfo2 = finfo0;
2670 finfo3 = finfo0;
2671 finfo4 = finfo0;
2672 finfo5 = finfo0;
2673 pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2674 pinfo0.basic_info.in.file.path = fname;
2675 pinfo1 = pinfo0;
2676 pinfo2 = pinfo0;
2677 pinfo3 = pinfo0;
2678 pinfo4 = pinfo0;
2679 pinfo5 = pinfo0;
2680 pinfo6 = pinfo0;
2681 pinfo7 = pinfo0;
2683 /* get the initial times */
2684 GET_INFO_BOTH(finfo0,pinfo0);
2686 /* do a write */
2687 torture_comment(tctx, "Do a write on the file handle\n");
2688 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2689 if (written != 1) {
2690 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2691 ret = false;
2692 goto done;
2695 GET_INFO_BOTH(finfo1,pinfo1);
2696 COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2698 torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
2699 SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
2700 GET_INFO_BOTH(finfo2,pinfo2);
2701 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2703 torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
2704 SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
2705 GET_INFO_BOTH(finfo2,pinfo2);
2706 COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2708 /* make sure the 2 second delay from the first write are canceled */
2709 start = timeval_current();
2710 end = timeval_add(&start, 15 * sec, 0);
2711 while (!timeval_expired(&end)) {
2713 /* get the times after the first write */
2714 GET_INFO_BOTH(finfo3,pinfo3);
2716 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2717 double diff = timeval_elapsed(&start);
2718 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2719 "(1sec == %.2f) (wrong!)\n",
2720 diff, sec);
2721 ret = false;
2722 break;
2724 msleep(2 * msec);
2727 GET_INFO_BOTH(finfo3,pinfo3);
2728 COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2729 if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2730 torture_comment(tctx, "Server did not update write_time (correct)\n");
2733 /* sure any further write doesn't update the write time */
2734 start = timeval_current();
2735 end = timeval_add(&start, 15 * sec, 0);
2736 while (!timeval_expired(&end)) {
2737 /* do a write */
2738 torture_comment(tctx, "Do a write on the file handle\n");
2739 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2740 if (written != 1) {
2741 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2742 ret = false;
2743 goto done;
2745 /* get the times after the write */
2746 GET_INFO_BOTH(finfo4,pinfo4);
2748 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2749 double diff = timeval_elapsed(&start);
2750 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2751 "(1sec == %.2f) (wrong!)\n",
2752 diff, sec);
2753 ret = false;
2754 break;
2756 msleep(2 * msec);
2759 GET_INFO_BOTH(finfo4,pinfo4);
2760 COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2761 if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2762 torture_comment(tctx, "Server did not update write_time (correct)\n");
2765 /* sleep */
2766 msleep(5 * msec);
2768 GET_INFO_BOTH(finfo5,pinfo5);
2769 COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2772 * the close updates the write time to the time of the close
2773 * as the write time was set on the 2nd handle
2775 torture_comment(tctx, "Close the file handle\n");
2776 smbcli_close(cli->tree, fnum1);
2777 fnum1 = -1;
2779 GET_INFO_PATH(pinfo6);
2780 COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
2782 if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
2783 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2786 /* keep the 2nd handle open and rerun tests */
2787 if (first) {
2788 first = false;
2789 goto again;
2793 * closing the 2nd handle will cause no write time update
2794 * as the write time was explicit set on this handle
2796 torture_comment(tctx, "Close the 2nd file handle\n");
2797 smbcli_close(cli2->tree, fnum2);
2798 fnum2 = -1;
2800 GET_INFO_PATH(pinfo7);
2801 COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
2803 if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
2804 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2807 done:
2808 if (fnum1 != -1)
2809 smbcli_close(cli->tree, fnum1);
2810 if (fnum2 != -1)
2811 smbcli_close(cli2->tree, fnum2);
2812 smbcli_unlink(cli->tree, fname);
2813 smbcli_deltree(cli->tree, BASEDIR);
2815 return ret;
2820 testing of delayed update of write_time
2822 struct torture_suite *torture_delay_write(void)
2824 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "DELAYWRITE");
2826 torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
2827 torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
2828 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate ", test_delayed_write_update1);
2829 torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a);
2830 torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b);
2831 torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c);
2832 torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
2833 torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
2834 torture_suite_add_2smb_test(suite, "delayed update of write time 3a", test_delayed_write_update3a);
2835 torture_suite_add_2smb_test(suite, "delayed update of write time 3b", test_delayed_write_update3b);
2836 torture_suite_add_2smb_test(suite, "delayed update of write time 3c", test_delayed_write_update3c);
2837 torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
2838 torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
2839 torture_suite_add_2smb_test(suite, "delayed update of write time 5b", test_delayed_write_update5b);
2840 torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
2842 return suite;