A truncate write must be a smbcli_smbwrite, not a
[metze/samba/wip.git] / source4 / torture / basic / delaywrite.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    test suite for delayed write update 
5
6    Copyright (C) Volker Lendecke 2004
7    Copyright (C) Andrew Tridgell 2004
8    Copyright (C) Jeremy Allison 2004
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "torture/torture.h"
26 #include "libcli/raw/libcliraw.h"
27 #include "libcli/raw/raw_proto.h"
28 #include "system/time.h"
29 #include "system/filesys.h"
30 #include "libcli/libcli.h"
31 #include "torture/util.h"
32
33 #define BASEDIR "\\delaywrite"
34
35 static bool test_delayed_write_update(struct torture_context *tctx, struct smbcli_state *cli)
36 {
37         union smb_fileinfo finfo1, finfo2;
38         const char *fname = BASEDIR "\\torture_file.txt";
39         NTSTATUS status;
40         int fnum1 = -1;
41         bool ret = true;
42         ssize_t written;
43         struct timeval start;
44         struct timeval end;
45         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
46         int normal_delay = 2000000;
47         double sec = ((double)used_delay) / ((double)normal_delay);
48         int msec = 1000 * sec;
49
50         torture_comment(tctx, "\nRunning test_delayed_write_update\n");
51
52         if (!torture_setup_dir(cli, BASEDIR)) {
53                 return false;
54         }
55
56         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
57         if (fnum1 == -1) {
58                 torture_comment(tctx, "Failed to open %s\n", fname);
59                 return false;
60         }
61
62         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
63         finfo1.basic_info.in.file.fnum = fnum1;
64         finfo2 = finfo1;
65
66         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
67
68         if (!NT_STATUS_IS_OK(status)) {
69                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
70                 return false;
71         }
72         
73         torture_comment(tctx, "Initial write time %s\n", 
74                nt_time_string(tctx, finfo1.basic_info.out.write_time));
75
76         /* 3 second delay to ensure we get past any 2 second time
77            granularity (older systems may have that) */
78         msleep(3 * msec);
79
80         written =  smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
81
82         if (written != 1) {
83                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
84                        (int)written, __location__);
85                 return false;
86         }
87
88         start = timeval_current();
89         end = timeval_add(&start, (120*sec), 0);
90         while (!timeval_expired(&end)) {
91                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
92
93                 if (!NT_STATUS_IS_OK(status)) {
94                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
95                         ret = false;
96                         break;
97                 }
98                 torture_comment(tctx, "write time %s\n", 
99                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
100                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
101                         double diff = timeval_elapsed(&start);
102                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
103                                 torture_comment(tctx, "Server updated write_time after %.2f seconds"
104                                                 "(1 sec == %.2f)(wrong!)\n",
105                                                 diff, sec);
106                                 ret = false;
107                                 break;
108                         }
109
110                         torture_comment(tctx, "Server updated write_time after %.2f seconds"
111                                         "(1 sec == %.2f)(correct)\n",
112                                         diff, sec);
113                         break;
114                 }
115                 fflush(stdout);
116                 msleep(1 * msec);
117         }
118         
119         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
120                 torture_comment(tctx, "Server did not update write time (wrong!)\n");
121                 ret = false;
122         }
123
124
125         if (fnum1 != -1)
126                 smbcli_close(cli->tree, fnum1);
127         smbcli_unlink(cli->tree, fname);
128         smbcli_deltree(cli->tree, BASEDIR);
129
130         return ret;
131 }
132
133 static bool test_delayed_write_update1(struct torture_context *tctx, struct smbcli_state *cli)
134 {
135         union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
136         const char *fname = BASEDIR "\\torture_file1.txt";
137         NTSTATUS status;
138         int fnum1 = -1;
139         bool ret = true;
140         ssize_t written;
141         struct timeval start;
142         struct timeval end;
143         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
144         int normal_delay = 2000000;
145         double sec = ((double)used_delay) / ((double)normal_delay);
146         int msec = 1000 * sec;
147         char buf[2048];
148
149         torture_comment(tctx, "\nRunning test_delayed_write_update1\n");
150
151         if (!torture_setup_dir(cli, BASEDIR)) {
152                 return false;
153         }
154
155         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
156         if (fnum1 == -1) {
157                 torture_comment(tctx, "Failed to open %s\n", fname);
158                 return false;
159         }
160
161         memset(buf, 'x', 2048);
162         written =  smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
163
164         /* 3 second delay to ensure we get past any 2 second time
165            granularity (older systems may have that) */
166         msleep(3 * msec);
167
168         finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
169         finfo1.all_info.in.file.fnum = fnum1;
170         finfo2 = finfo1;
171         finfo3 = finfo1;
172         pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
173         pinfo4.all_info.in.file.path = fname;
174
175         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
176
177         if (!NT_STATUS_IS_OK(status)) {
178                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
179                 return false;
180         }
181
182         torture_comment(tctx, "Initial write time %s\n", 
183                nt_time_string(tctx, finfo1.all_info.out.write_time));
184
185         /* Do a zero length SMBwrite call to truncate. */
186         written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
187
188         if (written != 0) {
189                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
190                        (int)written, __location__);
191                 return false;
192         }
193
194         start = timeval_current();
195         end = timeval_add(&start, (120*sec), 0);
196         while (!timeval_expired(&end)) {
197                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
198
199                 if (!NT_STATUS_IS_OK(status)) {
200                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
201                         ret = false;
202                         break;
203                 }
204
205                 if (finfo2.all_info.out.size != 1024) {
206                         DEBUG(0, ("file not truncated, size = %u (should be 1024)\n",
207                                 (unsigned int)finfo2.all_info.out.size));
208                         ret = false;
209                         break;
210                 }
211
212                 torture_comment(tctx, "write time %s\n",
213                        nt_time_string(tctx, finfo2.all_info.out.write_time));
214                 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
215                         double diff = timeval_elapsed(&start);
216                         if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
217                                 torture_comment(tctx, "After SMBwrite truncate "
218                                         "server updated write_time after %.2f seconds"
219                                         "(1 sec == %.2f)(wrong!)\n",
220                                         diff, sec);
221                                 ret = false;
222                                 break;
223                         }
224
225                         torture_comment(tctx, "After SMBwrite truncate "
226                                         "server updated write_time after %.2f seconds"
227                                         "(1 sec == %.2f)(correct)\n",
228                                         diff, sec);
229                         break;
230                 }
231                 fflush(stdout);
232                 msleep(1 * msec);
233         }
234
235         if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
236                 torture_comment(tctx, "Server did not update write time (wrong!)\n");
237                 ret = false;
238         }
239
240         /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
241         written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
242
243         if (written != 1) {
244                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
245                        (int)written, __location__);
246                 return false;
247         }
248
249         start = timeval_current();
250         end = timeval_add(&start, (10*sec), 0);
251         while (!timeval_expired(&end)) {
252                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
253
254                 if (!NT_STATUS_IS_OK(status)) {
255                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
256                         ret = false;
257                         break;
258                 }
259
260                 if (finfo3.all_info.out.size != 1024) {
261                         DEBUG(0, ("file not truncated, size = %u (should be 1024)\n",
262                                 (unsigned int)finfo3.all_info.out.size));
263                         ret = false;
264                         break;
265                 }
266
267                 torture_comment(tctx, "write time %s\n",
268                        nt_time_string(tctx, finfo3.all_info.out.write_time));
269                 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
270                         double diff = timeval_elapsed(&start);
271
272                         torture_comment(tctx, "server updated write_time after %.2f seconds"
273                                         "(1 sec == %.2f)(correct)\n",
274                                         diff, sec);
275                         break;
276                 }
277                 fflush(stdout);
278                 msleep(1 * msec);
279         }
280
281         if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
282                 torture_comment(tctx, "Server updated write time (wrong!)\n");
283                 ret = false;
284         }
285
286         /* the close should trigger an write time update */
287         smbcli_close(cli->tree, fnum1);
288         fnum1 = -1;
289
290         status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
291         if (!NT_STATUS_IS_OK(status)) {
292                 DEBUG(0, ("pathinfo failed: %s\n", nt_errstr(status)));
293                 return false;
294         }
295
296         if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
297                 torture_comment(tctx, "Server did not update write time on close (wrong!)\n");
298                 ret = false;
299         } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
300                 torture_comment(tctx, "Server updated write time on close (correct)\n");
301         }
302
303         if (fnum1 != -1)
304                 smbcli_close(cli->tree, fnum1);
305         smbcli_unlink(cli->tree, fname);
306         smbcli_deltree(cli->tree, BASEDIR);
307
308         return ret;
309 }
310
311 /* Updating with a SMBwrite of zero length
312  * changes the write time immediately - even on expand. */
313
314 static bool test_delayed_write_update1a(struct torture_context *tctx, struct smbcli_state *cli)
315 {
316         union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
317         const char *fname = BASEDIR "\\torture_file1a.txt";
318         NTSTATUS status;
319         int fnum1 = -1;
320         bool ret = true;
321         ssize_t written;
322         struct timeval start;
323         struct timeval end;
324         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
325         int normal_delay = 2000000;
326         double sec = ((double)used_delay) / ((double)normal_delay);
327         int msec = 1000 * sec;
328         char buf[2048];
329
330         torture_comment(tctx, "\nRunning test_delayed_write_update1a\n");
331
332         if (!torture_setup_dir(cli, BASEDIR)) {
333                 return false;
334         }
335
336         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
337         if (fnum1 == -1) {
338                 torture_comment(tctx, "Failed to open %s\n", fname);
339                 return false;
340         }
341
342         memset(buf, 'x', 2048);
343         written =  smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
344
345         /* 3 second delay to ensure we get past any 2 second time
346            granularity (older systems may have that) */
347         msleep(3 * msec);
348
349         finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
350         finfo1.all_info.in.file.fnum = fnum1;
351         finfo2 = finfo1;
352         finfo3 = finfo1;
353         pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
354         pinfo4.all_info.in.file.path = fname;
355
356         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
357
358         if (!NT_STATUS_IS_OK(status)) {
359                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
360                 return false;
361         }
362
363         torture_comment(tctx, "Initial write time %s\n", 
364                nt_time_string(tctx, finfo1.all_info.out.write_time));
365
366         /* Do a zero length SMBwrite call to truncate. */
367         written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
368
369         if (written != 0) {
370                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
371                        (int)written, __location__);
372                 return false;
373         }
374
375         start = timeval_current();
376         end = timeval_add(&start, (120*sec), 0);
377         while (!timeval_expired(&end)) {
378                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
379
380                 if (!NT_STATUS_IS_OK(status)) {
381                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
382                         ret = false;
383                         break;
384                 }
385
386                 if (finfo2.all_info.out.size != 10240) {
387                         DEBUG(0, ("file not truncated, size = %u (should be 10240)\n",
388                                 (unsigned int)finfo2.all_info.out.size));
389                         ret = false;
390                         break;
391                 }
392
393                 torture_comment(tctx, "write time %s\n",
394                        nt_time_string(tctx, finfo2.all_info.out.write_time));
395                 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
396                         double diff = timeval_elapsed(&start);
397                         if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
398                                 torture_comment(tctx, "After SMBwrite truncate "
399                                         "server updated write_time after %.2f seconds"
400                                         "(1 sec == %.2f)(wrong!)\n",
401                                         diff, sec);
402                                 ret = false;
403                                 break;
404                         }
405
406                         torture_comment(tctx, "After SMBwrite truncate "
407                                         "server updated write_time after %.2f seconds"
408                                         "(1 sec == %.2f)(correct)\n",
409                                         diff, sec);
410                         break;
411                 }
412                 fflush(stdout);
413                 msleep(1 * msec);
414         }
415
416         if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
417                 torture_comment(tctx, "Server did not update write time (wrong!)\n");
418                 ret = false;
419         }
420
421         /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
422         written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
423
424         if (written != 1) {
425                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
426                        (int)written, __location__);
427                 return false;
428         }
429
430         start = timeval_current();
431         end = timeval_add(&start, (10*sec), 0);
432         while (!timeval_expired(&end)) {
433                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
434
435                 if (!NT_STATUS_IS_OK(status)) {
436                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
437                         ret = false;
438                         break;
439                 }
440
441                 if (finfo3.all_info.out.size != 10240) {
442                         DEBUG(0, ("file not truncated, size = %u (should be 10240)\n",
443                                 (unsigned int)finfo3.all_info.out.size));
444                         ret = false;
445                         break;
446                 }
447
448                 torture_comment(tctx, "write time %s\n",
449                        nt_time_string(tctx, finfo3.all_info.out.write_time));
450                 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
451                         double diff = timeval_elapsed(&start);
452
453                         torture_comment(tctx, "server updated write_time after %.2f seconds"
454                                         "(1 sec == %.2f)(correct)\n",
455                                         diff, sec);
456                         break;
457                 }
458                 fflush(stdout);
459                 msleep(1 * msec);
460         }
461
462         if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
463                 torture_comment(tctx, "Server updated write time (wrong!)\n");
464                 ret = false;
465         }
466
467         /* the close should trigger an write time update */
468         smbcli_close(cli->tree, fnum1);
469         fnum1 = -1;
470
471         status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
472         if (!NT_STATUS_IS_OK(status)) {
473                 DEBUG(0, ("pathinfo failed: %s\n", nt_errstr(status)));
474                 return false;
475         }
476
477         if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
478                 torture_comment(tctx, "Server did not update write time on close (wrong!)\n");
479                 ret = false;
480         } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
481                 torture_comment(tctx, "Server updated write time on close (correct)\n");
482         }
483
484         if (fnum1 != -1)
485                 smbcli_close(cli->tree, fnum1);
486         smbcli_unlink(cli->tree, fname);
487         smbcli_deltree(cli->tree, BASEDIR);
488
489         return ret;
490 }
491
492 /* Updating with a SET_FILE_END_OF_FILE_INFO
493  * changes the write time immediately - even on expand. */
494
495 static bool test_delayed_write_update1b(struct torture_context *tctx, struct smbcli_state *cli)
496 {
497         union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
498         const char *fname = BASEDIR "\\torture_file1b.txt";
499         NTSTATUS status;
500         int fnum1 = -1;
501         bool ret = true;
502         ssize_t written;
503         struct timeval start;
504         struct timeval end;
505         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
506         int normal_delay = 2000000;
507         double sec = ((double)used_delay) / ((double)normal_delay);
508         int msec = 1000 * sec;
509         char buf[2048];
510
511         torture_comment(tctx, "\nRunning test_delayed_write_update1b\n");
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                 torture_comment(tctx, "Failed to open %s\n", fname);
520                 return false;
521         }
522
523         memset(buf, 'x', 2048);
524         written =  smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
525
526         /* 3 second delay to ensure we get past any 2 second time
527            granularity (older systems may have that) */
528         msleep(3 * msec);
529
530         finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
531         finfo1.all_info.in.file.fnum = fnum1;
532         finfo2 = finfo1;
533         finfo3 = finfo1;
534         pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
535         pinfo4.all_info.in.file.path = fname;
536
537         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
538
539         if (!NT_STATUS_IS_OK(status)) {
540                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
541                 return false;
542         }
543
544         torture_comment(tctx, "Initial write time %s\n",
545                nt_time_string(tctx, finfo1.all_info.out.write_time));
546
547         /* Do a SET_END_OF_FILE_INFO call to truncate. */
548         status = smbcli_ftruncate(cli->tree, fnum1, (uint64_t)10240);
549
550         if (!NT_STATUS_IS_OK(status)) {
551                 torture_comment(tctx, "SET_END_OF_FILE failed (%s)\n",
552                        nt_errstr(status));
553                 return false;
554         }
555
556         start = timeval_current();
557         end = timeval_add(&start, (120*sec), 0);
558         while (!timeval_expired(&end)) {
559                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
560
561                 if (!NT_STATUS_IS_OK(status)) {
562                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
563                         ret = false;
564                         break;
565                 }
566
567                 if (finfo2.all_info.out.size != 10240) {
568                         DEBUG(0, ("file not truncated (size = %u, should be 10240)\n",
569                                 (unsigned int)finfo2.all_info.out.size ));
570                         ret = false;
571                         break;
572                 }
573
574                 torture_comment(tctx, "write time %s\n",
575                        nt_time_string(tctx, finfo2.all_info.out.write_time));
576                 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
577                         double diff = timeval_elapsed(&start);
578                         if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
579                                 torture_comment(tctx, "After SET_END_OF_FILE truncate "
580                                         "server updated write_time after %.2f seconds"
581                                         "(1 sec == %.2f)(wrong!)\n",
582                                         diff, sec);
583                                 ret = false;
584                                 break;
585                         }
586
587                         torture_comment(tctx, "After SET_END_OF_FILE truncate "
588                                         "server updated write_time after %.2f seconds"
589                                         "(1 sec == %.2f)(correct)\n",
590                                         diff, sec);
591                         break;
592                 }
593                 fflush(stdout);
594                 msleep(1 * msec);
595         }
596
597         if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
598                 torture_comment(tctx, "Server did not update write time (wrong!)\n");
599                 ret = false;
600         }
601
602         /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
603         written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
604
605         if (written != 1) {
606                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
607                        (int)written, __location__);
608                 return false;
609         }
610
611         start = timeval_current();
612         end = timeval_add(&start, (10*sec), 0);
613         while (!timeval_expired(&end)) {
614                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
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
622                 if (finfo3.all_info.out.size != 10240) {
623                         DEBUG(0, ("file not truncated (size = %u, should be 10240)\n",
624                                 (unsigned int)finfo3.all_info.out.size ));
625                         ret = false;
626                         break;
627                 }
628
629                 torture_comment(tctx, "write time %s\n",
630                        nt_time_string(tctx, finfo3.all_info.out.write_time));
631                 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
632                         double diff = timeval_elapsed(&start);
633
634                         torture_comment(tctx, "server updated write_time after %.2f seconds"
635                                         "(1 sec == %.2f)(correct)\n",
636                                         diff, sec);
637                         break;
638                 }
639                 fflush(stdout);
640                 msleep(1 * msec);
641         }
642
643         if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
644                 torture_comment(tctx, "Server updated write time (wrong!)\n");
645                 ret = false;
646         }
647
648         /* the close should trigger an write time update */
649         smbcli_close(cli->tree, fnum1);
650         fnum1 = -1;
651
652         status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
653         if (!NT_STATUS_IS_OK(status)) {
654                 DEBUG(0, ("pathinfo failed: %s\n", nt_errstr(status)));
655                 return false;
656         }
657
658         if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
659                 torture_comment(tctx, "Server did not update write time on close (wrong!)\n");
660                 ret = false;
661         } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
662                 torture_comment(tctx, "Server updated write time on close (correct)\n");
663         }
664
665         if (fnum1 != -1)
666                 smbcli_close(cli->tree, fnum1);
667         smbcli_unlink(cli->tree, fname);
668         smbcli_deltree(cli->tree, BASEDIR);
669
670         return ret;
671 }
672
673 /* Updating with a SET_ALLOCATION_INFO (truncate) does so immediately. */
674
675 static bool test_delayed_write_update1c(struct torture_context *tctx, struct smbcli_state *cli)
676 {
677         union smb_setfileinfo parms;
678         union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
679         const char *fname = BASEDIR "\\torture_file1c.txt";
680         NTSTATUS status;
681         int fnum1 = -1;
682         bool ret = true;
683         ssize_t written;
684         struct timeval start;
685         struct timeval end;
686         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
687         int normal_delay = 2000000;
688         double sec = ((double)used_delay) / ((double)normal_delay);
689         int msec = 1000 * sec;
690         char buf[2048];
691
692         torture_comment(tctx, "\nRunning test_delayed_write_update1c\n");
693
694         if (!torture_setup_dir(cli, BASEDIR)) {
695                 return false;
696         }
697
698         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
699         if (fnum1 == -1) {
700                 torture_comment(tctx, "Failed to open %s\n", fname);
701                 return false;
702         }
703
704         memset(buf, 'x', 2048);
705         written =  smbcli_write(cli->tree, fnum1, 0, buf, 0, 2048);
706
707         /* 3 second delay to ensure we get past any 2 second time
708            granularity (older systems may have that) */
709         msleep(3 * msec);
710
711         finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
712         finfo1.all_info.in.file.fnum = fnum1;
713         finfo2 = finfo1;
714         finfo3 = finfo1;
715         pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
716         pinfo4.all_info.in.file.path = fname;
717
718         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
719
720         if (!NT_STATUS_IS_OK(status)) {
721                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
722                 return false;
723         }
724
725         torture_comment(tctx, "Initial write time %s\n",
726                nt_time_string(tctx, finfo1.all_info.out.write_time));
727
728         /* Do a SET_ALLOCATION_SIZE call to truncate. */
729         parms.allocation_info.level = RAW_SFILEINFO_ALLOCATION_INFO;
730         parms.allocation_info.in.file.fnum = fnum1;
731         parms.allocation_info.in.alloc_size = 0;
732
733         status = smb_raw_setfileinfo(cli->tree, &parms);
734
735         if (!NT_STATUS_IS_OK(status)) {
736                 torture_comment(tctx, "RAW_SFILEINFO_ALLOCATION_INFO failed (%s)\n",
737                        nt_errstr(status));
738                 return false;
739         }
740
741         start = timeval_current();
742         end = timeval_add(&start, (120*sec), 0);
743         while (!timeval_expired(&end)) {
744                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
745
746                 if (!NT_STATUS_IS_OK(status)) {
747                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
748                         ret = false;
749                         break;
750                 }
751
752                 if (finfo2.all_info.out.size != 0) {
753                         DEBUG(0, ("file not truncated (size = %u, should be 10240)\n",
754                                 (unsigned int)finfo2.all_info.out.size ));
755                         ret = false;
756                         break;
757                 }
758
759                 torture_comment(tctx, "write time %s\n",
760                        nt_time_string(tctx, finfo2.all_info.out.write_time));
761                 if (finfo1.all_info.out.write_time != finfo2.all_info.out.write_time) {
762                         double diff = timeval_elapsed(&start);
763                         if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
764                                 torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
765                                         "server updated write_time after %.2f seconds"
766                                         "(1 sec == %.2f)(wrong!)\n",
767                                         diff, sec);
768                                 ret = false;
769                                 break;
770                         }
771
772                         torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
773                                         "server updated write_time after %.2f seconds"
774                                         "(1 sec == %.2f)(correct)\n",
775                                         diff, sec);
776                         break;
777                 }
778                 fflush(stdout);
779                 msleep(1 * msec);
780         }
781
782         if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
783                 torture_comment(tctx, "Server did not update write time (wrong!)\n");
784                 ret = false;
785         }
786
787         /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
788         written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
789
790         if (written != 1) {
791                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n",
792                        (int)written, __location__);
793                 return false;
794         }
795
796         start = timeval_current();
797         end = timeval_add(&start, (10*sec), 0);
798         while (!timeval_expired(&end)) {
799                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
800
801                 if (!NT_STATUS_IS_OK(status)) {
802                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
803                         ret = false;
804                         break;
805                 }
806
807                 if (finfo3.all_info.out.size != 1) {
808                         DEBUG(0, ("file not expanded\n"));
809                         ret = false;
810                         break;
811                 }
812
813                 torture_comment(tctx, "write time %s\n",
814                        nt_time_string(tctx, finfo3.all_info.out.write_time));
815                 if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
816                         double diff = timeval_elapsed(&start);
817
818                         torture_comment(tctx, "server updated write_time after %.2f seconds"
819                                         "(1 sec == %.2f)(correct)\n",
820                                         diff, sec);
821                         break;
822                 }
823                 fflush(stdout);
824                 msleep(1 * msec);
825         }
826
827         if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
828                 torture_comment(tctx, "Server updated write time (wrong!)\n");
829                 ret = false;
830         }
831
832         /* the close should trigger an write time update */
833         smbcli_close(cli->tree, fnum1);
834         fnum1 = -1;
835
836         status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
837         if (!NT_STATUS_IS_OK(status)) {
838                 DEBUG(0, ("pathinfo failed: %s\n", nt_errstr(status)));
839                 return false;
840         }
841
842         if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
843                 torture_comment(tctx, "Server did not update write time on close (wrong!)\n");
844                 ret = false;
845         } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_time) {
846                 torture_comment(tctx, "Server updated write time on close (correct)\n");
847         }
848
849         if (fnum1 != -1)
850                 smbcli_close(cli->tree, fnum1);
851         smbcli_unlink(cli->tree, fname);
852         smbcli_deltree(cli->tree, BASEDIR);
853
854         return ret;
855 }
856
857 /*
858  * Do as above, but using 2 connections.
859  */
860
861 static bool test_delayed_write_update2(struct torture_context *tctx, struct smbcli_state *cli, 
862                                                                            struct smbcli_state *cli2)
863 {
864         union smb_fileinfo finfo1, finfo2;
865         const char *fname = BASEDIR "\\torture_file.txt";
866         NTSTATUS status;
867         int fnum1 = -1;
868         int fnum2 = -1;
869         bool ret = true;
870         ssize_t written;
871         struct timeval start;
872         struct timeval end;
873         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
874         int normal_delay = 2000000;
875         double sec = ((double)used_delay) / ((double)normal_delay);
876         int msec = 1000 * sec;
877         union smb_flush flsh;
878
879         torture_comment(tctx, "\nRunning test_delayed_write_update2\n");
880
881         if (!torture_setup_dir(cli, BASEDIR)) {
882                 return false;
883         }
884
885         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
886         if (fnum1 == -1) {
887                 torture_comment(tctx, "Failed to open %s\n", fname);
888                 return false;
889         }
890
891         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
892         finfo1.basic_info.in.file.fnum = fnum1;
893         finfo2 = finfo1;
894
895         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
896
897         if (!NT_STATUS_IS_OK(status)) {
898                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
899                 return false;
900         }
901         
902         torture_comment(tctx, "Initial write time %s\n", 
903                nt_time_string(tctx, finfo1.basic_info.out.write_time));
904
905         /* 3 second delay to ensure we get past any 2 second time
906            granularity (older systems may have that) */
907         msleep(3 * msec);
908
909         {
910                 /* Try using setfileinfo instead of write to update write time. */
911                 union smb_setfileinfo sfinfo;
912                 time_t t_set = time(NULL);
913                 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
914                 sfinfo.basic_info.in.file.fnum = fnum1;
915                 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
916                 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
917
918                 /* I tried this with both + and - ve to see if it makes a different.
919                    It doesn't - once the filetime is set via setfileinfo it stays that way. */
920 #if 1
921                 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
922 #else
923                 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
924 #endif
925                 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
926                 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
927
928                 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
929
930                 if (!NT_STATUS_IS_OK(status)) {
931                         DEBUG(0, ("sfileinfo failed: %s\n", nt_errstr(status)));
932                         return false;
933                 }
934         }
935
936         finfo2.basic_info.in.file.path = fname;
937         
938         status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
939
940         if (!NT_STATUS_IS_OK(status)) {
941                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
942                 return false;
943         }
944         torture_comment(tctx, "write time %s\n",
945                nt_time_string(tctx, finfo2.basic_info.out.write_time));
946
947         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
948                 torture_comment(tctx, "Server updated write_time (correct)\n");
949         } else {
950                 torture_comment(tctx, "Server did not update write time (wrong!)\n");
951                 ret = false;
952         }
953
954         /* Now try a write to see if the write time gets reset. */
955
956         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
957         finfo1.basic_info.in.file.fnum = fnum1;
958         finfo2 = finfo1;
959
960         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
961
962         if (!NT_STATUS_IS_OK(status)) {
963                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
964                 return false;
965         }
966         
967         torture_comment(tctx, "Modified write time %s\n", 
968                nt_time_string(tctx, finfo1.basic_info.out.write_time));
969
970
971         torture_comment(tctx, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
972
973         written =  smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
974
975         if (written != 10) {
976                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
977                        (int)written, __location__);
978                 return false;
979         }
980
981         /* Just to prove to tridge that the an smbflush has no effect on
982            the write time :-). The setfileinfo IS STICKY. JRA. */
983
984         torture_comment(tctx, "Doing flush after write\n");
985
986         flsh.flush.level        = RAW_FLUSH_FLUSH;
987         flsh.flush.in.file.fnum = fnum1;
988         status = smb_raw_flush(cli->tree, &flsh);
989         if (!NT_STATUS_IS_OK(status)) {
990                 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
991                 return false;
992         }
993
994         /* Once the time was set using setfileinfo then it stays set - writes
995            don't have any effect. But make sure. */
996         start = timeval_current();
997         end = timeval_add(&start, (15*sec), 0);
998         while (!timeval_expired(&end)) {
999                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1000
1001                 if (!NT_STATUS_IS_OK(status)) {
1002                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1003                         ret = false;
1004                         break;
1005                 }
1006                 torture_comment(tctx, "write time %s\n", 
1007                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
1008                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1009                         double diff = timeval_elapsed(&start);
1010                         torture_comment(tctx, "Server updated write_time after %.2f seconds"
1011                                         "(1sec == %.2f) (wrong!)\n",
1012                                         diff, sec);
1013                         ret = false;
1014                         break;
1015                 }
1016                 fflush(stdout);
1017                 msleep(1 * msec);
1018         }
1019         
1020         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1021                 torture_comment(tctx, "Server did not update write time (correct)\n");
1022         }
1023
1024         fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
1025         if (fnum2 == -1) {
1026                 torture_comment(tctx, "Failed to open %s\n", fname);
1027                 return false;
1028         }
1029         
1030         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");
1031
1032         written =  smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
1033
1034         if (written != 10) {
1035                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
1036                        (int)written, __location__);
1037                 return false;
1038         }
1039
1040         status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1041
1042         if (!NT_STATUS_IS_OK(status)) {
1043                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1044                 return false;
1045         }
1046         torture_comment(tctx, "write time %s\n", 
1047                nt_time_string(tctx, finfo2.basic_info.out.write_time));
1048         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1049                 torture_comment(tctx, "Server updated write_time (wrong!)\n");
1050                 ret = false;
1051         }
1052
1053         torture_comment(tctx, "Closing the first fd to see if write time updated.\n");
1054         smbcli_close(cli->tree, fnum1);
1055         fnum1 = -1;
1056
1057         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");
1058
1059         written =  smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
1060
1061         if (written != 10) {
1062                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
1063                        (int)written, __location__);
1064                 return false;
1065         }
1066
1067         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1068         finfo1.basic_info.in.file.fnum = fnum2;
1069         finfo2 = finfo1;
1070         status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1071
1072         if (!NT_STATUS_IS_OK(status)) {
1073                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1074                 return false;
1075         }
1076         torture_comment(tctx, "write time %s\n", 
1077                nt_time_string(tctx, finfo2.basic_info.out.write_time));
1078         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1079                 torture_comment(tctx, "Server updated write_time (wrong!)\n");
1080                 ret = false;
1081         }
1082
1083         /* Once the time was set using setfileinfo then it stays set - writes
1084            don't have any effect. But make sure. */
1085         start = timeval_current();
1086         end = timeval_add(&start, (15*sec), 0);
1087         while (!timeval_expired(&end)) {
1088                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1089
1090                 if (!NT_STATUS_IS_OK(status)) {
1091                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1092                         ret = false;
1093                         break;
1094                 }
1095                 torture_comment(tctx, "write time %s\n", 
1096                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
1097                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1098                         double diff = timeval_elapsed(&start);
1099                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1100                                         "(1sec == %.2f) (wrong!)\n",
1101                                         diff, sec);
1102                         ret = false;
1103                         break;
1104                 }
1105                 fflush(stdout);
1106                 msleep(1 * msec);
1107         }
1108         
1109         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1110                 torture_comment(tctx, "Server did not update write time (correct)\n");
1111         }
1112
1113         torture_comment(tctx, "Closing second fd to see if write time updated.\n");
1114
1115         smbcli_close(cli->tree, fnum2);
1116         fnum2 = -1;
1117
1118         fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
1119         if (fnum1 == -1) {
1120                 torture_comment(tctx, "Failed to open %s\n", fname);
1121                 return false;
1122         }
1123
1124         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1125         finfo1.basic_info.in.file.fnum = fnum1;
1126         finfo2 = finfo1;
1127
1128         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1129
1130         if (!NT_STATUS_IS_OK(status)) {
1131                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1132                 return false;
1133         }
1134         
1135         torture_comment(tctx, "Second open initial write time %s\n", 
1136                nt_time_string(tctx, finfo1.basic_info.out.write_time));
1137
1138         msleep(10 * msec);
1139         torture_comment(tctx, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
1140
1141         written =  smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
1142
1143         if (written != 10) {
1144                 torture_comment(tctx, "write failed - wrote %d bytes (%s)\n", 
1145                        (int)written, __location__);
1146                 return false;
1147         }
1148
1149         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1150         finfo1.basic_info.in.file.fnum = fnum1;
1151         finfo2 = finfo1;
1152         status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1153
1154         if (!NT_STATUS_IS_OK(status)) {
1155                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1156                 return false;
1157         }
1158         torture_comment(tctx, "write time %s\n", 
1159                nt_time_string(tctx, finfo2.basic_info.out.write_time));
1160         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1161                 torture_comment(tctx, "Server updated write_time (wrong!)\n");
1162                 ret = false;
1163         }
1164
1165         /* Now the write time should be updated again */
1166         start = timeval_current();
1167         end = timeval_add(&start, (15*sec), 0);
1168         while (!timeval_expired(&end)) {
1169                 status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1170
1171                 if (!NT_STATUS_IS_OK(status)) {
1172                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1173                         ret = false;
1174                         break;
1175                 }
1176                 torture_comment(tctx, "write time %s\n", 
1177                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
1178                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1179                         double diff = timeval_elapsed(&start);
1180                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
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
1188                         torture_comment(tctx, "Server updated write_time after %.2f seconds"
1189                                         "(1sec == %.2f) (correct)\n",
1190                                         diff, sec);
1191                         break;
1192                 }
1193                 fflush(stdout);
1194                 msleep(1*msec);
1195         }
1196         
1197         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1198                 torture_comment(tctx, "Server did not update write time (wrong!)\n");
1199                 ret = false;
1200         }
1201
1202
1203         /* One more test to do. We should read the filetime via findfirst on the
1204            second connection to ensure it's the same. This is very easy for a Windows
1205            server but a bastard to get right on a POSIX server. JRA. */
1206
1207         if (fnum1 != -1)
1208                 smbcli_close(cli->tree, fnum1);
1209         smbcli_unlink(cli->tree, fname);
1210         smbcli_deltree(cli->tree, BASEDIR);
1211
1212         return ret;
1213 }
1214
1215
1216 /* Windows does obviously not update the stat info during a write call. I
1217  * *think* this is the problem causing a spurious Excel 2003 on XP error
1218  * message when saving a file. Excel does a setfileinfo, writes, and then does
1219  * a getpath(!)info. Or so... For Samba sometimes it displays an error message
1220  * that the file might have been changed in between. What i've been able to
1221  * trace down is that this happens if the getpathinfo after the write shows a
1222  * different last write time than the setfileinfo showed. This is really
1223  * nasty....
1224  */
1225
1226 static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli, 
1227                                                                    struct smbcli_state *cli2)
1228 {
1229         union smb_fileinfo finfo1, finfo2;
1230         const char *fname = BASEDIR "\\torture_file.txt";
1231         NTSTATUS status;
1232         int fnum1 = -1;
1233         int fnum2;
1234         bool ret = true;
1235         ssize_t written;
1236         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1237         int normal_delay = 2000000;
1238         double sec = ((double)used_delay) / ((double)normal_delay);
1239         int msec = 1000 * sec;
1240
1241         torture_comment(tctx, "\nRunning test_finfo_after_write\n");
1242
1243         if (!torture_setup_dir(cli, BASEDIR)) {
1244                 return false;
1245         }
1246
1247         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1248         if (fnum1 == -1) {
1249                 ret = false;
1250                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1251                 goto done;
1252         }
1253
1254         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1255         finfo1.basic_info.in.file.fnum = fnum1;
1256
1257         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1258
1259         if (!NT_STATUS_IS_OK(status)) {
1260                 ret = false;
1261                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1262                 goto done;
1263         }
1264
1265         msleep(1 * msec);
1266
1267         written =  smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1268
1269         if (written != 1) {
1270                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1271                 ret = false;
1272                 goto done;
1273         }
1274
1275         fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
1276         if (fnum2 == -1) {
1277                 torture_result(tctx, TORTURE_FAIL, __location__": failed to open 2nd time - %s", 
1278                        smbcli_errstr(cli2->tree));
1279                 ret = false;
1280                 goto done;
1281         }
1282         
1283         written =  smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
1284         
1285         if (written != 1) {
1286                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", 
1287                        (int)written);
1288                 ret = false;
1289                 goto done;
1290         }
1291         
1292         finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1293         finfo2.basic_info.in.file.path = fname;
1294         
1295         status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
1296         
1297         if (!NT_STATUS_IS_OK(status)) {
1298                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", 
1299                           nt_errstr(status));
1300                 ret = false;
1301                 goto done;
1302         }
1303         
1304         if (finfo1.basic_info.out.create_time !=
1305             finfo2.basic_info.out.create_time) {
1306                 torture_result(tctx, TORTURE_FAIL, __location__": create_time changed");
1307                 ret = false;
1308                 goto done;
1309         }
1310         
1311         if (finfo1.basic_info.out.access_time !=
1312             finfo2.basic_info.out.access_time) {
1313                 torture_result(tctx, TORTURE_FAIL, __location__": access_time changed");
1314                 ret = false;
1315                 goto done;
1316         }
1317         
1318         if (finfo1.basic_info.out.write_time !=
1319             finfo2.basic_info.out.write_time) {
1320                 torture_result(tctx, TORTURE_FAIL, __location__": write_time changed:\n"
1321                                            "write time conn 1 = %s, conn 2 = %s", 
1322                        nt_time_string(tctx, finfo1.basic_info.out.write_time),
1323                        nt_time_string(tctx, finfo2.basic_info.out.write_time));
1324                 ret = false;
1325                 goto done;
1326         }
1327         
1328         if (finfo1.basic_info.out.change_time !=
1329             finfo2.basic_info.out.change_time) {
1330                 torture_result(tctx, TORTURE_FAIL, __location__": change_time changed");
1331                 ret = false;
1332                 goto done;
1333         }
1334         
1335         /* One of the two following calls updates the qpathinfo. */
1336         
1337         /* If you had skipped the smbcli_write on fnum2, it would
1338          * *not* have updated the stat on disk */
1339         
1340         smbcli_close(cli2->tree, fnum2);
1341         cli2 = NULL;
1342
1343         /* This call is only for the people looking at ethereal :-) */
1344         finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1345         finfo2.basic_info.in.file.path = fname;
1346
1347         status = smb_raw_pathinfo(cli->tree, tctx, &finfo2);
1348
1349         if (!NT_STATUS_IS_OK(status)) {
1350                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", nt_errstr(status));
1351                 ret = false;
1352                 goto done;
1353         }
1354
1355  done:
1356         if (fnum1 != -1)
1357                 smbcli_close(cli->tree, fnum1);
1358         smbcli_unlink(cli->tree, fname);
1359         smbcli_deltree(cli->tree, BASEDIR);
1360
1361         return ret;
1362 }
1363
1364 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
1365         uint64_t r = 10*1000*1000; \
1366         NTTIME g = (given).basic_info.out.write_time; \
1367         NTTIME gr = (g / r) * r; \
1368         NTTIME c = (correct).basic_info.out.write_time; \
1369         NTTIME cr = (c / r) * r; \
1370         bool strict = torture_setting_bool(tctx, "strict mode", false); \
1371         bool err = false; \
1372         if (strict && (g cmp c)) { \
1373                 err = true; \
1374         } else if ((g cmp c) && (gr cmp cr)) { \
1375                 /* handle filesystem without high resolution timestamps */ \
1376                 err = true; \
1377         } \
1378         if (err) { \
1379                 torture_result(tctx, TORTURE_FAIL, __location__": wrong write_time (%s)%s(%llu) %s (%s)%s(%llu)", \
1380                                 #given, nt_time_string(tctx, g), (unsigned long long)g, \
1381                                 #cmp, #correct, nt_time_string(tctx, c), (unsigned long long)c); \
1382                 ret = false; \
1383                 goto done; \
1384         } \
1385 } while (0)
1386 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
1387         COMPARE_WRITE_TIME_CMP(given,correct,!=)
1388 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
1389         COMPARE_WRITE_TIME_CMP(given,correct,<=)
1390 #define COMPARE_WRITE_TIME_LESS(given,correct) \
1391         COMPARE_WRITE_TIME_CMP(given,correct,>=)
1392
1393 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
1394         NTTIME g = (given).basic_info.out.access_time; \
1395         NTTIME c = (correct).basic_info.out.access_time; \
1396         if (g cmp c) { \
1397                 torture_result(tctx, TORTURE_FAIL, __location__": wrong access_time (%s)%s %s (%s)%s", \
1398                                 #given, nt_time_string(tctx, g), \
1399                                 #cmp, #correct, nt_time_string(tctx, c)); \
1400                 ret = false; \
1401                 goto done; \
1402         } \
1403 } while (0)
1404 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
1405         COMPARE_ACCESS_TIME_CMP(given,correct,!=)
1406
1407 #define COMPARE_BOTH_TIMES_EQUAL(given,correct) do { \
1408         COMPARE_ACCESS_TIME_EQUAL(given,correct); \
1409         COMPARE_WRITE_TIME_EQUAL(given,correct); \
1410 } while (0)
1411
1412 #define GET_INFO_FILE(finfo) do { \
1413         NTSTATUS _status; \
1414         _status = smb_raw_fileinfo(cli->tree, tctx, &finfo); \
1415         if (!NT_STATUS_IS_OK(_status)) { \
1416                 ret = false; \
1417                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1418                                nt_errstr(_status)); \
1419                 goto done; \
1420         } \
1421         torture_comment(tctx, "fileinfo: Access(%s) Write(%s)\n", \
1422                         nt_time_string(tctx, finfo.basic_info.out.access_time), \
1423                         nt_time_string(tctx, finfo.basic_info.out.write_time)); \
1424 } while (0)
1425 #define GET_INFO_PATH(pinfo) do { \
1426         NTSTATUS _status; \
1427         _status = smb_raw_pathinfo(cli2->tree, tctx, &pinfo); \
1428         if (!NT_STATUS_IS_OK(_status)) { \
1429                 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \
1430                                nt_errstr(_status)); \
1431                 ret = false; \
1432                 goto done; \
1433         } \
1434         torture_comment(tctx, "pathinfo: Access(%s) Write(%s)\n", \
1435                         nt_time_string(tctx, pinfo.basic_info.out.access_time), \
1436                         nt_time_string(tctx, pinfo.basic_info.out.write_time)); \
1437 } while (0)
1438 #define GET_INFO_BOTH(finfo,pinfo) do { \
1439         GET_INFO_FILE(finfo); \
1440         GET_INFO_PATH(pinfo); \
1441         COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \
1442 } while (0)
1443
1444 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
1445         NTSTATUS _status; \
1446         union smb_setfileinfo sfinfo; \
1447         sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1448         sfinfo.basic_info.in.file.fnum = tfnum; \
1449         sfinfo.basic_info.in.create_time = 0; \
1450         sfinfo.basic_info.in.access_time = 0; \
1451         unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1452         sfinfo.basic_info.in.change_time = 0; \
1453         sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1454         _status = smb_raw_setfileinfo(tree, &sfinfo); \
1455         if (!NT_STATUS_IS_OK(_status)) { \
1456                 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1457                                nt_errstr(_status)); \
1458                 ret = false; \
1459                 goto done; \
1460         } \
1461 } while (0)
1462 #define SET_INFO_FILE(finfo, wrtime) \
1463         SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
1464
1465 static bool test_delayed_write_update3(struct torture_context *tctx,
1466                                        struct smbcli_state *cli,
1467                                        struct smbcli_state *cli2)
1468 {
1469         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1470         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1471         const char *fname = BASEDIR "\\torture_file3.txt";
1472         int fnum1 = -1;
1473         bool ret = true;
1474         ssize_t written;
1475         struct timeval start;
1476         struct timeval end;
1477         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1478         int normal_delay = 2000000;
1479         double sec = ((double)used_delay) / ((double)normal_delay);
1480         int msec = 1000 * sec;
1481
1482         torture_comment(tctx, "\nRunning test_delayed_write_update3\n");
1483
1484         if (!torture_setup_dir(cli, BASEDIR)) {
1485                 return false;
1486         }
1487
1488         torture_comment(tctx, "Open the file handle\n");
1489         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1490         if (fnum1 == -1) {
1491                 ret = false;
1492                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1493                 goto done;
1494         }
1495
1496         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1497         finfo0.basic_info.in.file.fnum = fnum1;
1498         finfo1 = finfo0;
1499         finfo2 = finfo0;
1500         finfo3 = finfo0;
1501         finfo4 = finfo0;
1502         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1503         pinfo0.basic_info.in.file.path = fname;
1504         pinfo1 = pinfo0;
1505         pinfo2 = pinfo0;
1506         pinfo3 = pinfo0;
1507         pinfo4 = pinfo0;
1508         pinfo5 = pinfo0;
1509
1510         /* get the initial times */
1511         GET_INFO_BOTH(finfo0,pinfo0);
1512
1513         /*
1514          * make sure the write time is updated 2 seconds later
1515          * calcuated from the first write
1516          * (but expect upto 5 seconds extra time for a busy server)
1517          */
1518         start = timeval_current();
1519         end = timeval_add(&start, 7 * sec, 0);
1520         while (!timeval_expired(&end)) {
1521                 /* do a write */
1522                 torture_comment(tctx, "Do a write on the file handle\n");
1523                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1524                 if (written != 1) {
1525                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1526                         ret = false;
1527                         goto done;
1528                 }
1529                 /* get the times after the write */
1530                 GET_INFO_FILE(finfo1);
1531
1532                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1533                         double diff = timeval_elapsed(&start);
1534                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
1535                                 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1536                                                 "(1sec == %.2f) (wrong!)\n",
1537                                                 diff, sec);
1538                                 ret = false;
1539                                 break;
1540                         }
1541
1542                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1543                                         "(1sec == %.2f) (correct)\n",
1544                                         diff, sec);
1545                         break;
1546                 }
1547                 msleep(0.5 * msec);
1548         }
1549
1550         GET_INFO_BOTH(finfo1,pinfo1);
1551         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1552
1553         /* sure any further write doesn't update the write time */
1554         start = timeval_current();
1555         end = timeval_add(&start, 15 * sec, 0);
1556         while (!timeval_expired(&end)) {
1557                 /* do a write */
1558                 torture_comment(tctx, "Do a write on the file handle\n");
1559                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1560                 if (written != 1) {
1561                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1562                         ret = false;
1563                         goto done;
1564                 }
1565                 /* get the times after the write */
1566                 GET_INFO_BOTH(finfo2,pinfo2);
1567
1568                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1569                         double diff = timeval_elapsed(&start);
1570                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1571                                         "(1sec == %.2f) (wrong!)\n",
1572                                         diff, sec);
1573                         ret = false;
1574                         break;
1575                 }
1576                 msleep(2 * msec);
1577         }
1578
1579         GET_INFO_BOTH(finfo2,pinfo2);
1580         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1581         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1582                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1583         }
1584
1585         /* sleep */
1586         msleep(5 * msec);
1587
1588         GET_INFO_BOTH(finfo3,pinfo3);
1589         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1590
1591         /*
1592          * the close updates the write time to the time of the close
1593          * and not to the time of the last write!
1594          */
1595         torture_comment(tctx, "Close the file handle\n");
1596         smbcli_close(cli->tree, fnum1);
1597         fnum1 = -1;
1598
1599         GET_INFO_PATH(pinfo4);
1600         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1601
1602         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1603                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1604         }
1605
1606  done:
1607         if (fnum1 != -1)
1608                 smbcli_close(cli->tree, fnum1);
1609         smbcli_unlink(cli->tree, fname);
1610         smbcli_deltree(cli->tree, BASEDIR);
1611
1612         return ret;
1613 }
1614
1615 static bool test_delayed_write_update3a(struct torture_context *tctx,
1616                                         struct smbcli_state *cli,
1617                                         struct smbcli_state *cli2)
1618 {
1619         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1620         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1621         const char *fname = BASEDIR "\\torture_file3a.txt";
1622         int fnum1 = -1;
1623         bool ret = true;
1624         ssize_t written;
1625         int i;
1626         struct timeval start;
1627         struct timeval end;
1628         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1629         int normal_delay = 2000000;
1630         double sec = ((double)used_delay) / ((double)normal_delay);
1631         int msec = 1000 * sec;
1632
1633         torture_comment(tctx, "\nRunning test_delayed_write_update3a\n");
1634
1635         if (!torture_setup_dir(cli, BASEDIR)) {
1636                 return false;
1637         }
1638
1639         torture_comment(tctx, "Open the file handle\n");
1640         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1641         if (fnum1 == -1) {
1642                 ret = false;
1643                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1644                 goto done;
1645         }
1646
1647         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1648         finfo0.basic_info.in.file.fnum = fnum1;
1649         finfo1 = finfo0;
1650         finfo2 = finfo0;
1651         finfo3 = finfo0;
1652         finfo4 = finfo0;
1653         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1654         pinfo0.basic_info.in.file.path = fname;
1655         pinfo1 = pinfo0;
1656         pinfo2 = pinfo0;
1657         pinfo3 = pinfo0;
1658         pinfo4 = pinfo0;
1659         pinfo5 = pinfo0;
1660
1661         /* get the initial times */
1662         GET_INFO_BOTH(finfo0,pinfo0);
1663
1664         /*
1665          * sleep some time, to demonstrate the handling of write times
1666          * doesn't depend on the time since the open
1667          */
1668         msleep(5 * msec);
1669
1670         /* get the initial times */
1671         GET_INFO_BOTH(finfo1,pinfo1);
1672         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1673
1674         /*
1675          * make sure the write time is updated 2 seconds later
1676          * calcuated from the first write
1677          * (but expect upto 5 seconds extra time for a busy server)
1678          */
1679         start = timeval_current();
1680         end = timeval_add(&start, 7 * sec, 0);
1681         while (!timeval_expired(&end)) {
1682                 /* do a write */
1683                 torture_comment(tctx, "Do a write on the file handle\n");
1684                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1685                 if (written != 1) {
1686                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1687                         ret = false;
1688                         goto done;
1689                 }
1690                 /* get the times after the write */
1691                 GET_INFO_FILE(finfo1);
1692
1693                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1694                         double diff = timeval_elapsed(&start);
1695                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
1696                                 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1697                                                 "(1sec == %.2f) (wrong!)\n",
1698                                                 diff, sec);
1699                                 ret = false;
1700                                 break;
1701                         }
1702
1703                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1704                                         "(1sec == %.2f) (correct)\n",
1705                                         diff, sec);
1706                         break;
1707                 }
1708                 msleep(0.5 * msec);
1709         }
1710
1711         GET_INFO_BOTH(finfo1,pinfo1);
1712         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1713
1714         /*
1715          * demonstrate that a truncate write always
1716          * updates the write time immediately
1717          */
1718         for (i=0; i < 3; i++) {
1719                 msleep(1 * msec);
1720                 /* do a write */
1721                 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1722                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
1723                 if (written != 0) {
1724                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1725                         ret = false;
1726                         goto done;
1727                 }
1728                 /* get the times after the write */
1729                 GET_INFO_BOTH(finfo2,pinfo2);
1730                 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1731                 finfo1 = finfo2;
1732         }
1733
1734         /* sure any further write doesn't update the write time */
1735         start = timeval_current();
1736         end = timeval_add(&start, 15 * sec, 0);
1737         while (!timeval_expired(&end)) {
1738                 /* do a write */
1739                 torture_comment(tctx, "Do a write on the file handle\n");
1740                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1741                 if (written != 1) {
1742                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1743                         ret = false;
1744                         goto done;
1745                 }
1746                 /* get the times after the write */
1747                 GET_INFO_BOTH(finfo2,pinfo2);
1748
1749                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1750                         double diff = timeval_elapsed(&start);
1751                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1752                                         "(1sec == %.2f) (wrong!)\n",
1753                                         diff, sec);
1754                         ret = false;
1755                         break;
1756                 }
1757                 msleep(2 * msec);
1758         }
1759
1760         GET_INFO_BOTH(finfo2,pinfo2);
1761         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1762         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1763                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1764         }
1765
1766         /* sleep */
1767         msleep(5 * msec);
1768
1769         /* get the initial times */
1770         GET_INFO_BOTH(finfo1,pinfo1);
1771         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
1772
1773         /*
1774          * demonstrate that a truncate write always
1775          * updates the write time immediately
1776          */
1777         for (i=0; i < 3; i++) {
1778                 msleep(1 * msec);
1779                 /* do a write */
1780                 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1781                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
1782                 if (written != 0) {
1783                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1784                         ret = false;
1785                         goto done;
1786                 }
1787                 /* get the times after the write */
1788                 GET_INFO_BOTH(finfo2,pinfo2);
1789                 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1790                 finfo1 = finfo2;
1791         }
1792
1793         /* sleep */
1794         msleep(5 * msec);
1795
1796         GET_INFO_BOTH(finfo3,pinfo3);
1797         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1798
1799         /*
1800          * the close doesn't update the write time
1801          */
1802         torture_comment(tctx, "Close the file handle\n");
1803         smbcli_close(cli->tree, fnum1);
1804         fnum1 = -1;
1805
1806         GET_INFO_PATH(pinfo4);
1807         COMPARE_WRITE_TIME_EQUAL(pinfo4, pinfo3);
1808
1809         if (pinfo4.basic_info.out.write_time == pinfo3.basic_info.out.write_time) {
1810                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1811         }
1812
1813  done:
1814         if (fnum1 != -1)
1815                 smbcli_close(cli->tree, fnum1);
1816         smbcli_unlink(cli->tree, fname);
1817         smbcli_deltree(cli->tree, BASEDIR);
1818
1819         return ret;
1820 }
1821
1822 static bool test_delayed_write_update3b(struct torture_context *tctx,
1823                                         struct smbcli_state *cli,
1824                                         struct smbcli_state *cli2)
1825 {
1826         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1827         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1828         const char *fname = BASEDIR "\\torture_file3b.txt";
1829         int fnum1 = -1;
1830         bool ret = true;
1831         ssize_t written;
1832         struct timeval start;
1833         struct timeval end;
1834         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1835         int normal_delay = 2000000;
1836         double sec = ((double)used_delay) / ((double)normal_delay);
1837         int msec = 1000 * sec;
1838
1839         torture_comment(tctx, "\nRunning test_delayed_write_update3b\n");
1840
1841         if (!torture_setup_dir(cli, BASEDIR)) {
1842                 return false;
1843         }
1844
1845         torture_comment(tctx, "Open the file handle\n");
1846         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1847         if (fnum1 == -1) {
1848                 ret = false;
1849                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1850                 goto done;
1851         }
1852
1853         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1854         finfo0.basic_info.in.file.fnum = fnum1;
1855         finfo1 = finfo0;
1856         finfo2 = finfo0;
1857         finfo3 = finfo0;
1858         finfo4 = finfo0;
1859         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1860         pinfo0.basic_info.in.file.path = fname;
1861         pinfo1 = pinfo0;
1862         pinfo2 = pinfo0;
1863         pinfo3 = pinfo0;
1864         pinfo4 = pinfo0;
1865         pinfo5 = pinfo0;
1866
1867         /* get the initial times */
1868         GET_INFO_BOTH(finfo0,pinfo0);
1869
1870         /*
1871          * sleep some time, to demonstrate the handling of write times
1872          * doesn't depend on the time since the open
1873          */
1874         msleep(5 * msec);
1875
1876         /* get the initial times */
1877         GET_INFO_BOTH(finfo1,pinfo1);
1878         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1879
1880         /*
1881          * make sure the write time is updated 2 seconds later
1882          * calcuated from the first write
1883          * (but expect upto 5 seconds extra time for a busy server)
1884          */
1885         start = timeval_current();
1886         end = timeval_add(&start, 7 * sec, 0);
1887         while (!timeval_expired(&end)) {
1888                 /* do a write */
1889                 torture_comment(tctx, "Do a write on the file handle\n");
1890                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1891                 if (written != 1) {
1892                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1893                         ret = false;
1894                         goto done;
1895                 }
1896                 /* get the times after the write */
1897                 GET_INFO_FILE(finfo1);
1898
1899                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1900                         double diff = timeval_elapsed(&start);
1901                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
1902                                 torture_comment(tctx, "Server updated write_time after %.2f seconds "
1903                                                 "(1sec == %.2f) (wrong!)\n",
1904                                                 diff, sec);
1905                                 ret = false;
1906                                 break;
1907                         }
1908
1909                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1910                                         "(1sec == %.2f) (correct)\n",
1911                                         diff, sec);
1912                         break;
1913                 }
1914                 msleep(0.5 * msec);
1915         }
1916
1917         GET_INFO_BOTH(finfo1,pinfo1);
1918         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1919
1920         /* sure any further write doesn't update the write time */
1921         start = timeval_current();
1922         end = timeval_add(&start, 15 * sec, 0);
1923         while (!timeval_expired(&end)) {
1924                 /* do a write */
1925                 torture_comment(tctx, "Do a write on the file handle\n");
1926                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1927                 if (written != 1) {
1928                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1929                         ret = false;
1930                         goto done;
1931                 }
1932                 /* get the times after the write */
1933                 GET_INFO_BOTH(finfo2,pinfo2);
1934
1935                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1936                         double diff = timeval_elapsed(&start);
1937                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1938                                         "(1sec == %.2f) (wrong!)\n",
1939                                         diff, sec);
1940                         ret = false;
1941                         break;
1942                 }
1943                 msleep(2 * msec);
1944         }
1945
1946         GET_INFO_BOTH(finfo2,pinfo2);
1947         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1948         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1949                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1950         }
1951
1952         /* sleep */
1953         msleep(5 * msec);
1954
1955         GET_INFO_BOTH(finfo3,pinfo3);
1956         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1957
1958         /*
1959          * the close updates the write time to the time of the close
1960          * and not to the time of the last write!
1961          */
1962         torture_comment(tctx, "Close the file handle\n");
1963         smbcli_close(cli->tree, fnum1);
1964         fnum1 = -1;
1965
1966         GET_INFO_PATH(pinfo4);
1967         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1968
1969         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1970                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1971         }
1972
1973  done:
1974         if (fnum1 != -1)
1975                 smbcli_close(cli->tree, fnum1);
1976         smbcli_unlink(cli->tree, fname);
1977         smbcli_deltree(cli->tree, BASEDIR);
1978
1979         return ret;
1980 }
1981
1982 static bool test_delayed_write_update3c(struct torture_context *tctx,
1983                                         struct smbcli_state *cli,
1984                                         struct smbcli_state *cli2)
1985 {
1986         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
1987         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
1988         const char *fname = BASEDIR "\\torture_file3c.txt";
1989         int fnum1 = -1;
1990         bool ret = true;
1991         ssize_t written;
1992         int i;
1993         struct timeval start;
1994         struct timeval end;
1995         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1996         int normal_delay = 2000000;
1997         double sec = ((double)used_delay) / ((double)normal_delay);
1998         int msec = 1000 * sec;
1999
2000         torture_comment(tctx, "\nRunning test_delayed_write_update3c\n");
2001
2002         if (!torture_setup_dir(cli, BASEDIR)) {
2003                 return false;
2004         }
2005
2006         torture_comment(tctx, "Open the file handle\n");
2007         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2008         if (fnum1 == -1) {
2009                 ret = false;
2010                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2011                 goto done;
2012         }
2013
2014         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2015         finfo0.basic_info.in.file.fnum = fnum1;
2016         finfo1 = finfo0;
2017         finfo2 = finfo0;
2018         finfo3 = finfo0;
2019         finfo4 = finfo0;
2020         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2021         pinfo0.basic_info.in.file.path = fname;
2022         pinfo1 = pinfo0;
2023         pinfo2 = pinfo0;
2024         pinfo3 = pinfo0;
2025         pinfo4 = pinfo0;
2026         pinfo5 = pinfo0;
2027
2028         /* get the initial times */
2029         GET_INFO_BOTH(finfo0,pinfo0);
2030
2031         /*
2032          * sleep some time, to demonstrate the handling of write times
2033          * doesn't depend on the time since the open
2034          */
2035         msleep(5 * msec);
2036
2037         /* get the initial times */
2038         GET_INFO_BOTH(finfo1,pinfo1);
2039         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2040
2041         /*
2042          * demonstrate that a truncate write always
2043          * updates the write time immediately
2044          */
2045         for (i=0; i < 3; i++) {
2046                 msleep(1 * msec);
2047                 /* do a write */
2048                 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
2049                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2050                 if (written != 0) {
2051                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2052                         ret = false;
2053                         goto done;
2054                 }
2055                 /* get the times after the write */
2056                 GET_INFO_BOTH(finfo2,pinfo2);
2057                 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2058                 finfo1 = finfo2;
2059         }
2060
2061         start = timeval_current();
2062         end = timeval_add(&start, 7 * sec, 0);
2063         while (!timeval_expired(&end)) {
2064                 /* do a write */
2065                 torture_comment(tctx, "Do a write on the file handle\n");
2066                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2067                 if (written != 1) {
2068                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2069                         ret = false;
2070                         goto done;
2071                 }
2072                 /* get the times after the write */
2073                 GET_INFO_FILE(finfo2);
2074
2075                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2076                         double diff = timeval_elapsed(&start);
2077                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2078                                         "(1sec == %.2f) (wrong!)\n",
2079                                         diff, sec);
2080                         ret = false;
2081                         break;
2082                 }
2083                 msleep(2 * msec);
2084         }
2085
2086         GET_INFO_BOTH(finfo2,pinfo2);
2087         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2088         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2089                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2090         }
2091
2092         /* sleep */
2093         msleep(5 * msec);
2094
2095         /* get the initial times */
2096         GET_INFO_BOTH(finfo1,pinfo1);
2097         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
2098
2099         /*
2100          * demonstrate that a truncate write always
2101          * updates the write time immediately
2102          */
2103         for (i=0; i < 3; i++) {
2104                 msleep(1 * msec);
2105                 /* do a write */
2106                 torture_comment(tctx, "Do a truncate write [%d] on the file handle\n", i);
2107                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2108                 if (written != 0) {
2109                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2110                         ret = false;
2111                         goto done;
2112                 }
2113                 /* get the times after the write */
2114                 GET_INFO_BOTH(finfo2,pinfo2);
2115                 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2116                 finfo1 = finfo2;
2117         }
2118
2119         /* sleep */
2120         msleep(5 * msec);
2121
2122         GET_INFO_BOTH(finfo2,pinfo2);
2123         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2124
2125         /* sure any further write doesn't update the write time */
2126         start = timeval_current();
2127         end = timeval_add(&start, 15 * sec, 0);
2128         while (!timeval_expired(&end)) {
2129                 /* do a write */
2130                 torture_comment(tctx, "Do a write on the file handle\n");
2131                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2132                 if (written != 1) {
2133                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2134                         ret = false;
2135                         goto done;
2136                 }
2137                 /* get the times after the write */
2138                 GET_INFO_BOTH(finfo2,pinfo2);
2139
2140                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2141                         double diff = timeval_elapsed(&start);
2142                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2143                                         "(1sec == %.2f) (wrong!)\n",
2144                                         diff, sec);
2145                         ret = false;
2146                         break;
2147                 }
2148                 msleep(2 * msec);
2149         }
2150
2151         GET_INFO_BOTH(finfo2,pinfo2);
2152         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2153         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2154                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2155         }
2156
2157         /* sleep */
2158         msleep(5 * msec);
2159
2160         GET_INFO_BOTH(finfo3,pinfo3);
2161         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2162
2163         /*
2164          * the close updates the write time to the time of the close
2165          * and not to the time of the last write!
2166          */
2167         torture_comment(tctx, "Close the file handle\n");
2168         smbcli_close(cli->tree, fnum1);
2169         fnum1 = -1;
2170
2171         GET_INFO_PATH(pinfo4);
2172         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2173
2174         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2175                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2176         }
2177
2178  done:
2179         if (fnum1 != -1)
2180                 smbcli_close(cli->tree, fnum1);
2181         smbcli_unlink(cli->tree, fname);
2182         smbcli_deltree(cli->tree, BASEDIR);
2183
2184         return ret;
2185 }
2186
2187 static bool test_delayed_write_update4(struct torture_context *tctx,
2188                                        struct smbcli_state *cli,
2189                                        struct smbcli_state *cli2)
2190 {
2191         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4;
2192         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
2193         const char *fname = BASEDIR "\\torture_file4.txt";
2194         int fnum1 = -1;
2195         bool ret = true;
2196         ssize_t written;
2197         struct timeval start;
2198         struct timeval end;
2199         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2200         int normal_delay = 2000000;
2201         double sec = ((double)used_delay) / ((double)normal_delay);
2202         int msec = 1000 * sec;
2203
2204         torture_comment(tctx, "\nRunning test_delayed_write_update4\n");
2205
2206         if (!torture_setup_dir(cli, BASEDIR)) {
2207                 return false;
2208         }
2209
2210         torture_comment(tctx, "Open the file handle\n");
2211         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2212         if (fnum1 == -1) {
2213                 ret = false;
2214                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2215                 goto done;
2216         }
2217
2218         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2219         finfo0.basic_info.in.file.fnum = fnum1;
2220         finfo1 = finfo0;
2221         finfo2 = finfo0;
2222         finfo3 = finfo0;
2223         finfo4 = finfo0;
2224         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2225         pinfo0.basic_info.in.file.path = fname;
2226         pinfo1 = pinfo0;
2227         pinfo2 = pinfo0;
2228         pinfo3 = pinfo0;
2229         pinfo4 = pinfo0;
2230         pinfo5 = pinfo0;
2231
2232         /* get the initial times */
2233         GET_INFO_BOTH(finfo0,pinfo0);
2234
2235         /* sleep a bit */
2236         msleep(5 * msec);
2237
2238         /* do a write */
2239         torture_comment(tctx, "Do a write on the file handle\n");
2240         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2241         if (written != 1) {
2242                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2243                 ret = false;
2244                 goto done;
2245         }
2246
2247         GET_INFO_BOTH(finfo1,pinfo1);
2248         COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
2249
2250         /*
2251          * make sure the write time is updated 2 seconds later
2252          * calcuated from the first write
2253          * (but expect upto 3 seconds extra time for a busy server)
2254          */
2255         start = timeval_current();
2256         end = timeval_add(&start, 5 * sec, 0);
2257         while (!timeval_expired(&end)) {
2258                 /* get the times after the first write */
2259                 GET_INFO_FILE(finfo1);
2260
2261                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
2262                         double diff = timeval_elapsed(&start);
2263                         if (diff < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
2264                                 torture_comment(tctx, "Server updated write_time after %.2f seconds "
2265                                                 "(1sec == %.2f) (wrong!)\n",
2266                                                 diff, sec);
2267                                 ret = false;
2268                                 break;
2269                         }
2270
2271                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2272                                         "(1sec == %.2f) (correct)\n",
2273                                         diff, sec);
2274                         break;
2275                 }
2276                 msleep(0.5 * msec);
2277         }
2278
2279         GET_INFO_BOTH(finfo1,pinfo1);
2280         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
2281
2282         /* sure any further write doesn't update the write time */
2283         start = timeval_current();
2284         end = timeval_add(&start, 15 * sec, 0);
2285         while (!timeval_expired(&end)) {
2286                 /* do a write */
2287                 torture_comment(tctx, "Do a write on the file handle\n");
2288                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2289                 if (written != 1) {
2290                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2291                         ret = false;
2292                         goto done;
2293                 }
2294                 /* get the times after the write */
2295                 GET_INFO_BOTH(finfo2,pinfo2);
2296
2297                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2298                         double diff = timeval_elapsed(&start);
2299                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2300                                         "(1sec == %.2f) (wrong!)\n",
2301                                         diff, sec);
2302                         ret = false;
2303                         break;
2304                 }
2305                 msleep(2 * msec);
2306         }
2307
2308         GET_INFO_BOTH(finfo2,pinfo2);
2309         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2310         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2311                 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
2312         }
2313
2314         /* sleep */
2315         msleep(5 * msec);
2316
2317         GET_INFO_BOTH(finfo3,pinfo3);
2318         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2319
2320         /*
2321          * the close updates the write time to the time of the close
2322          * and not to the time of the last write!
2323          */
2324         torture_comment(tctx, "Close the file handle\n");
2325         smbcli_close(cli->tree, fnum1);
2326         fnum1 = -1;
2327
2328         GET_INFO_PATH(pinfo4);
2329         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2330
2331         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2332                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2333         }
2334
2335  done:
2336         if (fnum1 != -1)
2337                 smbcli_close(cli->tree, fnum1);
2338         smbcli_unlink(cli->tree, fname);
2339         smbcli_deltree(cli->tree, BASEDIR);
2340
2341         return ret;
2342 }
2343
2344 static bool test_delayed_write_update5(struct torture_context *tctx,
2345                                        struct smbcli_state *cli,
2346                                        struct smbcli_state *cli2)
2347 {
2348         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2349         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2350         const char *fname = BASEDIR "\\torture_file5.txt";
2351         int fnum1 = -1;
2352         bool ret = true;
2353         ssize_t written;
2354         struct timeval start;
2355         struct timeval end;
2356         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2357         int normal_delay = 2000000;
2358         double sec = ((double)used_delay) / ((double)normal_delay);
2359         int msec = 1000 * sec;
2360
2361         torture_comment(tctx, "\nRunning test_delayed_write_update5\n");
2362
2363         if (!torture_setup_dir(cli, BASEDIR)) {
2364                 return false;
2365         }
2366
2367         torture_comment(tctx, "Open the file handle\n");
2368         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2369         if (fnum1 == -1) {
2370                 ret = false;
2371                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2372                 goto done;
2373         }
2374
2375         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2376         finfo0.basic_info.in.file.fnum = fnum1;
2377         finfo1 = finfo0;
2378         finfo2 = finfo0;
2379         finfo3 = finfo0;
2380         finfo4 = finfo0;
2381         finfo5 = finfo0;
2382         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2383         pinfo0.basic_info.in.file.path = fname;
2384         pinfo1 = pinfo0;
2385         pinfo2 = pinfo0;
2386         pinfo3 = pinfo0;
2387         pinfo4 = pinfo0;
2388         pinfo5 = pinfo0;
2389         pinfo6 = pinfo0;
2390
2391         /* get the initial times */
2392         GET_INFO_BOTH(finfo0,pinfo0);
2393
2394         /* do a write */
2395         torture_comment(tctx, "Do a write on the file handle\n");
2396         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2397         if (written != 1) {
2398                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2399                 ret = false;
2400                 goto done;
2401         }
2402
2403         GET_INFO_BOTH(finfo1,pinfo1);
2404         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2405
2406         torture_comment(tctx, "Set write time in the future on the file handle\n");
2407         SET_INFO_FILE(finfo0, time(NULL) + 86400);
2408         GET_INFO_BOTH(finfo2,pinfo2);
2409         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2410
2411         torture_comment(tctx, "Set write time in the past on the file handle\n");
2412         SET_INFO_FILE(finfo0, time(NULL) - 86400);
2413         GET_INFO_BOTH(finfo2,pinfo2);
2414         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2415
2416         /* make sure the 2 second delay from the first write are canceled */
2417         start = timeval_current();
2418         end = timeval_add(&start, 15 * sec, 0);
2419         while (!timeval_expired(&end)) {
2420
2421                 /* get the times after the first write */
2422                 GET_INFO_BOTH(finfo3,pinfo3);
2423
2424                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2425                         double diff = timeval_elapsed(&start);
2426                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2427                                         "(1sec == %.2f) (wrong!)\n",
2428                                         diff, sec);
2429                         ret = false;
2430                         break;
2431                 }
2432                 msleep(2 * msec);
2433         }
2434
2435         GET_INFO_BOTH(finfo3,pinfo3);
2436         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2437         if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2438                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2439         }
2440
2441         /* sure any further write doesn't update the write time */
2442         start = timeval_current();
2443         end = timeval_add(&start, 15 * sec, 0);
2444         while (!timeval_expired(&end)) {
2445                 /* do a write */
2446                 torture_comment(tctx, "Do a write on the file handle\n");
2447                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2448                 if (written != 1) {
2449                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2450                         ret = false;
2451                         goto done;
2452                 }
2453                 /* get the times after the write */
2454                 GET_INFO_BOTH(finfo4,pinfo4);
2455
2456                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2457                         double diff = timeval_elapsed(&start);
2458                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2459                                         "(1sec == %.2f) (wrong!)\n",
2460                                         diff, sec);
2461                         ret = false;
2462                         break;
2463                 }
2464                 msleep(2 * msec);
2465         }
2466
2467         GET_INFO_BOTH(finfo4,pinfo4);
2468         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2469         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2470                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2471         }
2472
2473         /* sleep */
2474         msleep(5 * msec);
2475
2476         GET_INFO_BOTH(finfo5,pinfo5);
2477         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2478
2479         /*
2480          * the close doesn't update the write time
2481          */
2482         torture_comment(tctx, "Close the file handle\n");
2483         smbcli_close(cli->tree, fnum1);
2484         fnum1 = -1;
2485
2486         GET_INFO_PATH(pinfo6);
2487         COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2488
2489         if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2490                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2491         }
2492
2493  done:
2494         if (fnum1 != -1)
2495                 smbcli_close(cli->tree, fnum1);
2496         smbcli_unlink(cli->tree, fname);
2497         smbcli_deltree(cli->tree, BASEDIR);
2498
2499         return ret;
2500 }
2501
2502 static bool test_delayed_write_update5b(struct torture_context *tctx,
2503                                         struct smbcli_state *cli,
2504                                         struct smbcli_state *cli2)
2505 {
2506         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2507         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2508         const char *fname = BASEDIR "\\torture_fileb.txt";
2509         int fnum1 = -1;
2510         bool ret = true;
2511         ssize_t written;
2512         struct timeval start;
2513         struct timeval end;
2514         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2515         int normal_delay = 2000000;
2516         double sec = ((double)used_delay) / ((double)normal_delay);
2517         int msec = 1000 * sec;
2518
2519         torture_comment(tctx, "\nRunning test_delayed_write_update5b\n");
2520
2521         if (!torture_setup_dir(cli, BASEDIR)) {
2522                 return false;
2523         }
2524
2525         torture_comment(tctx, "Open the file handle\n");
2526         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2527         if (fnum1 == -1) {
2528                 ret = false;
2529                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2530                 goto done;
2531         }
2532
2533         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2534         finfo0.basic_info.in.file.fnum = fnum1;
2535         finfo1 = finfo0;
2536         finfo2 = finfo0;
2537         finfo3 = finfo0;
2538         finfo4 = finfo0;
2539         finfo5 = finfo0;
2540         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2541         pinfo0.basic_info.in.file.path = fname;
2542         pinfo1 = pinfo0;
2543         pinfo2 = pinfo0;
2544         pinfo3 = pinfo0;
2545         pinfo4 = pinfo0;
2546         pinfo5 = pinfo0;
2547         pinfo6 = pinfo0;
2548
2549         /* get the initial times */
2550         GET_INFO_BOTH(finfo0,pinfo0);
2551
2552         /* do a write */
2553         torture_comment(tctx, "Do a write on the file handle\n");
2554         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2555         if (written != 1) {
2556                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2557                 ret = false;
2558                 goto done;
2559         }
2560
2561         GET_INFO_BOTH(finfo1,pinfo1);
2562         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2563
2564         torture_comment(tctx, "Set write time in the future on the file handle\n");
2565         SET_INFO_FILE(finfo0, time(NULL) + 86400);
2566         GET_INFO_BOTH(finfo2,pinfo2);
2567         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2568
2569         torture_comment(tctx, "Set write time in the past on the file handle\n");
2570         SET_INFO_FILE(finfo0, time(NULL) - 86400);
2571         GET_INFO_BOTH(finfo2,pinfo2);
2572         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2573
2574         /* make sure the 2 second delay from the first write are canceled */
2575         start = timeval_current();
2576         end = timeval_add(&start, 15 * sec, 0);
2577         while (!timeval_expired(&end)) {
2578
2579                 /* get the times after the first write */
2580                 GET_INFO_BOTH(finfo3,pinfo3);
2581
2582                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2583                         double diff = timeval_elapsed(&start);
2584                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2585                                         "(1sec == %.2f) (wrong!)\n",
2586                                         diff, sec);
2587                         ret = false;
2588                         break;
2589                 }
2590                 msleep(2 * msec);
2591         }
2592
2593         GET_INFO_BOTH(finfo3,pinfo3);
2594         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2595         if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2596                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2597         }
2598
2599         /* sure any further write (truncates) update the write time */
2600         start = timeval_current();
2601         end = timeval_add(&start, 15 * sec, 0);
2602         while (!timeval_expired(&end)) {
2603                 /* do a write */
2604                 torture_comment(tctx, "Do a truncate write on the file handle\n");
2605                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
2606                 if (written != 0) {
2607                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2608                         ret = false;
2609                         goto done;
2610                 }
2611                 /* get the times after the write */
2612                 GET_INFO_BOTH(finfo4,pinfo4);
2613
2614                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2615                         double diff = timeval_elapsed(&start);
2616                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2617                                         "(1sec == %.2f) (wrong!)\n",
2618                                         diff, sec);
2619                         ret = false;
2620                         break;
2621                 }
2622                 msleep(2 * msec);
2623         }
2624
2625         GET_INFO_BOTH(finfo4,pinfo4);
2626         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2627         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2628                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2629         }
2630
2631         /* sleep */
2632         msleep(5 * msec);
2633
2634         GET_INFO_BOTH(finfo5,pinfo5);
2635         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2636
2637         /*
2638          * the close doesn't update the write time
2639          */
2640         torture_comment(tctx, "Close the file handle\n");
2641         smbcli_close(cli->tree, fnum1);
2642         fnum1 = -1;
2643
2644         GET_INFO_PATH(pinfo6);
2645         COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2646
2647         if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2648                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2649         }
2650
2651  done:
2652         if (fnum1 != -1)
2653                 smbcli_close(cli->tree, fnum1);
2654         smbcli_unlink(cli->tree, fname);
2655         smbcli_deltree(cli->tree, BASEDIR);
2656
2657         return ret;
2658 }
2659
2660 static bool test_delayed_write_update6(struct torture_context *tctx,
2661                                        struct smbcli_state *cli,
2662                                        struct smbcli_state *cli2)
2663 {
2664         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2665         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
2666         const char *fname = BASEDIR "\\torture_file6.txt";
2667         int fnum1 = -1;
2668         int fnum2 = -1;
2669         bool ret = true;
2670         ssize_t written;
2671         struct timeval start;
2672         struct timeval end;
2673         int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2674         int normal_delay = 2000000;
2675         double sec = ((double)used_delay) / ((double)normal_delay);
2676         int msec = 1000 * sec;
2677         bool first = true;
2678
2679         torture_comment(tctx, "\nRunning test_delayed_write_update6\n");
2680
2681         if (!torture_setup_dir(cli, BASEDIR)) {
2682                 return false;
2683         }
2684 again:
2685         torture_comment(tctx, "Open the file handle\n");
2686         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2687         if (fnum1 == -1) {
2688                 ret = false;
2689                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2690                 goto done;
2691         }
2692
2693         if (fnum2 == -1) {
2694                 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
2695                 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2696                 if (fnum2 == -1) {
2697                         ret = false;
2698                         torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2699                         goto done;
2700                 }
2701         }
2702
2703         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2704         finfo0.basic_info.in.file.fnum = fnum1;
2705         finfo1 = finfo0;
2706         finfo2 = finfo0;
2707         finfo3 = finfo0;
2708         finfo4 = finfo0;
2709         finfo5 = finfo0;
2710         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2711         pinfo0.basic_info.in.file.path = fname;
2712         pinfo1 = pinfo0;
2713         pinfo2 = pinfo0;
2714         pinfo3 = pinfo0;
2715         pinfo4 = pinfo0;
2716         pinfo5 = pinfo0;
2717         pinfo6 = pinfo0;
2718         pinfo7 = pinfo0;
2719
2720         /* get the initial times */
2721         GET_INFO_BOTH(finfo0,pinfo0);
2722
2723         /* do a write */
2724         torture_comment(tctx, "Do a write on the file handle\n");
2725         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2726         if (written != 1) {
2727                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2728                 ret = false;
2729                 goto done;
2730         }
2731
2732         GET_INFO_BOTH(finfo1,pinfo1);
2733         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2734
2735         torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
2736         SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
2737         GET_INFO_BOTH(finfo2,pinfo2);
2738         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2739
2740         torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
2741         SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
2742         GET_INFO_BOTH(finfo2,pinfo2);
2743         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2744
2745         /* make sure the 2 second delay from the first write are canceled */
2746         start = timeval_current();
2747         end = timeval_add(&start, 15 * sec, 0);
2748         while (!timeval_expired(&end)) {
2749
2750                 /* get the times after the first write */
2751                 GET_INFO_BOTH(finfo3,pinfo3);
2752
2753                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2754                         double diff = timeval_elapsed(&start);
2755                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2756                                         "(1sec == %.2f) (wrong!)\n",
2757                                         diff, sec);
2758                         ret = false;
2759                         break;
2760                 }
2761                 msleep(2 * msec);
2762         }
2763
2764         GET_INFO_BOTH(finfo3,pinfo3);
2765         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2766         if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2767                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2768         }
2769
2770         /* sure any further write doesn't update the write time */
2771         start = timeval_current();
2772         end = timeval_add(&start, 15 * sec, 0);
2773         while (!timeval_expired(&end)) {
2774                 /* do a write */
2775                 torture_comment(tctx, "Do a write on the file handle\n");
2776                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2777                 if (written != 1) {
2778                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2779                         ret = false;
2780                         goto done;
2781                 }
2782                 /* get the times after the write */
2783                 GET_INFO_BOTH(finfo4,pinfo4);
2784
2785                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2786                         double diff = timeval_elapsed(&start);
2787                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2788                                         "(1sec == %.2f) (wrong!)\n",
2789                                         diff, sec);
2790                         ret = false;
2791                         break;
2792                 }
2793                 msleep(2 * msec);
2794         }
2795
2796         GET_INFO_BOTH(finfo4,pinfo4);
2797         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2798         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2799                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2800         }
2801
2802         /* sleep */
2803         msleep(5 * msec);
2804
2805         GET_INFO_BOTH(finfo5,pinfo5);
2806         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2807
2808         /*
2809          * the close updates the write time to the time of the close
2810          * as the write time was set on the 2nd handle
2811          */
2812         torture_comment(tctx, "Close the file handle\n");
2813         smbcli_close(cli->tree, fnum1);
2814         fnum1 = -1;
2815
2816         GET_INFO_PATH(pinfo6);
2817         COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
2818
2819         if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
2820                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2821         }
2822
2823         /* keep the 2nd handle open and rerun tests */
2824         if (first) {
2825                 first = false;
2826                 goto again;
2827         }
2828
2829         /*
2830          * closing the 2nd handle will cause no write time update
2831          * as the write time was explicit set on this handle
2832          */
2833         torture_comment(tctx, "Close the 2nd file handle\n");
2834         smbcli_close(cli2->tree, fnum2);
2835         fnum2 = -1;
2836
2837         GET_INFO_PATH(pinfo7);
2838         COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
2839
2840         if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
2841                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2842         }
2843
2844  done:
2845         if (fnum1 != -1)
2846                 smbcli_close(cli->tree, fnum1);
2847         if (fnum2 != -1)
2848                 smbcli_close(cli2->tree, fnum2);
2849         smbcli_unlink(cli->tree, fname);
2850         smbcli_deltree(cli->tree, BASEDIR);
2851
2852         return ret;
2853 }
2854
2855
2856 /* 
2857    testing of delayed update of write_time
2858 */
2859 struct torture_suite *torture_delay_write(void)
2860 {
2861         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "DELAYWRITE");
2862
2863         torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
2864         torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
2865         torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate ", test_delayed_write_update1);
2866         torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a);
2867         torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b);
2868         torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c);
2869         torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
2870         torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
2871         torture_suite_add_2smb_test(suite, "delayed update of write time 3a", test_delayed_write_update3a);
2872         torture_suite_add_2smb_test(suite, "delayed update of write time 3b", test_delayed_write_update3b);
2873         torture_suite_add_2smb_test(suite, "delayed update of write time 3c", test_delayed_write_update3c);
2874         torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
2875         torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
2876         torture_suite_add_2smb_test(suite, "delayed update of write time 5b", test_delayed_write_update5b);
2877         torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
2878
2879         return suite;
2880 }