BASE-DELAYWRITE: test behavior of SMBwrite truncate, writeX, SMBwrite truncate and...
[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_update3c(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         int i;
1966         struct timeval start;
1967         struct timeval end;
1968         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1969         int normal_delay = 2000000;
1970         double sec = ((double)used_delay) / ((double)normal_delay);
1971         int msec = 1000 * sec;
1972
1973         if (!torture_setup_dir(cli, BASEDIR)) {
1974                 return false;
1975         }
1976
1977         torture_comment(tctx, "Open the file handle\n");
1978         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1979         if (fnum1 == -1) {
1980                 ret = false;
1981                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1982                 goto done;
1983         }
1984
1985         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1986         finfo0.basic_info.in.file.fnum = fnum1;
1987         finfo1 = finfo0;
1988         finfo2 = finfo0;
1989         finfo3 = finfo0;
1990         finfo4 = finfo0;
1991         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1992         pinfo0.basic_info.in.file.path = fname;
1993         pinfo1 = pinfo0;
1994         pinfo2 = pinfo0;
1995         pinfo3 = pinfo0;
1996         pinfo4 = pinfo0;
1997         pinfo5 = pinfo0;
1998
1999         /* get the initial times */
2000         GET_INFO_BOTH(finfo0,pinfo0);
2001
2002         /*
2003          * sleep some time, to demonstrate the handling of write times
2004          * doesn't depend on the time since the open
2005          */
2006         msleep(5 * msec);
2007
2008         /* get the initial times */
2009         GET_INFO_BOTH(finfo1,pinfo1);
2010         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2011
2012         /*
2013          * demonstrate that a truncate write always
2014          * updates the write time immediately
2015          */
2016         for (i=0; i < 3; i++) {
2017                 msleep(1 * msec);
2018                 /* do a write */
2019                 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
2020                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2021                 if (written != 0) {
2022                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2023                         ret = false;
2024                         goto done;
2025                 }
2026                 /* get the times after the write */
2027                 GET_INFO_BOTH(finfo2,pinfo2);
2028                 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2029                 finfo1 = finfo2;
2030         }
2031
2032         start = timeval_current();
2033         end = timeval_add(&start, 7 * sec, 0);
2034         while (!timeval_expired(&end)) {
2035                 /* do a write */
2036                 torture_comment(tctx, "Do a write on the file handle\n");
2037                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2038                 if (written != 1) {
2039                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2040                         ret = false;
2041                         goto done;
2042                 }
2043                 /* get the times after the write */
2044                 GET_INFO_FILE(finfo2);
2045
2046                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2047                         double diff = timeval_elapsed(&start);
2048                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2049                                         "(1sec == %.2f) (wrong!)\n",
2050                                         diff, sec);
2051                         ret = false;
2052                         break;
2053                 }
2054                 msleep(2 * msec);
2055         }
2056
2057         GET_INFO_BOTH(finfo2,pinfo2);
2058         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2059         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2060                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2061         }
2062
2063         /* sleep */
2064         msleep(5 * msec);
2065
2066         /* get the initial times */
2067         GET_INFO_BOTH(finfo1,pinfo1);
2068         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
2069
2070         /*
2071          * demonstrate that a truncate write always
2072          * updates the write time immediately
2073          */
2074         for (i=0; i < 3; i++) {
2075                 msleep(1 * msec);
2076                 /* do a write */
2077                 torture_comment(tctx, "Do a truncate write [%d] on the file handle\n", i);
2078                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2079                 if (written != 0) {
2080                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2081                         ret = false;
2082                         goto done;
2083                 }
2084                 /* get the times after the write */
2085                 GET_INFO_BOTH(finfo2,pinfo2);
2086                 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2087                 finfo1 = finfo2;
2088         }
2089
2090         /* sleep */
2091         msleep(5 * msec);
2092
2093         GET_INFO_BOTH(finfo2,pinfo2);
2094         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2095
2096         /* sure any further write doesn't update the write time */
2097         start = timeval_current();
2098         end = timeval_add(&start, 15 * sec, 0);
2099         while (!timeval_expired(&end)) {
2100                 /* do a write */
2101                 torture_comment(tctx, "Do a write on the file handle\n");
2102                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2103                 if (written != 1) {
2104                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2105                         ret = false;
2106                         goto done;
2107                 }
2108                 /* get the times after the write */
2109                 GET_INFO_BOTH(finfo2,pinfo2);
2110
2111                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2112                         double diff = timeval_elapsed(&start);
2113                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2114                                         "(1sec == %.2f) (wrong!)\n",
2115                                         diff, sec);
2116                         ret = false;
2117                         break;
2118                 }
2119                 msleep(2 * msec);
2120         }
2121
2122         GET_INFO_BOTH(finfo2,pinfo2);
2123         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2124         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2125                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2126         }
2127
2128         /* sleep */
2129         msleep(5 * msec);
2130
2131         GET_INFO_BOTH(finfo3,pinfo3);
2132         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2133
2134         /*
2135          * the close updates the write time to the time of the close
2136          * and not to the time of the last write!
2137          */
2138         torture_comment(tctx, "Close the file handle\n");
2139         smbcli_close(cli->tree, fnum1);
2140         fnum1 = -1;
2141
2142         GET_INFO_PATH(pinfo4);
2143         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2144
2145         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2146                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2147         }
2148
2149  done:
2150         if (fnum1 != -1)
2151                 smbcli_close(cli->tree, fnum1);
2152         smbcli_unlink(cli->tree, fname);
2153         smbcli_deltree(cli->tree, BASEDIR);
2154
2155         return ret;
2156 }
2157
2158 static bool test_delayed_write_update4(struct torture_context *tctx,
2159                                        struct smbcli_state *cli,
2160                                        struct smbcli_state *cli2)
2161 {
2162         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
2163         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
2164         const char *fname = BASEDIR "\\torture_file.txt";
2165         int fnum1 = -1;
2166         bool ret = true;
2167         ssize_t written;
2168         struct timeval start;
2169         struct timeval end;
2170         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2171         int normal_delay = 2000000;
2172         double sec = ((double)used_delay) / ((double)normal_delay);
2173         int msec = 1000 * sec;
2174
2175         if (!torture_setup_dir(cli, BASEDIR)) {
2176                 return false;
2177         }
2178
2179         torture_comment(tctx, "Open the file handle\n");
2180         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2181         if (fnum1 == -1) {
2182                 ret = false;
2183                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2184                 goto done;
2185         }
2186
2187         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2188         finfo0.basic_info.in.file.fnum = fnum1;
2189         finfo1 = finfo0;
2190         finfo2 = finfo0;
2191         finfo3 = finfo0;
2192         finfo4 = finfo0;
2193         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2194         pinfo0.basic_info.in.file.path = fname;
2195         pinfo1 = pinfo0;
2196         pinfo2 = pinfo0;
2197         pinfo3 = pinfo0;
2198         pinfo4 = pinfo0;
2199         pinfo5 = pinfo0;
2200
2201         /* get the initial times */
2202         GET_INFO_BOTH(finfo0,pinfo0);
2203
2204         /* sleep a bit */
2205         msleep(5 * msec);
2206
2207         /* do a write */
2208         torture_comment(tctx, "Do a write on the file handle\n");
2209         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2210         if (written != 1) {
2211                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2212                 ret = false;
2213                 goto done;
2214         }
2215
2216         GET_INFO_BOTH(finfo1,pinfo1);
2217         COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
2218
2219         /*
2220          * make sure the write time is updated 2 seconds later
2221          * calcuated from the first write
2222          * (but expect upto 3 seconds extra time for a busy server)
2223          */
2224         start = timeval_current();
2225         end = timeval_add(&start, 5 * sec, 0);
2226         while (!timeval_expired(&end)) {
2227                 /* get the times after the first write */
2228                 GET_INFO_FILE(finfo1);
2229
2230                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
2231                         double diff = timeval_elapsed(&start);
2232                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
2233                                 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2234                                                 "(1sec == %.2f) (wrong!)\n",
2235                                                 diff, sec);
2236                                 ret = false;
2237                                 break;
2238                         }
2239
2240                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2241                                         "(1sec == %.2f) (correct)\n",
2242                                         diff, sec);
2243                         break;
2244                 }
2245                 msleep(0.5 * msec);
2246         }
2247
2248         GET_INFO_BOTH(finfo1,pinfo1);
2249         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
2250
2251         /* sure any further write doesn't update the write time */
2252         start = timeval_current();
2253         end = timeval_add(&start, 15 * sec, 0);
2254         while (!timeval_expired(&end)) {
2255                 /* do a write */
2256                 torture_comment(tctx, "Do a write on the file handle\n");
2257                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2258                 if (written != 1) {
2259                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2260                         ret = false;
2261                         goto done;
2262                 }
2263                 /* get the times after the write */
2264                 GET_INFO_BOTH(finfo2,pinfo2);
2265
2266                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2267                         double diff = timeval_elapsed(&start);
2268                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2269                                         "(1sec == %.2f) (wrong!)\n",
2270                                         diff, sec);
2271                         ret = false;
2272                         break;
2273                 }
2274                 msleep(2 * msec);
2275         }
2276
2277         GET_INFO_BOTH(finfo2,pinfo2);
2278         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2279         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2280                 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
2281         }
2282
2283         /* sleep */
2284         msleep(5 * msec);
2285
2286         GET_INFO_BOTH(finfo3,pinfo3);
2287         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2288
2289         /*
2290          * the close updates the write time to the time of the close
2291          * and not to the time of the last write!
2292          */
2293         torture_comment(tctx, "Close the file handle\n");
2294         smbcli_close(cli->tree, fnum1);
2295         fnum1 = -1;
2296
2297         GET_INFO_PATH(pinfo4);
2298         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2299
2300         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2301                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2302         }
2303
2304  done:
2305         if (fnum1 != -1)
2306                 smbcli_close(cli->tree, fnum1);
2307         smbcli_unlink(cli->tree, fname);
2308         smbcli_deltree(cli->tree, BASEDIR);
2309
2310         return ret;
2311 }
2312
2313 static bool test_delayed_write_update5(struct torture_context *tctx,
2314                                        struct smbcli_state *cli,
2315                                        struct smbcli_state *cli2)
2316 {
2317         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2318         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2319         const char *fname = BASEDIR "\\torture_file.txt";
2320         int fnum1 = -1;
2321         bool ret = true;
2322         ssize_t written;
2323         struct timeval start;
2324         struct timeval end;
2325         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2326         int normal_delay = 2000000;
2327         double sec = ((double)used_delay) / ((double)normal_delay);
2328         int msec = 1000 * sec;
2329
2330         if (!torture_setup_dir(cli, BASEDIR)) {
2331                 return false;
2332         }
2333
2334         torture_comment(tctx, "Open the file handle\n");
2335         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2336         if (fnum1 == -1) {
2337                 ret = false;
2338                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2339                 goto done;
2340         }
2341
2342         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2343         finfo0.basic_info.in.file.fnum = fnum1;
2344         finfo1 = finfo0;
2345         finfo2 = finfo0;
2346         finfo3 = finfo0;
2347         finfo4 = finfo0;
2348         finfo5 = finfo0;
2349         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2350         pinfo0.basic_info.in.file.path = fname;
2351         pinfo1 = pinfo0;
2352         pinfo2 = pinfo0;
2353         pinfo3 = pinfo0;
2354         pinfo4 = pinfo0;
2355         pinfo5 = pinfo0;
2356         pinfo6 = pinfo0;
2357
2358         /* get the initial times */
2359         GET_INFO_BOTH(finfo0,pinfo0);
2360
2361         /* do a write */
2362         torture_comment(tctx, "Do a write on the file handle\n");
2363         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2364         if (written != 1) {
2365                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2366                 ret = false;
2367                 goto done;
2368         }
2369
2370         GET_INFO_BOTH(finfo1,pinfo1);
2371         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2372
2373         torture_comment(tctx, "Set write time in the future on the file handle\n");
2374         SET_INFO_FILE(finfo0, time(NULL) + 86400);
2375         GET_INFO_BOTH(finfo2,pinfo2);
2376         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2377
2378         torture_comment(tctx, "Set write time in the past on the file handle\n");
2379         SET_INFO_FILE(finfo0, time(NULL) - 86400);
2380         GET_INFO_BOTH(finfo2,pinfo2);
2381         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2382
2383         /* make sure the 2 second delay from the first write are canceled */
2384         start = timeval_current();
2385         end = timeval_add(&start, 15 * sec, 0);
2386         while (!timeval_expired(&end)) {
2387
2388                 /* get the times after the first write */
2389                 GET_INFO_BOTH(finfo3,pinfo3);
2390
2391                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2392                         double diff = timeval_elapsed(&start);
2393                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2394                                         "(1sec == %.2f) (wrong!)\n",
2395                                         diff, sec);
2396                         ret = false;
2397                         break;
2398                 }
2399                 msleep(2 * msec);
2400         }
2401
2402         GET_INFO_BOTH(finfo3,pinfo3);
2403         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2404         if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2405                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2406         }
2407
2408         /* sure any further write doesn't update the write time */
2409         start = timeval_current();
2410         end = timeval_add(&start, 15 * sec, 0);
2411         while (!timeval_expired(&end)) {
2412                 /* do a write */
2413                 torture_comment(tctx, "Do a write on the file handle\n");
2414                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2415                 if (written != 1) {
2416                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2417                         ret = false;
2418                         goto done;
2419                 }
2420                 /* get the times after the write */
2421                 GET_INFO_BOTH(finfo4,pinfo4);
2422
2423                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2424                         double diff = timeval_elapsed(&start);
2425                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2426                                         "(1sec == %.2f) (wrong!)\n",
2427                                         diff, sec);
2428                         ret = false;
2429                         break;
2430                 }
2431                 msleep(2 * msec);
2432         }
2433
2434         GET_INFO_BOTH(finfo4,pinfo4);
2435         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2436         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2437                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2438         }
2439
2440         /* sleep */
2441         msleep(5 * msec);
2442
2443         GET_INFO_BOTH(finfo5,pinfo5);
2444         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2445
2446         /*
2447          * the close doesn't update the write time
2448          */
2449         torture_comment(tctx, "Close the file handle\n");
2450         smbcli_close(cli->tree, fnum1);
2451         fnum1 = -1;
2452
2453         GET_INFO_PATH(pinfo6);
2454         COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2455
2456         if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2457                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2458         }
2459
2460  done:
2461         if (fnum1 != -1)
2462                 smbcli_close(cli->tree, fnum1);
2463         smbcli_unlink(cli->tree, fname);
2464         smbcli_deltree(cli->tree, BASEDIR);
2465
2466         return ret;
2467 }
2468
2469 static bool test_delayed_write_update5b(struct torture_context *tctx,
2470                                         struct smbcli_state *cli,
2471                                         struct smbcli_state *cli2)
2472 {
2473         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2474         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2475         const char *fname = BASEDIR "\\torture_file.txt";
2476         int fnum1 = -1;
2477         bool ret = true;
2478         ssize_t written;
2479         struct timeval start;
2480         struct timeval end;
2481         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2482         int normal_delay = 2000000;
2483         double sec = ((double)used_delay) / ((double)normal_delay);
2484         int msec = 1000 * sec;
2485
2486         if (!torture_setup_dir(cli, BASEDIR)) {
2487                 return false;
2488         }
2489
2490         torture_comment(tctx, "Open the file handle\n");
2491         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2492         if (fnum1 == -1) {
2493                 ret = false;
2494                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2495                 goto done;
2496         }
2497
2498         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2499         finfo0.basic_info.in.file.fnum = fnum1;
2500         finfo1 = finfo0;
2501         finfo2 = finfo0;
2502         finfo3 = finfo0;
2503         finfo4 = finfo0;
2504         finfo5 = finfo0;
2505         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2506         pinfo0.basic_info.in.file.path = fname;
2507         pinfo1 = pinfo0;
2508         pinfo2 = pinfo0;
2509         pinfo3 = pinfo0;
2510         pinfo4 = pinfo0;
2511         pinfo5 = pinfo0;
2512         pinfo6 = pinfo0;
2513
2514         /* get the initial times */
2515         GET_INFO_BOTH(finfo0,pinfo0);
2516
2517         /* do a write */
2518         torture_comment(tctx, "Do a write on the file handle\n");
2519         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2520         if (written != 1) {
2521                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2522                 ret = false;
2523                 goto done;
2524         }
2525
2526         GET_INFO_BOTH(finfo1,pinfo1);
2527         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2528
2529         torture_comment(tctx, "Set write time in the future on the file handle\n");
2530         SET_INFO_FILE(finfo0, time(NULL) + 86400);
2531         GET_INFO_BOTH(finfo2,pinfo2);
2532         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2533
2534         torture_comment(tctx, "Set write time in the past on the file handle\n");
2535         SET_INFO_FILE(finfo0, time(NULL) - 86400);
2536         GET_INFO_BOTH(finfo2,pinfo2);
2537         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2538
2539         /* make sure the 2 second delay from the first write are canceled */
2540         start = timeval_current();
2541         end = timeval_add(&start, 15 * sec, 0);
2542         while (!timeval_expired(&end)) {
2543
2544                 /* get the times after the first write */
2545                 GET_INFO_BOTH(finfo3,pinfo3);
2546
2547                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2548                         double diff = timeval_elapsed(&start);
2549                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2550                                         "(1sec == %.2f) (wrong!)\n",
2551                                         diff, sec);
2552                         ret = false;
2553                         break;
2554                 }
2555                 msleep(2 * msec);
2556         }
2557
2558         GET_INFO_BOTH(finfo3,pinfo3);
2559         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2560         if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2561                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2562         }
2563
2564         /* sure any further write (truncates) update the write time */
2565         start = timeval_current();
2566         end = timeval_add(&start, 15 * sec, 0);
2567         while (!timeval_expired(&end)) {
2568                 /* do a write */
2569                 torture_comment(tctx, "Do a truncate write on the file handle\n");
2570                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 0);
2571                 if (written != 0) {
2572                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2573                         ret = false;
2574                         goto done;
2575                 }
2576                 /* get the times after the write */
2577                 GET_INFO_BOTH(finfo4,pinfo4);
2578
2579                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2580                         double diff = timeval_elapsed(&start);
2581                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2582                                         "(1sec == %.2f) (wrong!)\n",
2583                                         diff, sec);
2584                         ret = false;
2585                         break;
2586                 }
2587                 msleep(2 * msec);
2588         }
2589
2590         GET_INFO_BOTH(finfo4,pinfo4);
2591         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2592         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2593                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2594         }
2595
2596         /* sleep */
2597         msleep(5 * msec);
2598
2599         GET_INFO_BOTH(finfo5,pinfo5);
2600         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2601
2602         /*
2603          * the close doesn't update the write time
2604          */
2605         torture_comment(tctx, "Close the file handle\n");
2606         smbcli_close(cli->tree, fnum1);
2607         fnum1 = -1;
2608
2609         GET_INFO_PATH(pinfo6);
2610         COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2611
2612         if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2613                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2614         }
2615
2616  done:
2617         if (fnum1 != -1)
2618                 smbcli_close(cli->tree, fnum1);
2619         smbcli_unlink(cli->tree, fname);
2620         smbcli_deltree(cli->tree, BASEDIR);
2621
2622         return ret;
2623 }
2624
2625 static bool test_delayed_write_update6(struct torture_context *tctx,
2626                                        struct smbcli_state *cli,
2627                                        struct smbcli_state *cli2)
2628 {
2629         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2630         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
2631         const char *fname = BASEDIR "\\torture_file.txt";
2632         int fnum1 = -1;
2633         int fnum2 = -1;
2634         bool ret = true;
2635         ssize_t written;
2636         struct timeval start;
2637         struct timeval end;
2638         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2639         int normal_delay = 2000000;
2640         double sec = ((double)used_delay) / ((double)normal_delay);
2641         int msec = 1000 * sec;
2642         bool first = true;
2643
2644         if (!torture_setup_dir(cli, BASEDIR)) {
2645                 return false;
2646         }
2647 again:
2648         torture_comment(tctx, "Open the file handle\n");
2649         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2650         if (fnum1 == -1) {
2651                 ret = false;
2652                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2653                 goto done;
2654         }
2655
2656         if (fnum2 == -1) {
2657                 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
2658                 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2659                 if (fnum2 == -1) {
2660                         ret = false;
2661                         torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2662                         goto done;
2663                 }
2664         }
2665
2666         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2667         finfo0.basic_info.in.file.fnum = fnum1;
2668         finfo1 = finfo0;
2669         finfo2 = finfo0;
2670         finfo3 = finfo0;
2671         finfo4 = finfo0;
2672         finfo5 = finfo0;
2673         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2674         pinfo0.basic_info.in.file.path = fname;
2675         pinfo1 = pinfo0;
2676         pinfo2 = pinfo0;
2677         pinfo3 = pinfo0;
2678         pinfo4 = pinfo0;
2679         pinfo5 = pinfo0;
2680         pinfo6 = pinfo0;
2681         pinfo7 = pinfo0;
2682
2683         /* get the initial times */
2684         GET_INFO_BOTH(finfo0,pinfo0);
2685
2686         /* do a write */
2687         torture_comment(tctx, "Do a write on the file handle\n");
2688         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2689         if (written != 1) {
2690                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2691                 ret = false;
2692                 goto done;
2693         }
2694
2695         GET_INFO_BOTH(finfo1,pinfo1);
2696         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2697
2698         torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
2699         SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
2700         GET_INFO_BOTH(finfo2,pinfo2);
2701         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2702
2703         torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
2704         SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
2705         GET_INFO_BOTH(finfo2,pinfo2);
2706         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2707
2708         /* make sure the 2 second delay from the first write are canceled */
2709         start = timeval_current();
2710         end = timeval_add(&start, 15 * sec, 0);
2711         while (!timeval_expired(&end)) {
2712
2713                 /* get the times after the first write */
2714                 GET_INFO_BOTH(finfo3,pinfo3);
2715
2716                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2717                         double diff = timeval_elapsed(&start);
2718                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2719                                         "(1sec == %.2f) (wrong!)\n",
2720                                         diff, sec);
2721                         ret = false;
2722                         break;
2723                 }
2724                 msleep(2 * msec);
2725         }
2726
2727         GET_INFO_BOTH(finfo3,pinfo3);
2728         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2729         if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2730                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2731         }
2732
2733         /* sure any further write doesn't update the write time */
2734         start = timeval_current();
2735         end = timeval_add(&start, 15 * sec, 0);
2736         while (!timeval_expired(&end)) {
2737                 /* do a write */
2738                 torture_comment(tctx, "Do a write on the file handle\n");
2739                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2740                 if (written != 1) {
2741                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2742                         ret = false;
2743                         goto done;
2744                 }
2745                 /* get the times after the write */
2746                 GET_INFO_BOTH(finfo4,pinfo4);
2747
2748                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2749                         double diff = timeval_elapsed(&start);
2750                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2751                                         "(1sec == %.2f) (wrong!)\n",
2752                                         diff, sec);
2753                         ret = false;
2754                         break;
2755                 }
2756                 msleep(2 * msec);
2757         }
2758
2759         GET_INFO_BOTH(finfo4,pinfo4);
2760         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2761         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2762                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2763         }
2764
2765         /* sleep */
2766         msleep(5 * msec);
2767
2768         GET_INFO_BOTH(finfo5,pinfo5);
2769         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2770
2771         /*
2772          * the close updates the write time to the time of the close
2773          * as the write time was set on the 2nd handle
2774          */
2775         torture_comment(tctx, "Close the file handle\n");
2776         smbcli_close(cli->tree, fnum1);
2777         fnum1 = -1;
2778
2779         GET_INFO_PATH(pinfo6);
2780         COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
2781
2782         if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
2783                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2784         }
2785
2786         /* keep the 2nd handle open and rerun tests */
2787         if (first) {
2788                 first = false;
2789                 goto again;
2790         }
2791
2792         /*
2793          * closing the 2nd handle will cause no write time update
2794          * as the write time was explicit set on this handle
2795          */
2796         torture_comment(tctx, "Close the 2nd file handle\n");
2797         smbcli_close(cli2->tree, fnum2);
2798         fnum2 = -1;
2799
2800         GET_INFO_PATH(pinfo7);
2801         COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
2802
2803         if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
2804                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2805         }
2806
2807  done:
2808         if (fnum1 != -1)
2809                 smbcli_close(cli->tree, fnum1);
2810         if (fnum2 != -1)
2811                 smbcli_close(cli2->tree, fnum2);
2812         smbcli_unlink(cli->tree, fname);
2813         smbcli_deltree(cli->tree, BASEDIR);
2814
2815         return ret;
2816 }
2817
2818
2819 /* 
2820    testing of delayed update of write_time
2821 */
2822 struct torture_suite *torture_delay_write(void)
2823 {
2824         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "DELAYWRITE");
2825
2826         torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
2827         torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
2828         torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate ", test_delayed_write_update1);
2829         torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a);
2830         torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b);
2831         torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c);
2832         torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
2833         torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
2834         torture_suite_add_2smb_test(suite, "delayed update of write time 3a", test_delayed_write_update3a);
2835         torture_suite_add_2smb_test(suite, "delayed update of write time 3b", test_delayed_write_update3b);
2836         torture_suite_add_2smb_test(suite, "delayed update of write time 3c", test_delayed_write_update3c);
2837         torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
2838         torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
2839         torture_suite_add_2smb_test(suite, "delayed update of write time 5b", test_delayed_write_update5b);
2840         torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
2841
2842         return suite;
2843 }