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