ac4f565a2ba207139599801b2b2ff3b01bdcac86
[kai/samba.git] / source4 / torture / basic / delaywrite.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    test suite for delayed write update 
5
6    Copyright (C) Volker Lendecke 2004
7    Copyright (C) Andrew Tridgell 2004
8    Copyright (C) Jeremy Allison 2004
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "torture/torture.h"
26 #include "libcli/raw/libcliraw.h"
27 #include "libcli/raw/raw_proto.h"
28 #include "system/time.h"
29 #include "system/filesys.h"
30 #include "libcli/libcli.h"
31 #include "torture/util.h"
32
33 #define BASEDIR "\\delaywrite"
34
35 static bool test_delayed_write_update(struct torture_context *tctx, struct smbcli_state *cli)
36 {
37         union smb_fileinfo finfo1, finfo2;
38         const char *fname = BASEDIR "\\torture_file.txt";
39         NTSTATUS status;
40         int fnum1 = -1;
41         bool ret = true;
42         ssize_t written;
43         struct timeval start;
44         struct timeval end;
45         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
46         int normal_delay = 2000000;
47         double sec = ((double)used_delay) / ((double)normal_delay);
48         int msec = 1000 * sec;
49
50         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         uint64_t r = 10*1000*1000; \
636         NTTIME g = (given).basic_info.out.write_time; \
637         NTTIME gr = (g / r) * r; \
638         NTTIME c = (correct).basic_info.out.write_time; \
639         NTTIME cr = (c / r) * r; \
640         bool strict = torture_setting_bool(tctx, "strict mode", false); \
641         bool err = false; \
642         if (strict && (g cmp c)) { \
643                 err = true; \
644         } else if (gr cmp cr) { \
645                 /* handle filesystem without high resolution timestamps */ \
646                 err = true; \
647         } \
648         if (err) { \
649                 torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \
650                                 #given, nt_time_string(tctx, g), (unsigned long long)g, \
651                                 #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \
652                 ret = false; \
653                 goto done; \
654         } \
655 } while (0)
656 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
657         COMPARE_WRITE_TIME_CMP(given,correct,!=)
658 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
659         COMPARE_WRITE_TIME_CMP(given,correct,<=)
660 #define COMPARE_WRITE_TIME_LESS(given,correct) \
661         COMPARE_WRITE_TIME_CMP(given,correct,>=)
662
663 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
664         NTTIME g = (given).basic_info.out.access_time; \
665         NTTIME c = (correct).basic_info.out.access_time; \
666         if (g cmp c) { \
667                 torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \
668                                 #given, nt_time_string(tctx, g), \
669                                 #cmp, #correct, nt_time_string(tctx, c)); \
670                 ret = false; \
671                 goto done; \
672         } \
673 } while (0)
674 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
675         COMPARE_ACCESS_TIME_CMP(given,correct,!=)
676
677 #define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \
678         COMPARE_ACCESS_TIME_EQUAL(given,correct); \
679         COMPARE_WRITE_TIME_EQUAL(given,correct); \
680 } while (0)
681
682 #define GET_INFO_FILE(finfo) do { \
683         NTSTATUS _status; \
684         _status = smb_raw_fileinfo(cli->tree, tctx, &finfo); \
685         if (!NT_STATUS_IS_OK(_status)) { \
686                 ret = false; \
687                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
688                                nt_errstr(_status)); \
689                 goto done; \
690         } \
691         torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
692                         nt_time_string(tctx, finfo.basic_info.out.access_time), \
693                         nt_time_string(tctx, finfo.basic_info.out.write_time)); \
694 } while (0)
695 #define GET_INFO_PATH(pinfo) do { \
696         NTSTATUS _status; \
697         _status = smb_raw_pathinfo(cli2->tree, tctx, &pinfo); \
698         if (!NT_STATUS_IS_OK(_status)) { \
699                 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \
700                                nt_errstr(_status)); \
701                 ret = false; \
702                 goto done; \
703         } \
704         torture_comment(tctx, "pathinfo: Access(%s) Write(%s)\n", \
705                         nt_time_string(tctx, pinfo.basic_info.out.access_time), \
706                         nt_time_string(tctx, pinfo.basic_info.out.write_time)); \
707 } while (0)
708 #define GET_INFO_BOTH(finfo,pinfo) do { \
709         GET_INFO_FILE(finfo); \
710         GET_INFO_PATH(pinfo); \
711         COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \
712 } while (0)
713
714 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
715         NTSTATUS _status; \
716         union smb_setfileinfo sfinfo; \
717         sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
718         sfinfo.basic_info.in.file.fnum = tfnum; \
719         sfinfo.basic_info.in.create_time = 0; \
720         sfinfo.basic_info.in.access_time = 0; \
721         unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
722         sfinfo.basic_info.in.change_time = 0; \
723         sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
724         _status = smb_raw_setfileinfo(tree, &sfinfo); \
725         if (!NT_STATUS_IS_OK(_status)) { \
726                 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
727                                nt_errstr(_status)); \
728                 ret = false; \
729                 goto done; \
730         } \
731 } while (0)
732 #define SET_INFO_FILE(finfo, wrtime) \
733         SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
734
735 static bool test_delayed_write_update3(struct torture_context *tctx,
736                                        struct smbcli_state *cli,
737                                        struct smbcli_state *cli2)
738 {
739         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
740         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
741         const char *fname = BASEDIR "\\torture_file.txt";
742         int fnum1 = -1;
743         bool ret = true;
744         ssize_t written;
745         struct timeval start;
746         struct timeval end;
747         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
748         int normal_delay = 2000000;
749         double sec = ((double)used_delay) / ((double)normal_delay);
750         int msec = 1000 * sec;
751
752         if (!torture_setup_dir(cli, BASEDIR)) {
753                 return false;
754         }
755
756         torture_comment(tctx, "Open the file handle\n");
757         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
758         if (fnum1 == -1) {
759                 ret = false;
760                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
761                 goto done;
762         }
763
764         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
765         finfo0.basic_info.in.file.fnum = fnum1;
766         finfo1 = finfo0;
767         finfo2 = finfo0;
768         finfo3 = finfo0;
769         finfo4 = finfo0;
770         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
771         pinfo0.basic_info.in.file.path = fname;
772         pinfo1 = pinfo0;
773         pinfo2 = pinfo0;
774         pinfo3 = pinfo0;
775         pinfo4 = pinfo0;
776         pinfo5 = pinfo0;
777
778         /* get the initial times */
779         GET_INFO_BOTH(finfo0,pinfo0);
780
781         /*
782          * make sure the write time is updated 2 seconds later
783          * calcuated from the first write
784          * (but expect upto 5 seconds extra time for a busy server)
785          */
786         start = timeval_current();
787         end = timeval_add(&start, 7 * sec, 0);
788         while (!timeval_expired(&end)) {
789                 /* do a write */
790                 torture_comment(tctx, "Do a write on the file handle\n");
791                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
792                 if (written != 1) {
793                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
794                         ret = false;
795                         goto done;
796                 }
797                 /* get the times after the write */
798                 GET_INFO_FILE(finfo1);
799
800                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
801                         double diff = timeval_elapsed(&start);
802                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
803                                 torture_comment(tctx, "Server updated write_time after %.2f seconds "
804                                                 "(1sec == %.2f) (wrong!)\n",
805                                                 diff, sec);
806                                 ret = false;
807                                 break;
808                         }
809
810                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
811                                         "(1sec == %.2f) (correct)\n",
812                                         diff, sec);
813                         break;
814                 }
815                 msleep(0.5 * msec);
816         }
817
818         GET_INFO_BOTH(finfo1,pinfo1);
819
820         /* sure any further write doesn't update the write time */
821         start = timeval_current();
822         end = timeval_add(&start, 15 * sec, 0);
823         while (!timeval_expired(&end)) {
824                 /* do a write */
825                 torture_comment(tctx, "Do a write on the file handle\n");
826                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
827                 if (written != 1) {
828                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
829                         ret = false;
830                         goto done;
831                 }
832                 /* get the times after the write */
833                 GET_INFO_BOTH(finfo2,pinfo2);
834
835                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
836                         double diff = timeval_elapsed(&start);
837                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
838                                         "(1sec == %.2f) (wrong!)\n",
839                                         diff, sec);
840                         ret = false;
841                         break;
842                 }
843                 msleep(2 * msec);
844         }
845
846         GET_INFO_BOTH(finfo2,pinfo2);
847         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
848         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
849                 torture_comment(tctx, "Server did not update write_time (correct)\n");
850         }
851
852         /* sleep */
853         msleep(5 * msec);
854
855         GET_INFO_BOTH(finfo3,pinfo3);
856         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
857
858         /*
859          * the close updates the write time to the time of the close
860          * and not to the time of the last write!
861          */
862         torture_comment(tctx, "Close the file handle\n");
863         smbcli_close(cli->tree, fnum1);
864         fnum1 = -1;
865
866         GET_INFO_PATH(pinfo4);
867         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
868
869         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
870                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
871         }
872
873  done:
874         if (fnum1 != -1)
875                 smbcli_close(cli->tree, fnum1);
876         smbcli_unlink(cli->tree, fname);
877         smbcli_deltree(cli->tree, BASEDIR);
878
879         return ret;
880 }
881
882 static bool test_delayed_write_update4(struct torture_context *tctx,
883                                        struct smbcli_state *cli,
884                                        struct smbcli_state *cli2)
885 {
886         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
887         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
888         const char *fname = BASEDIR "\\torture_file.txt";
889         int fnum1 = -1;
890         bool ret = true;
891         ssize_t written;
892         struct timeval start;
893         struct timeval end;
894         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
895         int normal_delay = 2000000;
896         double sec = ((double)used_delay) / ((double)normal_delay);
897         int msec = 1000 * sec;
898
899         if (!torture_setup_dir(cli, BASEDIR)) {
900                 return false;
901         }
902
903         torture_comment(tctx, "Open the file handle\n");
904         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
905         if (fnum1 == -1) {
906                 ret = false;
907                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
908                 goto done;
909         }
910
911         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
912         finfo0.basic_info.in.file.fnum = fnum1;
913         finfo1 = finfo0;
914         finfo2 = finfo0;
915         finfo3 = finfo0;
916         finfo4 = finfo0;
917         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
918         pinfo0.basic_info.in.file.path = fname;
919         pinfo1 = pinfo0;
920         pinfo2 = pinfo0;
921         pinfo3 = pinfo0;
922         pinfo4 = pinfo0;
923         pinfo5 = pinfo0;
924
925         /* get the initial times */
926         GET_INFO_BOTH(finfo0,pinfo0);
927
928         /* sleep a bit */
929         msleep(5 * msec);
930
931         /* do a write */
932         torture_comment(tctx, "Do a write on the file handle\n");
933         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
934         if (written != 1) {
935                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
936                 ret = false;
937                 goto done;
938         }
939
940         GET_INFO_BOTH(finfo1,pinfo1);
941         COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
942
943         /*
944          * make sure the write time is updated 2 seconds later
945          * calcuated from the first write
946          * (but expect upto 3 seconds extra time for a busy server)
947          */
948         start = timeval_current();
949         end = timeval_add(&start, 5 * sec, 0);
950         while (!timeval_expired(&end)) {
951                 /* get the times after the first write */
952                 GET_INFO_FILE(finfo1);
953
954                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
955                         double diff = timeval_elapsed(&start);
956                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
957                                 torture_comment(tctx, "Server updated write_time after %.2f seconds "
958                                                 "(1sec == %.2f) (wrong!)\n",
959                                                 diff, sec);
960                                 ret = false;
961                                 break;
962                         }
963
964                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
965                                         "(1sec == %.2f) (correct)\n",
966                                         diff, sec);
967                         break;
968                 }
969                 msleep(0.5 * msec);
970         }
971
972         GET_INFO_BOTH(finfo1,pinfo1);
973
974         /* sure any further write doesn't update the write time */
975         start = timeval_current();
976         end = timeval_add(&start, 15 * sec, 0);
977         while (!timeval_expired(&end)) {
978                 /* do a write */
979                 torture_comment(tctx, "Do a write on the file handle\n");
980                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
981                 if (written != 1) {
982                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
983                         ret = false;
984                         goto done;
985                 }
986                 /* get the times after the write */
987                 GET_INFO_BOTH(finfo2,pinfo2);
988
989                 if (finfo2.basic_info.out.write_time > finfo1.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                 msleep(2 * msec);
998         }
999
1000         GET_INFO_BOTH(finfo2,pinfo2);
1001         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1002         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1003                 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
1004         }
1005
1006         /* sleep */
1007         msleep(5 * msec);
1008
1009         GET_INFO_BOTH(finfo3,pinfo3);
1010         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1011
1012         /*
1013          * the close updates the write time to the time of the close
1014          * and not to the time of the last write!
1015          */
1016         torture_comment(tctx, "Close the file handle\n");
1017         smbcli_close(cli->tree, fnum1);
1018         fnum1 = -1;
1019
1020         GET_INFO_PATH(pinfo4);
1021         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1022
1023         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1024                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1025         }
1026
1027  done:
1028         if (fnum1 != -1)
1029                 smbcli_close(cli->tree, fnum1);
1030         smbcli_unlink(cli->tree, fname);
1031         smbcli_deltree(cli->tree, BASEDIR);
1032
1033         return ret;
1034 }
1035
1036 static bool test_delayed_write_update5(struct torture_context *tctx,
1037                                        struct smbcli_state *cli,
1038                                        struct smbcli_state *cli2)
1039 {
1040         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
1041         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
1042         const char *fname = BASEDIR "\\torture_file.txt";
1043         int fnum1 = -1;
1044         bool ret = true;
1045         ssize_t written;
1046         struct timeval start;
1047         struct timeval end;
1048         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1049         int normal_delay = 2000000;
1050         double sec = ((double)used_delay) / ((double)normal_delay);
1051         int msec = 1000 * sec;
1052
1053         if (!torture_setup_dir(cli, BASEDIR)) {
1054                 return false;
1055         }
1056
1057         torture_comment(tctx, "Open the file handle\n");
1058         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1059         if (fnum1 == -1) {
1060                 ret = false;
1061                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1062                 goto done;
1063         }
1064
1065         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1066         finfo0.basic_info.in.file.fnum = fnum1;
1067         finfo1 = finfo0;
1068         finfo2 = finfo0;
1069         finfo3 = finfo0;
1070         finfo4 = finfo0;
1071         finfo5 = finfo0;
1072         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1073         pinfo0.basic_info.in.file.path = fname;
1074         pinfo1 = pinfo0;
1075         pinfo2 = pinfo0;
1076         pinfo3 = pinfo0;
1077         pinfo4 = pinfo0;
1078         pinfo5 = pinfo0;
1079         pinfo6 = pinfo0;
1080
1081         /* get the initial times */
1082         GET_INFO_BOTH(finfo0,pinfo0);
1083
1084         /* do a write */
1085         torture_comment(tctx, "Do a write on the file handle\n");
1086         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1087         if (written != 1) {
1088                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1089                 ret = false;
1090                 goto done;
1091         }
1092
1093         GET_INFO_BOTH(finfo1,pinfo1);
1094         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1095
1096         torture_comment(tctx, "Set write time in the future on the file handle\n");
1097         SET_INFO_FILE(finfo0, time(NULL) + 86400);
1098         GET_INFO_BOTH(finfo2,pinfo2);
1099         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1100
1101         torture_comment(tctx, "Set write time in the past on the file handle\n");
1102         SET_INFO_FILE(finfo0, time(NULL) - 86400);
1103         GET_INFO_BOTH(finfo2,pinfo2);
1104         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
1105
1106         /* make sure the 2 second delay from the first write are canceled */
1107         start = timeval_current();
1108         end = timeval_add(&start, 15 * sec, 0);
1109         while (!timeval_expired(&end)) {
1110
1111                 /* get the times after the first write */
1112                 GET_INFO_BOTH(finfo3,pinfo3);
1113
1114                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
1115                         double diff = timeval_elapsed(&start);
1116                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1117                                         "(1sec == %.2f) (wrong!)\n",
1118                                         diff, sec);
1119                         ret = false;
1120                         break;
1121                 }
1122                 msleep(2 * msec);
1123         }
1124
1125         GET_INFO_BOTH(finfo3,pinfo3);
1126         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1127         if (finfo3.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1128                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1129         }
1130
1131         /* sure any further write doesn't update the write time */
1132         start = timeval_current();
1133         end = timeval_add(&start, 15 * sec, 0);
1134         while (!timeval_expired(&end)) {
1135                 /* do a write */
1136                 torture_comment(tctx, "Do a write on the file handle\n");
1137                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1138                 if (written != 1) {
1139                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1140                         ret = false;
1141                         goto done;
1142                 }
1143                 /* get the times after the write */
1144                 GET_INFO_BOTH(finfo4,pinfo4);
1145
1146                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
1147                         double diff = timeval_elapsed(&start);
1148                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1149                                         "(1sec == %.2f) (wrong!)\n",
1150                                         diff, sec);
1151                         ret = false;
1152                         break;
1153                 }
1154                 msleep(2 * msec);
1155         }
1156
1157         GET_INFO_BOTH(finfo4,pinfo4);
1158         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
1159         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1160                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1161         }
1162
1163         /* sleep */
1164         msleep(5 * msec);
1165
1166         GET_INFO_BOTH(finfo5,pinfo5);
1167         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
1168
1169         /*
1170          * the close doesn't update the write time
1171          */
1172         torture_comment(tctx, "Close the file handle\n");
1173         smbcli_close(cli->tree, fnum1);
1174         fnum1 = -1;
1175
1176         GET_INFO_PATH(pinfo6);
1177         COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
1178
1179         if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
1180                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1181         }
1182
1183  done:
1184         if (fnum1 != -1)
1185                 smbcli_close(cli->tree, fnum1);
1186         smbcli_unlink(cli->tree, fname);
1187         smbcli_deltree(cli->tree, BASEDIR);
1188
1189         return ret;
1190 }
1191
1192 static bool test_delayed_write_update6(struct torture_context *tctx,
1193                                        struct smbcli_state *cli,
1194                                        struct smbcli_state *cli2)
1195 {
1196         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
1197         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
1198         const char *fname = BASEDIR "\\torture_file.txt";
1199         int fnum1 = -1;
1200         int fnum2 = -1;
1201         bool ret = true;
1202         ssize_t written;
1203         struct timeval start;
1204         struct timeval end;
1205         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1206         int normal_delay = 2000000;
1207         double sec = ((double)used_delay) / ((double)normal_delay);
1208         int msec = 1000 * sec;
1209         bool first = true;
1210
1211         if (!torture_setup_dir(cli, BASEDIR)) {
1212                 return false;
1213         }
1214 again:
1215         torture_comment(tctx, "Open the file handle\n");
1216         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1217         if (fnum1 == -1) {
1218                 ret = false;
1219                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1220                 goto done;
1221         }
1222
1223         if (fnum2 == -1) {
1224                 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
1225                 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1226                 if (fnum2 == -1) {
1227                         ret = false;
1228                         torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1229                         goto done;
1230                 }
1231         }
1232
1233         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1234         finfo0.basic_info.in.file.fnum = fnum1;
1235         finfo1 = finfo0;
1236         finfo2 = finfo0;
1237         finfo3 = finfo0;
1238         finfo4 = finfo0;
1239         finfo5 = finfo0;
1240         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1241         pinfo0.basic_info.in.file.path = fname;
1242         pinfo1 = pinfo0;
1243         pinfo2 = pinfo0;
1244         pinfo3 = pinfo0;
1245         pinfo4 = pinfo0;
1246         pinfo5 = pinfo0;
1247         pinfo6 = pinfo0;
1248         pinfo7 = pinfo0;
1249
1250         /* get the initial times */
1251         GET_INFO_BOTH(finfo0,pinfo0);
1252
1253         /* do a write */
1254         torture_comment(tctx, "Do a write on the file handle\n");
1255         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1256         if (written != 1) {
1257                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1258                 ret = false;
1259                 goto done;
1260         }
1261
1262         GET_INFO_BOTH(finfo1,pinfo1);
1263         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1264
1265         torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
1266         SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
1267         GET_INFO_BOTH(finfo2,pinfo2);
1268         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1269
1270         torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
1271         SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
1272         GET_INFO_BOTH(finfo2,pinfo2);
1273         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
1274
1275         /* make sure the 2 second delay from the first write are canceled */
1276         start = timeval_current();
1277         end = timeval_add(&start, 15 * sec, 0);
1278         while (!timeval_expired(&end)) {
1279
1280                 /* get the times after the first write */
1281                 GET_INFO_BOTH(finfo3,pinfo3);
1282
1283                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
1284                         double diff = timeval_elapsed(&start);
1285                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1286                                         "(1sec == %.2f) (wrong!)\n",
1287                                         diff, sec);
1288                         ret = false;
1289                         break;
1290                 }
1291                 msleep(2 * msec);
1292         }
1293
1294         GET_INFO_BOTH(finfo3,pinfo3);
1295         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1296         if (finfo3.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1297                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1298         }
1299
1300         /* sure any further write doesn't update the write time */
1301         start = timeval_current();
1302         end = timeval_add(&start, 15 * sec, 0);
1303         while (!timeval_expired(&end)) {
1304                 /* do a write */
1305                 torture_comment(tctx, "Do a write on the file handle\n");
1306                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1307                 if (written != 1) {
1308                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1309                         ret = false;
1310                         goto done;
1311                 }
1312                 /* get the times after the write */
1313                 GET_INFO_BOTH(finfo4,pinfo4);
1314
1315                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
1316                         double diff = timeval_elapsed(&start);
1317                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1318                                         "(1sec == %.2f) (wrong!)\n",
1319                                         diff, sec);
1320                         ret = false;
1321                         break;
1322                 }
1323                 msleep(2 * msec);
1324         }
1325
1326         GET_INFO_BOTH(finfo4,pinfo4);
1327         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
1328         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1329                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1330         }
1331
1332         /* sleep */
1333         msleep(5 * msec);
1334
1335         GET_INFO_BOTH(finfo5,pinfo5);
1336         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
1337
1338         /*
1339          * the close updates the write time to the time of the close
1340          * as the write time was set on the 2nd handle
1341          */
1342         torture_comment(tctx, "Close the file handle\n");
1343         smbcli_close(cli->tree, fnum1);
1344         fnum1 = -1;
1345
1346         GET_INFO_PATH(pinfo6);
1347         COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
1348
1349         if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
1350                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1351         }
1352
1353         /* keep the 2nd handle open and rerun tests */
1354         if (first) {
1355                 first = false;
1356                 goto again;
1357         }
1358
1359         /*
1360          * closing the 2nd handle will cause no write time update
1361          * as the write time was explicit set on this handle
1362          */
1363         torture_comment(tctx, "Close the 2nd file handle\n");
1364         smbcli_close(cli2->tree, fnum2);
1365         fnum2 = -1;
1366
1367         GET_INFO_PATH(pinfo7);
1368         COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
1369
1370         if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
1371                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1372         }
1373
1374  done:
1375         if (fnum1 != -1)
1376                 smbcli_close(cli->tree, fnum1);
1377         if (fnum2 != -1)
1378                 smbcli_close(cli2->tree, fnum2);
1379         smbcli_unlink(cli->tree, fname);
1380         smbcli_deltree(cli->tree, BASEDIR);
1381
1382         return ret;
1383 }
1384
1385
1386 /* 
1387    testing of delayed update of write_time
1388 */
1389 struct torture_suite *torture_delay_write(void)
1390 {
1391         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "DELAYWRITE");
1392
1393         torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
1394         torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
1395         torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
1396         torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
1397         torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
1398         torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
1399         torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
1400
1401         return suite;
1402 }