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