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