BASE-DELAYWRITE: use timeval_* and make it possible to spefic the writetime update...
[kai/samba.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 /* 
132  * Do as above, but using 2 connections.
133  */
134
135 static bool test_delayed_write_update2(struct torture_context *tctx, struct smbcli_state *cli, 
136                                                                            struct smbcli_state *cli2)
137 {
138         union smb_fileinfo finfo1, finfo2;
139         const char *fname = BASEDIR "\\torture_file.txt";
140         NTSTATUS status;
141         int fnum1 = -1;
142         int fnum2 = -1;
143         bool ret = true;
144         ssize_t written;
145         struct timeval start;
146         struct timeval end;
147         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
148         int normal_delay = 2000000;
149         double sec = ((double)used_delay) / ((double)normal_delay);
150         int msec = 1000 * sec;
151         union smb_flush flsh;
152
153         if (!torture_setup_dir(cli, BASEDIR)) {
154                 return false;
155         }
156
157         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
158         if (fnum1 == -1) {
159                 torture_comment(tctx, "Failed to open %s\n", fname);
160                 return false;
161         }
162
163         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
164         finfo1.basic_info.in.file.fnum = fnum1;
165         finfo2 = finfo1;
166
167         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
168
169         if (!NT_STATUS_IS_OK(status)) {
170                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
171                 return false;
172         }
173         
174         torture_comment(tctx, "Initial write time %s\n", 
175                nt_time_string(tctx, finfo1.basic_info.out.write_time));
176
177         /* 3 second delay to ensure we get past any 2 second time
178            granularity (older systems may have that) */
179         msleep(3 * msec);
180
181         {
182                 /* Try using setfileinfo instead of write to update write time. */
183                 union smb_setfileinfo sfinfo;
184                 time_t t_set = time(NULL);
185                 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
186                 sfinfo.basic_info.in.file.fnum = fnum1;
187                 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
188                 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
189
190                 /* I tried this with both + and - ve to see if it makes a different.
191                    It doesn't - once the filetime is set via setfileinfo it stays that way. */
192 #if 1
193                 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
194 #else
195                 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
196 #endif
197                 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
198                 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
199
200                 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
201
202                 if (!NT_STATUS_IS_OK(status)) {
203                         DEBUG(0, ("sfileinfo failed: %s\n", nt_errstr(status)));
204                         return false;
205                 }
206         }
207
208         finfo2.basic_info.in.file.path = fname;
209         
210         status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
211
212         if (!NT_STATUS_IS_OK(status)) {
213                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
214                 return false;
215         }
216         torture_comment(tctx, "write time %s\n",
217                nt_time_string(tctx, finfo2.basic_info.out.write_time));
218
219         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
220                 torture_comment(tctx, "Server updated write_time (correct)\n");
221         } else {
222                 torture_comment(tctx, "Server did not update write time (wrong!)\n");
223                 ret = false;
224         }
225
226         /* Now try a write to see if the write time gets reset. */
227
228         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
229         finfo1.basic_info.in.file.fnum = fnum1;
230         finfo2 = finfo1;
231
232         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
233
234         if (!NT_STATUS_IS_OK(status)) {
235                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
236                 return false;
237         }
238         
239         torture_comment(tctx, "Modified write time %s\n", 
240                nt_time_string(tctx, finfo1.basic_info.out.write_time));
241
242
243         torture_comment(tctx, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
244
245         written =  smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
246
247         if (written != 10) {
248                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
249                        (int)written, __location__);
250                 return false;
251         }
252
253         /* Just to prove to tridge that the an smbflush has no effect on
254            the write time :-). The setfileinfo IS STICKY. JRA. */
255
256         torture_comment(tctx, "Doing flush after write\n");
257
258         flsh.flush.level        = RAW_FLUSH_FLUSH;
259         flsh.flush.in.file.fnum = fnum1;
260         status = smb_raw_flush(cli->tree, &flsh);
261         if (!NT_STATUS_IS_OK(status)) {
262                 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
263                 return false;
264         }
265
266         /* Once the time was set using setfileinfo then it stays set - writes
267            don't have any effect. But make sure. */
268         start = timeval_current();
269         end = timeval_add(&start, (15*sec), 0);
270         while (!timeval_expired(&end)) {
271                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
272
273                 if (!NT_STATUS_IS_OK(status)) {
274                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
275                         ret = false;
276                         break;
277                 }
278                 torture_comment(tctx, "write time %s\n", 
279                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
280                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
281                         double diff = timeval_elapsed(&start);
282                         torture_comment(tctx, "Server updated write_time after %.2f seconds"
283                                         "(1sec == %.2f) (wrong!)\n",
284                                         diff, sec);
285                         ret = false;
286                         break;
287                 }
288                 fflush(stdout);
289                 msleep(1 * msec);
290         }
291         
292         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
293                 torture_comment(tctx, "Server did not update write time (correct)\n");
294         }
295
296         fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
297         if (fnum2 == -1) {
298                 torture_comment(tctx, "Failed to open %s\n", fname);
299                 return false;
300         }
301         
302         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");
303
304         written =  smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
305
306         if (written != 10) {
307                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
308                        (int)written, __location__);
309                 return false;
310         }
311
312         status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
313
314         if (!NT_STATUS_IS_OK(status)) {
315                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
316                 return false;
317         }
318         torture_comment(tctx, "write time %s\n", 
319                nt_time_string(tctx, finfo2.basic_info.out.write_time));
320         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
321                 torture_comment(tctx, "Server updated write_time (wrong!)\n");
322                 ret = false;
323         }
324
325         torture_comment(tctx, "Closing the first fd to see if write time updated.\n");
326         smbcli_close(cli->tree, fnum1);
327         fnum1 = -1;
328
329         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");
330
331         written =  smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
332
333         if (written != 10) {
334                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
335                        (int)written, __location__);
336                 return false;
337         }
338
339         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
340         finfo1.basic_info.in.file.fnum = fnum2;
341         finfo2 = finfo1;
342         status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
343
344         if (!NT_STATUS_IS_OK(status)) {
345                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
346                 return false;
347         }
348         torture_comment(tctx, "write time %s\n", 
349                nt_time_string(tctx, finfo2.basic_info.out.write_time));
350         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
351                 torture_comment(tctx, "Server updated write_time (wrong!)\n");
352                 ret = false;
353         }
354
355         /* Once the time was set using setfileinfo then it stays set - writes
356            don't have any effect. But make sure. */
357         start = timeval_current();
358         end = timeval_add(&start, (15*sec), 0);
359         while (!timeval_expired(&end)) {
360                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
361
362                 if (!NT_STATUS_IS_OK(status)) {
363                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
364                         ret = false;
365                         break;
366                 }
367                 torture_comment(tctx, "write time %s\n", 
368                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
369                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
370                         double diff = timeval_elapsed(&start);
371                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
372                                         "(1sec == %.2f) (wrong!)\n",
373                                         diff, sec);
374                         ret = false;
375                         break;
376                 }
377                 fflush(stdout);
378                 msleep(1 * msec);
379         }
380         
381         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
382                 torture_comment(tctx, "Server did not update write time (correct)\n");
383         }
384
385         torture_comment(tctx, "Closing second fd to see if write time updated.\n");
386
387         smbcli_close(cli->tree, fnum2);
388         fnum2 = -1;
389
390         fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
391         if (fnum1 == -1) {
392                 torture_comment(tctx, "Failed to open %s\n", fname);
393                 return false;
394         }
395
396         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
397         finfo1.basic_info.in.file.fnum = fnum1;
398         finfo2 = finfo1;
399
400         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
401
402         if (!NT_STATUS_IS_OK(status)) {
403                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
404                 return false;
405         }
406         
407         torture_comment(tctx, "Second open initial write time %s\n", 
408                nt_time_string(tctx, finfo1.basic_info.out.write_time));
409
410         msleep(10 * msec);
411         torture_comment(tctx, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
412
413         written =  smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
414
415         if (written != 10) {
416                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
417                        (int)written, __location__);
418                 return false;
419         }
420
421         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
422         finfo1.basic_info.in.file.fnum = fnum1;
423         finfo2 = finfo1;
424         status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
425
426         if (!NT_STATUS_IS_OK(status)) {
427                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
428                 return false;
429         }
430         torture_comment(tctx, "write time %s\n", 
431                nt_time_string(tctx, finfo2.basic_info.out.write_time));
432         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
433                 torture_comment(tctx, "Server updated write_time (wrong!)\n");
434                 ret = false;
435         }
436
437         /* Now the write time should be updated again */
438         start = timeval_current();
439         end = timeval_add(&start, (15*sec), 0);
440         while (!timeval_expired(&end)) {
441                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
442
443                 if (!NT_STATUS_IS_OK(status)) {
444                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
445                         ret = false;
446                         break;
447                 }
448                 torture_comment(tctx, "write time %s\n", 
449                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
450                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
451                         double diff = timeval_elapsed(&start);
452                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
453                                 torture_comment(tctx, "Server updated write_time after %.2f seconds"
454                                                 "(1sec == %.2f) (wrong!)\n",
455                                                 diff, sec);
456                                 ret = false;
457                                 break;
458                         }
459
460                         torture_comment(tctx, "Server updated write_time after %.2f seconds"
461                                         "(1sec == %.2f) (correct)\n",
462                                         diff, sec);
463                         break;
464                 }
465                 fflush(stdout);
466                 msleep(1*msec);
467         }
468         
469         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
470                 torture_comment(tctx, "Server did not update write time (wrong!)\n");
471                 ret = false;
472         }
473
474
475         /* One more test to do. We should read the filetime via findfirst on the
476            second connection to ensure it's the same. This is very easy for a Windows
477            server but a bastard to get right on a POSIX server. JRA. */
478
479         if (fnum1 != -1)
480                 smbcli_close(cli->tree, fnum1);
481         smbcli_unlink(cli->tree, fname);
482         smbcli_deltree(cli->tree, BASEDIR);
483
484         return ret;
485 }
486
487
488 /* Windows does obviously not update the stat info during a write call. I
489  * *think* this is the problem causing a spurious Excel 2003 on XP error
490  * message when saving a file. Excel does a setfileinfo, writes, and then does
491  * a getpath(!)info. Or so... For Samba sometimes it displays an error message
492  * that the file might have been changed in between. What i've been able to
493  * trace down is that this happens if the getpathinfo after the write shows a
494  * different last write time than the setfileinfo showed. This is really
495  * nasty....
496  */
497
498 static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli, 
499                                                                    struct smbcli_state *cli2)
500 {
501         union smb_fileinfo finfo1, finfo2;
502         const char *fname = BASEDIR "\\torture_file.txt";
503         NTSTATUS status;
504         int fnum1 = -1;
505         int fnum2;
506         bool ret = true;
507         ssize_t written;
508         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
509         int normal_delay = 2000000;
510         double sec = ((double)used_delay) / ((double)normal_delay);
511         int msec = 1000 * sec;
512
513         if (!torture_setup_dir(cli, BASEDIR)) {
514                 return false;
515         }
516
517         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
518         if (fnum1 == -1) {
519                 ret = false;
520                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
521                 goto done;
522         }
523
524         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
525         finfo1.basic_info.in.file.fnum = fnum1;
526
527         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
528
529         if (!NT_STATUS_IS_OK(status)) {
530                 ret = false;
531                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
532                 goto done;
533         }
534
535         msleep(1 * msec);
536
537         written =  smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
538
539         if (written != 1) {
540                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
541                 ret = false;
542                 goto done;
543         }
544
545         fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
546         if (fnum2 == -1) {
547                 torture_result(tctx, TORTURE_FAIL, __location__": failed to open 2nd time - %s", 
548                        smbcli_errstr(cli2->tree));
549                 ret = false;
550                 goto done;
551         }
552         
553         written =  smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
554         
555         if (written != 1) {
556                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", 
557                        (int)written);
558                 ret = false;
559                 goto done;
560         }
561         
562         finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
563         finfo2.basic_info.in.file.path = fname;
564         
565         status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
566         
567         if (!NT_STATUS_IS_OK(status)) {
568                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", 
569                           nt_errstr(status));
570                 ret = false;
571                 goto done;
572         }
573         
574         if (finfo1.basic_info.out.create_time !=
575             finfo2.basic_info.out.create_time) {
576                 torture_result(tctx, TORTURE_FAIL, __location__": create_time changed");
577                 ret = false;
578                 goto done;
579         }
580         
581         if (finfo1.basic_info.out.access_time !=
582             finfo2.basic_info.out.access_time) {
583                 torture_result(tctx, TORTURE_FAIL, __location__": access_time changed");
584                 ret = false;
585                 goto done;
586         }
587         
588         if (finfo1.basic_info.out.write_time !=
589             finfo2.basic_info.out.write_time) {
590                 torture_result(tctx, TORTURE_FAIL, __location__": write_time changed:\n"
591                                            "write time conn 1 = %s, conn 2 = %s", 
592                        nt_time_string(tctx, finfo1.basic_info.out.write_time),
593                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
594                 ret = false;
595                 goto done;
596         }
597         
598         if (finfo1.basic_info.out.change_time !=
599             finfo2.basic_info.out.change_time) {
600                 torture_result(tctx, TORTURE_FAIL, __location__": change_time changed");
601                 ret = false;
602                 goto done;
603         }
604         
605         /* One of the two following calls updates the qpathinfo. */
606         
607         /* If you had skipped the smbcli_write on fnum2, it would
608          * *not* have updated the stat on disk */
609         
610         smbcli_close(cli2->tree, fnum2);
611         cli2 = NULL;
612
613         /* This call is only for the people looking at ethereal :-) */
614         finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
615         finfo2.basic_info.in.file.path = fname;
616
617         status = smb_raw_pathinfo(cli->tree, tctx, &finfo2);
618
619         if (!NT_STATUS_IS_OK(status)) {
620                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
621                 ret = false;
622                 goto done;
623         }
624
625  done:
626         if (fnum1 != -1)
627                 smbcli_close(cli->tree, fnum1);
628         smbcli_unlink(cli->tree, fname);
629         smbcli_deltree(cli->tree, BASEDIR);
630
631         return ret;
632 }
633
634 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
635         NTTIME g = (given).basic_info.out.write_time; \
636         NTTIME c = (correct).basic_info.out.write_time; \
637         if (g cmp c) { \
638                 torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \
639                                 #given, nt_time_string(tctx, g), (unsigned long long)g, \
640                                 #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \
641                 ret = false; \
642                 goto done; \
643         } \
644 } while (0)
645 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
646         COMPARE_WRITE_TIME_CMP(given,correct,!=)
647 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
648         COMPARE_WRITE_TIME_CMP(given,correct,<=)
649 #define COMPARE_WRITE_TIME_LESS(given,correct) \
650         COMPARE_WRITE_TIME_CMP(given,correct,>=)
651
652 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
653         NTTIME g = (given).basic_info.out.access_time; \
654         NTTIME c = (correct).basic_info.out.access_time; \
655         if (g cmp c) { \
656                 torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \
657                                 #given, nt_time_string(tctx, g), \
658                                 #cmp, #correct, nt_time_string(tctx, c)); \
659                 ret = false; \
660                 goto done; \
661         } \
662 } while (0)
663 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
664         COMPARE_ACCESS_TIME_CMP(given,correct,!=)
665 #define COMPARE_ACCESS_TIME_GREATER(given,correct) \
666         COMPARE_ACCESS_TIME_CMP(given,correct,<=)
667 #define COMPARE_ACCESS_TIME_LESS(given,correct) \
668         COMPARE_ACCESS_TIME_CMP(given,correct,>=)
669
670 #define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \
671         COMPARE_ACCESS_TIME_EQUAL(given,correct); \
672         COMPARE_WRITE_TIME_EQUAL(given,correct); \
673 } while (0)
674 #define COMPARE_BOTH_TIMES_GEATER(given,correct) do { \
675         COMPARE_ACCESS_TIME_GREATER(given,correct); \
676         COMPARE_WRITE_TIME_GREATER(given,correct); \
677 } while (0)
678 #define COMPARE_BOTH_TIMES_LESS(given,correct) do { \
679         COMPARE_ACCESS_TIME_LESS(given,correct); \
680         COMPARE_WRITE_TIME_LESS(given,correct); \
681 } while (0)
682
683 #define GET_INFO_FILE(finfo) do { \
684         NTSTATUS _status; \
685         _status = smb_raw_fileinfo(cli->tree, tctx, &finfo); \
686         if (!NT_STATUS_IS_OK(_status)) { \
687                 ret = false; \
688                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
689                                nt_errstr(_status)); \
690                 goto done; \
691         } \
692         torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
693                         nt_time_string(tctx, finfo.basic_info.out.access_time), \
694                         nt_time_string(tctx, finfo.basic_info.out.write_time)); \
695 } while (0)
696 #define GET_INFO_PATH(pinfo) do { \
697         NTSTATUS _status; \
698         _status = smb_raw_pathinfo(cli2->tree, tctx, &pinfo); \
699         if (!NT_STATUS_IS_OK(_status)) { \
700                 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \
701                                nt_errstr(_status)); \
702                 ret = false; \
703                 goto done; \
704         } \
705         torture_comment(tctx, "pathinfo: Access(%s) Write(%s)\n", \
706                         nt_time_string(tctx, pinfo.basic_info.out.access_time), \
707                         nt_time_string(tctx, pinfo.basic_info.out.write_time)); \
708 } while (0)
709 #define GET_INFO_BOTH(finfo,pinfo) do { \
710         GET_INFO_FILE(finfo); \
711         GET_INFO_PATH(pinfo); \
712         COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \
713 } while (0)
714
715 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
716         NTSTATUS _status; \
717         union smb_setfileinfo sfinfo; \
718         sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
719         sfinfo.basic_info.in.file.fnum = tfnum; \
720         sfinfo.basic_info.in.create_time = 0; \
721         sfinfo.basic_info.in.access_time = 0; \
722         unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
723         sfinfo.basic_info.in.change_time = 0; \
724         sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
725         _status = smb_raw_setfileinfo(tree, &sfinfo); \
726         if (!NT_STATUS_IS_OK(_status)) { \
727                 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
728                                nt_errstr(_status)); \
729                 ret = false; \
730                 goto done; \
731         } \
732 } while (0)
733 #define SET_INFO_FILE(finfo, wrtime) \
734         SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
735
736 static bool test_delayed_write_update3(struct torture_context *tctx,
737                                        struct smbcli_state *cli,
738                                        struct smbcli_state *cli2)
739 {
740         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
741         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
742         const char *fname = BASEDIR "\\torture_file.txt";
743         int fnum1 = -1;
744         bool ret = true;
745         ssize_t written;
746         struct timeval start;
747         struct timeval end;
748         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
749         int normal_delay = 2000000;
750         double sec = ((double)used_delay) / ((double)normal_delay);
751         int msec = 1000 * sec;
752
753         if (!torture_setup_dir(cli, BASEDIR)) {
754                 return false;
755         }
756
757         torture_comment(tctx, "Open the file handle\n");
758         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
759         if (fnum1 == -1) {
760                 ret = false;
761                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
762                 goto done;
763         }
764
765         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
766         finfo0.basic_info.in.file.fnum = fnum1;
767         finfo1 = finfo0;
768         finfo2 = finfo0;
769         finfo3 = finfo0;
770         finfo4 = finfo0;
771         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
772         pinfo0.basic_info.in.file.path = fname;
773         pinfo1 = pinfo0;
774         pinfo2 = pinfo0;
775         pinfo3 = pinfo0;
776         pinfo4 = pinfo0;
777         pinfo5 = pinfo0;
778
779         /* get the initial times */
780         GET_INFO_BOTH(finfo0,pinfo0);
781
782         /*
783          * make sure the write time is updated 2 seconds later
784          * calcuated from the first write
785          * (but expect upto 5 seconds extra time for a busy server)
786          */
787         start = timeval_current();
788         end = timeval_add(&start, 7 * sec, 0);
789         while (!timeval_expired(&end)) {
790                 /* do a write */
791                 torture_comment(tctx, "Do a write on the file handle\n");
792                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
793                 if (written != 1) {
794                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
795                         ret = false;
796                         goto done;
797                 }
798                 /* get the times after the write */
799                 GET_INFO_FILE(finfo1);
800
801                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
802                         double diff = timeval_elapsed(&start);
803                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
804                                 torture_comment(tctx, "Server updated write_time after %.2f seconds "
805                                                 "(1sec == %.2f) (wrong!)\n",
806                                                 diff, sec);
807                                 ret = false;
808                                 break;
809                         }
810
811                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
812                                         "(1sec == %.2f) (correct)\n",
813                                         diff, sec);
814                         break;
815                 }
816                 msleep(0.5 * msec);
817         }
818
819         GET_INFO_BOTH(finfo1,pinfo1);
820
821         /* sure any further write doesn't update the write time */
822         start = timeval_current();
823         end = timeval_add(&start, 15 * sec, 0);
824         while (!timeval_expired(&end)) {
825                 /* do a write */
826                 torture_comment(tctx, "Do a write on the file handle\n");
827                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
828                 if (written != 1) {
829                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
830                         ret = false;
831                         goto done;
832                 }
833                 /* get the times after the write */
834                 GET_INFO_BOTH(finfo2,pinfo2);
835
836                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
837                         double diff = timeval_elapsed(&start);
838                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
839                                         "(1sec == %.2f) (wrong!)\n",
840                                         diff, sec);
841                         ret = false;
842                         break;
843                 }
844                 msleep(2 * msec);
845         }
846
847         GET_INFO_BOTH(finfo2,pinfo2);
848         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
849         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
850                 torture_comment(tctx, "Server did not update write_time (correct)\n");
851         }
852
853         /* sleep */
854         msleep(5 * msec);
855
856         GET_INFO_BOTH(finfo3,pinfo3);
857         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
858
859         /*
860          * the close updates the write time to the time of the close
861          * and not to the time of the last write!
862          */
863         torture_comment(tctx, "Close the file handle\n");
864         smbcli_close(cli->tree, fnum1);
865         fnum1 = -1;
866
867         GET_INFO_PATH(pinfo4);
868         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
869
870         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
871                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
872         }
873
874  done:
875         if (fnum1 != -1)
876                 smbcli_close(cli->tree, fnum1);
877         smbcli_unlink(cli->tree, fname);
878         smbcli_deltree(cli->tree, BASEDIR);
879
880         return ret;
881 }
882
883 static bool test_delayed_write_update4(struct torture_context *tctx,
884                                        struct smbcli_state *cli,
885                                        struct smbcli_state *cli2)
886 {
887         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
888         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
889         const char *fname = BASEDIR "\\torture_file.txt";
890         int fnum1 = -1;
891         bool ret = true;
892         ssize_t written;
893         struct timeval start;
894         struct timeval end;
895         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
896         int normal_delay = 2000000;
897         double sec = ((double)used_delay) / ((double)normal_delay);
898         int msec = 1000 * sec;
899
900         if (!torture_setup_dir(cli, BASEDIR)) {
901                 return false;
902         }
903
904         torture_comment(tctx, "Open the file handle\n");
905         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
906         if (fnum1 == -1) {
907                 ret = false;
908                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
909                 goto done;
910         }
911
912         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
913         finfo0.basic_info.in.file.fnum = fnum1;
914         finfo1 = finfo0;
915         finfo2 = finfo0;
916         finfo3 = finfo0;
917         finfo4 = finfo0;
918         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
919         pinfo0.basic_info.in.file.path = fname;
920         pinfo1 = pinfo0;
921         pinfo2 = pinfo0;
922         pinfo3 = pinfo0;
923         pinfo4 = pinfo0;
924         pinfo5 = pinfo0;
925
926         /* get the initial times */
927         GET_INFO_BOTH(finfo0,pinfo0);
928
929         /* sleep a bit */
930         msleep(5 * msec);
931
932         /* do a write */
933         torture_comment(tctx, "Do a write on the file handle\n");
934         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
935         if (written != 1) {
936                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
937                 ret = false;
938                 goto done;
939         }
940
941         GET_INFO_BOTH(finfo1,pinfo1);
942         COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
943
944         /*
945          * make sure the write time is updated 2 seconds later
946          * calcuated from the first write
947          * (but expect upto 3 seconds extra time for a busy server)
948          */
949         start = timeval_current();
950         end = timeval_add(&start, 5 * sec, 0);
951         while (!timeval_expired(&end)) {
952                 /* get the times after the first write */
953                 GET_INFO_FILE(finfo1);
954
955                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
956                         double diff = timeval_elapsed(&start);
957                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
958                                 torture_comment(tctx, "Server updated write_time after %.2f seconds "
959                                                 "(1sec == %.2f) (wrong!)\n",
960                                                 diff, sec);
961                                 ret = false;
962                                 break;
963                         }
964
965                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
966                                         "(1sec == %.2f) (correct)\n",
967                                         diff, sec);
968                         break;
969                 }
970                 msleep(0.5 * msec);
971         }
972
973         GET_INFO_BOTH(finfo1,pinfo1);
974
975         /* sure any further write doesn't update the write time */
976         start = timeval_current();
977         end = timeval_add(&start, 15 * sec, 0);
978         while (!timeval_expired(&end)) {
979                 /* do a write */
980                 torture_comment(tctx, "Do a write on the file handle\n");
981                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
982                 if (written != 1) {
983                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
984                         ret = false;
985                         goto done;
986                 }
987                 /* get the times after the write */
988                 GET_INFO_BOTH(finfo2,pinfo2);
989
990                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
991                         double diff = timeval_elapsed(&start);
992                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
993                                         "(1sec == %.2f) (wrong!)\n",
994                                         diff, sec);
995                         ret = false;
996                         break;
997                 }
998                 msleep(2 * msec);
999         }
1000
1001         GET_INFO_BOTH(finfo2,pinfo2);
1002         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1003         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1004                 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
1005         }
1006
1007         /* sleep */
1008         msleep(5 * msec);
1009
1010         GET_INFO_BOTH(finfo3,pinfo3);
1011         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1012
1013         /*
1014          * the close updates the write time to the time of the close
1015          * and not to the time of the last write!
1016          */
1017         torture_comment(tctx, "Close the file handle\n");
1018         smbcli_close(cli->tree, fnum1);
1019         fnum1 = -1;
1020
1021         GET_INFO_PATH(pinfo4);
1022         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1023
1024         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1025                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1026         }
1027
1028  done:
1029         if (fnum1 != -1)
1030                 smbcli_close(cli->tree, fnum1);
1031         smbcli_unlink(cli->tree, fname);
1032         smbcli_deltree(cli->tree, BASEDIR);
1033
1034         return ret;
1035 }
1036
1037 static bool test_delayed_write_update5(struct torture_context *tctx,
1038                                        struct smbcli_state *cli,
1039                                        struct smbcli_state *cli2)
1040 {
1041         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
1042         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
1043         const char *fname = BASEDIR "\\torture_file.txt";
1044         int fnum1 = -1;
1045         bool ret = true;
1046         ssize_t written;
1047         struct timeval start;
1048         struct timeval end;
1049         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1050         int normal_delay = 2000000;
1051         double sec = ((double)used_delay) / ((double)normal_delay);
1052         int msec = 1000 * sec;
1053
1054         if (!torture_setup_dir(cli, BASEDIR)) {
1055                 return false;
1056         }
1057
1058         torture_comment(tctx, "Open the file handle\n");
1059         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1060         if (fnum1 == -1) {
1061                 ret = false;
1062                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1063                 goto done;
1064         }
1065
1066         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1067         finfo0.basic_info.in.file.fnum = fnum1;
1068         finfo1 = finfo0;
1069         finfo2 = finfo0;
1070         finfo3 = finfo0;
1071         finfo4 = finfo0;
1072         finfo5 = finfo0;
1073         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1074         pinfo0.basic_info.in.file.path = fname;
1075         pinfo1 = pinfo0;
1076         pinfo2 = pinfo0;
1077         pinfo3 = pinfo0;
1078         pinfo4 = pinfo0;
1079         pinfo5 = pinfo0;
1080         pinfo6 = pinfo0;
1081
1082         /* get the initial times */
1083         GET_INFO_BOTH(finfo0,pinfo0);
1084
1085         /* do a write */
1086         torture_comment(tctx, "Do a write on the file handle\n");
1087         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1088         if (written != 1) {
1089                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1090                 ret = false;
1091                 goto done;
1092         }
1093
1094         GET_INFO_BOTH(finfo1,pinfo1);
1095         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1096
1097         torture_comment(tctx, "Set write time in the future on the file handle\n");
1098         SET_INFO_FILE(finfo0, time(NULL) + 86400);
1099         GET_INFO_BOTH(finfo2,pinfo2);
1100         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1101
1102         torture_comment(tctx, "Set write time in the past on the file handle\n");
1103         SET_INFO_FILE(finfo0, time(NULL) - 86400);
1104         GET_INFO_BOTH(finfo2,pinfo2);
1105         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
1106
1107         /* make sure the 2 second delay from the first write are canceled */
1108         start = timeval_current();
1109         end = timeval_add(&start, 15 * sec, 0);
1110         while (!timeval_expired(&end)) {
1111
1112                 /* get the times after the first write */
1113                 GET_INFO_BOTH(finfo3,pinfo3);
1114
1115                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
1116                         double diff = timeval_elapsed(&start);
1117                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1118                                         "(1sec == %.2f) (wrong!)\n",
1119                                         diff, sec);
1120                         ret = false;
1121                         break;
1122                 }
1123                 msleep(2 * msec);
1124         }
1125
1126         GET_INFO_BOTH(finfo3,pinfo3);
1127         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1128         if (finfo3.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1129                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1130         }
1131
1132         /* sure any further write doesn't update the write time */
1133         start = timeval_current();
1134         end = timeval_add(&start, 15 * sec, 0);
1135         while (!timeval_expired(&end)) {
1136                 /* do a write */
1137                 torture_comment(tctx, "Do a write on the file handle\n");
1138                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1139                 if (written != 1) {
1140                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1141                         ret = false;
1142                         goto done;
1143                 }
1144                 /* get the times after the write */
1145                 GET_INFO_BOTH(finfo4,pinfo4);
1146
1147                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
1148                         double diff = timeval_elapsed(&start);
1149                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1150                                         "(1sec == %.2f) (wrong!)\n",
1151                                         diff, sec);
1152                         ret = false;
1153                         break;
1154                 }
1155                 msleep(2 * msec);
1156         }
1157
1158         GET_INFO_BOTH(finfo4,pinfo4);
1159         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
1160         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1161                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1162         }
1163
1164         /* sleep */
1165         msleep(5 * msec);
1166
1167         GET_INFO_BOTH(finfo5,pinfo5);
1168         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
1169
1170         /*
1171          * the close doesn't update the write time
1172          */
1173         torture_comment(tctx, "Close the file handle\n");
1174         smbcli_close(cli->tree, fnum1);
1175         fnum1 = -1;
1176
1177         GET_INFO_PATH(pinfo6);
1178         COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
1179
1180         if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
1181                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1182         }
1183
1184  done:
1185         if (fnum1 != -1)
1186                 smbcli_close(cli->tree, fnum1);
1187         smbcli_unlink(cli->tree, fname);
1188         smbcli_deltree(cli->tree, BASEDIR);
1189
1190         return ret;
1191 }
1192
1193 static bool test_delayed_write_update6(struct torture_context *tctx,
1194                                        struct smbcli_state *cli,
1195                                        struct smbcli_state *cli2)
1196 {
1197         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
1198         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
1199         const char *fname = BASEDIR "\\torture_file.txt";
1200         int fnum1 = -1;
1201         int fnum2 = -1;
1202         bool ret = true;
1203         ssize_t written;
1204         struct timeval start;
1205         struct timeval end;
1206         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1207         int normal_delay = 2000000;
1208         double sec = ((double)used_delay) / ((double)normal_delay);
1209         int msec = 1000 * sec;
1210         bool first = true;
1211
1212         if (!torture_setup_dir(cli, BASEDIR)) {
1213                 return false;
1214         }
1215 again:
1216         torture_comment(tctx, "Open the file handle\n");
1217         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1218         if (fnum1 == -1) {
1219                 ret = false;
1220                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1221                 goto done;
1222         }
1223
1224         if (fnum2 == -1) {
1225                 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
1226                 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1227                 if (fnum2 == -1) {
1228                         ret = false;
1229                         torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1230                         goto done;
1231                 }
1232         }
1233
1234         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1235         finfo0.basic_info.in.file.fnum = fnum1;
1236         finfo1 = finfo0;
1237         finfo2 = finfo0;
1238         finfo3 = finfo0;
1239         finfo4 = finfo0;
1240         finfo5 = finfo0;
1241         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1242         pinfo0.basic_info.in.file.path = fname;
1243         pinfo1 = pinfo0;
1244         pinfo2 = pinfo0;
1245         pinfo3 = pinfo0;
1246         pinfo4 = pinfo0;
1247         pinfo5 = pinfo0;
1248         pinfo6 = pinfo0;
1249         pinfo7 = pinfo0;
1250
1251         /* get the initial times */
1252         GET_INFO_BOTH(finfo0,pinfo0);
1253
1254         /* do a write */
1255         torture_comment(tctx, "Do a write on the file handle\n");
1256         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1257         if (written != 1) {
1258                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1259                 ret = false;
1260                 goto done;
1261         }
1262
1263         GET_INFO_BOTH(finfo1,pinfo1);
1264         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1265
1266         torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
1267         SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
1268         GET_INFO_BOTH(finfo2,pinfo2);
1269         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1270
1271         torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
1272         SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
1273         GET_INFO_BOTH(finfo2,pinfo2);
1274         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
1275
1276         /* make sure the 2 second delay from the first write are canceled */
1277         start = timeval_current();
1278         end = timeval_add(&start, 15 * sec, 0);
1279         while (!timeval_expired(&end)) {
1280
1281                 /* get the times after the first write */
1282                 GET_INFO_BOTH(finfo3,pinfo3);
1283
1284                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
1285                         double diff = timeval_elapsed(&start);
1286                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1287                                         "(1sec == %.2f) (wrong!)\n",
1288                                         diff, sec);
1289                         ret = false;
1290                         break;
1291                 }
1292                 msleep(2 * msec);
1293         }
1294
1295         GET_INFO_BOTH(finfo3,pinfo3);
1296         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1297         if (finfo3.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1298                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1299         }
1300
1301         /* sure any further write doesn't update the write time */
1302         start = timeval_current();
1303         end = timeval_add(&start, 15 * sec, 0);
1304         while (!timeval_expired(&end)) {
1305                 /* do a write */
1306                 torture_comment(tctx, "Do a write on the file handle\n");
1307                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1308                 if (written != 1) {
1309                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1310                         ret = false;
1311                         goto done;
1312                 }
1313                 /* get the times after the write */
1314                 GET_INFO_BOTH(finfo4,pinfo4);
1315
1316                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
1317                         double diff = timeval_elapsed(&start);
1318                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1319                                         "(1sec == %.2f) (wrong!)\n",
1320                                         diff, sec);
1321                         ret = false;
1322                         break;
1323                 }
1324                 msleep(2 * msec);
1325         }
1326
1327         GET_INFO_BOTH(finfo4,pinfo4);
1328         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
1329         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1330                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1331         }
1332
1333         /* sleep */
1334         msleep(5 * msec);
1335
1336         GET_INFO_BOTH(finfo5,pinfo5);
1337         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
1338
1339         /*
1340          * the close updates the write time to the time of the close
1341          * as the write time was set on the 2nd handle
1342          */
1343         torture_comment(tctx, "Close the file handle\n");
1344         smbcli_close(cli->tree, fnum1);
1345         fnum1 = -1;
1346
1347         GET_INFO_PATH(pinfo6);
1348         COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
1349
1350         if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
1351                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1352         }
1353
1354         /* keep the 2nd handle open and rerun tests */
1355         if (first) {
1356                 first = false;
1357                 goto again;
1358         }
1359
1360         /*
1361          * closing the 2nd handle will cause no write time update
1362          * as the write time was explicit set on this handle
1363          */
1364         torture_comment(tctx, "Close the 2nd file handle\n");
1365         smbcli_close(cli2->tree, fnum2);
1366         fnum2 = -1;
1367
1368         GET_INFO_PATH(pinfo7);
1369         COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
1370
1371         if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
1372                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1373         }
1374
1375  done:
1376         if (fnum1 != -1)
1377                 smbcli_close(cli->tree, fnum1);
1378         if (fnum2 != -1)
1379                 smbcli_close(cli2->tree, fnum2);
1380         smbcli_unlink(cli->tree, fname);
1381         smbcli_deltree(cli->tree, BASEDIR);
1382
1383         return ret;
1384 }
1385
1386
1387 /* 
1388    testing of delayed update of write_time
1389 */
1390 struct torture_suite *torture_delay_write(void)
1391 {
1392         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "DELAYWRITE");
1393
1394         torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
1395         torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
1396         torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
1397         torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
1398         torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
1399         torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
1400         torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
1401
1402         return suite;
1403 }