6e718a11dfba0d15c2a11e74d59b78293eca8776
[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_update4(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         struct timeval start;
1603         struct timeval end;
1604         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1605         int normal_delay = 2000000;
1606         double sec = ((double)used_delay) / ((double)normal_delay);
1607         int msec = 1000 * sec;
1608
1609         if (!torture_setup_dir(cli, BASEDIR)) {
1610                 return false;
1611         }
1612
1613         torture_comment(tctx, "Open the file handle\n");
1614         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1615         if (fnum1 == -1) {
1616                 ret = false;
1617                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1618                 goto done;
1619         }
1620
1621         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1622         finfo0.basic_info.in.file.fnum = fnum1;
1623         finfo1 = finfo0;
1624         finfo2 = finfo0;
1625         finfo3 = finfo0;
1626         finfo4 = finfo0;
1627         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1628         pinfo0.basic_info.in.file.path = fname;
1629         pinfo1 = pinfo0;
1630         pinfo2 = pinfo0;
1631         pinfo3 = pinfo0;
1632         pinfo4 = pinfo0;
1633         pinfo5 = pinfo0;
1634
1635         /* get the initial times */
1636         GET_INFO_BOTH(finfo0,pinfo0);
1637
1638         /* sleep a bit */
1639         msleep(5 * msec);
1640
1641         /* do a write */
1642         torture_comment(tctx, "Do a write on the file handle\n");
1643         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1644         if (written != 1) {
1645                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1646                 ret = false;
1647                 goto done;
1648         }
1649
1650         GET_INFO_BOTH(finfo1,pinfo1);
1651         COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
1652
1653         /*
1654          * make sure the write time is updated 2 seconds later
1655          * calcuated from the first write
1656          * (but expect upto 3 seconds extra time for a busy server)
1657          */
1658         start = timeval_current();
1659         end = timeval_add(&start, 5 * sec, 0);
1660         while (!timeval_expired(&end)) {
1661                 /* get the times after the first write */
1662                 GET_INFO_FILE(finfo1);
1663
1664                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1665                         double diff = timeval_elapsed(&start);
1666                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
1667                                 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1668                                                 "(1sec == %.2f) (wrong!)\n",
1669                                                 diff, sec);
1670                                 ret = false;
1671                                 break;
1672                         }
1673
1674                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1675                                         "(1sec == %.2f) (correct)\n",
1676                                         diff, sec);
1677                         break;
1678                 }
1679                 msleep(0.5 * msec);
1680         }
1681
1682         GET_INFO_BOTH(finfo1,pinfo1);
1683         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1684
1685         /* sure any further write doesn't update the write time */
1686         start = timeval_current();
1687         end = timeval_add(&start, 15 * sec, 0);
1688         while (!timeval_expired(&end)) {
1689                 /* do a write */
1690                 torture_comment(tctx, "Do a write on the file handle\n");
1691                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1692                 if (written != 1) {
1693                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1694                         ret = false;
1695                         goto done;
1696                 }
1697                 /* get the times after the write */
1698                 GET_INFO_BOTH(finfo2,pinfo2);
1699
1700                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1701                         double diff = timeval_elapsed(&start);
1702                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1703                                         "(1sec == %.2f) (wrong!)\n",
1704                                         diff, sec);
1705                         ret = false;
1706                         break;
1707                 }
1708                 msleep(2 * msec);
1709         }
1710
1711         GET_INFO_BOTH(finfo2,pinfo2);
1712         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1713         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1714                 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
1715         }
1716
1717         /* sleep */
1718         msleep(5 * msec);
1719
1720         GET_INFO_BOTH(finfo3,pinfo3);
1721         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1722
1723         /*
1724          * the close updates the write time to the time of the close
1725          * and not to the time of the last write!
1726          */
1727         torture_comment(tctx, "Close the file handle\n");
1728         smbcli_close(cli->tree, fnum1);
1729         fnum1 = -1;
1730
1731         GET_INFO_PATH(pinfo4);
1732         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1733
1734         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1735                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1736         }
1737
1738  done:
1739         if (fnum1 != -1)
1740                 smbcli_close(cli->tree, fnum1);
1741         smbcli_unlink(cli->tree, fname);
1742         smbcli_deltree(cli->tree, BASEDIR);
1743
1744         return ret;
1745 }
1746
1747 static bool test_delayed_write_update5(struct torture_context *tctx,
1748                                        struct smbcli_state *cli,
1749                                        struct smbcli_state *cli2)
1750 {
1751         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
1752         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
1753         const char *fname = BASEDIR "\\torture_file.txt";
1754         int fnum1 = -1;
1755         bool ret = true;
1756         ssize_t written;
1757         struct timeval start;
1758         struct timeval end;
1759         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1760         int normal_delay = 2000000;
1761         double sec = ((double)used_delay) / ((double)normal_delay);
1762         int msec = 1000 * sec;
1763
1764         if (!torture_setup_dir(cli, BASEDIR)) {
1765                 return false;
1766         }
1767
1768         torture_comment(tctx, "Open the file handle\n");
1769         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1770         if (fnum1 == -1) {
1771                 ret = false;
1772                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1773                 goto done;
1774         }
1775
1776         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1777         finfo0.basic_info.in.file.fnum = fnum1;
1778         finfo1 = finfo0;
1779         finfo2 = finfo0;
1780         finfo3 = finfo0;
1781         finfo4 = finfo0;
1782         finfo5 = finfo0;
1783         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1784         pinfo0.basic_info.in.file.path = fname;
1785         pinfo1 = pinfo0;
1786         pinfo2 = pinfo0;
1787         pinfo3 = pinfo0;
1788         pinfo4 = pinfo0;
1789         pinfo5 = pinfo0;
1790         pinfo6 = pinfo0;
1791
1792         /* get the initial times */
1793         GET_INFO_BOTH(finfo0,pinfo0);
1794
1795         /* do a write */
1796         torture_comment(tctx, "Do a write on the file handle\n");
1797         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1798         if (written != 1) {
1799                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1800                 ret = false;
1801                 goto done;
1802         }
1803
1804         GET_INFO_BOTH(finfo1,pinfo1);
1805         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1806
1807         torture_comment(tctx, "Set write time in the future on the file handle\n");
1808         SET_INFO_FILE(finfo0, time(NULL) + 86400);
1809         GET_INFO_BOTH(finfo2,pinfo2);
1810         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1811
1812         torture_comment(tctx, "Set write time in the past on the file handle\n");
1813         SET_INFO_FILE(finfo0, time(NULL) - 86400);
1814         GET_INFO_BOTH(finfo2,pinfo2);
1815         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
1816
1817         /* make sure the 2 second delay from the first write are canceled */
1818         start = timeval_current();
1819         end = timeval_add(&start, 15 * sec, 0);
1820         while (!timeval_expired(&end)) {
1821
1822                 /* get the times after the first write */
1823                 GET_INFO_BOTH(finfo3,pinfo3);
1824
1825                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
1826                         double diff = timeval_elapsed(&start);
1827                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1828                                         "(1sec == %.2f) (wrong!)\n",
1829                                         diff, sec);
1830                         ret = false;
1831                         break;
1832                 }
1833                 msleep(2 * msec);
1834         }
1835
1836         GET_INFO_BOTH(finfo3,pinfo3);
1837         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1838         if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1839                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1840         }
1841
1842         /* sure any further write doesn't update the write time */
1843         start = timeval_current();
1844         end = timeval_add(&start, 15 * sec, 0);
1845         while (!timeval_expired(&end)) {
1846                 /* do a write */
1847                 torture_comment(tctx, "Do a write on the file handle\n");
1848                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1849                 if (written != 1) {
1850                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1851                         ret = false;
1852                         goto done;
1853                 }
1854                 /* get the times after the write */
1855                 GET_INFO_BOTH(finfo4,pinfo4);
1856
1857                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
1858                         double diff = timeval_elapsed(&start);
1859                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1860                                         "(1sec == %.2f) (wrong!)\n",
1861                                         diff, sec);
1862                         ret = false;
1863                         break;
1864                 }
1865                 msleep(2 * msec);
1866         }
1867
1868         GET_INFO_BOTH(finfo4,pinfo4);
1869         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
1870         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1871                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1872         }
1873
1874         /* sleep */
1875         msleep(5 * msec);
1876
1877         GET_INFO_BOTH(finfo5,pinfo5);
1878         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
1879
1880         /*
1881          * the close doesn't update the write time
1882          */
1883         torture_comment(tctx, "Close the file handle\n");
1884         smbcli_close(cli->tree, fnum1);
1885         fnum1 = -1;
1886
1887         GET_INFO_PATH(pinfo6);
1888         COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
1889
1890         if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
1891                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1892         }
1893
1894  done:
1895         if (fnum1 != -1)
1896                 smbcli_close(cli->tree, fnum1);
1897         smbcli_unlink(cli->tree, fname);
1898         smbcli_deltree(cli->tree, BASEDIR);
1899
1900         return ret;
1901 }
1902
1903 static bool test_delayed_write_update6(struct torture_context *tctx,
1904                                        struct smbcli_state *cli,
1905                                        struct smbcli_state *cli2)
1906 {
1907         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
1908         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
1909         const char *fname = BASEDIR "\\torture_file.txt";
1910         int fnum1 = -1;
1911         int fnum2 = -1;
1912         bool ret = true;
1913         ssize_t written;
1914         struct timeval start;
1915         struct timeval end;
1916         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1917         int normal_delay = 2000000;
1918         double sec = ((double)used_delay) / ((double)normal_delay);
1919         int msec = 1000 * sec;
1920         bool first = true;
1921
1922         if (!torture_setup_dir(cli, BASEDIR)) {
1923                 return false;
1924         }
1925 again:
1926         torture_comment(tctx, "Open the file handle\n");
1927         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1928         if (fnum1 == -1) {
1929                 ret = false;
1930                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1931                 goto done;
1932         }
1933
1934         if (fnum2 == -1) {
1935                 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
1936                 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1937                 if (fnum2 == -1) {
1938                         ret = false;
1939                         torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1940                         goto done;
1941                 }
1942         }
1943
1944         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1945         finfo0.basic_info.in.file.fnum = fnum1;
1946         finfo1 = finfo0;
1947         finfo2 = finfo0;
1948         finfo3 = finfo0;
1949         finfo4 = finfo0;
1950         finfo5 = finfo0;
1951         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1952         pinfo0.basic_info.in.file.path = fname;
1953         pinfo1 = pinfo0;
1954         pinfo2 = pinfo0;
1955         pinfo3 = pinfo0;
1956         pinfo4 = pinfo0;
1957         pinfo5 = pinfo0;
1958         pinfo6 = pinfo0;
1959         pinfo7 = pinfo0;
1960
1961         /* get the initial times */
1962         GET_INFO_BOTH(finfo0,pinfo0);
1963
1964         /* do a write */
1965         torture_comment(tctx, "Do a write on the file handle\n");
1966         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1967         if (written != 1) {
1968                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1969                 ret = false;
1970                 goto done;
1971         }
1972
1973         GET_INFO_BOTH(finfo1,pinfo1);
1974         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1975
1976         torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
1977         SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
1978         GET_INFO_BOTH(finfo2,pinfo2);
1979         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1980
1981         torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
1982         SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
1983         GET_INFO_BOTH(finfo2,pinfo2);
1984         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
1985
1986         /* make sure the 2 second delay from the first write are canceled */
1987         start = timeval_current();
1988         end = timeval_add(&start, 15 * sec, 0);
1989         while (!timeval_expired(&end)) {
1990
1991                 /* get the times after the first write */
1992                 GET_INFO_BOTH(finfo3,pinfo3);
1993
1994                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
1995                         double diff = timeval_elapsed(&start);
1996                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1997                                         "(1sec == %.2f) (wrong!)\n",
1998                                         diff, sec);
1999                         ret = false;
2000                         break;
2001                 }
2002                 msleep(2 * msec);
2003         }
2004
2005         GET_INFO_BOTH(finfo3,pinfo3);
2006         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2007         if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2008                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2009         }
2010
2011         /* sure any further write doesn't update the write time */
2012         start = timeval_current();
2013         end = timeval_add(&start, 15 * sec, 0);
2014         while (!timeval_expired(&end)) {
2015                 /* do a write */
2016                 torture_comment(tctx, "Do a write on the file handle\n");
2017                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2018                 if (written != 1) {
2019                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2020                         ret = false;
2021                         goto done;
2022                 }
2023                 /* get the times after the write */
2024                 GET_INFO_BOTH(finfo4,pinfo4);
2025
2026                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2027                         double diff = timeval_elapsed(&start);
2028                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2029                                         "(1sec == %.2f) (wrong!)\n",
2030                                         diff, sec);
2031                         ret = false;
2032                         break;
2033                 }
2034                 msleep(2 * msec);
2035         }
2036
2037         GET_INFO_BOTH(finfo4,pinfo4);
2038         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2039         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2040                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2041         }
2042
2043         /* sleep */
2044         msleep(5 * msec);
2045
2046         GET_INFO_BOTH(finfo5,pinfo5);
2047         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2048
2049         /*
2050          * the close updates the write time to the time of the close
2051          * as the write time was set on the 2nd handle
2052          */
2053         torture_comment(tctx, "Close the file handle\n");
2054         smbcli_close(cli->tree, fnum1);
2055         fnum1 = -1;
2056
2057         GET_INFO_PATH(pinfo6);
2058         COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
2059
2060         if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
2061                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2062         }
2063
2064         /* keep the 2nd handle open and rerun tests */
2065         if (first) {
2066                 first = false;
2067                 goto again;
2068         }
2069
2070         /*
2071          * closing the 2nd handle will cause no write time update
2072          * as the write time was explicit set on this handle
2073          */
2074         torture_comment(tctx, "Close the 2nd file handle\n");
2075         smbcli_close(cli2->tree, fnum2);
2076         fnum2 = -1;
2077
2078         GET_INFO_PATH(pinfo7);
2079         COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
2080
2081         if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
2082                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2083         }
2084
2085  done:
2086         if (fnum1 != -1)
2087                 smbcli_close(cli->tree, fnum1);
2088         if (fnum2 != -1)
2089                 smbcli_close(cli2->tree, fnum2);
2090         smbcli_unlink(cli->tree, fname);
2091         smbcli_deltree(cli->tree, BASEDIR);
2092
2093         return ret;
2094 }
2095
2096
2097 /* 
2098    testing of delayed update of write_time
2099 */
2100 struct torture_suite *torture_delay_write(void)
2101 {
2102         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "DELAYWRITE");
2103
2104         torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
2105         torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
2106         torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate ", test_delayed_write_update1);
2107         torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a);
2108         torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b);
2109         torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c);
2110         torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
2111         torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
2112         torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
2113         torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
2114         torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
2115
2116         return suite;
2117 }