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