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