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