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