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