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