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