s4:torture:basic: use torture_assert macros in delayed_write_update
[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, finfo4;
1998         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
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         finfo4 = finfo0;
2029         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2030         pinfo0.basic_info.in.file.path = fname;
2031         pinfo1 = pinfo0;
2032         pinfo2 = pinfo0;
2033         pinfo3 = pinfo0;
2034         pinfo4 = pinfo0;
2035         pinfo5 = pinfo0;
2036
2037         /* get the initial times */
2038         GET_INFO_BOTH(finfo0,pinfo0);
2039
2040         /*
2041          * sleep some time, to demonstrate the handling of write times
2042          * doesn't depend on the time since the open
2043          */
2044         smb_msleep(5 * msec);
2045
2046         /* get the initial times */
2047         GET_INFO_BOTH(finfo1,pinfo1);
2048         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2049
2050         /*
2051          * demonstrate that a truncate write always
2052          * updates the write time immediately
2053          */
2054         for (i=0; i < 3; i++) {
2055                 smb_msleep(2 * msec);
2056                 /* do a write */
2057                 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
2058                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2059                 if (written != 0) {
2060                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2061                         ret = false;
2062                         goto done;
2063                 }
2064                 /* get the times after the write */
2065                 GET_INFO_BOTH(finfo2,pinfo2);
2066                 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2067                 finfo1 = finfo2;
2068         }
2069
2070         start = timeval_current();
2071         end = timeval_add(&start, 7 * sec, 0);
2072         while (!timeval_expired(&end)) {
2073                 /* do a write */
2074                 torture_comment(tctx, "Do a write on the file handle\n");
2075                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2076                 if (written != 1) {
2077                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2078                         ret = false;
2079                         goto done;
2080                 }
2081                 /* get the times after the write */
2082                 GET_INFO_FILE(finfo2);
2083
2084                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2085                         double diff = timeval_elapsed(&start);
2086                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2087                                         "(wrong!)\n",
2088                                         diff);
2089                         ret = false;
2090                         break;
2091                 }
2092                 smb_msleep(1 * msec);
2093         }
2094
2095         GET_INFO_BOTH(finfo2,pinfo2);
2096         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2097         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2098                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2099         }
2100
2101         /* sleep */
2102         smb_msleep(5 * msec);
2103
2104         /* get the initial times */
2105         GET_INFO_BOTH(finfo1,pinfo1);
2106         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
2107
2108         /*
2109          * demonstrate that a truncate write always
2110          * updates the write time immediately
2111          */
2112         for (i=0; i < 3; i++) {
2113                 smb_msleep(2 * msec);
2114                 /* do a write */
2115                 torture_comment(tctx, "Do a truncate write [%d] on the file handle\n", i);
2116                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2117                 if (written != 0) {
2118                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2119                         ret = false;
2120                         goto done;
2121                 }
2122                 /* get the times after the write */
2123                 GET_INFO_BOTH(finfo2,pinfo2);
2124                 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2125                 finfo1 = finfo2;
2126         }
2127
2128         /* sleep */
2129         smb_msleep(5 * msec);
2130
2131         GET_INFO_BOTH(finfo2,pinfo2);
2132         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2133
2134         /* sure any further write doesn't update the write time */
2135         start = timeval_current();
2136         end = timeval_add(&start, 15 * sec, 0);
2137         while (!timeval_expired(&end)) {
2138                 /* do a write */
2139                 torture_comment(tctx, "Do a write on the file handle\n");
2140                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2141                 if (written != 1) {
2142                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2143                         ret = false;
2144                         goto done;
2145                 }
2146                 /* get the times after the write */
2147                 GET_INFO_BOTH(finfo2,pinfo2);
2148
2149                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2150                         double diff = timeval_elapsed(&start);
2151                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2152                                         "(wrong!)\n",
2153                                         diff);
2154                         ret = false;
2155                         break;
2156                 }
2157                 smb_msleep(1 * msec);
2158         }
2159
2160         GET_INFO_BOTH(finfo2,pinfo2);
2161         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2162         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2163                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2164         }
2165
2166         /* sleep */
2167         smb_msleep(5 * msec);
2168
2169         GET_INFO_BOTH(finfo3,pinfo3);
2170         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2171
2172         /*
2173          * the close updates the write time to the time of the close
2174          * and not to the time of the last write!
2175          */
2176         torture_comment(tctx, "Close the file handle\n");
2177         smbcli_close(cli->tree, fnum1);
2178         fnum1 = -1;
2179
2180         GET_INFO_PATH(pinfo4);
2181         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2182
2183         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2184                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2185         }
2186
2187  done:
2188         if (fnum1 != -1)
2189                 smbcli_close(cli->tree, fnum1);
2190         smbcli_unlink(cli->tree, fname);
2191         smbcli_deltree(cli->tree, BASEDIR);
2192
2193         return ret;
2194 }
2195
2196 /*
2197  * Show only the first write updates the timestamp, and a close
2198  * after writes updates to current (I think this is the same
2199  * as test 3b. JRA).
2200  */
2201
2202 static bool test_delayed_write_update4(struct torture_context *tctx,
2203                                        struct smbcli_state *cli,
2204                                        struct smbcli_state *cli2)
2205 {
2206         union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
2207         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
2208         const char *fname = BASEDIR "\\torture_file4.txt";
2209         int fnum1 = -1;
2210         bool ret = true;
2211         ssize_t written;
2212         struct timeval start;
2213         struct timeval end;
2214         double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2215         int normal_delay = 2000000;
2216         double sec = ((double)used_delay) / ((double)normal_delay);
2217         int msec = 1000 * sec;
2218
2219         torture_comment(tctx, "\nRunning test_delayed_write_update4\n");
2220
2221         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2222
2223         torture_comment(tctx, "Open the file handle\n");
2224         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2225         if (fnum1 == -1) {
2226                 ret = false;
2227                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2228                 goto done;
2229         }
2230
2231         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2232         finfo0.basic_info.in.file.fnum = fnum1;
2233         finfo1 = finfo0;
2234         finfo2 = finfo0;
2235         finfo3 = finfo0;
2236         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2237         pinfo0.basic_info.in.file.path = fname;
2238         pinfo1 = pinfo0;
2239         pinfo2 = pinfo0;
2240         pinfo3 = pinfo0;
2241         pinfo4 = pinfo0;
2242
2243         /* get the initial times */
2244         GET_INFO_BOTH(finfo0,pinfo0);
2245
2246         /* sleep a bit */
2247         smb_msleep(5 * msec);
2248
2249         /* do a write */
2250         torture_comment(tctx, "Do a write on the file handle\n");
2251         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2252         if (written != 1) {
2253                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2254                 ret = false;
2255                 goto done;
2256         }
2257
2258         GET_INFO_BOTH(finfo1,pinfo1);
2259         COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
2260
2261         /*
2262          * make sure the write time is updated 2 seconds later
2263          * calcuated from the first write
2264          * (but expect upto 3 seconds extra time for a busy server)
2265          */
2266         start = timeval_current();
2267         end = timeval_add(&start, 5 * sec, 0);
2268         while (!timeval_expired(&end)) {
2269                 /* get the times after the first write */
2270                 GET_INFO_FILE(finfo1);
2271
2272                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
2273                         double diff = timeval_elapsed(&start);
2274                         if (diff < (used_delay / (double)1000000)) {
2275                                 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
2276                                                 "(expected > %.2f) (wrong!)\n",
2277                                                 diff, used_delay / (double)1000000);
2278                                 ret = false;
2279                                 break;
2280                         }
2281
2282                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2283                                         "(write time update delay == %.2f) (correct)\n",
2284                                         diff, used_delay / (double)1000000);
2285                         break;
2286                 }
2287                 smb_msleep(0.5 * msec);
2288         }
2289
2290         GET_INFO_BOTH(finfo1,pinfo1);
2291         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
2292
2293         /* sure any further write doesn't update the write time */
2294         start = timeval_current();
2295         end = timeval_add(&start, 15 * sec, 0);
2296         while (!timeval_expired(&end)) {
2297                 /* do a write */
2298                 torture_comment(tctx, "Do a write on the file handle\n");
2299                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2300                 if (written != 1) {
2301                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2302                         ret = false;
2303                         goto done;
2304                 }
2305                 /* get the times after the write */
2306                 GET_INFO_BOTH(finfo2,pinfo2);
2307
2308                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2309                         double diff = timeval_elapsed(&start);
2310                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2311                                         "(wrong!)\n",
2312                                         diff);
2313                         ret = false;
2314                         break;
2315                 }
2316                 smb_msleep(1 * msec);
2317         }
2318
2319         GET_INFO_BOTH(finfo2,pinfo2);
2320         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2321         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2322                 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
2323         }
2324
2325         /* sleep */
2326         smb_msleep(5 * msec);
2327
2328         GET_INFO_BOTH(finfo3,pinfo3);
2329         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2330
2331         /*
2332          * the close updates the write time to the time of the close
2333          * and not to the time of the last write!
2334          */
2335         torture_comment(tctx, "Close the file handle\n");
2336         smbcli_close(cli->tree, fnum1);
2337         fnum1 = -1;
2338
2339         GET_INFO_PATH(pinfo4);
2340         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2341
2342         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2343                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2344         }
2345
2346  done:
2347         if (fnum1 != -1)
2348                 smbcli_close(cli->tree, fnum1);
2349         smbcli_unlink(cli->tree, fname);
2350         smbcli_deltree(cli->tree, BASEDIR);
2351
2352         return ret;
2353 }
2354
2355 /*
2356  * Show writes and closes have no effect on updating times once a SETWRITETIME is done.
2357  */
2358
2359 static bool test_delayed_write_update5(struct torture_context *tctx,
2360                                        struct smbcli_state *cli,
2361                                        struct smbcli_state *cli2)
2362 {
2363         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2364         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2365         const char *fname = BASEDIR "\\torture_file5.txt";
2366         int fnum1 = -1;
2367         bool ret = true;
2368         ssize_t written;
2369         struct timeval start;
2370         struct timeval end;
2371         double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2372         int normal_delay = 2000000;
2373         double sec = ((double)used_delay) / ((double)normal_delay);
2374         int msec = 1000 * sec;
2375
2376         torture_comment(tctx, "\nRunning test_delayed_write_update5\n");
2377
2378         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2379
2380         torture_comment(tctx, "Open the file handle\n");
2381         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2382         if (fnum1 == -1) {
2383                 ret = false;
2384                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2385                 goto done;
2386         }
2387
2388         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2389         finfo0.basic_info.in.file.fnum = fnum1;
2390         finfo1 = finfo0;
2391         finfo2 = finfo0;
2392         finfo3 = finfo0;
2393         finfo4 = finfo0;
2394         finfo5 = finfo0;
2395         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2396         pinfo0.basic_info.in.file.path = fname;
2397         pinfo1 = pinfo0;
2398         pinfo2 = pinfo0;
2399         pinfo3 = pinfo0;
2400         pinfo4 = pinfo0;
2401         pinfo5 = pinfo0;
2402         pinfo6 = pinfo0;
2403
2404         /* get the initial times */
2405         GET_INFO_BOTH(finfo0,pinfo0);
2406
2407         /* do a write */
2408         torture_comment(tctx, "Do a write on the file handle\n");
2409         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2410         if (written != 1) {
2411                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2412                 ret = false;
2413                 goto done;
2414         }
2415
2416         GET_INFO_BOTH(finfo1,pinfo1);
2417         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2418
2419         torture_comment(tctx, "Set write time in the future on the file handle\n");
2420         SET_INFO_FILE(finfo0, time(NULL) + 86400);
2421         GET_INFO_BOTH(finfo2,pinfo2);
2422         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2423
2424         torture_comment(tctx, "Set write time in the past on the file handle\n");
2425         SET_INFO_FILE(finfo0, time(NULL) - 86400);
2426         GET_INFO_BOTH(finfo2,pinfo2);
2427         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2428
2429         /* make sure the 2 second delay from the first write are canceled */
2430         start = timeval_current();
2431         end = timeval_add(&start, 15 * sec, 0);
2432         while (!timeval_expired(&end)) {
2433
2434                 /* get the times after the first write */
2435                 GET_INFO_BOTH(finfo3,pinfo3);
2436
2437                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2438                         double diff = timeval_elapsed(&start);
2439                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2440                                         "(wrong!)\n",
2441                                         diff);
2442                         ret = false;
2443                         break;
2444                 }
2445                 smb_msleep(1 * msec);
2446         }
2447
2448         GET_INFO_BOTH(finfo3,pinfo3);
2449         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2450         if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2451                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2452         }
2453
2454         /* sure any further write doesn't update the write time */
2455         start = timeval_current();
2456         end = timeval_add(&start, 15 * sec, 0);
2457         while (!timeval_expired(&end)) {
2458                 /* do a write */
2459                 torture_comment(tctx, "Do a write on the file handle\n");
2460                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2461                 if (written != 1) {
2462                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2463                         ret = false;
2464                         goto done;
2465                 }
2466                 /* get the times after the write */
2467                 GET_INFO_BOTH(finfo4,pinfo4);
2468
2469                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2470                         double diff = timeval_elapsed(&start);
2471                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2472                                         "(wrong!)\n",
2473                                         diff);
2474                         ret = false;
2475                         break;
2476                 }
2477                 smb_msleep(1 * msec);
2478         }
2479
2480         GET_INFO_BOTH(finfo4,pinfo4);
2481         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2482         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2483                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2484         }
2485
2486         /* sleep */
2487         smb_msleep(5 * msec);
2488
2489         GET_INFO_BOTH(finfo5,pinfo5);
2490         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2491
2492         /*
2493          * the close doesn't update the write time
2494          */
2495         torture_comment(tctx, "Close the file handle\n");
2496         smbcli_close(cli->tree, fnum1);
2497         fnum1 = -1;
2498
2499         GET_INFO_PATH(pinfo6);
2500         COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2501
2502         if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2503                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2504         }
2505
2506  done:
2507         if (fnum1 != -1)
2508                 smbcli_close(cli->tree, fnum1);
2509         smbcli_unlink(cli->tree, fname);
2510         smbcli_deltree(cli->tree, BASEDIR);
2511
2512         return ret;
2513 }
2514
2515 /*
2516  * Show truncate writes and closes have no effect on updating times once a SETWRITETIME is done.
2517  */
2518
2519 static bool test_delayed_write_update5b(struct torture_context *tctx,
2520                                         struct smbcli_state *cli,
2521                                         struct smbcli_state *cli2)
2522 {
2523         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2524         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2525         const char *fname = BASEDIR "\\torture_fileb.txt";
2526         int fnum1 = -1;
2527         bool ret = true;
2528         ssize_t written;
2529         struct timeval start;
2530         struct timeval end;
2531         double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2532         int normal_delay = 2000000;
2533         double sec = ((double)used_delay) / ((double)normal_delay);
2534         int msec = 1000 * sec;
2535
2536         torture_comment(tctx, "\nRunning test_delayed_write_update5b\n");
2537
2538         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2539
2540         torture_comment(tctx, "Open the file handle\n");
2541         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2542         if (fnum1 == -1) {
2543                 ret = false;
2544                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2545                 goto done;
2546         }
2547
2548         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2549         finfo0.basic_info.in.file.fnum = fnum1;
2550         finfo1 = finfo0;
2551         finfo2 = finfo0;
2552         finfo3 = finfo0;
2553         finfo4 = finfo0;
2554         finfo5 = finfo0;
2555         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2556         pinfo0.basic_info.in.file.path = fname;
2557         pinfo1 = pinfo0;
2558         pinfo2 = pinfo0;
2559         pinfo3 = pinfo0;
2560         pinfo4 = pinfo0;
2561         pinfo5 = pinfo0;
2562         pinfo6 = pinfo0;
2563
2564         /* get the initial times */
2565         GET_INFO_BOTH(finfo0,pinfo0);
2566
2567         /* do a write */
2568         torture_comment(tctx, "Do a write on the file handle\n");
2569         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2570         if (written != 1) {
2571                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2572                 ret = false;
2573                 goto done;
2574         }
2575
2576         GET_INFO_BOTH(finfo1,pinfo1);
2577         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2578
2579         torture_comment(tctx, "Set write time in the future on the file handle\n");
2580         SET_INFO_FILE(finfo0, time(NULL) + 86400);
2581         GET_INFO_BOTH(finfo2,pinfo2);
2582         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2583
2584         torture_comment(tctx, "Set write time in the past on the file handle\n");
2585         SET_INFO_FILE(finfo0, time(NULL) - 86400);
2586         GET_INFO_BOTH(finfo2,pinfo2);
2587         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2588
2589         /* make sure the 2 second delay from the first write are canceled */
2590         start = timeval_current();
2591         end = timeval_add(&start, 15 * sec, 0);
2592         while (!timeval_expired(&end)) {
2593
2594                 /* get the times after the first write */
2595                 GET_INFO_BOTH(finfo3,pinfo3);
2596
2597                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2598                         double diff = timeval_elapsed(&start);
2599                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2600                                         "(wrong!)\n",
2601                                         diff);
2602                         ret = false;
2603                         break;
2604                 }
2605                 smb_msleep(1 * msec);
2606         }
2607
2608         GET_INFO_BOTH(finfo3,pinfo3);
2609         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2610         if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2611                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2612         }
2613
2614         /* Do any further write (truncates) update the write time ? */
2615         start = timeval_current();
2616         end = timeval_add(&start, 15 * sec, 0);
2617         while (!timeval_expired(&end)) {
2618                 /* do a write */
2619                 torture_comment(tctx, "Do a truncate write on the file handle\n");
2620                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
2621                 if (written != 0) {
2622                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2623                         ret = false;
2624                         goto done;
2625                 }
2626                 /* get the times after the write */
2627                 GET_INFO_BOTH(finfo4,pinfo4);
2628
2629                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2630                         double diff = timeval_elapsed(&start);
2631                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2632                                         "(wrong!)\n",
2633                                         diff);
2634                         ret = false;
2635                         break;
2636                 }
2637                 smb_msleep(1 * msec);
2638         }
2639
2640         GET_INFO_BOTH(finfo4,pinfo4);
2641         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2642         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2643                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2644         }
2645
2646         /* sleep */
2647         smb_msleep(5 * msec);
2648
2649         GET_INFO_BOTH(finfo5,pinfo5);
2650         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2651
2652         /*
2653          * the close doesn't update the write time
2654          */
2655         torture_comment(tctx, "Close the file handle\n");
2656         smbcli_close(cli->tree, fnum1);
2657         fnum1 = -1;
2658
2659         GET_INFO_PATH(pinfo6);
2660         COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2661
2662         if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2663                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2664         }
2665
2666  done:
2667         if (fnum1 != -1)
2668                 smbcli_close(cli->tree, fnum1);
2669         smbcli_unlink(cli->tree, fname);
2670         smbcli_deltree(cli->tree, BASEDIR);
2671
2672         return ret;
2673 }
2674
2675 /*
2676  * Open 2 handles on a file. Write one one and then set the
2677  * WRITE TIME explicitly on the other. Ensure the write time
2678  * update is cancelled. Ensure the write time is updated to
2679  * the close time when the non-explicit set handle is closed.
2680  *
2681  */
2682
2683 static bool test_delayed_write_update6(struct torture_context *tctx,
2684                                        struct smbcli_state *cli,
2685                                        struct smbcli_state *cli2)
2686 {
2687         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2688         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
2689         const char *fname = BASEDIR "\\torture_file6.txt";
2690         int fnum1 = -1;
2691         int fnum2 = -1;
2692         bool ret = true;
2693         ssize_t written;
2694         struct timeval start;
2695         struct timeval end;
2696         double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2697         int normal_delay = 2000000;
2698         double sec = ((double)used_delay) / ((double)normal_delay);
2699         int msec = 1000 * sec;
2700         bool first = true;
2701
2702         torture_comment(tctx, "\nRunning test_delayed_write_update6\n");
2703
2704         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2705 again:
2706         torture_comment(tctx, "Open the file handle\n");
2707         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2708         if (fnum1 == -1) {
2709                 ret = false;
2710                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2711                 goto done;
2712         }
2713
2714         if (fnum2 == -1) {
2715                 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
2716                 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2717                 if (fnum2 == -1) {
2718                         ret = false;
2719                         torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2720                         goto done;
2721                 }
2722         }
2723
2724         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2725         finfo0.basic_info.in.file.fnum = fnum1;
2726         finfo1 = finfo0;
2727         finfo2 = finfo0;
2728         finfo3 = finfo0;
2729         finfo4 = finfo0;
2730         finfo5 = finfo0;
2731         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2732         pinfo0.basic_info.in.file.path = fname;
2733         pinfo1 = pinfo0;
2734         pinfo2 = pinfo0;
2735         pinfo3 = pinfo0;
2736         pinfo4 = pinfo0;
2737         pinfo5 = pinfo0;
2738         pinfo6 = pinfo0;
2739         pinfo7 = pinfo0;
2740
2741         /* get the initial times */
2742         GET_INFO_BOTH(finfo0,pinfo0);
2743
2744         /* do a write */
2745         torture_comment(tctx, "Do a write on the file handle\n");
2746         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2747         if (written != 1) {
2748                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2749                 ret = false;
2750                 goto done;
2751         }
2752
2753         GET_INFO_BOTH(finfo1,pinfo1);
2754         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2755
2756         torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
2757         SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
2758         GET_INFO_BOTH(finfo2,pinfo2);
2759         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2760
2761         torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
2762         SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
2763         GET_INFO_BOTH(finfo2,pinfo2);
2764         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2765
2766         /* make sure the 2 second delay from the first write are canceled */
2767         start = timeval_current();
2768         end = timeval_add(&start, 10 * sec, 0);
2769         while (!timeval_expired(&end)) {
2770
2771                 /* get the times after the first write */
2772                 GET_INFO_BOTH(finfo3,pinfo3);
2773
2774                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2775                         double diff = timeval_elapsed(&start);
2776                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2777                                         "(wrong!)\n",
2778                                         diff);
2779                         ret = false;
2780                         break;
2781                 }
2782                 smb_msleep(1 * msec);
2783         }
2784
2785         GET_INFO_BOTH(finfo3,pinfo3);
2786         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2787         if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2788                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2789         }
2790
2791         /* sure any further write doesn't update the write time */
2792         start = timeval_current();
2793         end = timeval_add(&start, 10 * sec, 0);
2794         while (!timeval_expired(&end)) {
2795                 /* do a write */
2796                 torture_comment(tctx, "Do a write on the file handle\n");
2797                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2798                 if (written != 1) {
2799                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2800                         ret = false;
2801                         goto done;
2802                 }
2803                 /* get the times after the write */
2804                 GET_INFO_BOTH(finfo4,pinfo4);
2805
2806                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2807                         double diff = timeval_elapsed(&start);
2808                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2809                                         "(wrong!)\n",
2810                                         diff);
2811                         ret = false;
2812                         break;
2813                 }
2814                 smb_msleep(1 * msec);
2815         }
2816
2817         GET_INFO_BOTH(finfo4,pinfo4);
2818         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2819         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2820                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2821         }
2822
2823         /* sleep */
2824         smb_msleep(5 * msec);
2825
2826         GET_INFO_BOTH(finfo5,pinfo5);
2827         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2828
2829         /*
2830          * the close updates the write time to the time of the close
2831          * as the write time was set on the 2nd handle
2832          */
2833         torture_comment(tctx, "Close the file handle\n");
2834         smbcli_close(cli->tree, fnum1);
2835         fnum1 = -1;
2836
2837         GET_INFO_PATH(pinfo6);
2838         COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
2839
2840         if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
2841                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2842         }
2843
2844         /* See what the second write handle thinks the time is ? */
2845         finfo5.basic_info.in.file.fnum = fnum2;
2846         GET_INFO_FILE2(finfo5);
2847         COMPARE_WRITE_TIME_EQUAL(finfo5, pinfo6);
2848
2849         /* See if we have lost the sticky write time on handle2 */
2850         smb_msleep(3 * msec);
2851         torture_comment(tctx, "Have we lost the sticky write time ?\n");
2852
2853         /* Make sure any further normal write doesn't update the write time */
2854         start = timeval_current();
2855         end = timeval_add(&start, 10 * sec, 0);
2856         while (!timeval_expired(&end)) {
2857                 /* do a write */
2858                 torture_comment(tctx, "Do a write on the second file handle\n");
2859                 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
2860                 if (written != 1) {
2861                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2862                         ret = false;
2863                         goto done;
2864                 }
2865                 /* get the times after the write */
2866                 GET_INFO_FILE2(finfo5);
2867                 GET_INFO_PATH(pinfo6);
2868
2869                 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2870                         double diff = timeval_elapsed(&start);
2871                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2872                                         "(wrong!)\n",
2873                                         diff);
2874                         ret = false;
2875                         break;
2876                 }
2877                 smb_msleep(1 * msec);
2878         }
2879
2880         /* What about a truncate write ? */
2881         start = timeval_current();
2882         end = timeval_add(&start, 10 * sec, 0);
2883         while (!timeval_expired(&end)) {
2884                 /* do a write */
2885                 torture_comment(tctx, "Do a truncate write on the second file handle\n");
2886                 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 0);
2887                 if (written != 0) {
2888                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2889                         ret = false;
2890                         goto done;
2891                 }
2892                 /* get the times after the write */
2893                 GET_INFO_FILE2(finfo5);
2894                 GET_INFO_PATH(pinfo6);
2895
2896                 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2897                         double diff = timeval_elapsed(&start);
2898                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2899                                         "(wrong!)\n",
2900                                         diff);
2901                         ret = false;
2902                         break;
2903                 }
2904                 smb_msleep(1 * msec);
2905         }
2906
2907
2908         /* keep the 2nd handle open and rerun tests */
2909         if (first) {
2910                 first = false;
2911                 goto again;
2912         }
2913
2914         /*
2915          * closing the 2nd handle will cause no write time update
2916          * as the write time was explicit set on this handle
2917          */
2918         torture_comment(tctx, "Close the 2nd file handle\n");
2919         smbcli_close(cli2->tree, fnum2);
2920         fnum2 = -1;
2921
2922         GET_INFO_PATH(pinfo7);
2923         COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
2924
2925         if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
2926                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2927         }
2928
2929  done:
2930         if (fnum1 != -1)
2931                 smbcli_close(cli->tree, fnum1);
2932         if (fnum2 != -1)
2933                 smbcli_close(cli2->tree, fnum2);
2934         smbcli_unlink(cli->tree, fname);
2935         smbcli_deltree(cli->tree, BASEDIR);
2936
2937         return ret;
2938 }
2939
2940 static bool test_delayed_write_update7(struct torture_context *tctx, struct smbcli_state *cli)
2941 {
2942         union smb_open open_parms;
2943         union smb_fileinfo finfo1, finfo2, finfo3;
2944         const char *fname = BASEDIR "\\torture_file7.txt";
2945         NTSTATUS status;
2946         int fnum1 = -1;
2947         bool ret = true;
2948         TALLOC_CTX *mem_ctx; 
2949
2950         torture_comment(tctx, "\nRunning test_delayed_write_update7 (timestamp resolution test)\n");
2951
2952         mem_ctx = talloc_init("test_delayed_write_update7");
2953         if (!mem_ctx) return false;
2954
2955         ZERO_STRUCT(finfo1);
2956         ZERO_STRUCT(finfo2);
2957         ZERO_STRUCT(finfo3);
2958         ZERO_STRUCT(open_parms);
2959
2960         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2961
2962         /* Create the file. */
2963         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2964         if (fnum1 == -1) {
2965                 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
2966                 return false;
2967         }
2968
2969         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2970         finfo1.basic_info.in.file.fnum = fnum1;
2971         finfo2 = finfo1;
2972         finfo3 = finfo1;
2973
2974         /* Get the initial timestamps. */
2975         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
2976
2977         torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
2978         
2979         /* Set the pending write time to a value with ns. */
2980         SET_INFO_FILE_NS(finfo, time(NULL) + 86400, 103, cli->tree, fnum1);
2981
2982         /* Get the current pending write time by fnum. */
2983         status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
2984
2985         torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
2986
2987         /* Ensure the time is actually different. */
2988         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2989                 torture_result(tctx, TORTURE_FAIL,
2990                         "setfileinfo time matches original fileinfo time");
2991                 ret = false;
2992         }
2993
2994         /* Get the current pending write time by path. */
2995         finfo3.basic_info.in.file.path = fname;
2996         status = smb_raw_pathinfo(cli->tree, tctx, &finfo3);
2997
2998         if (finfo2.basic_info.out.write_time != finfo3.basic_info.out.write_time) {
2999                 torture_result(tctx, TORTURE_FAIL, 
3000                         "qpathinfo time doens't match fileinfo time");
3001                 ret = false;
3002         }
3003
3004         /* Now close the file. Re-open and check that the write
3005            time is identical to the one we wrote. */
3006
3007         smbcli_close(cli->tree, fnum1);
3008
3009         open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
3010         open_parms.ntcreatex.in.flags = 0;
3011         open_parms.ntcreatex.in.access_mask = SEC_GENERIC_READ;
3012         open_parms.ntcreatex.in.file_attr = 0;
3013         open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE|
3014                                         NTCREATEX_SHARE_ACCESS_READ|
3015                                         NTCREATEX_SHARE_ACCESS_WRITE;
3016         open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
3017         open_parms.ntcreatex.in.create_options = 0;
3018         open_parms.ntcreatex.in.fname = fname;
3019
3020         status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
3021         talloc_free(mem_ctx);
3022
3023         if (!NT_STATUS_IS_OK(status)) {
3024                 torture_result(tctx, TORTURE_FAIL,
3025                         "setfileinfo time matches original fileinfo time");
3026                 ret = false;
3027         }
3028
3029         fnum1 = open_parms.ntcreatex.out.file.fnum;
3030
3031         /* Check the returned time matches. */
3032         if (open_parms.ntcreatex.out.write_time != finfo2.basic_info.out.write_time) {
3033                 torture_result(tctx, TORTURE_FAIL,
3034                         "final open time does not match set time");
3035                 ret = false;
3036         }
3037
3038  done:
3039
3040         smbcli_close(cli->tree, fnum1);
3041
3042         smbcli_unlink(cli->tree, fname);
3043         smbcli_deltree(cli->tree, BASEDIR);
3044         return ret;
3045 }
3046
3047 /*
3048    Test if creating a file in a directory with an open handle updates the
3049    write timestamp (it should).
3050 */
3051 static bool test_directory_update8(struct torture_context *tctx, struct smbcli_state *cli)
3052 {
3053         union smb_fileinfo dir_info1, dir_info2;
3054         union smb_open open_parms;
3055         const char *fname = BASEDIR "\\torture_file.txt";
3056         NTSTATUS status;
3057         int fnum1 = -1;
3058         int fnum2 = -1;
3059         bool ret = true;
3060         double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
3061         int normal_delay = 2000000;
3062         double sec = ((double)used_delay) / ((double)normal_delay);
3063         int msec = 1000 * sec;
3064         TALLOC_CTX *mem_ctx = talloc_init("test_delayed_write_update8");
3065
3066         if (!mem_ctx) return false;
3067
3068         torture_comment(tctx, "\nRunning test directory write update\n");
3069
3070         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
3071
3072         /* Open a handle on the directory - and leave it open. */
3073         ZERO_STRUCT(open_parms);
3074         open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
3075         open_parms.ntcreatex.in.flags = 0;
3076         open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ;
3077         open_parms.ntcreatex.in.file_attr = 0;
3078         open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE|
3079                                         NTCREATEX_SHARE_ACCESS_READ|
3080                                         NTCREATEX_SHARE_ACCESS_WRITE;
3081         open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
3082         open_parms.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
3083         open_parms.ntcreatex.in.fname = BASEDIR;
3084
3085         status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
3086         talloc_free(mem_ctx);
3087
3088         if (!NT_STATUS_IS_OK(status)) {
3089                 torture_result(tctx, TORTURE_FAIL,
3090                         "failed to open directory handle");
3091                 ret = false;
3092                 goto done;
3093         }
3094
3095         fnum1 = open_parms.ntcreatex.out.file.fnum;
3096
3097         /* Store the returned write time. */
3098         ZERO_STRUCT(dir_info1);
3099         dir_info1.basic_info.out.write_time = open_parms.ntcreatex.out.write_time;
3100
3101         torture_comment(tctx, "Initial write time %s\n",
3102                nt_time_string(tctx, dir_info1.basic_info.out.write_time));
3103
3104         /* sleep */
3105         smb_msleep(3 * msec);
3106
3107         /* Now create a file within the directory. */
3108         fnum2 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
3109         if (fnum2 == -1) {
3110                 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
3111                 ret = false;
3112                 goto done;
3113         }
3114         smbcli_close(cli->tree, fnum2);
3115
3116         /* Read the directory write time again. */
3117         ZERO_STRUCT(dir_info2);
3118         dir_info2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
3119         dir_info2.basic_info.in.file.fnum = fnum1;
3120
3121         status = smb_raw_fileinfo(cli->tree, tctx, &dir_info2);
3122
3123         torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
3124
3125         /* Ensure it's been incremented. */
3126         COMPARE_WRITE_TIME_GREATER(dir_info2, dir_info1);
3127
3128         torture_comment(tctx, "Updated write time %s\n",
3129                nt_time_string(tctx, dir_info2.basic_info.out.write_time));
3130
3131  done:
3132
3133         if (fnum1 != -1)
3134                 smbcli_close(cli->tree, fnum1);
3135         smbcli_unlink(cli->tree, fname);
3136         smbcli_deltree(cli->tree, BASEDIR);
3137
3138         return ret;
3139 }
3140
3141 /*
3142    testing of delayed update of write_time
3143 */
3144 struct torture_suite *torture_delay_write(void)
3145 {
3146         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "delaywrite");
3147
3148         torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
3149         torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
3150         torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate", test_delayed_write_update1);
3151         torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a);
3152         torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b);
3153         torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c);
3154         torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
3155         torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
3156         torture_suite_add_2smb_test(suite, "delayed update of write time 3a", test_delayed_write_update3a);
3157         torture_suite_add_2smb_test(suite, "delayed update of write time 3b", test_delayed_write_update3b);
3158         torture_suite_add_2smb_test(suite, "delayed update of write time 3c", test_delayed_write_update3c);
3159         torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
3160         torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
3161         torture_suite_add_2smb_test(suite, "delayed update of write time 5b", test_delayed_write_update5b);
3162         torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
3163         torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7);
3164         torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7);
3165         torture_suite_add_1smb_test(suite, "directory timestamp update test", test_directory_update8);
3166
3167         return suite;
3168 }