6dfe410386e5ddae32cffd7592a83adaf40be622
[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 /* Updating with a SMBwrite of zero length
132  * changes the write time immediately - even on expand. */
133
134 static bool test_delayed_write_update1a(struct torture_context *tctx, struct smbcli_state *cli)
135 {
136         union smb_fileinfo finfo1, finfo2;
137         const char *fname = BASEDIR "\\torture_file1a.txt";
138         NTSTATUS status;
139         int fnum1 = -1;
140         bool ret = true;
141         ssize_t written;
142         struct timeval start;
143         struct timeval end;
144         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
145         int normal_delay = 2000000;
146         double sec = ((double)used_delay) / ((double)normal_delay);
147         int msec = 1000 * sec;
148         char buf[2048];
149
150         if (!torture_setup_dir(cli, BASEDIR)) {
151                 return false;
152         }
153
154         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
155         if (fnum1 == -1) {
156                 torture_comment(tctx, "Failed to open %s\n", fname);
157                 return false;
158         }
159
160         memset(buf, 'x', 2048);
161         written =  smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
162
163         /* 3 second delay to ensure we get past any 2 second time
164            granularity (older systems may have that) */
165         msleep(3 * msec);
166
167         finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
168         finfo1.all_info.in.file.fnum = fnum1;
169         finfo2 = finfo1;
170
171         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
172
173         if (!NT_STATUS_IS_OK(status)) {
174                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
175                 return false;
176         }
177
178         torture_comment(tctx, "Initial write time %s\n", 
179                nt_time_string(tctx, finfo1.all_info.out.write_time));
180
181         /* Do a zero length SMBwrite call to truncate. */
182         written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
183
184         if (written != 0) {
185                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
186                        (int)written, __location__);
187                 return false;
188         }
189
190         start = timeval_current();
191         end = timeval_add(&start, (120*sec), 0);
192         while (!timeval_expired(&end)) {
193                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
194
195                 if (!NT_STATUS_IS_OK(status)) {
196                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
197                         ret = false;
198                         break;
199                 }
200
201                 if (finfo2.all_info.out.size != 10240) {
202                         DEBUG(0, ("file not truncated\n"));
203                         ret = false;
204                         break;
205                 }
206
207                 torture_comment(tctx, "write time %s\n",
208                        nt_time_string(tctx, finfo2.all_info.out.write_time));
209                 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
210                         double diff = timeval_elapsed(&start);
211                         if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
212                                 torture_comment(tctx, "After SMBwrite truncate "
213                                         "server updated write_time after %.2f seconds"
214                                         "(1 sec == %.2f)(wrong!)\n",
215                                         diff, sec);
216                                 ret = false;
217                                 break;
218                         }
219
220                         torture_comment(tctx, "After SMBwrite truncate "
221                                         "server updated write_time after %.2f seconds"
222                                         "(1 sec == %.2f)(correct)\n",
223                                         diff, sec);
224                         break;
225                 }
226                 fflush(stdout);
227                 msleep(1 * msec);
228         }
229
230         if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
231                 torture_comment(tctx, "Server did not update write time (wrong!)\n");
232                 ret = false;
233         }
234
235
236         if (fnum1 != -1)
237                 smbcli_close(cli->tree, fnum1);
238         smbcli_unlink(cli->tree, fname);
239         smbcli_deltree(cli->tree, BASEDIR);
240
241         return ret;
242 }
243
244 /* Updating with a SET_FILE_END_OF_FILE_INFO
245  * changes the write time immediately - even on expand. */
246
247 static bool test_delayed_write_update1b(struct torture_context *tctx, struct smbcli_state *cli)
248 {
249         union smb_fileinfo finfo1, finfo2;
250         const char *fname = BASEDIR "\\torture_file1b.txt";
251         NTSTATUS status;
252         int fnum1 = -1;
253         bool ret = true;
254         ssize_t written;
255         struct timeval start;
256         struct timeval end;
257         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
258         int normal_delay = 2000000;
259         double sec = ((double)used_delay) / ((double)normal_delay);
260         int msec = 1000 * sec;
261         char buf[2048];
262
263         if (!torture_setup_dir(cli, BASEDIR)) {
264                 return false;
265         }
266
267         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
268         if (fnum1 == -1) {
269                 torture_comment(tctx, "Failed to open %s\n", fname);
270                 return false;
271         }
272
273         memset(buf, 'x', 2048);
274         written =  smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
275
276         /* 3 second delay to ensure we get past any 2 second time
277            granularity (older systems may have that) */
278         msleep(3 * msec);
279
280         finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
281         finfo1.all_info.in.file.fnum = fnum1;
282         finfo2 = finfo1;
283
284         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
285
286         if (!NT_STATUS_IS_OK(status)) {
287                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
288                 return false;
289         }
290
291         torture_comment(tctx, "Initial write time %s\n",
292                nt_time_string(tctx, finfo1.all_info.out.write_time));
293
294         /* Do a SET_END_OF_FILE_INFO call to truncate. */
295         status = smbcli_ftruncate(cli->tree, fnum1, (uint64_t)10240);
296
297         if (!NT_STATUS_IS_OK(status)) {
298                 torture_comment(tctx, "SET_END_OF_FILE failed (%s)\n",
299                        nt_errstr(status));
300                 return false;
301         }
302
303         start = timeval_current();
304         end = timeval_add(&start, (120*sec), 0);
305         while (!timeval_expired(&end)) {
306                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
307
308                 if (!NT_STATUS_IS_OK(status)) {
309                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
310                         ret = false;
311                         break;
312                 }
313
314                 if (finfo2.all_info.out.size != 10240) {
315                         DEBUG(0, ("file not truncated\n"));
316                         ret = false;
317                         break;
318                 }
319
320                 torture_comment(tctx, "write time %s\n",
321                        nt_time_string(tctx, finfo2.all_info.out.write_time));
322                 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
323                         double diff = timeval_elapsed(&start);
324                         if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
325                                 torture_comment(tctx, "After SET_END_OF_FILE truncate "
326                                         "server updated write_time after %.2f seconds"
327                                         "(1 sec == %.2f)(wrong!)\n",
328                                         diff, sec);
329                                 ret = false;
330                                 break;
331                         }
332
333                         torture_comment(tctx, "After SET_END_OF_FILE truncate "
334                                         "server updated write_time after %.2f seconds"
335                                         "(1 sec == %.2f)(correct)\n",
336                                         diff, sec);
337                         break;
338                 }
339                 fflush(stdout);
340                 msleep(1 * msec);
341         }
342
343         if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
344                 torture_comment(tctx, "Server did not update write time (wrong!)\n");
345                 ret = false;
346         }
347
348
349         if (fnum1 != -1)
350                 smbcli_close(cli->tree, fnum1);
351         smbcli_unlink(cli->tree, fname);
352         smbcli_deltree(cli->tree, BASEDIR);
353
354         return ret;
355 }
356
357 /* Updating with a SET_ALLOCATION_INFO (truncate) does so immediately. */
358
359 static bool test_delayed_write_update1c(struct torture_context *tctx, struct smbcli_state *cli)
360 {
361         union smb_setfileinfo parms;
362         union smb_fileinfo finfo1, finfo2;
363         const char *fname = BASEDIR "\\torture_file1c.txt";
364         NTSTATUS status;
365         int fnum1 = -1;
366         bool ret = true;
367         ssize_t written;
368         struct timeval start;
369         struct timeval end;
370         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
371         int normal_delay = 2000000;
372         double sec = ((double)used_delay) / ((double)normal_delay);
373         int msec = 1000 * sec;
374         char buf[2048];
375
376         if (!torture_setup_dir(cli, BASEDIR)) {
377                 return false;
378         }
379
380         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
381         if (fnum1 == -1) {
382                 torture_comment(tctx, "Failed to open %s\n", fname);
383                 return false;
384         }
385
386         memset(buf, 'x', 2048);
387         written =  smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
388
389         /* 3 second delay to ensure we get past any 2 second time
390            granularity (older systems may have that) */
391         msleep(3 * msec);
392
393         finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
394         finfo1.all_info.in.file.fnum = fnum1;
395         finfo2 = finfo1;
396
397         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
398
399         if (!NT_STATUS_IS_OK(status)) {
400                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
401                 return false;
402         }
403
404         torture_comment(tctx, "Initial write time %s\n",
405                nt_time_string(tctx, finfo1.all_info.out.write_time));
406
407         /* Do a SET_ALLOCATION_SIZE call to truncate. */
408         parms.allocation_info.level = RAW_SFILEINFO_ALLOCATION_INFO;
409         parms.allocation_info.in.file.fnum = fnum1;
410         parms.allocation_info.in.alloc_size = 0;
411
412         status = smb_raw_setfileinfo(cli->tree, &parms);
413
414         if (!NT_STATUS_IS_OK(status)) {
415                 torture_comment(tctx, "RAW_SFILEINFO_ALLOCATION_INFO failed (%s)\n",
416                        nt_errstr(status));
417                 return false;
418         }
419
420         start = timeval_current();
421         end = timeval_add(&start, (120*sec), 0);
422         while (!timeval_expired(&end)) {
423                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
424
425                 if (!NT_STATUS_IS_OK(status)) {
426                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
427                         ret = false;
428                         break;
429                 }
430
431                 if (finfo2.all_info.out.size != 0) {
432                         DEBUG(0, ("file not truncated\n"));
433                         ret = false;
434                         break;
435                 }
436
437                 torture_comment(tctx, "write time %s\n",
438                        nt_time_string(tctx, finfo2.all_info.out.write_time));
439                 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
440                         double diff = timeval_elapsed(&start);
441                         if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
442                                 torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
443                                         "server updated write_time after %.2f seconds"
444                                         "(1 sec == %.2f)(wrong!)\n",
445                                         diff, sec);
446                                 ret = false;
447                                 break;
448                         }
449
450                         torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
451                                         "server updated write_time after %.2f seconds"
452                                         "(1 sec == %.2f)(correct)\n",
453                                         diff, sec);
454                         break;
455                 }
456                 fflush(stdout);
457                 msleep(1 * msec);
458         }
459
460         if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
461                 torture_comment(tctx, "Server did not update write time (wrong!)\n");
462                 ret = false;
463         }
464
465
466         if (fnum1 != -1)
467                 smbcli_close(cli->tree, fnum1);
468         smbcli_unlink(cli->tree, fname);
469         smbcli_deltree(cli->tree, BASEDIR);
470
471         return ret;
472 }
473
474 /*
475  * Do as above, but using 2 connections.
476  */
477
478 static bool test_delayed_write_update2(struct torture_context *tctx, struct smbcli_state *cli, 
479                                                                            struct smbcli_state *cli2)
480 {
481         union smb_fileinfo finfo1, finfo2;
482         const char *fname = BASEDIR "\\torture_file.txt";
483         NTSTATUS status;
484         int fnum1 = -1;
485         int fnum2 = -1;
486         bool ret = true;
487         ssize_t written;
488         struct timeval start;
489         struct timeval end;
490         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
491         int normal_delay = 2000000;
492         double sec = ((double)used_delay) / ((double)normal_delay);
493         int msec = 1000 * sec;
494         union smb_flush flsh;
495
496         if (!torture_setup_dir(cli, BASEDIR)) {
497                 return false;
498         }
499
500         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
501         if (fnum1 == -1) {
502                 torture_comment(tctx, "Failed to open %s\n", fname);
503                 return false;
504         }
505
506         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
507         finfo1.basic_info.in.file.fnum = fnum1;
508         finfo2 = finfo1;
509
510         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
511
512         if (!NT_STATUS_IS_OK(status)) {
513                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
514                 return false;
515         }
516         
517         torture_comment(tctx, "Initial write time %s\n", 
518                nt_time_string(tctx, finfo1.basic_info.out.write_time));
519
520         /* 3 second delay to ensure we get past any 2 second time
521            granularity (older systems may have that) */
522         msleep(3 * msec);
523
524         {
525                 /* Try using setfileinfo instead of write to update write time. */
526                 union smb_setfileinfo sfinfo;
527                 time_t t_set = time(NULL);
528                 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
529                 sfinfo.basic_info.in.file.fnum = fnum1;
530                 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
531                 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
532
533                 /* I tried this with both + and - ve to see if it makes a different.
534                    It doesn't - once the filetime is set via setfileinfo it stays that way. */
535 #if 1
536                 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
537 #else
538                 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
539 #endif
540                 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
541                 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
542
543                 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
544
545                 if (!NT_STATUS_IS_OK(status)) {
546                         DEBUG(0, ("sfileinfo failed: %s\n", nt_errstr(status)));
547                         return false;
548                 }
549         }
550
551         finfo2.basic_info.in.file.path = fname;
552         
553         status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
554
555         if (!NT_STATUS_IS_OK(status)) {
556                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
557                 return false;
558         }
559         torture_comment(tctx, "write time %s\n",
560                nt_time_string(tctx, finfo2.basic_info.out.write_time));
561
562         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
563                 torture_comment(tctx, "Server updated write_time (correct)\n");
564         } else {
565                 torture_comment(tctx, "Server did not update write time (wrong!)\n");
566                 ret = false;
567         }
568
569         /* Now try a write to see if the write time gets reset. */
570
571         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
572         finfo1.basic_info.in.file.fnum = fnum1;
573         finfo2 = finfo1;
574
575         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
576
577         if (!NT_STATUS_IS_OK(status)) {
578                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
579                 return false;
580         }
581         
582         torture_comment(tctx, "Modified write time %s\n", 
583                nt_time_string(tctx, finfo1.basic_info.out.write_time));
584
585
586         torture_comment(tctx, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
587
588         written =  smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
589
590         if (written != 10) {
591                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
592                        (int)written, __location__);
593                 return false;
594         }
595
596         /* Just to prove to tridge that the an smbflush has no effect on
597            the write time :-). The setfileinfo IS STICKY. JRA. */
598
599         torture_comment(tctx, "Doing flush after write\n");
600
601         flsh.flush.level        = RAW_FLUSH_FLUSH;
602         flsh.flush.in.file.fnum = fnum1;
603         status = smb_raw_flush(cli->tree, &flsh);
604         if (!NT_STATUS_IS_OK(status)) {
605                 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
606                 return false;
607         }
608
609         /* Once the time was set using setfileinfo then it stays set - writes
610            don't have any effect. But make sure. */
611         start = timeval_current();
612         end = timeval_add(&start, (15*sec), 0);
613         while (!timeval_expired(&end)) {
614                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
615
616                 if (!NT_STATUS_IS_OK(status)) {
617                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
618                         ret = false;
619                         break;
620                 }
621                 torture_comment(tctx, "write time %s\n", 
622                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
623                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
624                         double diff = timeval_elapsed(&start);
625                         torture_comment(tctx, "Server updated write_time after %.2f seconds"
626                                         "(1sec == %.2f) (wrong!)\n",
627                                         diff, sec);
628                         ret = false;
629                         break;
630                 }
631                 fflush(stdout);
632                 msleep(1 * msec);
633         }
634         
635         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
636                 torture_comment(tctx, "Server did not update write time (correct)\n");
637         }
638
639         fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
640         if (fnum2 == -1) {
641                 torture_comment(tctx, "Failed to open %s\n", fname);
642                 return false;
643         }
644         
645         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");
646
647         written =  smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
648
649         if (written != 10) {
650                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
651                        (int)written, __location__);
652                 return false;
653         }
654
655         status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
656
657         if (!NT_STATUS_IS_OK(status)) {
658                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
659                 return false;
660         }
661         torture_comment(tctx, "write time %s\n", 
662                nt_time_string(tctx, finfo2.basic_info.out.write_time));
663         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
664                 torture_comment(tctx, "Server updated write_time (wrong!)\n");
665                 ret = false;
666         }
667
668         torture_comment(tctx, "Closing the first fd to see if write time updated.\n");
669         smbcli_close(cli->tree, fnum1);
670         fnum1 = -1;
671
672         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");
673
674         written =  smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
675
676         if (written != 10) {
677                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
678                        (int)written, __location__);
679                 return false;
680         }
681
682         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
683         finfo1.basic_info.in.file.fnum = fnum2;
684         finfo2 = finfo1;
685         status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
686
687         if (!NT_STATUS_IS_OK(status)) {
688                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
689                 return false;
690         }
691         torture_comment(tctx, "write time %s\n", 
692                nt_time_string(tctx, finfo2.basic_info.out.write_time));
693         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
694                 torture_comment(tctx, "Server updated write_time (wrong!)\n");
695                 ret = false;
696         }
697
698         /* Once the time was set using setfileinfo then it stays set - writes
699            don't have any effect. But make sure. */
700         start = timeval_current();
701         end = timeval_add(&start, (15*sec), 0);
702         while (!timeval_expired(&end)) {
703                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
704
705                 if (!NT_STATUS_IS_OK(status)) {
706                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
707                         ret = false;
708                         break;
709                 }
710                 torture_comment(tctx, "write time %s\n", 
711                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
712                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
713                         double diff = timeval_elapsed(&start);
714                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
715                                         "(1sec == %.2f) (wrong!)\n",
716                                         diff, sec);
717                         ret = false;
718                         break;
719                 }
720                 fflush(stdout);
721                 msleep(1 * msec);
722         }
723         
724         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
725                 torture_comment(tctx, "Server did not update write time (correct)\n");
726         }
727
728         torture_comment(tctx, "Closing second fd to see if write time updated.\n");
729
730         smbcli_close(cli->tree, fnum2);
731         fnum2 = -1;
732
733         fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
734         if (fnum1 == -1) {
735                 torture_comment(tctx, "Failed to open %s\n", fname);
736                 return false;
737         }
738
739         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
740         finfo1.basic_info.in.file.fnum = fnum1;
741         finfo2 = finfo1;
742
743         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
744
745         if (!NT_STATUS_IS_OK(status)) {
746                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
747                 return false;
748         }
749         
750         torture_comment(tctx, "Second open initial write time %s\n", 
751                nt_time_string(tctx, finfo1.basic_info.out.write_time));
752
753         msleep(10 * msec);
754         torture_comment(tctx, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
755
756         written =  smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
757
758         if (written != 10) {
759                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
760                        (int)written, __location__);
761                 return false;
762         }
763
764         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
765         finfo1.basic_info.in.file.fnum = fnum1;
766         finfo2 = finfo1;
767         status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
768
769         if (!NT_STATUS_IS_OK(status)) {
770                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
771                 return false;
772         }
773         torture_comment(tctx, "write time %s\n", 
774                nt_time_string(tctx, finfo2.basic_info.out.write_time));
775         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
776                 torture_comment(tctx, "Server updated write_time (wrong!)\n");
777                 ret = false;
778         }
779
780         /* Now the write time should be updated again */
781         start = timeval_current();
782         end = timeval_add(&start, (15*sec), 0);
783         while (!timeval_expired(&end)) {
784                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
785
786                 if (!NT_STATUS_IS_OK(status)) {
787                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
788                         ret = false;
789                         break;
790                 }
791                 torture_comment(tctx, "write time %s\n", 
792                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
793                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
794                         double diff = timeval_elapsed(&start);
795                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
796                                 torture_comment(tctx, "Server updated write_time after %.2f seconds"
797                                                 "(1sec == %.2f) (wrong!)\n",
798                                                 diff, sec);
799                                 ret = false;
800                                 break;
801                         }
802
803                         torture_comment(tctx, "Server updated write_time after %.2f seconds"
804                                         "(1sec == %.2f) (correct)\n",
805                                         diff, sec);
806                         break;
807                 }
808                 fflush(stdout);
809                 msleep(1*msec);
810         }
811         
812         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
813                 torture_comment(tctx, "Server did not update write time (wrong!)\n");
814                 ret = false;
815         }
816
817
818         /* One more test to do. We should read the filetime via findfirst on the
819            second connection to ensure it's the same. This is very easy for a Windows
820            server but a bastard to get right on a POSIX server. JRA. */
821
822         if (fnum1 != -1)
823                 smbcli_close(cli->tree, fnum1);
824         smbcli_unlink(cli->tree, fname);
825         smbcli_deltree(cli->tree, BASEDIR);
826
827         return ret;
828 }
829
830
831 /* Windows does obviously not update the stat info during a write call. I
832  * *think* this is the problem causing a spurious Excel 2003 on XP error
833  * message when saving a file. Excel does a setfileinfo, writes, and then does
834  * a getpath(!)info. Or so... For Samba sometimes it displays an error message
835  * that the file might have been changed in between. What i've been able to
836  * trace down is that this happens if the getpathinfo after the write shows a
837  * different last write time than the setfileinfo showed. This is really
838  * nasty....
839  */
840
841 static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli, 
842                                                                    struct smbcli_state *cli2)
843 {
844         union smb_fileinfo finfo1, finfo2;
845         const char *fname = BASEDIR "\\torture_file.txt";
846         NTSTATUS status;
847         int fnum1 = -1;
848         int fnum2;
849         bool ret = true;
850         ssize_t written;
851         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
852         int normal_delay = 2000000;
853         double sec = ((double)used_delay) / ((double)normal_delay);
854         int msec = 1000 * sec;
855
856         if (!torture_setup_dir(cli, BASEDIR)) {
857                 return false;
858         }
859
860         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
861         if (fnum1 == -1) {
862                 ret = false;
863                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
864                 goto done;
865         }
866
867         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
868         finfo1.basic_info.in.file.fnum = fnum1;
869
870         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
871
872         if (!NT_STATUS_IS_OK(status)) {
873                 ret = false;
874                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
875                 goto done;
876         }
877
878         msleep(1 * msec);
879
880         written =  smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
881
882         if (written != 1) {
883                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
884                 ret = false;
885                 goto done;
886         }
887
888         fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
889         if (fnum2 == -1) {
890                 torture_result(tctx, TORTURE_FAIL, __location__": failed to open 2nd time - %s", 
891                        smbcli_errstr(cli2->tree));
892                 ret = false;
893                 goto done;
894         }
895         
896         written =  smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
897         
898         if (written != 1) {
899                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", 
900                        (int)written);
901                 ret = false;
902                 goto done;
903         }
904         
905         finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
906         finfo2.basic_info.in.file.path = fname;
907         
908         status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
909         
910         if (!NT_STATUS_IS_OK(status)) {
911                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", 
912                           nt_errstr(status));
913                 ret = false;
914                 goto done;
915         }
916         
917         if (finfo1.basic_info.out.create_time !=
918             finfo2.basic_info.out.create_time) {
919                 torture_result(tctx, TORTURE_FAIL, __location__": create_time changed");
920                 ret = false;
921                 goto done;
922         }
923         
924         if (finfo1.basic_info.out.access_time !=
925             finfo2.basic_info.out.access_time) {
926                 torture_result(tctx, TORTURE_FAIL, __location__": access_time changed");
927                 ret = false;
928                 goto done;
929         }
930         
931         if (finfo1.basic_info.out.write_time !=
932             finfo2.basic_info.out.write_time) {
933                 torture_result(tctx, TORTURE_FAIL, __location__": write_time changed:\n"
934                                            "write time conn 1 = %s, conn 2 = %s", 
935                        nt_time_string(tctx, finfo1.basic_info.out.write_time),
936                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
937                 ret = false;
938                 goto done;
939         }
940         
941         if (finfo1.basic_info.out.change_time !=
942             finfo2.basic_info.out.change_time) {
943                 torture_result(tctx, TORTURE_FAIL, __location__": change_time changed");
944                 ret = false;
945                 goto done;
946         }
947         
948         /* One of the two following calls updates the qpathinfo. */
949         
950         /* If you had skipped the smbcli_write on fnum2, it would
951          * *not* have updated the stat on disk */
952         
953         smbcli_close(cli2->tree, fnum2);
954         cli2 = NULL;
955
956         /* This call is only for the people looking at ethereal :-) */
957         finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
958         finfo2.basic_info.in.file.path = fname;
959
960         status = smb_raw_pathinfo(cli->tree, tctx, &finfo2);
961
962         if (!NT_STATUS_IS_OK(status)) {
963                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
964                 ret = false;
965                 goto done;
966         }
967
968  done:
969         if (fnum1 != -1)
970                 smbcli_close(cli->tree, fnum1);
971         smbcli_unlink(cli->tree, fname);
972         smbcli_deltree(cli->tree, BASEDIR);
973
974         return ret;
975 }
976
977 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
978         uint64_t r = 10*1000*1000; \
979         NTTIME g = (given).basic_info.out.write_time; \
980         NTTIME gr = (g / r) * r; \
981         NTTIME c = (correct).basic_info.out.write_time; \
982         NTTIME cr = (c / r) * r; \
983         bool strict = torture_setting_bool(tctx, "strict mode", false); \
984         bool err = false; \
985         if (strict && (g cmp c)) { \
986                 err = true; \
987         } else if ((g cmp c) && (gr cmp cr)) { \
988                 /* handle filesystem without high resolution timestamps */ \
989                 err = true; \
990         } \
991         if (err) { \
992                 torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \
993                                 #given, nt_time_string(tctx, g), (unsigned long long)g, \
994                                 #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \
995                 ret = false; \
996                 goto done; \
997         } \
998 } while (0)
999 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
1000         COMPARE_WRITE_TIME_CMP(given,correct,!=)
1001 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
1002         COMPARE_WRITE_TIME_CMP(given,correct,<=)
1003 #define COMPARE_WRITE_TIME_LESS(given,correct) \
1004         COMPARE_WRITE_TIME_CMP(given,correct,>=)
1005
1006 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
1007         NTTIME g = (given).basic_info.out.access_time; \
1008         NTTIME c = (correct).basic_info.out.access_time; \
1009         if (g cmp c) { \
1010                 torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \
1011                                 #given, nt_time_string(tctx, g), \
1012                                 #cmp, #correct, nt_time_string(tctx, c)); \
1013                 ret = false; \
1014                 goto done; \
1015         } \
1016 } while (0)
1017 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
1018         COMPARE_ACCESS_TIME_CMP(given,correct,!=)
1019
1020 #define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \
1021         COMPARE_ACCESS_TIME_EQUAL(given,correct); \
1022         COMPARE_WRITE_TIME_EQUAL(given,correct); \
1023 } while (0)
1024
1025 #define GET_INFO_FILE(finfo) do { \
1026         NTSTATUS _status; \
1027         _status = smb_raw_fileinfo(cli->tree, tctx, &finfo); \
1028         if (!NT_STATUS_IS_OK(_status)) { \
1029                 ret = false; \
1030                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1031                                nt_errstr(_status)); \
1032                 goto done; \
1033         } \
1034         torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1035                         nt_time_string(tctx, finfo.basic_info.out.access_time), \
1036                         nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1037 } while (0)
1038 #define GET_INFO_PATH(pinfo) do { \
1039         NTSTATUS _status; \
1040         _status = smb_raw_pathinfo(cli2->tree, tctx, &pinfo); \
1041         if (!NT_STATUS_IS_OK(_status)) { \
1042                 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \
1043                                nt_errstr(_status)); \
1044                 ret = false; \
1045                 goto done; \
1046         } \
1047         torture_comment(tctx, "pathinfo: Access(%s) Write(%s)\n", \
1048                         nt_time_string(tctx, pinfo.basic_info.out.access_time), \
1049                         nt_time_string(tctx, pinfo.basic_info.out.write_time)); \
1050 } while (0)
1051 #define GET_INFO_BOTH(finfo,pinfo) do { \
1052         GET_INFO_FILE(finfo); \
1053         GET_INFO_PATH(pinfo); \
1054         COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \
1055 } while (0)
1056
1057 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
1058         NTSTATUS _status; \
1059         union smb_setfileinfo sfinfo; \
1060         sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1061         sfinfo.basic_info.in.file.fnum = tfnum; \
1062         sfinfo.basic_info.in.create_time = 0; \
1063         sfinfo.basic_info.in.access_time = 0; \
1064         unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1065         sfinfo.basic_info.in.change_time = 0; \
1066         sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1067         _status = smb_raw_setfileinfo(tree, &sfinfo); \
1068         if (!NT_STATUS_IS_OK(_status)) { \
1069                 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1070                                nt_errstr(_status)); \
1071                 ret = false; \
1072                 goto done; \
1073         } \
1074 } while (0)
1075 #define SET_INFO_FILE(finfo, wrtime) \
1076         SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
1077
1078 static bool test_delayed_write_update3(struct torture_context *tctx,
1079                                        struct smbcli_state *cli,
1080                                        struct smbcli_state *cli2)
1081 {
1082         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1083         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1084         const char *fname = BASEDIR "\\torture_file.txt";
1085         int fnum1 = -1;
1086         bool ret = true;
1087         ssize_t written;
1088         struct timeval start;
1089         struct timeval end;
1090         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1091         int normal_delay = 2000000;
1092         double sec = ((double)used_delay) / ((double)normal_delay);
1093         int msec = 1000 * sec;
1094
1095         if (!torture_setup_dir(cli, BASEDIR)) {
1096                 return false;
1097         }
1098
1099         torture_comment(tctx, "Open the file handle\n");
1100         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1101         if (fnum1 == -1) {
1102                 ret = false;
1103                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1104                 goto done;
1105         }
1106
1107         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1108         finfo0.basic_info.in.file.fnum = fnum1;
1109         finfo1 = finfo0;
1110         finfo2 = finfo0;
1111         finfo3 = finfo0;
1112         finfo4 = finfo0;
1113         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1114         pinfo0.basic_info.in.file.path = fname;
1115         pinfo1 = pinfo0;
1116         pinfo2 = pinfo0;
1117         pinfo3 = pinfo0;
1118         pinfo4 = pinfo0;
1119         pinfo5 = pinfo0;
1120
1121         /* get the initial times */
1122         GET_INFO_BOTH(finfo0,pinfo0);
1123
1124         /*
1125          * make sure the write time is updated 2 seconds later
1126          * calcuated from the first write
1127          * (but expect upto 5 seconds extra time for a busy server)
1128          */
1129         start = timeval_current();
1130         end = timeval_add(&start, 7 * sec, 0);
1131         while (!timeval_expired(&end)) {
1132                 /* do a write */
1133                 torture_comment(tctx, "Do a write on the file handle\n");
1134                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1135                 if (written != 1) {
1136                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1137                         ret = false;
1138                         goto done;
1139                 }
1140                 /* get the times after the write */
1141                 GET_INFO_FILE(finfo1);
1142
1143                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1144                         double diff = timeval_elapsed(&start);
1145                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
1146                                 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1147                                                 "(1sec == %.2f) (wrong!)\n",
1148                                                 diff, sec);
1149                                 ret = false;
1150                                 break;
1151                         }
1152
1153                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1154                                         "(1sec == %.2f) (correct)\n",
1155                                         diff, sec);
1156                         break;
1157                 }
1158                 msleep(0.5 * msec);
1159         }
1160
1161         GET_INFO_BOTH(finfo1,pinfo1);
1162         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1163
1164         /* sure any further write doesn't update the write time */
1165         start = timeval_current();
1166         end = timeval_add(&start, 15 * sec, 0);
1167         while (!timeval_expired(&end)) {
1168                 /* do a write */
1169                 torture_comment(tctx, "Do a write on the file handle\n");
1170                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1171                 if (written != 1) {
1172                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1173                         ret = false;
1174                         goto done;
1175                 }
1176                 /* get the times after the write */
1177                 GET_INFO_BOTH(finfo2,pinfo2);
1178
1179                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1180                         double diff = timeval_elapsed(&start);
1181                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1182                                         "(1sec == %.2f) (wrong!)\n",
1183                                         diff, sec);
1184                         ret = false;
1185                         break;
1186                 }
1187                 msleep(2 * msec);
1188         }
1189
1190         GET_INFO_BOTH(finfo2,pinfo2);
1191         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1192         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1193                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1194         }
1195
1196         /* sleep */
1197         msleep(5 * msec);
1198
1199         GET_INFO_BOTH(finfo3,pinfo3);
1200         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1201
1202         /*
1203          * the close updates the write time to the time of the close
1204          * and not to the time of the last write!
1205          */
1206         torture_comment(tctx, "Close the file handle\n");
1207         smbcli_close(cli->tree, fnum1);
1208         fnum1 = -1;
1209
1210         GET_INFO_PATH(pinfo4);
1211         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1212
1213         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1214                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1215         }
1216
1217  done:
1218         if (fnum1 != -1)
1219                 smbcli_close(cli->tree, fnum1);
1220         smbcli_unlink(cli->tree, fname);
1221         smbcli_deltree(cli->tree, BASEDIR);
1222
1223         return ret;
1224 }
1225
1226 static bool test_delayed_write_update4(struct torture_context *tctx,
1227                                        struct smbcli_state *cli,
1228                                        struct smbcli_state *cli2)
1229 {
1230         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1231         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1232         const char *fname = BASEDIR "\\torture_file.txt";
1233         int fnum1 = -1;
1234         bool ret = true;
1235         ssize_t written;
1236         struct timeval start;
1237         struct timeval end;
1238         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1239         int normal_delay = 2000000;
1240         double sec = ((double)used_delay) / ((double)normal_delay);
1241         int msec = 1000 * sec;
1242
1243         if (!torture_setup_dir(cli, BASEDIR)) {
1244                 return false;
1245         }
1246
1247         torture_comment(tctx, "Open the file handle\n");
1248         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1249         if (fnum1 == -1) {
1250                 ret = false;
1251                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1252                 goto done;
1253         }
1254
1255         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1256         finfo0.basic_info.in.file.fnum = fnum1;
1257         finfo1 = finfo0;
1258         finfo2 = finfo0;
1259         finfo3 = finfo0;
1260         finfo4 = finfo0;
1261         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1262         pinfo0.basic_info.in.file.path = fname;
1263         pinfo1 = pinfo0;
1264         pinfo2 = pinfo0;
1265         pinfo3 = pinfo0;
1266         pinfo4 = pinfo0;
1267         pinfo5 = pinfo0;
1268
1269         /* get the initial times */
1270         GET_INFO_BOTH(finfo0,pinfo0);
1271
1272         /* sleep a bit */
1273         msleep(5 * msec);
1274
1275         /* do a write */
1276         torture_comment(tctx, "Do a write on the file handle\n");
1277         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1278         if (written != 1) {
1279                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1280                 ret = false;
1281                 goto done;
1282         }
1283
1284         GET_INFO_BOTH(finfo1,pinfo1);
1285         COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
1286
1287         /*
1288          * make sure the write time is updated 2 seconds later
1289          * calcuated from the first write
1290          * (but expect upto 3 seconds extra time for a busy server)
1291          */
1292         start = timeval_current();
1293         end = timeval_add(&start, 5 * sec, 0);
1294         while (!timeval_expired(&end)) {
1295                 /* get the times after the first write */
1296                 GET_INFO_FILE(finfo1);
1297
1298                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1299                         double diff = timeval_elapsed(&start);
1300                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
1301                                 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1302                                                 "(1sec == %.2f) (wrong!)\n",
1303                                                 diff, sec);
1304                                 ret = false;
1305                                 break;
1306                         }
1307
1308                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1309                                         "(1sec == %.2f) (correct)\n",
1310                                         diff, sec);
1311                         break;
1312                 }
1313                 msleep(0.5 * msec);
1314         }
1315
1316         GET_INFO_BOTH(finfo1,pinfo1);
1317         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1318
1319         /* sure any further write doesn't update the write time */
1320         start = timeval_current();
1321         end = timeval_add(&start, 15 * sec, 0);
1322         while (!timeval_expired(&end)) {
1323                 /* do a write */
1324                 torture_comment(tctx, "Do a write on the file handle\n");
1325                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1326                 if (written != 1) {
1327                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1328                         ret = false;
1329                         goto done;
1330                 }
1331                 /* get the times after the write */
1332                 GET_INFO_BOTH(finfo2,pinfo2);
1333
1334                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1335                         double diff = timeval_elapsed(&start);
1336                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1337                                         "(1sec == %.2f) (wrong!)\n",
1338                                         diff, sec);
1339                         ret = false;
1340                         break;
1341                 }
1342                 msleep(2 * msec);
1343         }
1344
1345         GET_INFO_BOTH(finfo2,pinfo2);
1346         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1347         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1348                 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
1349         }
1350
1351         /* sleep */
1352         msleep(5 * msec);
1353
1354         GET_INFO_BOTH(finfo3,pinfo3);
1355         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1356
1357         /*
1358          * the close updates the write time to the time of the close
1359          * and not to the time of the last write!
1360          */
1361         torture_comment(tctx, "Close the file handle\n");
1362         smbcli_close(cli->tree, fnum1);
1363         fnum1 = -1;
1364
1365         GET_INFO_PATH(pinfo4);
1366         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1367
1368         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1369                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1370         }
1371
1372  done:
1373         if (fnum1 != -1)
1374                 smbcli_close(cli->tree, fnum1);
1375         smbcli_unlink(cli->tree, fname);
1376         smbcli_deltree(cli->tree, BASEDIR);
1377
1378         return ret;
1379 }
1380
1381 static bool test_delayed_write_update5(struct torture_context *tctx,
1382                                        struct smbcli_state *cli,
1383                                        struct smbcli_state *cli2)
1384 {
1385         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
1386         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
1387         const char *fname = BASEDIR "\\torture_file.txt";
1388         int fnum1 = -1;
1389         bool ret = true;
1390         ssize_t written;
1391         struct timeval start;
1392         struct timeval end;
1393         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1394         int normal_delay = 2000000;
1395         double sec = ((double)used_delay) / ((double)normal_delay);
1396         int msec = 1000 * sec;
1397
1398         if (!torture_setup_dir(cli, BASEDIR)) {
1399                 return false;
1400         }
1401
1402         torture_comment(tctx, "Open the file handle\n");
1403         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1404         if (fnum1 == -1) {
1405                 ret = false;
1406                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1407                 goto done;
1408         }
1409
1410         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1411         finfo0.basic_info.in.file.fnum = fnum1;
1412         finfo1 = finfo0;
1413         finfo2 = finfo0;
1414         finfo3 = finfo0;
1415         finfo4 = finfo0;
1416         finfo5 = finfo0;
1417         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1418         pinfo0.basic_info.in.file.path = fname;
1419         pinfo1 = pinfo0;
1420         pinfo2 = pinfo0;
1421         pinfo3 = pinfo0;
1422         pinfo4 = pinfo0;
1423         pinfo5 = pinfo0;
1424         pinfo6 = pinfo0;
1425
1426         /* get the initial times */
1427         GET_INFO_BOTH(finfo0,pinfo0);
1428
1429         /* do a write */
1430         torture_comment(tctx, "Do a write on the file handle\n");
1431         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1432         if (written != 1) {
1433                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1434                 ret = false;
1435                 goto done;
1436         }
1437
1438         GET_INFO_BOTH(finfo1,pinfo1);
1439         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1440
1441         torture_comment(tctx, "Set write time in the future on the file handle\n");
1442         SET_INFO_FILE(finfo0, time(NULL) + 86400);
1443         GET_INFO_BOTH(finfo2,pinfo2);
1444         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1445
1446         torture_comment(tctx, "Set write time in the past on the file handle\n");
1447         SET_INFO_FILE(finfo0, time(NULL) - 86400);
1448         GET_INFO_BOTH(finfo2,pinfo2);
1449         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
1450
1451         /* make sure the 2 second delay from the first write are canceled */
1452         start = timeval_current();
1453         end = timeval_add(&start, 15 * sec, 0);
1454         while (!timeval_expired(&end)) {
1455
1456                 /* get the times after the first write */
1457                 GET_INFO_BOTH(finfo3,pinfo3);
1458
1459                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
1460                         double diff = timeval_elapsed(&start);
1461                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1462                                         "(1sec == %.2f) (wrong!)\n",
1463                                         diff, sec);
1464                         ret = false;
1465                         break;
1466                 }
1467                 msleep(2 * msec);
1468         }
1469
1470         GET_INFO_BOTH(finfo3,pinfo3);
1471         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1472         if (finfo3.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1473                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1474         }
1475
1476         /* sure any further write doesn't update the write time */
1477         start = timeval_current();
1478         end = timeval_add(&start, 15 * sec, 0);
1479         while (!timeval_expired(&end)) {
1480                 /* do a write */
1481                 torture_comment(tctx, "Do a write on the file handle\n");
1482                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1483                 if (written != 1) {
1484                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1485                         ret = false;
1486                         goto done;
1487                 }
1488                 /* get the times after the write */
1489                 GET_INFO_BOTH(finfo4,pinfo4);
1490
1491                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
1492                         double diff = timeval_elapsed(&start);
1493                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1494                                         "(1sec == %.2f) (wrong!)\n",
1495                                         diff, sec);
1496                         ret = false;
1497                         break;
1498                 }
1499                 msleep(2 * msec);
1500         }
1501
1502         GET_INFO_BOTH(finfo4,pinfo4);
1503         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
1504         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1505                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1506         }
1507
1508         /* sleep */
1509         msleep(5 * msec);
1510
1511         GET_INFO_BOTH(finfo5,pinfo5);
1512         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
1513
1514         /*
1515          * the close doesn't update the write time
1516          */
1517         torture_comment(tctx, "Close the file handle\n");
1518         smbcli_close(cli->tree, fnum1);
1519         fnum1 = -1;
1520
1521         GET_INFO_PATH(pinfo6);
1522         COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
1523
1524         if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
1525                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1526         }
1527
1528  done:
1529         if (fnum1 != -1)
1530                 smbcli_close(cli->tree, fnum1);
1531         smbcli_unlink(cli->tree, fname);
1532         smbcli_deltree(cli->tree, BASEDIR);
1533
1534         return ret;
1535 }
1536
1537 static bool test_delayed_write_update6(struct torture_context *tctx,
1538                                        struct smbcli_state *cli,
1539                                        struct smbcli_state *cli2)
1540 {
1541         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
1542         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
1543         const char *fname = BASEDIR "\\torture_file.txt";
1544         int fnum1 = -1;
1545         int fnum2 = -1;
1546         bool ret = true;
1547         ssize_t written;
1548         struct timeval start;
1549         struct timeval end;
1550         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1551         int normal_delay = 2000000;
1552         double sec = ((double)used_delay) / ((double)normal_delay);
1553         int msec = 1000 * sec;
1554         bool first = true;
1555
1556         if (!torture_setup_dir(cli, BASEDIR)) {
1557                 return false;
1558         }
1559 again:
1560         torture_comment(tctx, "Open the file handle\n");
1561         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1562         if (fnum1 == -1) {
1563                 ret = false;
1564                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1565                 goto done;
1566         }
1567
1568         if (fnum2 == -1) {
1569                 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
1570                 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1571                 if (fnum2 == -1) {
1572                         ret = false;
1573                         torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1574                         goto done;
1575                 }
1576         }
1577
1578         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1579         finfo0.basic_info.in.file.fnum = fnum1;
1580         finfo1 = finfo0;
1581         finfo2 = finfo0;
1582         finfo3 = finfo0;
1583         finfo4 = finfo0;
1584         finfo5 = finfo0;
1585         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1586         pinfo0.basic_info.in.file.path = fname;
1587         pinfo1 = pinfo0;
1588         pinfo2 = pinfo0;
1589         pinfo3 = pinfo0;
1590         pinfo4 = pinfo0;
1591         pinfo5 = pinfo0;
1592         pinfo6 = pinfo0;
1593         pinfo7 = pinfo0;
1594
1595         /* get the initial times */
1596         GET_INFO_BOTH(finfo0,pinfo0);
1597
1598         /* do a write */
1599         torture_comment(tctx, "Do a write on the file handle\n");
1600         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1601         if (written != 1) {
1602                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1603                 ret = false;
1604                 goto done;
1605         }
1606
1607         GET_INFO_BOTH(finfo1,pinfo1);
1608         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1609
1610         torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
1611         SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
1612         GET_INFO_BOTH(finfo2,pinfo2);
1613         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1614
1615         torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
1616         SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
1617         GET_INFO_BOTH(finfo2,pinfo2);
1618         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
1619
1620         /* make sure the 2 second delay from the first write are canceled */
1621         start = timeval_current();
1622         end = timeval_add(&start, 15 * sec, 0);
1623         while (!timeval_expired(&end)) {
1624
1625                 /* get the times after the first write */
1626                 GET_INFO_BOTH(finfo3,pinfo3);
1627
1628                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
1629                         double diff = timeval_elapsed(&start);
1630                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1631                                         "(1sec == %.2f) (wrong!)\n",
1632                                         diff, sec);
1633                         ret = false;
1634                         break;
1635                 }
1636                 msleep(2 * msec);
1637         }
1638
1639         GET_INFO_BOTH(finfo3,pinfo3);
1640         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1641         if (finfo3.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1642                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1643         }
1644
1645         /* sure any further write doesn't update the write time */
1646         start = timeval_current();
1647         end = timeval_add(&start, 15 * sec, 0);
1648         while (!timeval_expired(&end)) {
1649                 /* do a write */
1650                 torture_comment(tctx, "Do a write on the file handle\n");
1651                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1652                 if (written != 1) {
1653                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1654                         ret = false;
1655                         goto done;
1656                 }
1657                 /* get the times after the write */
1658                 GET_INFO_BOTH(finfo4,pinfo4);
1659
1660                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
1661                         double diff = timeval_elapsed(&start);
1662                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1663                                         "(1sec == %.2f) (wrong!)\n",
1664                                         diff, sec);
1665                         ret = false;
1666                         break;
1667                 }
1668                 msleep(2 * msec);
1669         }
1670
1671         GET_INFO_BOTH(finfo4,pinfo4);
1672         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
1673         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
1674                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1675         }
1676
1677         /* sleep */
1678         msleep(5 * msec);
1679
1680         GET_INFO_BOTH(finfo5,pinfo5);
1681         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
1682
1683         /*
1684          * the close updates the write time to the time of the close
1685          * as the write time was set on the 2nd handle
1686          */
1687         torture_comment(tctx, "Close the file handle\n");
1688         smbcli_close(cli->tree, fnum1);
1689         fnum1 = -1;
1690
1691         GET_INFO_PATH(pinfo6);
1692         COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
1693
1694         if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
1695                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1696         }
1697
1698         /* keep the 2nd handle open and rerun tests */
1699         if (first) {
1700                 first = false;
1701                 goto again;
1702         }
1703
1704         /*
1705          * closing the 2nd handle will cause no write time update
1706          * as the write time was explicit set on this handle
1707          */
1708         torture_comment(tctx, "Close the 2nd file handle\n");
1709         smbcli_close(cli2->tree, fnum2);
1710         fnum2 = -1;
1711
1712         GET_INFO_PATH(pinfo7);
1713         COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
1714
1715         if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
1716                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1717         }
1718
1719  done:
1720         if (fnum1 != -1)
1721                 smbcli_close(cli->tree, fnum1);
1722         if (fnum2 != -1)
1723                 smbcli_close(cli2->tree, fnum2);
1724         smbcli_unlink(cli->tree, fname);
1725         smbcli_deltree(cli->tree, BASEDIR);
1726
1727         return ret;
1728 }
1729
1730
1731 /* 
1732    testing of delayed update of write_time
1733 */
1734 struct torture_suite *torture_delay_write(void)
1735 {
1736         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "DELAYWRITE");
1737
1738         torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
1739         torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
1740         torture_suite_add_1smb_test(suite, "update of write time and SMBread truncate", test_delayed_write_update1a);
1741         torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b);
1742         torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c);
1743         torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
1744         torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
1745         torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
1746         torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
1747         torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
1748
1749         return suite;
1750 }