c7bccae08f8cee2caa16d73f95f740230115f66b
[kai/samba.git] / source / torture / basic / delaywrite.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    test suite for delayed write update 
5
6    Copyright (C) Volker Lendecke 2004
7    Copyright (C) Andrew Tridgell 2004
8    Copyright (C) Jeremy Allison 2004
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "torture/torture.h"
26 #include "libcli/raw/libcliraw.h"
27 #include "libcli/raw/raw_proto.h"
28 #include "system/time.h"
29 #include "system/filesys.h"
30 #include "libcli/libcli.h"
31 #include "torture/util.h"
32
33 #define BASEDIR "\\delaywrite"
34
35 static bool test_delayed_write_update(struct torture_context *tctx, struct smbcli_state *cli)
36 {
37         union smb_fileinfo finfo1, finfo2;
38         const char *fname = BASEDIR "\\torture_file.txt";
39         NTSTATUS status;
40         int fnum1 = -1;
41         bool ret = true;
42         ssize_t written;
43         struct timeval start;
44         struct timeval end;
45         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
46         int normal_delay = 2000000;
47         double sec = ((double)used_delay) / ((double)normal_delay);
48         int msec = 1000 * sec;
49
50         if (!torture_setup_dir(cli, BASEDIR)) {
51                 return false;
52         }
53
54         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
55         if (fnum1 == -1) {
56                 torture_comment(tctx, "Failed to open %s\n", fname);
57                 return false;
58         }
59
60         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
61         finfo1.basic_info.in.file.fnum = fnum1;
62         finfo2 = finfo1;
63
64         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
65
66         if (!NT_STATUS_IS_OK(status)) {
67                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
68                 return false;
69         }
70         
71         torture_comment(tctx, "Initial write time %s\n", 
72                nt_time_string(tctx, finfo1.basic_info.out.write_time));
73
74         /* 3 second delay to ensure we get past any 2 second time
75            granularity (older systems may have that) */
76         msleep(3 * msec);
77
78         written =  smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
79
80         if (written != 1) {
81                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
82                        (int)written, __location__);
83                 return false;
84         }
85
86         start = timeval_current();
87         end = timeval_add(&start, (120*sec), 0);
88         while (!timeval_expired(&end)) {
89                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
90
91                 if (!NT_STATUS_IS_OK(status)) {
92                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
93                         ret = false;
94                         break;
95                 }
96                 torture_comment(tctx, "write time %s\n", 
97                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
98                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
99                         double diff = timeval_elapsed(&start);
100                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
101                                 torture_comment(tctx, "Server updated write_time after %.2f seconds"
102                                                 "(1 sec == %.2f)(wrong!)\n",
103                                                 diff, sec);
104                                 ret = false;
105                                 break;
106                         }
107
108                         torture_comment(tctx, "Server updated write_time after %.2f seconds"
109                                         "(1 sec == %.2f)(correct)\n",
110                                         diff, sec);
111                         break;
112                 }
113                 fflush(stdout);
114                 msleep(1 * msec);
115         }
116         
117         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
118                 torture_comment(tctx, "Server did not update write time (wrong!)\n");
119                 ret = false;
120         }
121
122
123         if (fnum1 != -1)
124                 smbcli_close(cli->tree, fnum1);
125         smbcli_unlink(cli->tree, fname);
126         smbcli_deltree(cli->tree, BASEDIR);
127
128         return ret;
129 }
130
131 /* 
132  * Do as above, but using 2 connections.
133  */
134
135 static bool test_delayed_write_update2(struct torture_context *tctx, struct smbcli_state *cli, 
136                                                                            struct smbcli_state *cli2)
137 {
138         union smb_fileinfo finfo1, finfo2;
139         const char *fname = BASEDIR "\\torture_file.txt";
140         NTSTATUS status;
141         int fnum1 = -1;
142         int fnum2 = -1;
143         bool ret = true;
144         ssize_t written;
145         struct timeval start;
146         struct timeval end;
147         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
148         int normal_delay = 2000000;
149         double sec = ((double)used_delay) / ((double)normal_delay);
150         int msec = 1000 * sec;
151         union smb_flush flsh;
152
153         if (!torture_setup_dir(cli, BASEDIR)) {
154                 return false;
155         }
156
157         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
158         if (fnum1 == -1) {
159                 torture_comment(tctx, "Failed to open %s\n", fname);
160                 return false;
161         }
162
163         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
164         finfo1.basic_info.in.file.fnum = fnum1;
165         finfo2 = finfo1;
166
167         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
168
169         if (!NT_STATUS_IS_OK(status)) {
170                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
171                 return false;
172         }
173         
174         torture_comment(tctx, "Initial write time %s\n", 
175                nt_time_string(tctx, finfo1.basic_info.out.write_time));
176
177         /* 3 second delay to ensure we get past any 2 second time
178            granularity (older systems may have that) */
179         msleep(3 * msec);
180
181         {
182                 /* Try using setfileinfo instead of write to update write time. */
183                 union smb_setfileinfo sfinfo;
184                 time_t t_set = time(NULL);
185                 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
186                 sfinfo.basic_info.in.file.fnum = fnum1;
187                 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
188                 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
189
190                 /* I tried this with both + and - ve to see if it makes a different.
191                    It doesn't - once the filetime is set via setfileinfo it stays that way. */
192 #if 1
193                 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
194 #else
195                 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
196 #endif
197                 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
198                 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
199
200                 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
201
202                 if (!NT_STATUS_IS_OK(status)) {
203                         DEBUG(0, ("sfileinfo failed: %s\n", nt_errstr(status)));
204                         return false;
205                 }
206         }
207
208         finfo2.basic_info.in.file.path = fname;
209         
210         status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
211
212         if (!NT_STATUS_IS_OK(status)) {
213                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
214                 return false;
215         }
216         torture_comment(tctx, "write time %s\n",
217                nt_time_string(tctx, finfo2.basic_info.out.write_time));
218
219         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
220                 torture_comment(tctx, "Server updated write_time (correct)\n");
221         } else {
222                 torture_comment(tctx, "Server did not update write time (wrong!)\n");
223                 ret = false;
224         }
225
226         /* Now try a write to see if the write time gets reset. */
227
228         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
229         finfo1.basic_info.in.file.fnum = fnum1;
230         finfo2 = finfo1;
231
232         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
233
234         if (!NT_STATUS_IS_OK(status)) {
235                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
236                 return false;
237         }
238         
239         torture_comment(tctx, "Modified write time %s\n", 
240                nt_time_string(tctx, finfo1.basic_info.out.write_time));
241
242
243         torture_comment(tctx, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
244
245         written =  smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
246
247         if (written != 10) {
248                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
249                        (int)written, __location__);
250                 return false;
251         }
252
253         /* Just to prove to tridge that the an smbflush has no effect on
254            the write time :-). The setfileinfo IS STICKY. JRA. */
255
256         torture_comment(tctx, "Doing flush after write\n");
257
258         flsh.flush.level        = RAW_FLUSH_FLUSH;
259         flsh.flush.in.file.fnum = fnum1;
260         status = smb_raw_flush(cli->tree, &flsh);
261         if (!NT_STATUS_IS_OK(status)) {
262                 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
263                 return false;
264         }
265
266         /* Once the time was set using setfileinfo then it stays set - writes
267            don't have any effect. But make sure. */
268         start = timeval_current();
269         end = timeval_add(&start, (15*sec), 0);
270         while (!timeval_expired(&end)) {
271                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
272
273                 if (!NT_STATUS_IS_OK(status)) {
274                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
275                         ret = false;
276                         break;
277                 }
278                 torture_comment(tctx, "write time %s\n", 
279                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
280                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
281                         double diff = timeval_elapsed(&start);
282                         torture_comment(tctx, "Server updated write_time after %.2f seconds"
283                                         "(1sec == %.2f) (wrong!)\n",
284                                         diff, sec);
285                         ret = false;
286                         break;
287                 }
288                 fflush(stdout);
289                 msleep(1 * msec);
290         }
291         
292         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
293                 torture_comment(tctx, "Server did not update write time (correct)\n");
294         }
295
296         fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
297         if (fnum2 == -1) {
298                 torture_comment(tctx, "Failed to open %s\n", fname);
299                 return false;
300         }
301         
302         torture_comment(tctx, "Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n");
303
304         written =  smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
305
306         if (written != 10) {
307                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
308                        (int)written, __location__);
309                 return false;
310         }
311
312         status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
313
314         if (!NT_STATUS_IS_OK(status)) {
315                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
316                 return false;
317         }
318         torture_comment(tctx, "write time %s\n", 
319                nt_time_string(tctx, finfo2.basic_info.out.write_time));
320         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
321                 torture_comment(tctx, "Server updated write_time (wrong!)\n");
322                 ret = false;
323         }
324
325         torture_comment(tctx, "Closing the first fd to see if write time updated.\n");
326         smbcli_close(cli->tree, fnum1);
327         fnum1 = -1;
328
329         torture_comment(tctx, "Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n");
330
331         written =  smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
332
333         if (written != 10) {
334                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
335                        (int)written, __location__);
336                 return false;
337         }
338
339         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
340         finfo1.basic_info.in.file.fnum = fnum2;
341         finfo2 = finfo1;
342         status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
343
344         if (!NT_STATUS_IS_OK(status)) {
345                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
346                 return false;
347         }
348         torture_comment(tctx, "write time %s\n", 
349                nt_time_string(tctx, finfo2.basic_info.out.write_time));
350         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
351                 torture_comment(tctx, "Server updated write_time (wrong!)\n");
352                 ret = false;
353         }
354
355         /* Once the time was set using setfileinfo then it stays set - writes
356            don't have any effect. But make sure. */
357         start = timeval_current();
358         end = timeval_add(&start, (15*sec), 0);
359         while (!timeval_expired(&end)) {
360                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
361
362                 if (!NT_STATUS_IS_OK(status)) {
363                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
364                         ret = false;
365                         break;
366                 }
367                 torture_comment(tctx, "write time %s\n", 
368                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
369                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
370                         double diff = timeval_elapsed(&start);
371                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
372                                         "(1sec == %.2f) (wrong!)\n",
373                                         diff, sec);
374                         ret = false;
375                         break;
376                 }
377                 fflush(stdout);
378                 msleep(1 * msec);
379         }
380         
381         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
382                 torture_comment(tctx, "Server did not update write time (correct)\n");
383         }
384
385         torture_comment(tctx, "Closing second fd to see if write time updated.\n");
386
387         smbcli_close(cli->tree, fnum2);
388         fnum2 = -1;
389
390         fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
391         if (fnum1 == -1) {
392                 torture_comment(tctx, "Failed to open %s\n", fname);
393                 return false;
394         }
395
396         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
397         finfo1.basic_info.in.file.fnum = fnum1;
398         finfo2 = finfo1;
399
400         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
401
402         if (!NT_STATUS_IS_OK(status)) {
403                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
404                 return false;
405         }
406         
407         torture_comment(tctx, "Second open initial write time %s\n", 
408                nt_time_string(tctx, finfo1.basic_info.out.write_time));
409
410         msleep(10 * msec);
411         torture_comment(tctx, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
412
413         written =  smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
414
415         if (written != 10) {
416                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
417                        (int)written, __location__);
418                 return false;
419         }
420
421         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
422         finfo1.basic_info.in.file.fnum = fnum1;
423         finfo2 = finfo1;
424         status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
425
426         if (!NT_STATUS_IS_OK(status)) {
427                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
428                 return false;
429         }
430         torture_comment(tctx, "write time %s\n", 
431                nt_time_string(tctx, finfo2.basic_info.out.write_time));
432         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
433                 torture_comment(tctx, "Server updated write_time (wrong!)\n");
434                 ret = false;
435         }
436
437         /* Now the write time should be updated again */
438         start = timeval_current();
439         end = timeval_add(&start, (15*sec), 0);
440         while (!timeval_expired(&end)) {
441                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
442
443                 if (!NT_STATUS_IS_OK(status)) {
444                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
445                         ret = false;
446                         break;
447                 }
448                 torture_comment(tctx, "write time %s\n", 
449                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
450                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
451                         double diff = timeval_elapsed(&start);
452                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
453                                 torture_comment(tctx, "Server updated write_time after %.2f seconds"
454                                                 "(1sec == %.2f) (wrong!)\n",
455                                                 diff, sec);
456                                 ret = false;
457                                 break;
458                         }
459
460                         torture_comment(tctx, "Server updated write_time after %.2f seconds"
461                                         "(1sec == %.2f) (correct)\n",
462                                         diff, sec);
463                         break;
464                 }
465                 fflush(stdout);
466                 msleep(1*msec);
467         }
468         
469         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
470                 torture_comment(tctx, "Server did not update write time (wrong!)\n");
471                 ret = false;
472         }
473
474
475         /* One more test to do. We should read the filetime via findfirst on the
476            second connection to ensure it's the same. This is very easy for a Windows
477            server but a bastard to get right on a POSIX server. JRA. */
478
479         if (fnum1 != -1)
480                 smbcli_close(cli->tree, fnum1);
481         smbcli_unlink(cli->tree, fname);
482         smbcli_deltree(cli->tree, BASEDIR);
483
484         return ret;
485 }
486
487
488 /* Windows does obviously not update the stat info during a write call. I
489  * *think* this is the problem causing a spurious Excel 2003 on XP error
490  * message when saving a file. Excel does a setfileinfo, writes, and then does
491  * a getpath(!)info. Or so... For Samba sometimes it displays an error message
492  * that the file might have been changed in between. What i've been able to
493  * trace down is that this happens if the getpathinfo after the write shows a
494  * different last write time than the setfileinfo showed. This is really
495  * nasty....
496  */
497
498 static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli, 
499                                                                    struct smbcli_state *cli2)
500 {
501         union smb_fileinfo finfo1, finfo2;
502         const char *fname = BASEDIR "\\torture_file.txt";
503         NTSTATUS status;
504         int fnum1 = -1;
505         int fnum2;
506         bool ret = true;
507         ssize_t written;
508         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
509         int normal_delay = 2000000;
510         double sec = ((double)used_delay) / ((double)normal_delay);
511         int msec = 1000 * sec;
512
513         if (!torture_setup_dir(cli, BASEDIR)) {
514                 return false;
515         }
516
517         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
518         if (fnum1 == -1) {
519                 ret = false;
520                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
521                 goto done;
522         }
523
524         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
525         finfo1.basic_info.in.file.fnum = fnum1;
526
527         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
528
529         if (!NT_STATUS_IS_OK(status)) {
530                 ret = false;
531                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
532                 goto done;
533         }
534
535         msleep(1 * msec);
536
537         written =  smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
538
539         if (written != 1) {
540                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
541                 ret = false;
542                 goto done;
543         }
544
545         fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
546         if (fnum2 == -1) {
547                 torture_result(tctx, TORTURE_FAIL, __location__": failed to open 2nd time - %s", 
548                        smbcli_errstr(cli2->tree));
549                 ret = false;
550                 goto done;
551         }
552         
553         written =  smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
554         
555         if (written != 1) {
556                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", 
557                        (int)written);
558                 ret = false;
559                 goto done;
560         }
561         
562         finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
563         finfo2.basic_info.in.file.path = fname;
564         
565         status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
566         
567         if (!NT_STATUS_IS_OK(status)) {
568                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", 
569                           nt_errstr(status));
570                 ret = false;
571                 goto done;
572         }
573         
574         if (finfo1.basic_info.out.create_time !=
575             finfo2.basic_info.out.create_time) {
576                 torture_result(tctx, TORTURE_FAIL, __location__": create_time changed");
577                 ret = false;
578                 goto done;
579         }
580         
581         if (finfo1.basic_info.out.access_time !=
582             finfo2.basic_info.out.access_time) {
583                 torture_result(tctx, TORTURE_FAIL, __location__": access_time changed");
584                 ret = false;
585                 goto done;
586         }
587         
588         if (finfo1.basic_info.out.write_time !=
589             finfo2.basic_info.out.write_time) {
590                 torture_result(tctx, TORTURE_FAIL, __location__": write_time changed:\n"
591                                            "write time conn 1 = %s, conn 2 = %s", 
592                        nt_time_string(tctx, finfo1.basic_info.out.write_time),
593                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
594                 ret = false;
595                 goto done;
596         }
597         
598         if (finfo1.basic_info.out.change_time !=
599             finfo2.basic_info.out.change_time) {
600                 torture_result(tctx, TORTURE_FAIL, __location__": change_time changed");
601                 ret = false;
602                 goto done;
603         }
604         
605         /* One of the two following calls updates the qpathinfo. */
606         
607         /* If you had skipped the smbcli_write on fnum2, it would
608          * *not* have updated the stat on disk */
609         
610         smbcli_close(cli2->tree, fnum2);
611         cli2 = NULL;
612
613         /* This call is only for the people looking at ethereal :-) */
614         finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
615         finfo2.basic_info.in.file.path = fname;
616
617         status = smb_raw_pathinfo(cli->tree, tctx, &finfo2);
618
619         if (!NT_STATUS_IS_OK(status)) {
620                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
621                 ret = false;
622                 goto done;
623         }
624
625  done:
626         if (fnum1 != -1)
627                 smbcli_close(cli->tree, fnum1);
628         smbcli_unlink(cli->tree, fname);
629         smbcli_deltree(cli->tree, BASEDIR);
630
631         return ret;
632 }
633
634 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
635         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 ((g cmp c) && (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         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
832
833         /* sure any further write doesn't update the write time */
834         start = timeval_current();
835         end = timeval_add(&start, 15 * sec, 0);
836         while (!timeval_expired(&end)) {
837                 /* do a write */
838                 torture_comment(tctx, "Do a write on the file handle\n");
839                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
840                 if (written != 1) {
841                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
842                         ret = false;
843                         goto done;
844                 }
845                 /* get the times after the write */
846                 GET_INFO_BOTH(finfo2,pinfo2);
847
848                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
849                         double diff = timeval_elapsed(&start);
850                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
851                                         "(1sec == %.2f) (wrong!)\n",
852                                         diff, sec);
853                         ret = false;
854                         break;
855                 }
856                 msleep(2 * msec);
857         }
858
859         GET_INFO_BOTH(finfo2,pinfo2);
860         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
861         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
862                 torture_comment(tctx, "Server did not update write_time (correct)\n");
863         }
864
865         /* sleep */
866         msleep(5 * msec);
867
868         GET_INFO_BOTH(finfo3,pinfo3);
869         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
870
871         /*
872          * the close updates the write time to the time of the close
873          * and not to the time of the last write!
874          */
875         torture_comment(tctx, "Close the file handle\n");
876         smbcli_close(cli->tree, fnum1);
877         fnum1 = -1;
878
879         GET_INFO_PATH(pinfo4);
880         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
881
882         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
883                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
884         }
885
886  done:
887         if (fnum1 != -1)
888                 smbcli_close(cli->tree, fnum1);
889         smbcli_unlink(cli->tree, fname);
890         smbcli_deltree(cli->tree, BASEDIR);
891
892         return ret;
893 }
894
895 static bool test_delayed_write_update4(struct torture_context *tctx,
896                                        struct smbcli_state *cli,
897                                        struct smbcli_state *cli2)
898 {
899         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
900         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
901         const char *fname = BASEDIR "\\torture_file.txt";
902         int fnum1 = -1;
903         bool ret = true;
904         ssize_t written;
905         struct timeval start;
906         struct timeval end;
907         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
908         int normal_delay = 2000000;
909         double sec = ((double)used_delay) / ((double)normal_delay);
910         int msec = 1000 * sec;
911
912         if (!torture_setup_dir(cli, BASEDIR)) {
913                 return false;
914         }
915
916         torture_comment(tctx, "Open the file handle\n");
917         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
918         if (fnum1 == -1) {
919                 ret = false;
920                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
921                 goto done;
922         }
923
924         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
925         finfo0.basic_info.in.file.fnum = fnum1;
926         finfo1 = finfo0;
927         finfo2 = finfo0;
928         finfo3 = finfo0;
929         finfo4 = finfo0;
930         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
931         pinfo0.basic_info.in.file.path = fname;
932         pinfo1 = pinfo0;
933         pinfo2 = pinfo0;
934         pinfo3 = pinfo0;
935         pinfo4 = pinfo0;
936         pinfo5 = pinfo0;
937
938         /* get the initial times */
939         GET_INFO_BOTH(finfo0,pinfo0);
940
941         /* sleep a bit */
942         msleep(5 * msec);
943
944         /* do a write */
945         torture_comment(tctx, "Do a write on the file handle\n");
946         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
947         if (written != 1) {
948                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
949                 ret = false;
950                 goto done;
951         }
952
953         GET_INFO_BOTH(finfo1,pinfo1);
954         COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
955
956         /*
957          * make sure the write time is updated 2 seconds later
958          * calcuated from the first write
959          * (but expect upto 3 seconds extra time for a busy server)
960          */
961         start = timeval_current();
962         end = timeval_add(&start, 5 * sec, 0);
963         while (!timeval_expired(&end)) {
964                 /* get the times after the first write */
965                 GET_INFO_FILE(finfo1);
966
967                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
968                         double diff = timeval_elapsed(&start);
969                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
970                                 torture_comment(tctx, "Server updated write_time after %.2f seconds "
971                                                 "(1sec == %.2f) (wrong!)\n",
972                                                 diff, sec);
973                                 ret = false;
974                                 break;
975                         }
976
977                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
978                                         "(1sec == %.2f) (correct)\n",
979                                         diff, sec);
980                         break;
981                 }
982                 msleep(0.5 * msec);
983         }
984
985         GET_INFO_BOTH(finfo1,pinfo1);
986         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
987
988         /* sure any further write doesn't update the write time */
989         start = timeval_current();
990         end = timeval_add(&start, 15 * sec, 0);
991         while (!timeval_expired(&end)) {
992                 /* do a write */
993                 torture_comment(tctx, "Do a write on the file handle\n");
994                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
995                 if (written != 1) {
996                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
997                         ret = false;
998                         goto done;
999                 }
1000                 /* get the times after the write */
1001                 GET_INFO_BOTH(finfo2,pinfo2);
1002
1003                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1004                         double diff = timeval_elapsed(&start);
1005                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1006                                         "(1sec == %.2f) (wrong!)\n",
1007                                         diff, sec);
1008                         ret = false;
1009                         break;
1010                 }
1011                 msleep(2 * msec);
1012         }
1013
1014         GET_INFO_BOTH(finfo2,pinfo2);
1015         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1016         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1017                 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
1018         }
1019
1020         /* sleep */
1021         msleep(5 * msec);
1022
1023         GET_INFO_BOTH(finfo3,pinfo3);
1024         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1025
1026         /*
1027          * the close updates the write time to the time of the close
1028          * and not to the time of the last write!
1029          */
1030         torture_comment(tctx, "Close the file handle\n");
1031         smbcli_close(cli->tree, fnum1);
1032         fnum1 = -1;
1033
1034         GET_INFO_PATH(pinfo4);
1035         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1036
1037         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1038                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1039         }
1040
1041  done:
1042         if (fnum1 != -1)
1043                 smbcli_close(cli->tree, fnum1);
1044         smbcli_unlink(cli->tree, fname);
1045         smbcli_deltree(cli->tree, BASEDIR);
1046
1047         return ret;
1048 }
1049
1050 static bool test_delayed_write_update5(struct torture_context *tctx,
1051                                        struct smbcli_state *cli,
1052                                        struct smbcli_state *cli2)
1053 {
1054         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
1055         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
1056         const char *fname = BASEDIR "\\torture_file.txt";
1057         int fnum1 = -1;
1058         bool ret = true;
1059         ssize_t written;
1060         struct timeval start;
1061         struct timeval end;
1062         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1063         int normal_delay = 2000000;
1064         double sec = ((double)used_delay) / ((double)normal_delay);
1065         int msec = 1000 * sec;
1066
1067         if (!torture_setup_dir(cli, BASEDIR)) {
1068                 return false;
1069         }
1070
1071         torture_comment(tctx, "Open the file handle\n");
1072         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1073         if (fnum1 == -1) {
1074                 ret = false;
1075                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1076                 goto done;
1077         }
1078
1079         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1080         finfo0.basic_info.in.file.fnum = fnum1;
1081         finfo1 = finfo0;
1082         finfo2 = finfo0;
1083         finfo3 = finfo0;
1084         finfo4 = finfo0;
1085         finfo5 = finfo0;
1086         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1087         pinfo0.basic_info.in.file.path = fname;
1088         pinfo1 = pinfo0;
1089         pinfo2 = pinfo0;
1090         pinfo3 = pinfo0;
1091         pinfo4 = pinfo0;
1092         pinfo5 = pinfo0;
1093         pinfo6 = pinfo0;
1094
1095         /* get the initial times */
1096         GET_INFO_BOTH(finfo0,pinfo0);
1097
1098         /* do a write */
1099         torture_comment(tctx, "Do a write on the file handle\n");
1100         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1101         if (written != 1) {
1102                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1103                 ret = false;
1104                 goto done;
1105         }
1106
1107         GET_INFO_BOTH(finfo1,pinfo1);
1108         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1109
1110         torture_comment(tctx, "Set write time in the future on the file handle\n");
1111         SET_INFO_FILE(finfo0, time(NULL) + 86400);
1112         GET_INFO_BOTH(finfo2,pinfo2);
1113         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1114
1115         torture_comment(tctx, "Set write time in the past on the file handle\n");
1116         SET_INFO_FILE(finfo0, time(NULL) - 86400);
1117         GET_INFO_BOTH(finfo2,pinfo2);
1118         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
1119
1120         /* make sure the 2 second delay from the first write are canceled */
1121         start = timeval_current();
1122         end = timeval_add(&start, 15 * sec, 0);
1123         while (!timeval_expired(&end)) {
1124
1125                 /* get the times after the first write */
1126                 GET_INFO_BOTH(finfo3,pinfo3);
1127
1128                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
1129                         double diff = timeval_elapsed(&start);
1130                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1131                                         "(1sec == %.2f) (wrong!)\n",
1132                                         diff, sec);
1133                         ret = false;
1134                         break;
1135                 }
1136                 msleep(2 * msec);
1137         }
1138
1139         GET_INFO_BOTH(finfo3,pinfo3);
1140         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1141         if (finfo3.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1142                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1143         }
1144
1145         /* sure any further write doesn't update the write time */
1146         start = timeval_current();
1147         end = timeval_add(&start, 15 * sec, 0);
1148         while (!timeval_expired(&end)) {
1149                 /* do a write */
1150                 torture_comment(tctx, "Do a write on the file handle\n");
1151                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1152                 if (written != 1) {
1153                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1154                         ret = false;
1155                         goto done;
1156                 }
1157                 /* get the times after the write */
1158                 GET_INFO_BOTH(finfo4,pinfo4);
1159
1160                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
1161                         double diff = timeval_elapsed(&start);
1162                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1163                                         "(1sec == %.2f) (wrong!)\n",
1164                                         diff, sec);
1165                         ret = false;
1166                         break;
1167                 }
1168                 msleep(2 * msec);
1169         }
1170
1171         GET_INFO_BOTH(finfo4,pinfo4);
1172         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
1173         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1174                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1175         }
1176
1177         /* sleep */
1178         msleep(5 * msec);
1179
1180         GET_INFO_BOTH(finfo5,pinfo5);
1181         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
1182
1183         /*
1184          * the close doesn't update the write time
1185          */
1186         torture_comment(tctx, "Close the file handle\n");
1187         smbcli_close(cli->tree, fnum1);
1188         fnum1 = -1;
1189
1190         GET_INFO_PATH(pinfo6);
1191         COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
1192
1193         if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
1194                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1195         }
1196
1197  done:
1198         if (fnum1 != -1)
1199                 smbcli_close(cli->tree, fnum1);
1200         smbcli_unlink(cli->tree, fname);
1201         smbcli_deltree(cli->tree, BASEDIR);
1202
1203         return ret;
1204 }
1205
1206 static bool test_delayed_write_update6(struct torture_context *tctx,
1207                                        struct smbcli_state *cli,
1208                                        struct smbcli_state *cli2)
1209 {
1210         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
1211         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
1212         const char *fname = BASEDIR "\\torture_file.txt";
1213         int fnum1 = -1;
1214         int fnum2 = -1;
1215         bool ret = true;
1216         ssize_t written;
1217         struct timeval start;
1218         struct timeval end;
1219         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1220         int normal_delay = 2000000;
1221         double sec = ((double)used_delay) / ((double)normal_delay);
1222         int msec = 1000 * sec;
1223         bool first = true;
1224
1225         if (!torture_setup_dir(cli, BASEDIR)) {
1226                 return false;
1227         }
1228 again:
1229         torture_comment(tctx, "Open the file handle\n");
1230         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1231         if (fnum1 == -1) {
1232                 ret = false;
1233                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1234                 goto done;
1235         }
1236
1237         if (fnum2 == -1) {
1238                 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
1239                 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1240                 if (fnum2 == -1) {
1241                         ret = false;
1242                         torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1243                         goto done;
1244                 }
1245         }
1246
1247         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1248         finfo0.basic_info.in.file.fnum = fnum1;
1249         finfo1 = finfo0;
1250         finfo2 = finfo0;
1251         finfo3 = finfo0;
1252         finfo4 = finfo0;
1253         finfo5 = finfo0;
1254         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1255         pinfo0.basic_info.in.file.path = fname;
1256         pinfo1 = pinfo0;
1257         pinfo2 = pinfo0;
1258         pinfo3 = pinfo0;
1259         pinfo4 = pinfo0;
1260         pinfo5 = pinfo0;
1261         pinfo6 = pinfo0;
1262         pinfo7 = pinfo0;
1263
1264         /* get the initial times */
1265         GET_INFO_BOTH(finfo0,pinfo0);
1266
1267         /* do a write */
1268         torture_comment(tctx, "Do a write on the file handle\n");
1269         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1270         if (written != 1) {
1271                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1272                 ret = false;
1273                 goto done;
1274         }
1275
1276         GET_INFO_BOTH(finfo1,pinfo1);
1277         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1278
1279         torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
1280         SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
1281         GET_INFO_BOTH(finfo2,pinfo2);
1282         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1283
1284         torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
1285         SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
1286         GET_INFO_BOTH(finfo2,pinfo2);
1287         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
1288
1289         /* make sure the 2 second delay from the first write are canceled */
1290         start = timeval_current();
1291         end = timeval_add(&start, 15 * sec, 0);
1292         while (!timeval_expired(&end)) {
1293
1294                 /* get the times after the first write */
1295                 GET_INFO_BOTH(finfo3,pinfo3);
1296
1297                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
1298                         double diff = timeval_elapsed(&start);
1299                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1300                                         "(1sec == %.2f) (wrong!)\n",
1301                                         diff, sec);
1302                         ret = false;
1303                         break;
1304                 }
1305                 msleep(2 * msec);
1306         }
1307
1308         GET_INFO_BOTH(finfo3,pinfo3);
1309         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1310         if (finfo3.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1311                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1312         }
1313
1314         /* sure any further write doesn't update the write time */
1315         start = timeval_current();
1316         end = timeval_add(&start, 15 * sec, 0);
1317         while (!timeval_expired(&end)) {
1318                 /* do a write */
1319                 torture_comment(tctx, "Do a write on the file handle\n");
1320                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1321                 if (written != 1) {
1322                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1323                         ret = false;
1324                         goto done;
1325                 }
1326                 /* get the times after the write */
1327                 GET_INFO_BOTH(finfo4,pinfo4);
1328
1329                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
1330                         double diff = timeval_elapsed(&start);
1331                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1332                                         "(1sec == %.2f) (wrong!)\n",
1333                                         diff, sec);
1334                         ret = false;
1335                         break;
1336                 }
1337                 msleep(2 * msec);
1338         }
1339
1340         GET_INFO_BOTH(finfo4,pinfo4);
1341         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
1342         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1343                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1344         }
1345
1346         /* sleep */
1347         msleep(5 * msec);
1348
1349         GET_INFO_BOTH(finfo5,pinfo5);
1350         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
1351
1352         /*
1353          * the close updates the write time to the time of the close
1354          * as the write time was set on the 2nd handle
1355          */
1356         torture_comment(tctx, "Close the file handle\n");
1357         smbcli_close(cli->tree, fnum1);
1358         fnum1 = -1;
1359
1360         GET_INFO_PATH(pinfo6);
1361         COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
1362
1363         if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
1364                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1365         }
1366
1367         /* keep the 2nd handle open and rerun tests */
1368         if (first) {
1369                 first = false;
1370                 goto again;
1371         }
1372
1373         /*
1374          * closing the 2nd handle will cause no write time update
1375          * as the write time was explicit set on this handle
1376          */
1377         torture_comment(tctx, "Close the 2nd file handle\n");
1378         smbcli_close(cli2->tree, fnum2);
1379         fnum2 = -1;
1380
1381         GET_INFO_PATH(pinfo7);
1382         COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
1383
1384         if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
1385                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1386         }
1387
1388  done:
1389         if (fnum1 != -1)
1390                 smbcli_close(cli->tree, fnum1);
1391         if (fnum2 != -1)
1392                 smbcli_close(cli2->tree, fnum2);
1393         smbcli_unlink(cli->tree, fname);
1394         smbcli_deltree(cli->tree, BASEDIR);
1395
1396         return ret;
1397 }
1398
1399
1400 /* 
1401    testing of delayed update of write_time
1402 */
1403 struct torture_suite *torture_delay_write(void)
1404 {
1405         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "DELAYWRITE");
1406
1407         torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
1408         torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
1409         torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
1410         torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
1411         torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
1412         torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
1413         torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
1414
1415         return suite;
1416 }