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