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