Merge branch 'master' of ssh://git.samba.org/data/git/samba into noejs
[sfrench/samba-autobuild/.git] / source4 / torture / basic / delaywrite.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    test suite for delayed write update 
5
6    Copyright (C) Volker Lendecke 2004
7    Copyright (C) Andrew Tridgell 2004
8    Copyright (C) Jeremy Allison 2004
9    
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.
14    
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.
19    
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/>.
22 */
23
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
33 #define BASEDIR "\\delaywrite"
34
35 static bool test_delayed_write_update(struct torture_context *tctx, struct smbcli_state *cli)
36 {
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;
49
50         torture_comment(tctx, "\nRunning test_delayed_write_update\n");
51
52         if (!torture_setup_dir(cli, BASEDIR)) {
53                 return false;
54         }
55
56         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
57         if (fnum1 == -1) {
58                 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
59                 return false;
60         }
61
62         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
63         finfo1.basic_info.in.file.fnum = fnum1;
64         finfo2 = finfo1;
65
66         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
67
68         torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
69         
70         torture_comment(tctx, "Initial write time %s\n", 
71                nt_time_string(tctx, finfo1.basic_info.out.write_time));
72
73         /* 3 second delay to ensure we get past any 2 second time
74            granularity (older systems may have that) */
75         msleep(3 * msec);
76
77         written =  smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
78
79         if (written != 1) {
80                 torture_result(tctx, TORTURE_FAIL, 
81                                            "write failed - wrote %d bytes (%s)\n", 
82                                            (int)written, __location__);
83                 return false;
84         }
85
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);
90
91                 if (!NT_STATUS_IS_OK(status)) {
92                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
93                         ret = false;
94                         break;
95                 }
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;
106                         }
107
108                         torture_comment(tctx, "Server updated write_time after %.2f seconds"
109                                         "(1 sec == %.2f)(correct)\n",
110                                         diff, sec);
111                         break;
112                 }
113                 fflush(stdout);
114                 msleep(1 * msec);
115         }
116         
117         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
118                 torture_result(tctx, TORTURE_FAIL, 
119                                            "Server did not update write time (wrong!)");
120                 ret = false;
121         }
122
123
124         if (fnum1 != -1)
125                 smbcli_close(cli->tree, fnum1);
126         smbcli_unlink(cli->tree, fname);
127         smbcli_deltree(cli->tree, BASEDIR);
128
129         return ret;
130 }
131
132 static bool test_delayed_write_update1(struct torture_context *tctx, struct smbcli_state *cli)
133 {
134         union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
135         const char *fname = BASEDIR "\\torture_file1.txt";
136         NTSTATUS status;
137         int fnum1 = -1;
138         bool ret = true;
139         ssize_t written;
140         struct timeval start;
141         struct timeval end;
142         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
143         int normal_delay = 2000000;
144         double sec = ((double)used_delay) / ((double)normal_delay);
145         int msec = 1000 * sec;
146         char buf[2048];
147
148         torture_comment(tctx, "\nRunning test_delayed_write_update1\n");
149
150         if (!torture_setup_dir(cli, BASEDIR)) {
151                 return false;
152         }
153
154         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
155         if (fnum1 == -1) {
156                 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
157                 return false;
158         }
159
160         memset(buf, 'x', 2048);
161         written =  smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
162
163         /* 3 second delay to ensure we get past any 2 second time
164            granularity (older systems may have that) */
165         msleep(3 * msec);
166
167         finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
168         finfo1.all_info.in.file.fnum = fnum1;
169         finfo2 = finfo1;
170         finfo3 = finfo1;
171         pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
172         pinfo4.all_info.in.file.path = fname;
173
174         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
175
176         torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
177
178         torture_comment(tctx, "Initial write time %s\n", 
179                nt_time_string(tctx, finfo1.all_info.out.write_time));
180
181         /* Do a zero length SMBwrite call to truncate. */
182         written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
183
184         if (written != 0) {
185                 torture_result(tctx, TORTURE_FAIL, 
186                                            "write failed - wrote %d bytes (%s)\n",
187                                            (int)written, __location__);
188                 return false;
189         }
190
191         start = timeval_current();
192         end = timeval_add(&start, (120*sec), 0);
193         while (!timeval_expired(&end)) {
194                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
195
196                 if (!NT_STATUS_IS_OK(status)) {
197                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
198                         ret = false;
199                         break;
200                 }
201
202                 if (finfo2.all_info.out.size != 1024) {
203                         torture_result(tctx, TORTURE_FAIL, 
204                                                    "file not truncated, size = %u (should be 1024)",
205                                 (unsigned int)finfo2.all_info.out.size);
206                         ret = false;
207                         break;
208                 }
209
210                 torture_comment(tctx, "write time %s\n",
211                        nt_time_string(tctx, finfo2.all_info.out.write_time));
212                 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
213                         double diff = timeval_elapsed(&start);
214                         if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
215                                 torture_comment(tctx, "After SMBwrite truncate "
216                                         "server updated write_time after %.2f seconds"
217                                         "(1 sec == %.2f)(wrong!)\n",
218                                         diff, sec);
219                                 ret = false;
220                                 break;
221                         }
222
223                         torture_comment(tctx, "After SMBwrite truncate "
224                                         "server updated write_time after %.2f seconds"
225                                         "(1 sec == %.2f)(correct)\n",
226                                         diff, sec);
227                         break;
228                 }
229                 fflush(stdout);
230                 msleep(1 * msec);
231         }
232
233         if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
234                 torture_result(tctx, TORTURE_FAIL, 
235                                            "Server did not update write time (wrong!)");
236                 ret = false;
237         }
238
239         /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
240         written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
241
242         if (written != 1) {
243                 torture_result(tctx, TORTURE_FAIL, 
244                                            "write failed - wrote %d bytes (%s)",
245                                            (int)written, __location__);
246                 return false;
247         }
248
249         start = timeval_current();
250         end = timeval_add(&start, (10*sec), 0);
251         while (!timeval_expired(&end)) {
252                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
253
254                 if (!NT_STATUS_IS_OK(status)) {
255                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
256                         ret = false;
257                         break;
258                 }
259
260                 if (finfo3.all_info.out.size != 1024) {
261                         DEBUG(0, ("file not truncated, size = %u (should be 1024)\n",
262                                 (unsigned int)finfo3.all_info.out.size));
263                         ret = false;
264                         break;
265                 }
266
267                 torture_comment(tctx, "write time %s\n",
268                        nt_time_string(tctx, finfo3.all_info.out.write_time));
269                 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
270                         double diff = timeval_elapsed(&start);
271
272                         torture_comment(tctx, "server updated write_time after %.2f seconds"
273                                         "(1 sec == %.2f)(correct)\n",
274                                         diff, sec);
275                         break;
276                 }
277                 fflush(stdout);
278                 msleep(1 * msec);
279         }
280
281         if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
282                 torture_result(tctx, TORTURE_FAIL, 
283                                            "Server updated write time (wrong!)");
284                 ret = false;
285         }
286
287         /* the close should trigger an write time update */
288         smbcli_close(cli->tree, fnum1);
289         fnum1 = -1;
290
291         status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
292         torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
293
294         if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
295                 torture_result(tctx, TORTURE_FAIL,
296                                            "Server did not update write time on close (wrong!)");
297                 ret = false;
298         } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
299                 torture_comment(tctx, "Server updated write time on close (correct)\n");
300         }
301
302         if (fnum1 != -1)
303                 smbcli_close(cli->tree, fnum1);
304         smbcli_unlink(cli->tree, fname);
305         smbcli_deltree(cli->tree, BASEDIR);
306
307         return ret;
308 }
309
310 /* Updating with a SMBwrite of zero length
311  * changes the write time immediately - even on expand. */
312
313 static bool test_delayed_write_update1a(struct torture_context *tctx, struct smbcli_state *cli)
314 {
315         union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
316         const char *fname = BASEDIR "\\torture_file1a.txt";
317         NTSTATUS status;
318         int fnum1 = -1;
319         bool ret = true;
320         ssize_t written;
321         struct timeval start;
322         struct timeval end;
323         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
324         int normal_delay = 2000000;
325         double sec = ((double)used_delay) / ((double)normal_delay);
326         int msec = 1000 * sec;
327         char buf[2048];
328
329         torture_comment(tctx, "\nRunning test_delayed_write_update1a\n");
330
331         if (!torture_setup_dir(cli, BASEDIR)) {
332                 return false;
333         }
334
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;
339         }
340
341         memset(buf, 'x', 2048);
342         written =  smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
343
344         /* 3 second delay to ensure we get past any 2 second time
345            granularity (older systems may have that) */
346         msleep(3 * msec);
347
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;
354
355         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
356
357         torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
358
359         torture_comment(tctx, "Initial write time %s\n", 
360                nt_time_string(tctx, finfo1.all_info.out.write_time));
361
362         /* Do a zero length SMBwrite call to truncate. */
363         written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
364
365         if (written != 0) {
366                 torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)",
367                        (int)written, __location__);
368                 return false;
369         }
370
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);
375
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;
381                 }
382
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;
389                 }
390
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 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
396                                 torture_comment(tctx, "After SMBwrite truncate "
397                                         "server updated write_time after %.2f seconds"
398                                         "(1 sec == %.2f)(wrong!)\n",
399                                         diff, sec);
400                                 ret = false;
401                                 break;
402                         }
403
404                         torture_comment(tctx, "After SMBwrite truncate "
405                                         "server updated write_time after %.2f seconds"
406                                         "(1 sec == %.2f)(correct)\n",
407                                         diff, sec);
408                         break;
409                 }
410                 fflush(stdout);
411                 msleep(1 * msec);
412         }
413
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;
418         }
419
420         /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
421         written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
422
423         torture_assert_int_equal(tctx, written, 1, 
424                                                          "unexpected number of bytes written");
425
426         start = timeval_current();
427         end = timeval_add(&start, (10*sec), 0);
428         while (!timeval_expired(&end)) {
429                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
430
431                 if (!NT_STATUS_IS_OK(status)) {
432                         torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s\n", 
433                                                    nt_errstr(status));
434                         ret = false;
435                         break;
436                 }
437
438                 if (finfo3.all_info.out.size != 10240) {
439                         torture_result(tctx, TORTURE_FAIL, 
440                                                    "file not truncated, size = %u (should be 10240)",
441                                                    (unsigned int)finfo3.all_info.out.size);
442                         ret = false;
443                         break;
444                 }
445
446                 torture_comment(tctx, "write time %s\n",
447                        nt_time_string(tctx, finfo3.all_info.out.write_time));
448                 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
449                         double diff = timeval_elapsed(&start);
450
451                         torture_comment(tctx, "server updated write_time after %.2f seconds"
452                                         "(1 sec == %.2f)(correct)\n",
453                                         diff, sec);
454                         break;
455                 }
456                 fflush(stdout);
457                 msleep(1 * msec);
458         }
459
460         if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
461                 torture_result(tctx, TORTURE_FAIL, 
462                                            "Server updated write time (wrong!)");
463                 ret = false;
464         }
465
466         /* the close should trigger an write time update */
467         smbcli_close(cli->tree, fnum1);
468         fnum1 = -1;
469
470         status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
471         torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
472
473         if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
474                 torture_result(tctx, TORTURE_FAIL, 
475                                            "Server did not update write time on close (wrong!)");
476                 ret = false;
477         } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
478                 torture_comment(tctx, "Server updated write time on close (correct)\n");
479         }
480
481         if (fnum1 != -1)
482                 smbcli_close(cli->tree, fnum1);
483         smbcli_unlink(cli->tree, fname);
484         smbcli_deltree(cli->tree, BASEDIR);
485
486         return ret;
487 }
488
489 /* Updating with a SET_FILE_END_OF_FILE_INFO
490  * changes the write time immediately - even on expand. */
491
492 static bool test_delayed_write_update1b(struct torture_context *tctx, struct smbcli_state *cli)
493 {
494         union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
495         const char *fname = BASEDIR "\\torture_file1b.txt";
496         NTSTATUS status;
497         int fnum1 = -1;
498         bool ret = true;
499         ssize_t written;
500         struct timeval start;
501         struct timeval end;
502         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
503         int normal_delay = 2000000;
504         double sec = ((double)used_delay) / ((double)normal_delay);
505         int msec = 1000 * sec;
506         char buf[2048];
507
508         torture_comment(tctx, "\nRunning test_delayed_write_update1b\n");
509
510         if (!torture_setup_dir(cli, BASEDIR)) {
511                 return false;
512         }
513
514         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
515         if (fnum1 == -1) {
516                 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
517                 return false;
518         }
519
520         memset(buf, 'x', 2048);
521         written =  smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
522
523         /* 3 second delay to ensure we get past any 2 second time
524            granularity (older systems may have that) */
525         msleep(3 * msec);
526
527         finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
528         finfo1.all_info.in.file.fnum = fnum1;
529         finfo2 = finfo1;
530         finfo3 = finfo1;
531         pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
532         pinfo4.all_info.in.file.path = fname;
533
534         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
535
536         torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
537
538         torture_comment(tctx, "Initial write time %s\n",
539                nt_time_string(tctx, finfo1.all_info.out.write_time));
540
541         /* Do a SET_END_OF_FILE_INFO call to truncate. */
542         status = smbcli_ftruncate(cli->tree, fnum1, (uint64_t)10240);
543
544         torture_assert_ntstatus_ok(tctx, status, "SET_END_OF_FILE failed");
545
546         start = timeval_current();
547         end = timeval_add(&start, (120*sec), 0);
548         while (!timeval_expired(&end)) {
549                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
550
551                 if (!NT_STATUS_IS_OK(status)) {
552                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
553                         ret = false;
554                         break;
555                 }
556
557                 if (finfo2.all_info.out.size != 10240) {
558                         torture_result(tctx, TORTURE_FAIL,
559                                                    "file not truncated (size = %u, should be 10240)",
560                                                    (unsigned int)finfo2.all_info.out.size );
561                         ret = false;
562                         break;
563                 }
564
565                 torture_comment(tctx, "write time %s\n",
566                        nt_time_string(tctx, finfo2.all_info.out.write_time));
567                 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
568                         double diff = timeval_elapsed(&start);
569                         if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
570                                 torture_result(tctx, TORTURE_FAIL, 
571                                         "After SET_END_OF_FILE truncate "
572                                         "server updated write_time after %.2f seconds"
573                                         "(1 sec == %.2f)(wrong!)",
574                                         diff, sec);
575                                 ret = false;
576                                 break;
577                         }
578
579                         torture_comment(tctx, "After SET_END_OF_FILE truncate "
580                                         "server updated write_time after %.2f seconds"
581                                         "(1 sec == %.2f)(correct)\n",
582                                         diff, sec);
583                         break;
584                 }
585                 fflush(stdout);
586                 msleep(1 * msec);
587         }
588
589         if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
590                 torture_result(tctx, TORTURE_FAIL,
591                                            "Server did not update write time (wrong!)");
592                 ret = false;
593         }
594
595         /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
596         written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
597
598         torture_assert_int_equal(tctx, written, 1, 
599                                                          "unexpected number of bytes written");
600
601         start = timeval_current();
602         end = timeval_add(&start, (10*sec), 0);
603         while (!timeval_expired(&end)) {
604                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
605
606                 if (!NT_STATUS_IS_OK(status)) {
607                         torture_result(tctx, TORTURE_FAIL,
608                                                    "fileinfo failed: %s", nt_errstr(status));
609                         ret = false;
610                         break;
611                 }
612
613                 if (finfo3.all_info.out.size != 10240) {
614                         DEBUG(0, ("file not truncated (size = %u, should be 10240)\n",
615                                 (unsigned int)finfo3.all_info.out.size ));
616                         ret = false;
617                         break;
618                 }
619
620                 torture_comment(tctx, "write time %s\n",
621                        nt_time_string(tctx, finfo3.all_info.out.write_time));
622                 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
623                         double diff = timeval_elapsed(&start);
624
625                         torture_comment(tctx, "server updated write_time after %.2f seconds"
626                                         "(1 sec == %.2f)(correct)\n",
627                                         diff, sec);
628                         break;
629                 }
630                 fflush(stdout);
631                 msleep(1 * msec);
632         }
633
634         if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
635                 torture_result(tctx, TORTURE_FAIL, "Server updated write time (wrong!)\n");
636                 ret = false;
637         }
638
639         /* the close should trigger an write time update */
640         smbcli_close(cli->tree, fnum1);
641         fnum1 = -1;
642
643         status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
644         torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
645
646         if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
647                 torture_result(tctx, TORTURE_FAIL, "Server did not update write time on close (wrong!)\n");
648                 ret = false;
649         } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
650                 torture_comment(tctx, "Server updated write time on close (correct)\n");
651         }
652
653         if (fnum1 != -1)
654                 smbcli_close(cli->tree, fnum1);
655         smbcli_unlink(cli->tree, fname);
656         smbcli_deltree(cli->tree, BASEDIR);
657
658         return ret;
659 }
660
661 /* Updating with a SET_ALLOCATION_INFO (truncate) does so immediately. */
662
663 static bool test_delayed_write_update1c(struct torture_context *tctx, struct smbcli_state *cli)
664 {
665         union smb_setfileinfo parms;
666         union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
667         const char *fname = BASEDIR "\\torture_file1c.txt";
668         NTSTATUS status;
669         int fnum1 = -1;
670         bool ret = true;
671         ssize_t written;
672         struct timeval start;
673         struct timeval end;
674         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
675         int normal_delay = 2000000;
676         double sec = ((double)used_delay) / ((double)normal_delay);
677         int msec = 1000 * sec;
678         char buf[2048];
679
680         torture_comment(tctx, "\nRunning test_delayed_write_update1c\n");
681
682         if (!torture_setup_dir(cli, BASEDIR)) {
683                 return false;
684         }
685
686         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
687         if (fnum1 == -1) {
688                 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
689                 return false;
690         }
691
692         memset(buf, 'x', 2048);
693         written =  smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
694
695         /* 3 second delay to ensure we get past any 2 second time
696            granularity (older systems may have that) */
697         msleep(3 * msec);
698
699         finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
700         finfo1.all_info.in.file.fnum = fnum1;
701         finfo2 = finfo1;
702         finfo3 = finfo1;
703         pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
704         pinfo4.all_info.in.file.path = fname;
705
706         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
707
708         torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
709
710         torture_comment(tctx, "Initial write time %s\n",
711                nt_time_string(tctx, finfo1.all_info.out.write_time));
712
713         /* Do a SET_ALLOCATION_SIZE call to truncate. */
714         parms.allocation_info.level = RAW_SFILEINFO_ALLOCATION_INFO;
715         parms.allocation_info.in.file.fnum = fnum1;
716         parms.allocation_info.in.alloc_size = 0;
717
718         status = smb_raw_setfileinfo(cli->tree, &parms);
719
720         torture_assert_ntstatus_ok(tctx, status, 
721                                                            "RAW_SFILEINFO_ALLOCATION_INFO failed");
722
723         start = timeval_current();
724         end = timeval_add(&start, (120*sec), 0);
725         while (!timeval_expired(&end)) {
726                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
727
728                 if (!NT_STATUS_IS_OK(status)) {
729                         torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s", 
730                                                    nt_errstr(status));
731                         ret = false;
732                         break;
733                 }
734
735                 if (finfo2.all_info.out.size != 0) {
736                         torture_result(tctx, TORTURE_FAIL, 
737                                                    "file not truncated (size = %u, should be 10240)",
738                                 (unsigned int)finfo2.all_info.out.size);
739                         ret = false;
740                         break;
741                 }
742
743                 torture_comment(tctx, "write time %s\n",
744                        nt_time_string(tctx, finfo2.all_info.out.write_time));
745                 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
746                         double diff = timeval_elapsed(&start);
747                         if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
748                                 torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
749                                         "server updated write_time after %.2f seconds"
750                                         "(1 sec == %.2f)(wrong!)\n",
751                                         diff, sec);
752                                 ret = false;
753                                 break;
754                         }
755
756                         torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
757                                         "server updated write_time after %.2f seconds"
758                                         "(1 sec == %.2f)(correct)\n",
759                                         diff, sec);
760                         break;
761                 }
762                 fflush(stdout);
763                 msleep(1 * msec);
764         }
765
766         if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
767                 torture_result(tctx, TORTURE_FAIL, 
768                                            "Server did not update write time (wrong!)");
769                 ret = false;
770         }
771
772         /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
773         written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
774         torture_assert_int_equal(tctx, written, 1, 
775                                                          "Unexpected number of bytes written");
776
777         start = timeval_current();
778         end = timeval_add(&start, (10*sec), 0);
779         while (!timeval_expired(&end)) {
780                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
781
782                 if (!NT_STATUS_IS_OK(status)) {
783                         torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s", 
784                                                    nt_errstr(status));
785                         ret = false;
786                         break;
787                 }
788
789                 if (finfo3.all_info.out.size != 1) {
790                         torture_result(tctx, TORTURE_FAIL, "file not expanded");
791                         ret = false;
792                         break;
793                 }
794
795                 torture_comment(tctx, "write time %s\n",
796                        nt_time_string(tctx, finfo3.all_info.out.write_time));
797                 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
798                         double diff = timeval_elapsed(&start);
799
800                         torture_comment(tctx, "server updated write_time after %.2f seconds"
801                                         "(1 sec == %.2f)(correct)\n",
802                                         diff, sec);
803                         break;
804                 }
805                 fflush(stdout);
806                 msleep(1 * msec);
807         }
808
809         if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
810                 torture_result(tctx, TORTURE_FAIL, 
811                                            "Server updated write time (wrong!)");
812                 ret = false;
813         }
814
815         /* the close should trigger an write time update */
816         smbcli_close(cli->tree, fnum1);
817         fnum1 = -1;
818
819         status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
820         torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
821
822         if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
823                 torture_result(tctx, TORTURE_FAIL, "Server did not update write time on close (wrong!)\n");
824                 ret = false;
825         } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
826                 torture_comment(tctx, "Server updated write time on close (correct)\n");
827         }
828
829         if (fnum1 != -1)
830                 smbcli_close(cli->tree, fnum1);
831         smbcli_unlink(cli->tree, fname);
832         smbcli_deltree(cli->tree, BASEDIR);
833
834         return ret;
835 }
836
837 /*
838  * Do as above, but using 2 connections.
839  */
840
841 static bool test_delayed_write_update2(struct torture_context *tctx, struct smbcli_state *cli, 
842                                                                            struct smbcli_state *cli2)
843 {
844         union smb_fileinfo finfo1, finfo2;
845         const char *fname = BASEDIR "\\torture_file.txt";
846         NTSTATUS status;
847         int fnum1 = -1;
848         int fnum2 = -1;
849         bool ret = true;
850         ssize_t written;
851         struct timeval start;
852         struct timeval end;
853         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
854         int normal_delay = 2000000;
855         double sec = ((double)used_delay) / ((double)normal_delay);
856         int msec = 1000 * sec;
857         union smb_flush flsh;
858
859         torture_comment(tctx, "\nRunning test_delayed_write_update2\n");
860
861         if (!torture_setup_dir(cli, BASEDIR)) {
862                 return false;
863         }
864
865         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
866         if (fnum1 == -1) {
867                 torture_comment(tctx, "Failed to open %s\n", fname);
868                 return false;
869         }
870
871         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
872         finfo1.basic_info.in.file.fnum = fnum1;
873         finfo2 = finfo1;
874
875         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
876
877         torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
878         
879         torture_comment(tctx, "Initial write time %s\n", 
880                nt_time_string(tctx, finfo1.basic_info.out.write_time));
881
882         /* 3 second delay to ensure we get past any 2 second time
883            granularity (older systems may have that) */
884         msleep(3 * msec);
885
886         {
887                 /* Try using setfileinfo instead of write to update write time. */
888                 union smb_setfileinfo sfinfo;
889                 time_t t_set = time(NULL);
890                 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
891                 sfinfo.basic_info.in.file.fnum = fnum1;
892                 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
893                 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
894
895                 /* I tried this with both + and - ve to see if it makes a different.
896                    It doesn't - once the filetime is set via setfileinfo it stays that way. */
897 #if 1
898                 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
899 #else
900                 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
901 #endif
902                 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
903                 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
904
905                 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
906
907                 torture_assert_ntstatus_ok(tctx, status, "sfileinfo failed");
908         }
909
910         finfo2.basic_info.in.file.path = fname;
911         
912         status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
913
914         if (!NT_STATUS_IS_OK(status)) {
915                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
916                 return false;
917         }
918         torture_comment(tctx, "write time %s\n",
919                nt_time_string(tctx, finfo2.basic_info.out.write_time));
920
921         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
922                 torture_comment(tctx, "Server updated write_time (correct)\n");
923         } else {
924                 torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n");
925                 ret = false;
926         }
927
928         /* Now try a write to see if the write time gets reset. */
929
930         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
931         finfo1.basic_info.in.file.fnum = fnum1;
932         finfo2 = finfo1;
933
934         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
935
936         if (!NT_STATUS_IS_OK(status)) {
937                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
938                 return false;
939         }
940         
941         torture_comment(tctx, "Modified write time %s\n", 
942                nt_time_string(tctx, finfo1.basic_info.out.write_time));
943
944
945         torture_comment(tctx, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
946
947         written =  smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
948
949         if (written != 10) {
950                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
951                        (int)written, __location__);
952                 return false;
953         }
954
955         /* Just to prove to tridge that the an smbflush has no effect on
956            the write time :-). The setfileinfo IS STICKY. JRA. */
957
958         torture_comment(tctx, "Doing flush after write\n");
959
960         flsh.flush.level        = RAW_FLUSH_FLUSH;
961         flsh.flush.in.file.fnum = fnum1;
962         status = smb_raw_flush(cli->tree, &flsh);
963         if (!NT_STATUS_IS_OK(status)) {
964                 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
965                 return false;
966         }
967
968         /* Once the time was set using setfileinfo then it stays set - writes
969            don't have any effect. But make sure. */
970         start = timeval_current();
971         end = timeval_add(&start, (15*sec), 0);
972         while (!timeval_expired(&end)) {
973                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
974
975                 if (!NT_STATUS_IS_OK(status)) {
976                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
977                         ret = false;
978                         break;
979                 }
980                 torture_comment(tctx, "write time %s\n", 
981                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
982                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
983                         double diff = timeval_elapsed(&start);
984                         torture_comment(tctx, "Server updated write_time after %.2f seconds"
985                                         "(1sec == %.2f) (wrong!)\n",
986                                         diff, sec);
987                         ret = false;
988                         break;
989                 }
990                 fflush(stdout);
991                 msleep(1 * msec);
992         }
993         
994         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
995                 torture_comment(tctx, "Server did not update write time (correct)\n");
996         }
997
998         fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
999         if (fnum2 == -1) {
1000                 torture_comment(tctx, "Failed to open %s\n", fname);
1001                 return false;
1002         }
1003         
1004         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");
1005
1006         written =  smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
1007
1008         if (written != 10) {
1009                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
1010                        (int)written, __location__);
1011                 return false;
1012         }
1013
1014         status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1015
1016         if (!NT_STATUS_IS_OK(status)) {
1017                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1018                 return false;
1019         }
1020         torture_comment(tctx, "write time %s\n", 
1021                nt_time_string(tctx, finfo2.basic_info.out.write_time));
1022         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1023                 torture_comment(tctx, "Server updated write_time (wrong!)\n");
1024                 ret = false;
1025         }
1026
1027         torture_comment(tctx, "Closing the first fd to see if write time updated.\n");
1028         smbcli_close(cli->tree, fnum1);
1029         fnum1 = -1;
1030
1031         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");
1032
1033         written =  smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
1034
1035         if (written != 10) {
1036                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
1037                        (int)written, __location__);
1038                 return false;
1039         }
1040
1041         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1042         finfo1.basic_info.in.file.fnum = fnum2;
1043         finfo2 = finfo1;
1044         status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1045
1046         if (!NT_STATUS_IS_OK(status)) {
1047                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1048                 return false;
1049         }
1050         torture_comment(tctx, "write time %s\n", 
1051                nt_time_string(tctx, finfo2.basic_info.out.write_time));
1052         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1053                 torture_comment(tctx, "Server updated write_time (wrong!)\n");
1054                 ret = false;
1055         }
1056
1057         /* Once the time was set using setfileinfo then it stays set - writes
1058            don't have any effect. But make sure. */
1059         start = timeval_current();
1060         end = timeval_add(&start, (15*sec), 0);
1061         while (!timeval_expired(&end)) {
1062                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1063
1064                 if (!NT_STATUS_IS_OK(status)) {
1065                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1066                         ret = false;
1067                         break;
1068                 }
1069                 torture_comment(tctx, "write time %s\n", 
1070                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
1071                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1072                         double diff = timeval_elapsed(&start);
1073                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1074                                         "(1sec == %.2f) (wrong!)\n",
1075                                         diff, sec);
1076                         ret = false;
1077                         break;
1078                 }
1079                 fflush(stdout);
1080                 msleep(1 * msec);
1081         }
1082         
1083         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1084                 torture_comment(tctx, "Server did not update write time (correct)\n");
1085         }
1086
1087         torture_comment(tctx, "Closing second fd to see if write time updated.\n");
1088
1089         smbcli_close(cli->tree, fnum2);
1090         fnum2 = -1;
1091
1092         fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
1093         if (fnum1 == -1) {
1094                 torture_comment(tctx, "Failed to open %s\n", fname);
1095                 return false;
1096         }
1097
1098         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1099         finfo1.basic_info.in.file.fnum = fnum1;
1100         finfo2 = finfo1;
1101
1102         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1103
1104         if (!NT_STATUS_IS_OK(status)) {
1105                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1106                 return false;
1107         }
1108         
1109         torture_comment(tctx, "Second open initial write time %s\n", 
1110                nt_time_string(tctx, finfo1.basic_info.out.write_time));
1111
1112         msleep(10 * msec);
1113         torture_comment(tctx, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
1114
1115         written =  smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
1116
1117         if (written != 10) {
1118                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
1119                        (int)written, __location__);
1120                 return false;
1121         }
1122
1123         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1124         finfo1.basic_info.in.file.fnum = fnum1;
1125         finfo2 = finfo1;
1126         status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1127
1128         if (!NT_STATUS_IS_OK(status)) {
1129                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1130                 return false;
1131         }
1132         torture_comment(tctx, "write time %s\n", 
1133                nt_time_string(tctx, finfo2.basic_info.out.write_time));
1134         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1135                 torture_comment(tctx, "Server updated write_time (wrong!)\n");
1136                 ret = false;
1137         }
1138
1139         /* Now the write time should be updated again */
1140         start = timeval_current();
1141         end = timeval_add(&start, (15*sec), 0);
1142         while (!timeval_expired(&end)) {
1143                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1144
1145                 if (!NT_STATUS_IS_OK(status)) {
1146                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1147                         ret = false;
1148                         break;
1149                 }
1150                 torture_comment(tctx, "write time %s\n", 
1151                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
1152                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1153                         double diff = timeval_elapsed(&start);
1154                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
1155                                 torture_comment(tctx, "Server updated write_time after %.2f seconds"
1156                                                 "(1sec == %.2f) (wrong!)\n",
1157                                                 diff, sec);
1158                                 ret = false;
1159                                 break;
1160                         }
1161
1162                         torture_comment(tctx, "Server updated write_time after %.2f seconds"
1163                                         "(1sec == %.2f) (correct)\n",
1164                                         diff, sec);
1165                         break;
1166                 }
1167                 fflush(stdout);
1168                 msleep(1*msec);
1169         }
1170         
1171         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1172                 torture_result(tctx, TORTURE_FAIL, "Server did not update write time (wrong!)\n");
1173                 ret = false;
1174         }
1175
1176
1177         /* One more test to do. We should read the filetime via findfirst on the
1178            second connection to ensure it's the same. This is very easy for a Windows
1179            server but a bastard to get right on a POSIX server. JRA. */
1180
1181         if (fnum1 != -1)
1182                 smbcli_close(cli->tree, fnum1);
1183         smbcli_unlink(cli->tree, fname);
1184         smbcli_deltree(cli->tree, BASEDIR);
1185
1186         return ret;
1187 }
1188
1189
1190 /* Windows does obviously not update the stat info during a write call. I
1191  * *think* this is the problem causing a spurious Excel 2003 on XP error
1192  * message when saving a file. Excel does a setfileinfo, writes, and then does
1193  * a getpath(!)info. Or so... For Samba sometimes it displays an error message
1194  * that the file might have been changed in between. What i've been able to
1195  * trace down is that this happens if the getpathinfo after the write shows a
1196  * different last write time than the setfileinfo showed. This is really
1197  * nasty....
1198  */
1199
1200 static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli, 
1201                                                                    struct smbcli_state *cli2)
1202 {
1203         union smb_fileinfo finfo1, finfo2;
1204         const char *fname = BASEDIR "\\torture_file.txt";
1205         NTSTATUS status;
1206         int fnum1 = -1;
1207         int fnum2;
1208         bool ret = true;
1209         ssize_t written;
1210         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1211         int normal_delay = 2000000;
1212         double sec = ((double)used_delay) / ((double)normal_delay);
1213         int msec = 1000 * sec;
1214
1215         torture_comment(tctx, "\nRunning test_finfo_after_write\n");
1216
1217         if (!torture_setup_dir(cli, BASEDIR)) {
1218                 return false;
1219         }
1220
1221         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1222         if (fnum1 == -1) {
1223                 ret = false;
1224                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1225                 goto done;
1226         }
1227
1228         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1229         finfo1.basic_info.in.file.fnum = fnum1;
1230
1231         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1232
1233         if (!NT_STATUS_IS_OK(status)) {
1234                 ret = false;
1235                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1236                 goto done;
1237         }
1238
1239         msleep(1 * msec);
1240
1241         written =  smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1242
1243         if (written != 1) {
1244                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1245                 ret = false;
1246                 goto done;
1247         }
1248
1249         fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
1250         if (fnum2 == -1) {
1251                 torture_result(tctx, TORTURE_FAIL, __location__": failed to open 2nd time - %s", 
1252                        smbcli_errstr(cli2->tree));
1253                 ret = false;
1254                 goto done;
1255         }
1256         
1257         written =  smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
1258         
1259         if (written != 1) {
1260                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", 
1261                        (int)written);
1262                 ret = false;
1263                 goto done;
1264         }
1265         
1266         finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1267         finfo2.basic_info.in.file.path = fname;
1268         
1269         status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
1270         
1271         if (!NT_STATUS_IS_OK(status)) {
1272                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", 
1273                           nt_errstr(status));
1274                 ret = false;
1275                 goto done;
1276         }
1277         
1278         if (finfo1.basic_info.out.create_time !=
1279             finfo2.basic_info.out.create_time) {
1280                 torture_result(tctx, TORTURE_FAIL, __location__": create_time changed");
1281                 ret = false;
1282                 goto done;
1283         }
1284         
1285         if (finfo1.basic_info.out.access_time !=
1286             finfo2.basic_info.out.access_time) {
1287                 torture_result(tctx, TORTURE_FAIL, __location__": access_time changed");
1288                 ret = false;
1289                 goto done;
1290         }
1291         
1292         if (finfo1.basic_info.out.write_time !=
1293             finfo2.basic_info.out.write_time) {
1294                 torture_result(tctx, TORTURE_FAIL, __location__": write_time changed:\n"
1295                                            "write time conn 1 = %s, conn 2 = %s", 
1296                        nt_time_string(tctx, finfo1.basic_info.out.write_time),
1297                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
1298                 ret = false;
1299                 goto done;
1300         }
1301         
1302         if (finfo1.basic_info.out.change_time !=
1303             finfo2.basic_info.out.change_time) {
1304                 torture_result(tctx, TORTURE_FAIL, __location__": change_time changed");
1305                 ret = false;
1306                 goto done;
1307         }
1308         
1309         /* One of the two following calls updates the qpathinfo. */
1310         
1311         /* If you had skipped the smbcli_write on fnum2, it would
1312          * *not* have updated the stat on disk */
1313         
1314         smbcli_close(cli2->tree, fnum2);
1315         cli2 = NULL;
1316
1317         /* This call is only for the people looking at ethereal :-) */
1318         finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1319         finfo2.basic_info.in.file.path = fname;
1320
1321         status = smb_raw_pathinfo(cli->tree, tctx, &finfo2);
1322
1323         if (!NT_STATUS_IS_OK(status)) {
1324                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1325                 ret = false;
1326                 goto done;
1327         }
1328
1329  done:
1330         if (fnum1 != -1)
1331                 smbcli_close(cli->tree, fnum1);
1332         smbcli_unlink(cli->tree, fname);
1333         smbcli_deltree(cli->tree, BASEDIR);
1334
1335         return ret;
1336 }
1337
1338 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
1339         uint64_t r = 10*1000*1000; \
1340         NTTIME g = (given).basic_info.out.write_time; \
1341         NTTIME gr = (g / r) * r; \
1342         NTTIME c = (correct).basic_info.out.write_time; \
1343         NTTIME cr = (c / r) * r; \
1344         bool strict = torture_setting_bool(tctx, "strict mode", false); \
1345         bool err = false; \
1346         if (strict && (g cmp c)) { \
1347                 err = true; \
1348         } else if ((g cmp c) && (gr cmp cr)) { \
1349                 /* handle filesystem without high resolution timestamps */ \
1350                 err = true; \
1351         } \
1352         if (err) { \
1353                 torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \
1354                                 #given, nt_time_string(tctx, g), (unsigned long long)g, \
1355                                 #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \
1356                 ret = false; \
1357                 goto done; \
1358         } \
1359 } while (0)
1360 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
1361         COMPARE_WRITE_TIME_CMP(given,correct,!=)
1362 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
1363         COMPARE_WRITE_TIME_CMP(given,correct,<=)
1364 #define COMPARE_WRITE_TIME_LESS(given,correct) \
1365         COMPARE_WRITE_TIME_CMP(given,correct,>=)
1366
1367 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
1368         NTTIME g = (given).basic_info.out.access_time; \
1369         NTTIME c = (correct).basic_info.out.access_time; \
1370         if (g cmp c) { \
1371                 torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \
1372                                 #given, nt_time_string(tctx, g), \
1373                                 #cmp, #correct, nt_time_string(tctx, c)); \
1374                 ret = false; \
1375                 goto done; \
1376         } \
1377 } while (0)
1378 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
1379         COMPARE_ACCESS_TIME_CMP(given,correct,!=)
1380
1381 #define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \
1382         COMPARE_ACCESS_TIME_EQUAL(given,correct); \
1383         COMPARE_WRITE_TIME_EQUAL(given,correct); \
1384 } while (0)
1385
1386 #define GET_INFO_FILE(finfo) do { \
1387         NTSTATUS _status; \
1388         _status = smb_raw_fileinfo(cli->tree, tctx, &finfo); \
1389         if (!NT_STATUS_IS_OK(_status)) { \
1390                 ret = false; \
1391                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1392                                nt_errstr(_status)); \
1393                 goto done; \
1394         } \
1395         torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1396                         nt_time_string(tctx, finfo.basic_info.out.access_time), \
1397                         nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1398 } while (0)
1399 #define GET_INFO_PATH(pinfo) do { \
1400         NTSTATUS _status; \
1401         _status = smb_raw_pathinfo(cli2->tree, tctx, &pinfo); \
1402         if (!NT_STATUS_IS_OK(_status)) { \
1403                 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \
1404                                nt_errstr(_status)); \
1405                 ret = false; \
1406                 goto done; \
1407         } \
1408         torture_comment(tctx, "pathinfo: Access(%s) Write(%s)\n", \
1409                         nt_time_string(tctx, pinfo.basic_info.out.access_time), \
1410                         nt_time_string(tctx, pinfo.basic_info.out.write_time)); \
1411 } while (0)
1412 #define GET_INFO_BOTH(finfo,pinfo) do { \
1413         GET_INFO_FILE(finfo); \
1414         GET_INFO_PATH(pinfo); \
1415         COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \
1416 } while (0)
1417
1418 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
1419         NTSTATUS _status; \
1420         union smb_setfileinfo sfinfo; \
1421         sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1422         sfinfo.basic_info.in.file.fnum = tfnum; \
1423         sfinfo.basic_info.in.create_time = 0; \
1424         sfinfo.basic_info.in.access_time = 0; \
1425         unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1426         sfinfo.basic_info.in.change_time = 0; \
1427         sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1428         _status = smb_raw_setfileinfo(tree, &sfinfo); \
1429         if (!NT_STATUS_IS_OK(_status)) { \
1430                 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1431                                nt_errstr(_status)); \
1432                 ret = false; \
1433                 goto done; \
1434         } \
1435 } while (0)
1436 #define SET_INFO_FILE(finfo, wrtime) \
1437         SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
1438
1439 static bool test_delayed_write_update3(struct torture_context *tctx,
1440                                        struct smbcli_state *cli,
1441                                        struct smbcli_state *cli2)
1442 {
1443         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1444         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1445         const char *fname = BASEDIR "\\torture_file3.txt";
1446         int fnum1 = -1;
1447         bool ret = true;
1448         ssize_t written;
1449         struct timeval start;
1450         struct timeval end;
1451         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1452         int normal_delay = 2000000;
1453         double sec = ((double)used_delay) / ((double)normal_delay);
1454         int msec = 1000 * sec;
1455
1456         torture_comment(tctx, "\nRunning test_delayed_write_update3\n");
1457
1458         if (!torture_setup_dir(cli, BASEDIR)) {
1459                 return false;
1460         }
1461
1462         torture_comment(tctx, "Open the file handle\n");
1463         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1464         if (fnum1 == -1) {
1465                 ret = false;
1466                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1467                 goto done;
1468         }
1469
1470         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1471         finfo0.basic_info.in.file.fnum = fnum1;
1472         finfo1 = finfo0;
1473         finfo2 = finfo0;
1474         finfo3 = finfo0;
1475         finfo4 = finfo0;
1476         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1477         pinfo0.basic_info.in.file.path = fname;
1478         pinfo1 = pinfo0;
1479         pinfo2 = pinfo0;
1480         pinfo3 = pinfo0;
1481         pinfo4 = pinfo0;
1482         pinfo5 = pinfo0;
1483
1484         /* get the initial times */
1485         GET_INFO_BOTH(finfo0,pinfo0);
1486
1487         /*
1488          * make sure the write time is updated 2 seconds later
1489          * calcuated from the first write
1490          * (but expect upto 5 seconds extra time for a busy server)
1491          */
1492         start = timeval_current();
1493         end = timeval_add(&start, 7 * sec, 0);
1494         while (!timeval_expired(&end)) {
1495                 /* do a write */
1496                 torture_comment(tctx, "Do a write on the file handle\n");
1497                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1498                 if (written != 1) {
1499                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1500                         ret = false;
1501                         goto done;
1502                 }
1503                 /* get the times after the write */
1504                 GET_INFO_FILE(finfo1);
1505
1506                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1507                         double diff = timeval_elapsed(&start);
1508                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
1509                                 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1510                                                 "(1sec == %.2f) (wrong!)\n",
1511                                                 diff, sec);
1512                                 ret = false;
1513                                 break;
1514                         }
1515
1516                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1517                                         "(1sec == %.2f) (correct)\n",
1518                                         diff, sec);
1519                         break;
1520                 }
1521                 msleep(0.5 * msec);
1522         }
1523
1524         GET_INFO_BOTH(finfo1,pinfo1);
1525         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1526
1527         /* sure any further write doesn't update the write time */
1528         start = timeval_current();
1529         end = timeval_add(&start, 15 * sec, 0);
1530         while (!timeval_expired(&end)) {
1531                 /* do a write */
1532                 torture_comment(tctx, "Do a write on the file handle\n");
1533                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1534                 if (written != 1) {
1535                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1536                         ret = false;
1537                         goto done;
1538                 }
1539                 /* get the times after the write */
1540                 GET_INFO_BOTH(finfo2,pinfo2);
1541
1542                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1543                         double diff = timeval_elapsed(&start);
1544                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1545                                         "(1sec == %.2f) (wrong!)\n",
1546                                         diff, sec);
1547                         ret = false;
1548                         break;
1549                 }
1550                 msleep(2 * msec);
1551         }
1552
1553         GET_INFO_BOTH(finfo2,pinfo2);
1554         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1555         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1556                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1557         }
1558
1559         /* sleep */
1560         msleep(5 * msec);
1561
1562         GET_INFO_BOTH(finfo3,pinfo3);
1563         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1564
1565         /*
1566          * the close updates the write time to the time of the close
1567          * and not to the time of the last write!
1568          */
1569         torture_comment(tctx, "Close the file handle\n");
1570         smbcli_close(cli->tree, fnum1);
1571         fnum1 = -1;
1572
1573         GET_INFO_PATH(pinfo4);
1574         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1575
1576         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1577                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1578         }
1579
1580  done:
1581         if (fnum1 != -1)
1582                 smbcli_close(cli->tree, fnum1);
1583         smbcli_unlink(cli->tree, fname);
1584         smbcli_deltree(cli->tree, BASEDIR);
1585
1586         return ret;
1587 }
1588
1589 /*
1590  * Show that a truncate write always updates the write time even
1591  * if an initial write has already updated the write time.
1592  */
1593
1594 static bool test_delayed_write_update3a(struct torture_context *tctx,
1595                                         struct smbcli_state *cli,
1596                                         struct smbcli_state *cli2)
1597 {
1598         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1599         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1600         const char *fname = BASEDIR "\\torture_file3a.txt";
1601         int fnum1 = -1;
1602         bool ret = true;
1603         ssize_t written;
1604         int i;
1605         struct timeval start;
1606         struct timeval end;
1607         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1608         int normal_delay = 2000000;
1609         double sec = ((double)used_delay) / ((double)normal_delay);
1610         int msec = 1000 * sec;
1611
1612         torture_comment(tctx, "\nRunning test_delayed_write_update3a\n");
1613
1614         if (!torture_setup_dir(cli, BASEDIR)) {
1615                 return false;
1616         }
1617
1618         torture_comment(tctx, "Open the file handle\n");
1619         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1620         if (fnum1 == -1) {
1621                 ret = false;
1622                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1623                 goto done;
1624         }
1625
1626         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1627         finfo0.basic_info.in.file.fnum = fnum1;
1628         finfo1 = finfo0;
1629         finfo2 = finfo0;
1630         finfo3 = finfo0;
1631         finfo4 = finfo0;
1632         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1633         pinfo0.basic_info.in.file.path = fname;
1634         pinfo1 = pinfo0;
1635         pinfo2 = pinfo0;
1636         pinfo3 = pinfo0;
1637         pinfo4 = pinfo0;
1638         pinfo5 = pinfo0;
1639
1640         /* get the initial times */
1641         GET_INFO_BOTH(finfo0,pinfo0);
1642
1643         /*
1644          * sleep some time, to demonstrate the handling of write times
1645          * doesn't depend on the time since the open
1646          */
1647         msleep(5 * msec);
1648
1649         /* get the initial times */
1650         GET_INFO_BOTH(finfo1,pinfo1);
1651         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1652
1653         /*
1654          * make sure the write time is updated 2 seconds later
1655          * calcuated from the first write
1656          * (but expect upto 5 seconds extra time for a busy server)
1657          */
1658         start = timeval_current();
1659         end = timeval_add(&start, 7 * sec, 0);
1660         while (!timeval_expired(&end)) {
1661                 /* do a write */
1662                 torture_comment(tctx, "Do a write on the file handle\n");
1663                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1664                 if (written != 1) {
1665                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1666                         ret = false;
1667                         goto done;
1668                 }
1669                 /* get the times after the write */
1670                 GET_INFO_FILE(finfo1);
1671
1672                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1673                         double diff = timeval_elapsed(&start);
1674                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
1675                                 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1676                                                 "(1sec == %.2f) (wrong!)\n",
1677                                                 diff, sec);
1678                                 ret = false;
1679                                 break;
1680                         }
1681
1682                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1683                                         "(1sec == %.2f) (correct)\n",
1684                                         diff, sec);
1685                         break;
1686                 }
1687                 msleep(0.5 * msec);
1688         }
1689
1690         GET_INFO_BOTH(finfo1,pinfo1);
1691         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1692
1693         /*
1694          * demonstrate that a truncate write always
1695          * updates the write time immediately
1696          */
1697         for (i=0; i < 3; i++) {
1698                 msleep(1 * msec);
1699                 /* do a write */
1700                 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1701                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
1702                 if (written != 0) {
1703                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1704                         ret = false;
1705                         goto done;
1706                 }
1707                 /* get the times after the write */
1708                 GET_INFO_BOTH(finfo2,pinfo2);
1709                 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1710                 finfo1 = finfo2;
1711         }
1712
1713         /* sure any further write doesn't update the write time */
1714         start = timeval_current();
1715         end = timeval_add(&start, 15 * sec, 0);
1716         while (!timeval_expired(&end)) {
1717                 /* do a write */
1718                 torture_comment(tctx, "Do a write on the file handle\n");
1719                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1720                 if (written != 1) {
1721                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1722                         ret = false;
1723                         goto done;
1724                 }
1725                 /* get the times after the write */
1726                 GET_INFO_BOTH(finfo2,pinfo2);
1727
1728                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1729                         double diff = timeval_elapsed(&start);
1730                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1731                                         "(1sec == %.2f) (wrong!)\n",
1732                                         diff, sec);
1733                         ret = false;
1734                         break;
1735                 }
1736                 msleep(2 * msec);
1737         }
1738
1739         GET_INFO_BOTH(finfo2,pinfo2);
1740         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1741         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1742                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1743         }
1744
1745         /* sleep */
1746         msleep(5 * msec);
1747
1748         /* get the initial times */
1749         GET_INFO_BOTH(finfo1,pinfo1);
1750         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
1751
1752         /*
1753          * demonstrate that a truncate write always
1754          * updates the write time immediately
1755          */
1756         for (i=0; i < 3; i++) {
1757                 msleep(1 * msec);
1758                 /* do a write */
1759                 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1760                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
1761                 if (written != 0) {
1762                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1763                         ret = false;
1764                         goto done;
1765                 }
1766                 /* get the times after the write */
1767                 GET_INFO_BOTH(finfo2,pinfo2);
1768                 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1769                 finfo1 = finfo2;
1770         }
1771
1772         /* sleep */
1773         msleep(5 * msec);
1774
1775         GET_INFO_BOTH(finfo3,pinfo3);
1776         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1777
1778         /*
1779          * the close doesn't update the write time
1780          */
1781         torture_comment(tctx, "Close the file handle\n");
1782         smbcli_close(cli->tree, fnum1);
1783         fnum1 = -1;
1784
1785         GET_INFO_PATH(pinfo4);
1786         COMPARE_WRITE_TIME_EQUAL(pinfo4, pinfo3);
1787
1788         if (pinfo4.basic_info.out.write_time == pinfo3.basic_info.out.write_time) {
1789                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1790         }
1791
1792  done:
1793         if (fnum1 != -1)
1794                 smbcli_close(cli->tree, fnum1);
1795         smbcli_unlink(cli->tree, fname);
1796         smbcli_deltree(cli->tree, BASEDIR);
1797
1798         return ret;
1799 }
1800
1801 /*
1802  * Show a close after write updates the write timestamp to
1803  * the close time, not the last write time.
1804  */
1805
1806 static bool test_delayed_write_update3b(struct torture_context *tctx,
1807                                         struct smbcli_state *cli,
1808                                         struct smbcli_state *cli2)
1809 {
1810         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1811         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1812         const char *fname = BASEDIR "\\torture_file3b.txt";
1813         int fnum1 = -1;
1814         bool ret = true;
1815         ssize_t written;
1816         struct timeval start;
1817         struct timeval end;
1818         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1819         int normal_delay = 2000000;
1820         double sec = ((double)used_delay) / ((double)normal_delay);
1821         int msec = 1000 * sec;
1822
1823         torture_comment(tctx, "\nRunning test_delayed_write_update3b\n");
1824
1825         if (!torture_setup_dir(cli, BASEDIR)) {
1826                 return false;
1827         }
1828
1829         torture_comment(tctx, "Open the file handle\n");
1830         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1831         if (fnum1 == -1) {
1832                 ret = false;
1833                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1834                 goto done;
1835         }
1836
1837         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1838         finfo0.basic_info.in.file.fnum = fnum1;
1839         finfo1 = finfo0;
1840         finfo2 = finfo0;
1841         finfo3 = finfo0;
1842         finfo4 = finfo0;
1843         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1844         pinfo0.basic_info.in.file.path = fname;
1845         pinfo1 = pinfo0;
1846         pinfo2 = pinfo0;
1847         pinfo3 = pinfo0;
1848         pinfo4 = pinfo0;
1849         pinfo5 = pinfo0;
1850
1851         /* get the initial times */
1852         GET_INFO_BOTH(finfo0,pinfo0);
1853
1854         /*
1855          * sleep some time, to demonstrate the handling of write times
1856          * doesn't depend on the time since the open
1857          */
1858         msleep(5 * msec);
1859
1860         /* get the initial times */
1861         GET_INFO_BOTH(finfo1,pinfo1);
1862         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1863
1864         /*
1865          * make sure the write time is updated 2 seconds later
1866          * calcuated from the first write
1867          * (but expect upto 5 seconds extra time for a busy server)
1868          */
1869         start = timeval_current();
1870         end = timeval_add(&start, 7 * sec, 0);
1871         while (!timeval_expired(&end)) {
1872                 /* do a write */
1873                 torture_comment(tctx, "Do a write on the file handle\n");
1874                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1875                 if (written != 1) {
1876                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1877                         ret = false;
1878                         goto done;
1879                 }
1880                 /* get the times after the write */
1881                 GET_INFO_FILE(finfo1);
1882
1883                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1884                         double diff = timeval_elapsed(&start);
1885                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
1886                                 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1887                                                 "(1sec == %.2f) (wrong!)\n",
1888                                                 diff, sec);
1889                                 ret = false;
1890                                 break;
1891                         }
1892
1893                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1894                                         "(1sec == %.2f) (correct)\n",
1895                                         diff, sec);
1896                         break;
1897                 }
1898                 msleep(0.5 * msec);
1899         }
1900
1901         GET_INFO_BOTH(finfo1,pinfo1);
1902         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1903
1904         /* sure any further write doesn't update the write time */
1905         start = timeval_current();
1906         end = timeval_add(&start, 15 * sec, 0);
1907         while (!timeval_expired(&end)) {
1908                 /* do a write */
1909                 torture_comment(tctx, "Do a write on the file handle\n");
1910                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1911                 if (written != 1) {
1912                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1913                         ret = false;
1914                         goto done;
1915                 }
1916                 /* get the times after the write */
1917                 GET_INFO_BOTH(finfo2,pinfo2);
1918
1919                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1920                         double diff = timeval_elapsed(&start);
1921                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1922                                         "(1sec == %.2f) (wrong!)\n",
1923                                         diff, sec);
1924                         ret = false;
1925                         break;
1926                 }
1927                 msleep(2 * msec);
1928         }
1929
1930         GET_INFO_BOTH(finfo2,pinfo2);
1931         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1932         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1933                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1934         }
1935
1936         /* sleep */
1937         msleep(5 * msec);
1938
1939         GET_INFO_BOTH(finfo3,pinfo3);
1940         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1941
1942         /*
1943          * the close updates the write time to the time of the close
1944          * and not to the time of the last write!
1945          */
1946         torture_comment(tctx, "Close the file handle\n");
1947         smbcli_close(cli->tree, fnum1);
1948         fnum1 = -1;
1949
1950         GET_INFO_PATH(pinfo4);
1951         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1952
1953         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1954                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1955         }
1956
1957  done:
1958         if (fnum1 != -1)
1959                 smbcli_close(cli->tree, fnum1);
1960         smbcli_unlink(cli->tree, fname);
1961         smbcli_deltree(cli->tree, BASEDIR);
1962
1963         return ret;
1964 }
1965
1966 /*
1967  * Check that a write after a truncate write doesn't update
1968  * the timestamp, but a truncate write after a write does.
1969  * Also prove that a close after a truncate write updates the
1970  * timestamp to current, not the time of last write.
1971  */
1972
1973 static bool test_delayed_write_update3c(struct torture_context *tctx,
1974                                         struct smbcli_state *cli,
1975                                         struct smbcli_state *cli2)
1976 {
1977         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1978         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1979         const char *fname = BASEDIR "\\torture_file3c.txt";
1980         int fnum1 = -1;
1981         bool ret = true;
1982         ssize_t written;
1983         int i;
1984         struct timeval start;
1985         struct timeval end;
1986         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1987         int normal_delay = 2000000;
1988         double sec = ((double)used_delay) / ((double)normal_delay);
1989         int msec = 1000 * sec;
1990
1991         torture_comment(tctx, "\nRunning test_delayed_write_update3c\n");
1992
1993         if (!torture_setup_dir(cli, BASEDIR)) {
1994                 return false;
1995         }
1996
1997         torture_comment(tctx, "Open the file handle\n");
1998         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1999         if (fnum1 == -1) {
2000                 ret = false;
2001                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2002                 goto done;
2003         }
2004
2005         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2006         finfo0.basic_info.in.file.fnum = fnum1;
2007         finfo1 = finfo0;
2008         finfo2 = finfo0;
2009         finfo3 = finfo0;
2010         finfo4 = finfo0;
2011         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2012         pinfo0.basic_info.in.file.path = fname;
2013         pinfo1 = pinfo0;
2014         pinfo2 = pinfo0;
2015         pinfo3 = pinfo0;
2016         pinfo4 = pinfo0;
2017         pinfo5 = pinfo0;
2018
2019         /* get the initial times */
2020         GET_INFO_BOTH(finfo0,pinfo0);
2021
2022         /*
2023          * sleep some time, to demonstrate the handling of write times
2024          * doesn't depend on the time since the open
2025          */
2026         msleep(5 * msec);
2027
2028         /* get the initial times */
2029         GET_INFO_BOTH(finfo1,pinfo1);
2030         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2031
2032         /*
2033          * demonstrate that a truncate write always
2034          * updates the write time immediately
2035          */
2036         for (i=0; i < 3; i++) {
2037                 msleep(1 * msec);
2038                 /* do a write */
2039                 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
2040                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2041                 if (written != 0) {
2042                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2043                         ret = false;
2044                         goto done;
2045                 }
2046                 /* get the times after the write */
2047                 GET_INFO_BOTH(finfo2,pinfo2);
2048                 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2049                 finfo1 = finfo2;
2050         }
2051
2052         start = timeval_current();
2053         end = timeval_add(&start, 7 * sec, 0);
2054         while (!timeval_expired(&end)) {
2055                 /* do a write */
2056                 torture_comment(tctx, "Do a write on the file handle\n");
2057                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2058                 if (written != 1) {
2059                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2060                         ret = false;
2061                         goto done;
2062                 }
2063                 /* get the times after the write */
2064                 GET_INFO_FILE(finfo2);
2065
2066                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2067                         double diff = timeval_elapsed(&start);
2068                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2069                                         "(1sec == %.2f) (wrong!)\n",
2070                                         diff, sec);
2071                         ret = false;
2072                         break;
2073                 }
2074                 msleep(2 * msec);
2075         }
2076
2077         GET_INFO_BOTH(finfo2,pinfo2);
2078         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2079         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2080                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2081         }
2082
2083         /* sleep */
2084         msleep(5 * msec);
2085
2086         /* get the initial times */
2087         GET_INFO_BOTH(finfo1,pinfo1);
2088         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
2089
2090         /*
2091          * demonstrate that a truncate write always
2092          * updates the write time immediately
2093          */
2094         for (i=0; i < 3; i++) {
2095                 msleep(1 * msec);
2096                 /* do a write */
2097                 torture_comment(tctx, "Do a truncate write [%d] on the file handle\n", i);
2098                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2099                 if (written != 0) {
2100                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2101                         ret = false;
2102                         goto done;
2103                 }
2104                 /* get the times after the write */
2105                 GET_INFO_BOTH(finfo2,pinfo2);
2106                 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2107                 finfo1 = finfo2;
2108         }
2109
2110         /* sleep */
2111         msleep(5 * msec);
2112
2113         GET_INFO_BOTH(finfo2,pinfo2);
2114         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2115
2116         /* sure any further write doesn't update the write time */
2117         start = timeval_current();
2118         end = timeval_add(&start, 15 * sec, 0);
2119         while (!timeval_expired(&end)) {
2120                 /* do a write */
2121                 torture_comment(tctx, "Do a write on the file handle\n");
2122                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2123                 if (written != 1) {
2124                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2125                         ret = false;
2126                         goto done;
2127                 }
2128                 /* get the times after the write */
2129                 GET_INFO_BOTH(finfo2,pinfo2);
2130
2131                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2132                         double diff = timeval_elapsed(&start);
2133                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2134                                         "(1sec == %.2f) (wrong!)\n",
2135                                         diff, sec);
2136                         ret = false;
2137                         break;
2138                 }
2139                 msleep(2 * msec);
2140         }
2141
2142         GET_INFO_BOTH(finfo2,pinfo2);
2143         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2144         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2145                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2146         }
2147
2148         /* sleep */
2149         msleep(5 * msec);
2150
2151         GET_INFO_BOTH(finfo3,pinfo3);
2152         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2153
2154         /*
2155          * the close updates the write time to the time of the close
2156          * and not to the time of the last write!
2157          */
2158         torture_comment(tctx, "Close the file handle\n");
2159         smbcli_close(cli->tree, fnum1);
2160         fnum1 = -1;
2161
2162         GET_INFO_PATH(pinfo4);
2163         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2164
2165         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2166                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2167         }
2168
2169  done:
2170         if (fnum1 != -1)
2171                 smbcli_close(cli->tree, fnum1);
2172         smbcli_unlink(cli->tree, fname);
2173         smbcli_deltree(cli->tree, BASEDIR);
2174
2175         return ret;
2176 }
2177
2178 /*
2179  * Show only the first write updates the timestamp, and a close
2180  * after writes updates to current (I think this is the same
2181  * as test 3b. JRA).
2182  */
2183
2184 static bool test_delayed_write_update4(struct torture_context *tctx,
2185                                        struct smbcli_state *cli,
2186                                        struct smbcli_state *cli2)
2187 {
2188         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
2189         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
2190         const char *fname = BASEDIR "\\torture_file4.txt";
2191         int fnum1 = -1;
2192         bool ret = true;
2193         ssize_t written;
2194         struct timeval start;
2195         struct timeval end;
2196         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2197         int normal_delay = 2000000;
2198         double sec = ((double)used_delay) / ((double)normal_delay);
2199         int msec = 1000 * sec;
2200
2201         torture_comment(tctx, "\nRunning test_delayed_write_update4\n");
2202
2203         if (!torture_setup_dir(cli, BASEDIR)) {
2204                 return false;
2205         }
2206
2207         torture_comment(tctx, "Open the file handle\n");
2208         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2209         if (fnum1 == -1) {
2210                 ret = false;
2211                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2212                 goto done;
2213         }
2214
2215         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2216         finfo0.basic_info.in.file.fnum = fnum1;
2217         finfo1 = finfo0;
2218         finfo2 = finfo0;
2219         finfo3 = finfo0;
2220         finfo4 = finfo0;
2221         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2222         pinfo0.basic_info.in.file.path = fname;
2223         pinfo1 = pinfo0;
2224         pinfo2 = pinfo0;
2225         pinfo3 = pinfo0;
2226         pinfo4 = pinfo0;
2227         pinfo5 = pinfo0;
2228
2229         /* get the initial times */
2230         GET_INFO_BOTH(finfo0,pinfo0);
2231
2232         /* sleep a bit */
2233         msleep(5 * msec);
2234
2235         /* do a write */
2236         torture_comment(tctx, "Do a write on the file handle\n");
2237         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2238         if (written != 1) {
2239                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2240                 ret = false;
2241                 goto done;
2242         }
2243
2244         GET_INFO_BOTH(finfo1,pinfo1);
2245         COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
2246
2247         /*
2248          * make sure the write time is updated 2 seconds later
2249          * calcuated from the first write
2250          * (but expect upto 3 seconds extra time for a busy server)
2251          */
2252         start = timeval_current();
2253         end = timeval_add(&start, 5 * sec, 0);
2254         while (!timeval_expired(&end)) {
2255                 /* get the times after the first write */
2256                 GET_INFO_FILE(finfo1);
2257
2258                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
2259                         double diff = timeval_elapsed(&start);
2260                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
2261                                 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2262                                                 "(1sec == %.2f) (wrong!)\n",
2263                                                 diff, sec);
2264                                 ret = false;
2265                                 break;
2266                         }
2267
2268                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2269                                         "(1sec == %.2f) (correct)\n",
2270                                         diff, sec);
2271                         break;
2272                 }
2273                 msleep(0.5 * msec);
2274         }
2275
2276         GET_INFO_BOTH(finfo1,pinfo1);
2277         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
2278
2279         /* sure any further write doesn't update the write time */
2280         start = timeval_current();
2281         end = timeval_add(&start, 15 * sec, 0);
2282         while (!timeval_expired(&end)) {
2283                 /* do a write */
2284                 torture_comment(tctx, "Do a write on the file handle\n");
2285                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2286                 if (written != 1) {
2287                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2288                         ret = false;
2289                         goto done;
2290                 }
2291                 /* get the times after the write */
2292                 GET_INFO_BOTH(finfo2,pinfo2);
2293
2294                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2295                         double diff = timeval_elapsed(&start);
2296                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2297                                         "(1sec == %.2f) (wrong!)\n",
2298                                         diff, sec);
2299                         ret = false;
2300                         break;
2301                 }
2302                 msleep(2 * msec);
2303         }
2304
2305         GET_INFO_BOTH(finfo2,pinfo2);
2306         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2307         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2308                 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
2309         }
2310
2311         /* sleep */
2312         msleep(5 * msec);
2313
2314         GET_INFO_BOTH(finfo3,pinfo3);
2315         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2316
2317         /*
2318          * the close updates the write time to the time of the close
2319          * and not to the time of the last write!
2320          */
2321         torture_comment(tctx, "Close the file handle\n");
2322         smbcli_close(cli->tree, fnum1);
2323         fnum1 = -1;
2324
2325         GET_INFO_PATH(pinfo4);
2326         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2327
2328         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2329                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2330         }
2331
2332  done:
2333         if (fnum1 != -1)
2334                 smbcli_close(cli->tree, fnum1);
2335         smbcli_unlink(cli->tree, fname);
2336         smbcli_deltree(cli->tree, BASEDIR);
2337
2338         return ret;
2339 }
2340
2341 /*
2342  * Show writes and closes have no effect on updating times once a SETWRITETIME is done.
2343  */
2344
2345 static bool test_delayed_write_update5(struct torture_context *tctx,
2346                                        struct smbcli_state *cli,
2347                                        struct smbcli_state *cli2)
2348 {
2349         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2350         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2351         const char *fname = BASEDIR "\\torture_file5.txt";
2352         int fnum1 = -1;
2353         bool ret = true;
2354         ssize_t written;
2355         struct timeval start;
2356         struct timeval end;
2357         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2358         int normal_delay = 2000000;
2359         double sec = ((double)used_delay) / ((double)normal_delay);
2360         int msec = 1000 * sec;
2361
2362         torture_comment(tctx, "\nRunning test_delayed_write_update5\n");
2363
2364         if (!torture_setup_dir(cli, BASEDIR)) {
2365                 return false;
2366         }
2367
2368         torture_comment(tctx, "Open the file handle\n");
2369         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2370         if (fnum1 == -1) {
2371                 ret = false;
2372                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2373                 goto done;
2374         }
2375
2376         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2377         finfo0.basic_info.in.file.fnum = fnum1;
2378         finfo1 = finfo0;
2379         finfo2 = finfo0;
2380         finfo3 = finfo0;
2381         finfo4 = finfo0;
2382         finfo5 = finfo0;
2383         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2384         pinfo0.basic_info.in.file.path = fname;
2385         pinfo1 = pinfo0;
2386         pinfo2 = pinfo0;
2387         pinfo3 = pinfo0;
2388         pinfo4 = pinfo0;
2389         pinfo5 = pinfo0;
2390         pinfo6 = pinfo0;
2391
2392         /* get the initial times */
2393         GET_INFO_BOTH(finfo0,pinfo0);
2394
2395         /* do a write */
2396         torture_comment(tctx, "Do a write on the file handle\n");
2397         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2398         if (written != 1) {
2399                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2400                 ret = false;
2401                 goto done;
2402         }
2403
2404         GET_INFO_BOTH(finfo1,pinfo1);
2405         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2406
2407         torture_comment(tctx, "Set write time in the future on the file handle\n");
2408         SET_INFO_FILE(finfo0, time(NULL) + 86400);
2409         GET_INFO_BOTH(finfo2,pinfo2);
2410         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2411
2412         torture_comment(tctx, "Set write time in the past on the file handle\n");
2413         SET_INFO_FILE(finfo0, time(NULL) - 86400);
2414         GET_INFO_BOTH(finfo2,pinfo2);
2415         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2416
2417         /* make sure the 2 second delay from the first write are canceled */
2418         start = timeval_current();
2419         end = timeval_add(&start, 15 * sec, 0);
2420         while (!timeval_expired(&end)) {
2421
2422                 /* get the times after the first write */
2423                 GET_INFO_BOTH(finfo3,pinfo3);
2424
2425                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2426                         double diff = timeval_elapsed(&start);
2427                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2428                                         "(1sec == %.2f) (wrong!)\n",
2429                                         diff, sec);
2430                         ret = false;
2431                         break;
2432                 }
2433                 msleep(2 * msec);
2434         }
2435
2436         GET_INFO_BOTH(finfo3,pinfo3);
2437         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2438         if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2439                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2440         }
2441
2442         /* sure any further write doesn't update the write time */
2443         start = timeval_current();
2444         end = timeval_add(&start, 15 * sec, 0);
2445         while (!timeval_expired(&end)) {
2446                 /* do a write */
2447                 torture_comment(tctx, "Do a write on the file handle\n");
2448                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2449                 if (written != 1) {
2450                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2451                         ret = false;
2452                         goto done;
2453                 }
2454                 /* get the times after the write */
2455                 GET_INFO_BOTH(finfo4,pinfo4);
2456
2457                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2458                         double diff = timeval_elapsed(&start);
2459                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2460                                         "(1sec == %.2f) (wrong!)\n",
2461                                         diff, sec);
2462                         ret = false;
2463                         break;
2464                 }
2465                 msleep(2 * msec);
2466         }
2467
2468         GET_INFO_BOTH(finfo4,pinfo4);
2469         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2470         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2471                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2472         }
2473
2474         /* sleep */
2475         msleep(5 * msec);
2476
2477         GET_INFO_BOTH(finfo5,pinfo5);
2478         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2479
2480         /*
2481          * the close doesn't update the write time
2482          */
2483         torture_comment(tctx, "Close the file handle\n");
2484         smbcli_close(cli->tree, fnum1);
2485         fnum1 = -1;
2486
2487         GET_INFO_PATH(pinfo6);
2488         COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2489
2490         if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2491                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2492         }
2493
2494  done:
2495         if (fnum1 != -1)
2496                 smbcli_close(cli->tree, fnum1);
2497         smbcli_unlink(cli->tree, fname);
2498         smbcli_deltree(cli->tree, BASEDIR);
2499
2500         return ret;
2501 }
2502
2503 /*
2504  * Show truncate writes and closes have no effect on updating times once a SETWRITETIME is done.
2505  */
2506
2507 static bool test_delayed_write_update5b(struct torture_context *tctx,
2508                                         struct smbcli_state *cli,
2509                                         struct smbcli_state *cli2)
2510 {
2511         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2512         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2513         const char *fname = BASEDIR "\\torture_fileb.txt";
2514         int fnum1 = -1;
2515         bool ret = true;
2516         ssize_t written;
2517         struct timeval start;
2518         struct timeval end;
2519         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2520         int normal_delay = 2000000;
2521         double sec = ((double)used_delay) / ((double)normal_delay);
2522         int msec = 1000 * sec;
2523
2524         torture_comment(tctx, "\nRunning test_delayed_write_update5b\n");
2525
2526         if (!torture_setup_dir(cli, BASEDIR)) {
2527                 return false;
2528         }
2529
2530         torture_comment(tctx, "Open the file handle\n");
2531         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2532         if (fnum1 == -1) {
2533                 ret = false;
2534                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2535                 goto done;
2536         }
2537
2538         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2539         finfo0.basic_info.in.file.fnum = fnum1;
2540         finfo1 = finfo0;
2541         finfo2 = finfo0;
2542         finfo3 = finfo0;
2543         finfo4 = finfo0;
2544         finfo5 = finfo0;
2545         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2546         pinfo0.basic_info.in.file.path = fname;
2547         pinfo1 = pinfo0;
2548         pinfo2 = pinfo0;
2549         pinfo3 = pinfo0;
2550         pinfo4 = pinfo0;
2551         pinfo5 = pinfo0;
2552         pinfo6 = pinfo0;
2553
2554         /* get the initial times */
2555         GET_INFO_BOTH(finfo0,pinfo0);
2556
2557         /* do a write */
2558         torture_comment(tctx, "Do a write on the file handle\n");
2559         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2560         if (written != 1) {
2561                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2562                 ret = false;
2563                 goto done;
2564         }
2565
2566         GET_INFO_BOTH(finfo1,pinfo1);
2567         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2568
2569         torture_comment(tctx, "Set write time in the future on the file handle\n");
2570         SET_INFO_FILE(finfo0, time(NULL) + 86400);
2571         GET_INFO_BOTH(finfo2,pinfo2);
2572         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2573
2574         torture_comment(tctx, "Set write time in the past on the file handle\n");
2575         SET_INFO_FILE(finfo0, time(NULL) - 86400);
2576         GET_INFO_BOTH(finfo2,pinfo2);
2577         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2578
2579         /* make sure the 2 second delay from the first write are canceled */
2580         start = timeval_current();
2581         end = timeval_add(&start, 15 * sec, 0);
2582         while (!timeval_expired(&end)) {
2583
2584                 /* get the times after the first write */
2585                 GET_INFO_BOTH(finfo3,pinfo3);
2586
2587                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2588                         double diff = timeval_elapsed(&start);
2589                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2590                                         "(1sec == %.2f) (wrong!)\n",
2591                                         diff, sec);
2592                         ret = false;
2593                         break;
2594                 }
2595                 msleep(2 * msec);
2596         }
2597
2598         GET_INFO_BOTH(finfo3,pinfo3);
2599         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2600         if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2601                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2602         }
2603
2604         /* Do any further write (truncates) update the write time ? */
2605         start = timeval_current();
2606         end = timeval_add(&start, 15 * sec, 0);
2607         while (!timeval_expired(&end)) {
2608                 /* do a write */
2609                 torture_comment(tctx, "Do a truncate write on the file handle\n");
2610                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
2611                 if (written != 0) {
2612                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2613                         ret = false;
2614                         goto done;
2615                 }
2616                 /* get the times after the write */
2617                 GET_INFO_BOTH(finfo4,pinfo4);
2618
2619                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2620                         double diff = timeval_elapsed(&start);
2621                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2622                                         "(1sec == %.2f) (wrong!)\n",
2623                                         diff, sec);
2624                         ret = false;
2625                         break;
2626                 }
2627                 msleep(2 * msec);
2628         }
2629
2630         GET_INFO_BOTH(finfo4,pinfo4);
2631         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2632         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2633                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2634         }
2635
2636         /* sleep */
2637         msleep(5 * msec);
2638
2639         GET_INFO_BOTH(finfo5,pinfo5);
2640         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2641
2642         /*
2643          * the close doesn't update the write time
2644          */
2645         torture_comment(tctx, "Close the file handle\n");
2646         smbcli_close(cli->tree, fnum1);
2647         fnum1 = -1;
2648
2649         GET_INFO_PATH(pinfo6);
2650         COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2651
2652         if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2653                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2654         }
2655
2656  done:
2657         if (fnum1 != -1)
2658                 smbcli_close(cli->tree, fnum1);
2659         smbcli_unlink(cli->tree, fname);
2660         smbcli_deltree(cli->tree, BASEDIR);
2661
2662         return ret;
2663 }
2664
2665 /*
2666  * Open 2 handles on a file. Write one one and then set the
2667  * WRITE TIME explicitly on the other. Ensure the write time
2668  * update is cancelled. Ensure the write time is updated to
2669  * the close time when the non-explicit set handle is closed.
2670  *
2671  */
2672
2673 static bool test_delayed_write_update6(struct torture_context *tctx,
2674                                        struct smbcli_state *cli,
2675                                        struct smbcli_state *cli2)
2676 {
2677         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2678         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
2679         const char *fname = BASEDIR "\\torture_file6.txt";
2680         int fnum1 = -1;
2681         int fnum2 = -1;
2682         bool ret = true;
2683         ssize_t written;
2684         struct timeval start;
2685         struct timeval end;
2686         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2687         int normal_delay = 2000000;
2688         double sec = ((double)used_delay) / ((double)normal_delay);
2689         int msec = 1000 * sec;
2690         bool first = true;
2691
2692         torture_comment(tctx, "\nRunning test_delayed_write_update6\n");
2693
2694         if (!torture_setup_dir(cli, BASEDIR)) {
2695                 return false;
2696         }
2697 again:
2698         torture_comment(tctx, "Open the file handle\n");
2699         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2700         if (fnum1 == -1) {
2701                 ret = false;
2702                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2703                 goto done;
2704         }
2705
2706         if (fnum2 == -1) {
2707                 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
2708                 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2709                 if (fnum2 == -1) {
2710                         ret = false;
2711                         torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2712                         goto done;
2713                 }
2714         }
2715
2716         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2717         finfo0.basic_info.in.file.fnum = fnum1;
2718         finfo1 = finfo0;
2719         finfo2 = finfo0;
2720         finfo3 = finfo0;
2721         finfo4 = finfo0;
2722         finfo5 = finfo0;
2723         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2724         pinfo0.basic_info.in.file.path = fname;
2725         pinfo1 = pinfo0;
2726         pinfo2 = pinfo0;
2727         pinfo3 = pinfo0;
2728         pinfo4 = pinfo0;
2729         pinfo5 = pinfo0;
2730         pinfo6 = pinfo0;
2731         pinfo7 = pinfo0;
2732
2733         /* get the initial times */
2734         GET_INFO_BOTH(finfo0,pinfo0);
2735
2736         /* do a write */
2737         torture_comment(tctx, "Do a write on the file handle\n");
2738         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2739         if (written != 1) {
2740                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2741                 ret = false;
2742                 goto done;
2743         }
2744
2745         GET_INFO_BOTH(finfo1,pinfo1);
2746         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2747
2748         torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
2749         SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
2750         GET_INFO_BOTH(finfo2,pinfo2);
2751         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2752
2753         torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
2754         SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
2755         GET_INFO_BOTH(finfo2,pinfo2);
2756         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2757
2758         /* make sure the 2 second delay from the first write are canceled */
2759         start = timeval_current();
2760         end = timeval_add(&start, 15 * sec, 0);
2761         while (!timeval_expired(&end)) {
2762
2763                 /* get the times after the first write */
2764                 GET_INFO_BOTH(finfo3,pinfo3);
2765
2766                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2767                         double diff = timeval_elapsed(&start);
2768                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2769                                         "(1sec == %.2f) (wrong!)\n",
2770                                         diff, sec);
2771                         ret = false;
2772                         break;
2773                 }
2774                 msleep(2 * msec);
2775         }
2776
2777         GET_INFO_BOTH(finfo3,pinfo3);
2778         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2779         if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2780                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2781         }
2782
2783         /* sure any further write doesn't update the write time */
2784         start = timeval_current();
2785         end = timeval_add(&start, 15 * sec, 0);
2786         while (!timeval_expired(&end)) {
2787                 /* do a write */
2788                 torture_comment(tctx, "Do a write on the file handle\n");
2789                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2790                 if (written != 1) {
2791                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2792                         ret = false;
2793                         goto done;
2794                 }
2795                 /* get the times after the write */
2796                 GET_INFO_BOTH(finfo4,pinfo4);
2797
2798                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2799                         double diff = timeval_elapsed(&start);
2800                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2801                                         "(1sec == %.2f) (wrong!)\n",
2802                                         diff, sec);
2803                         ret = false;
2804                         break;
2805                 }
2806                 msleep(2 * msec);
2807         }
2808
2809         GET_INFO_BOTH(finfo4,pinfo4);
2810         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2811         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2812                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2813         }
2814
2815         /* sleep */
2816         msleep(5 * msec);
2817
2818         GET_INFO_BOTH(finfo5,pinfo5);
2819         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2820
2821         /*
2822          * the close updates the write time to the time of the close
2823          * as the write time was set on the 2nd handle
2824          */
2825         torture_comment(tctx, "Close the file handle\n");
2826         smbcli_close(cli->tree, fnum1);
2827         fnum1 = -1;
2828
2829         GET_INFO_PATH(pinfo6);
2830         COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
2831
2832         if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
2833                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2834         }
2835
2836         /* keep the 2nd handle open and rerun tests */
2837         if (first) {
2838                 first = false;
2839                 goto again;
2840         }
2841
2842         /*
2843          * closing the 2nd handle will cause no write time update
2844          * as the write time was explicit set on this handle
2845          */
2846         torture_comment(tctx, "Close the 2nd file handle\n");
2847         smbcli_close(cli2->tree, fnum2);
2848         fnum2 = -1;
2849
2850         GET_INFO_PATH(pinfo7);
2851         COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
2852
2853         if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
2854                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2855         }
2856
2857  done:
2858         if (fnum1 != -1)
2859                 smbcli_close(cli->tree, fnum1);
2860         if (fnum2 != -1)
2861                 smbcli_close(cli2->tree, fnum2);
2862         smbcli_unlink(cli->tree, fname);
2863         smbcli_deltree(cli->tree, BASEDIR);
2864
2865         return ret;
2866 }
2867
2868 /*
2869    testing of delayed update of write_time
2870 */
2871 struct torture_suite *torture_delay_write(void)
2872 {
2873         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "DELAYWRITE");
2874
2875         torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
2876         torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
2877         torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate", test_delayed_write_update1);
2878         torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a);
2879         torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b);
2880         torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c);
2881         torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
2882         torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
2883         torture_suite_add_2smb_test(suite, "delayed update of write time 3a", test_delayed_write_update3a);
2884         torture_suite_add_2smb_test(suite, "delayed update of write time 3b", test_delayed_write_update3b);
2885         torture_suite_add_2smb_test(suite, "delayed update of write time 3c", test_delayed_write_update3c);
2886         torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
2887         torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
2888         torture_suite_add_2smb_test(suite, "delayed update of write time 5b", test_delayed_write_update5b);
2889         torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
2890
2891         return suite;
2892 }