Revert "test_delayed_write_update TODO: research PATHINFO => DEFER_CHANGE_ON_CLOSE"
[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 _DEBUG_BASIC_INFO(finfo, comment) do { \
1318         struct timeval atv; \
1319         struct timeval wtv; \
1320         struct timeval_buf atvb; \
1321         struct timeval_buf wtvb; \
1322         nttime_to_timeval(&atv, finfo.basic_info.out.access_time); \
1323         nttime_to_timeval(&wtv, finfo.basic_info.out.write_time); \
1324         torture_comment(tctx, "%s: Access(%s) Write(%s)\n", \
1325                         comment, \
1326                         timeval_str_buf(&atv, false, true, &atvb), \
1327                         timeval_str_buf(&wtv, false, true, &wtvb)); \
1328 } while (0)
1329 #define _GET_INFO_FILE(tree, finfo) do { \
1330         NTSTATUS _status; \
1331         _status = smb_raw_fileinfo(tree, tctx, &finfo); \
1332         if (!NT_STATUS_IS_OK(_status)) { \
1333                 ret = false; \
1334                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
1335                                nt_errstr(_status)); \
1336                 goto done; \
1337         } \
1338         _DEBUG_BASIC_INFO(finfo, "fileinfo(" #tree ")"); \
1339 } while (0)
1340 #define _GET_INFO_PATH(tree, pinfo) do { \
1341         NTSTATUS _status; \
1342         _status = smb_raw_pathinfo(tree, tctx, &pinfo); \
1343         if (!NT_STATUS_IS_OK(_status)) { \
1344                 torture_result(tctx, TORTURE_FAIL, __location__": pathinfo failed: %s", \
1345                                nt_errstr(_status)); \
1346                 ret = false; \
1347                 goto done; \
1348         } \
1349         _DEBUG_BASIC_INFO(pinfo, "pathinfo(" #tree ")"); \
1350 } while (0)
1351 #define GET_INFO_FILE(finfo) do { \
1352         _GET_INFO_FILE(cli->tree, finfo); \
1353 } while (0)
1354 #define GET_INFO_FILE2(finfo) do { \
1355         _GET_INFO_FILE(cli2->tree, finfo); \
1356 } while (0)
1357 #define GET_INFO_PATH(pinfo) do { \
1358         _GET_INFO_PATH(cli2->tree, pinfo); \
1359 } while (0)
1360 #define GET_INFO_BOTH(finfo,pinfo) do { \
1361         GET_INFO_FILE(finfo); \
1362         GET_INFO_PATH(pinfo); \
1363         COMPARE_BOTH_TIMES_EQUAL(finfo,pinfo); \
1364 } while (0)
1365
1366 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
1367         NTSTATUS _status; \
1368         union smb_setfileinfo sfinfo; \
1369         sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1370         sfinfo.basic_info.in.file.fnum = tfnum; \
1371         sfinfo.basic_info.in.create_time = 0; \
1372         sfinfo.basic_info.in.access_time = 0; \
1373         unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1374         sfinfo.basic_info.in.change_time = 0; \
1375         sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1376         _status = smb_raw_setfileinfo(tree, &sfinfo); \
1377         if (!NT_STATUS_IS_OK(_status)) { \
1378                 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1379                                nt_errstr(_status)); \
1380                 ret = false; \
1381                 goto done; \
1382         } \
1383 } while (0)
1384 #define SET_INFO_FILE(finfo, wrtime) \
1385         SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
1386
1387 #define SET_INFO_FILE_NS(finfo, wrtime, ns, tree, tfnum) do { \
1388         NTSTATUS _status; \
1389         union smb_setfileinfo sfinfo; \
1390         sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
1391         sfinfo.basic_info.in.file.fnum = tfnum; \
1392         sfinfo.basic_info.in.create_time = 0; \
1393         sfinfo.basic_info.in.access_time = 0; \
1394         unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
1395         sfinfo.basic_info.in.write_time += (ns); \
1396         sfinfo.basic_info.in.change_time = 0; \
1397         sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
1398         _status = smb_raw_setfileinfo(tree, &sfinfo); \
1399         if (!NT_STATUS_IS_OK(_status)) { \
1400                 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
1401                                nt_errstr(_status)); \
1402                 ret = false; \
1403                 goto done; \
1404         } \
1405 } while (0)
1406
1407 static bool test_delayed_write_update3(struct torture_context *tctx,
1408                                        struct smbcli_state *cli,
1409                                        struct smbcli_state *cli2)
1410 {
1411         union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1412         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1413         const char *fname = BASEDIR "\\torture_file3.txt";
1414         int fnum1 = -1;
1415         bool ret = true;
1416         ssize_t written;
1417         struct timeval start;
1418         struct timeval end;
1419         double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1420         //double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 1000000);
1421         //int normal_delay = 1000000;
1422         int normal_delay = 2000000;
1423         double sec = ((double)used_delay) / ((double)normal_delay);
1424         int msec = 1000 * sec;
1425
1426         torture_comment(tctx, "\nRunning test_delayed_write_update3\n");
1427
1428         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1429
1430         torture_comment(tctx, "Open the file handle\n");
1431         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1432         if (fnum1 == -1) {
1433                 ret = false;
1434                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1435                 goto done;
1436         }
1437
1438         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1439         finfo0.basic_info.in.file.fnum = fnum1;
1440         finfo1 = finfo0;
1441         finfo2 = finfo0;
1442         finfo3 = finfo0;
1443         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1444         pinfo0.basic_info.in.file.path = fname;
1445         pinfo1 = pinfo0;
1446         pinfo2 = pinfo0;
1447         pinfo3 = pinfo0;
1448         pinfo4 = pinfo0;
1449
1450         /* get the initial times */
1451         GET_INFO_BOTH(finfo0,pinfo0);
1452
1453         /*
1454          * make sure the write time is updated 2 seconds later
1455          * calcuated from the first write
1456          * (but expect upto 5 seconds extra time for a busy server)
1457          */
1458         start = timeval_current();
1459         end = timeval_add(&start, 7 * sec, 0);
1460         while (!timeval_expired(&end)) {
1461                 /* do a write */
1462                 torture_comment(tctx, "Do a write on the file handle\n");
1463                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1464                 if (written != 1) {
1465                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1466                         ret = false;
1467                         goto done;
1468                 }
1469                 /* get the times after the write */
1470                 GET_INFO_FILE(finfo1);
1471
1472                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1473                         double diff = timeval_elapsed(&start);
1474                         if (diff < (used_delay / (double)1000000)) {
1475                                 torture_result(tctx, TORTURE_FAIL, "111Server updated write_time after %.2f seconds "
1476                                                 "(write time update delay == %.2f) (wrong!)\n",
1477                                                 diff, used_delay / (double)1000000);
1478                                 ret = false;
1479                                 break;
1480                         }
1481
1482                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1483                                         "(correct)\n",
1484                                         diff);
1485                         break;
1486                 }
1487                 smb_msleep(0.5 * msec);
1488         }
1489
1490         GET_INFO_BOTH(finfo1,pinfo1);
1491         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1492
1493         /* sure any further write doesn't update the write time */
1494         start = timeval_current();
1495         end = timeval_add(&start, 15 * sec, 0);
1496         while (!timeval_expired(&end)) {
1497                 /* do a write */
1498                 torture_comment(tctx, "Do a write on the file handle\n");
1499                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1500                 if (written != 1) {
1501                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1502                         ret = false;
1503                         goto done;
1504                 }
1505                 /* get the times after the write */
1506                 GET_INFO_BOTH(finfo2,pinfo2);
1507
1508                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1509                         double diff = timeval_elapsed(&start);
1510                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1511                                         "(wrong!)\n",
1512                                         diff);
1513                         ret = false;
1514                         break;
1515                 }
1516                 smb_msleep(1 * msec);
1517         }
1518
1519         GET_INFO_BOTH(finfo2,pinfo2);
1520         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1521         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1522                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1523         }
1524
1525         /* sleep */
1526         smb_msleep(5 * msec);
1527
1528         GET_INFO_BOTH(finfo3,pinfo3);
1529         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1530
1531         /*
1532          * the close updates the write time to the time of the close
1533          * and not to the time of the last write!
1534          */
1535         torture_comment(tctx, "Close the file handle\n");
1536         smbcli_close(cli->tree, fnum1);
1537         fnum1 = -1;
1538
1539         GET_INFO_PATH(pinfo4);
1540         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1541
1542         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1543                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1544         }
1545
1546  done:
1547         if (fnum1 != -1)
1548                 smbcli_close(cli->tree, fnum1);
1549         smbcli_unlink(cli->tree, fname);
1550         smbcli_deltree(cli->tree, BASEDIR);
1551
1552         return ret;
1553 }
1554
1555 /*
1556  * Show that a truncate write always updates the write time even
1557  * if an initial write has already updated the write time.
1558  */
1559
1560 static bool test_delayed_write_update3a(struct torture_context *tctx,
1561                                         struct smbcli_state *cli,
1562                                         struct smbcli_state *cli2)
1563 {
1564         union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1565         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1566         const char *fname = BASEDIR "\\torture_file3a.txt";
1567         int fnum1 = -1;
1568         bool ret = true;
1569         ssize_t written;
1570         int i;
1571         struct timeval start;
1572         struct timeval end;
1573         double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1574         int normal_delay = 2000000;
1575         double sec = ((double)used_delay) / ((double)normal_delay);
1576         int msec = 1000 * sec;
1577
1578         torture_comment(tctx, "\nRunning test_delayed_write_update3a\n");
1579
1580         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1581
1582         torture_comment(tctx, "Open the file handle\n");
1583         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1584         if (fnum1 == -1) {
1585                 ret = false;
1586                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1587                 goto done;
1588         }
1589
1590         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1591         finfo0.basic_info.in.file.fnum = fnum1;
1592         finfo1 = finfo0;
1593         finfo2 = finfo0;
1594         finfo3 = finfo0;
1595         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1596         pinfo0.basic_info.in.file.path = fname;
1597         pinfo1 = pinfo0;
1598         pinfo2 = pinfo0;
1599         pinfo3 = pinfo0;
1600         pinfo4 = pinfo0;
1601
1602         /* get the initial times */
1603         GET_INFO_BOTH(finfo0,pinfo0);
1604
1605         /*
1606          * sleep some time, to demonstrate the handling of write times
1607          * doesn't depend on the time since the open
1608          */
1609         smb_msleep(5 * msec);
1610
1611         /* get the initial times */
1612         GET_INFO_BOTH(finfo1,pinfo1);
1613         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1614
1615         /*
1616          * make sure the write time is updated 2 seconds later
1617          * calcuated from the first write
1618          * (but expect upto 5 seconds extra time for a busy server)
1619          */
1620         start = timeval_current();
1621         end = timeval_add(&start, 7 * sec, 0);
1622         while (!timeval_expired(&end)) {
1623                 /* do a write */
1624                 torture_comment(tctx, "Do a write on the file handle\n");
1625                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1626                 if (written != 1) {
1627                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1628                         ret = false;
1629                         goto done;
1630                 }
1631                 /* get the times after the write */
1632                 GET_INFO_FILE(finfo1);
1633
1634                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1635                         double diff = timeval_elapsed(&start);
1636                         if (diff < (used_delay / (double)1000000)) {
1637                                 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1638                                                 "(1sec == %.2f) (wrong!)\n",
1639                                                 diff, sec);
1640                                 ret = false;
1641                                 break;
1642                         }
1643
1644                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1645                                         "(correct)\n",
1646                                         diff);
1647                         break;
1648                 }
1649                 smb_msleep(0.5 * msec);
1650         }
1651
1652         GET_INFO_BOTH(finfo1,pinfo1);
1653         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1654
1655         smb_msleep(3 * msec);
1656
1657         /*
1658          * demonstrate that a truncate write always
1659          * updates the write time immediately
1660          */
1661         for (i=0; i < 3; i++) {
1662                 smb_msleep(2 * msec);
1663                 /* do a write */
1664                 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1665                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
1666                 if (written != 0) {
1667                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1668                         ret = false;
1669                         goto done;
1670                 }
1671                 /* get the times after the write */
1672                 GET_INFO_BOTH(finfo2,pinfo2);
1673                 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1674                 finfo1 = finfo2;
1675         }
1676
1677         smb_msleep(3 * msec);
1678
1679         /* sure any further write doesn't update the write time */
1680         start = timeval_current();
1681         end = timeval_add(&start, 15 * sec, 0);
1682         while (!timeval_expired(&end)) {
1683                 /* do a write */
1684                 torture_comment(tctx, "Do a write on the file handle\n");
1685                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1686                 if (written != 1) {
1687                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1688                         ret = false;
1689                         goto done;
1690                 }
1691                 /* get the times after the write */
1692                 GET_INFO_BOTH(finfo2,pinfo2);
1693
1694                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1695                         double diff = timeval_elapsed(&start);
1696                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1697                                         "(wrong!)\n",
1698                                         diff);
1699                         ret = false;
1700                         break;
1701                 }
1702                 smb_msleep(1 * msec);
1703         }
1704
1705         GET_INFO_BOTH(finfo2,pinfo2);
1706         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1707         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1708                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1709         }
1710
1711         /* sleep */
1712         smb_msleep(3 * msec);
1713
1714         /* get the initial times */
1715         GET_INFO_BOTH(finfo1,pinfo1);
1716         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
1717
1718         /*
1719          * demonstrate that a truncate write always
1720          * updates the write time immediately
1721          */
1722         for (i=0; i < 3; i++) {
1723                 smb_msleep(2 * msec);
1724                 /* do a write */
1725                 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1726                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
1727                 if (written != 0) {
1728                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
1729                         ret = false;
1730                         goto done;
1731                 }
1732                 /* get the times after the write */
1733                 GET_INFO_BOTH(finfo2,pinfo2);
1734                 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1735                 finfo1 = finfo2;
1736         }
1737
1738         /* sleep */
1739         smb_msleep(3 * msec);
1740
1741         GET_INFO_BOTH(finfo3,pinfo3);
1742         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1743
1744         /*
1745          * the close doesn't update the write time
1746          */
1747         torture_comment(tctx, "Close the file handle\n");
1748         smbcli_close(cli->tree, fnum1);
1749         fnum1 = -1;
1750
1751         GET_INFO_PATH(pinfo4);
1752         COMPARE_WRITE_TIME_EQUAL(pinfo4, pinfo3);
1753
1754         if (pinfo4.basic_info.out.write_time == pinfo3.basic_info.out.write_time) {
1755                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1756         }
1757
1758  done:
1759         if (fnum1 != -1)
1760                 smbcli_close(cli->tree, fnum1);
1761         smbcli_unlink(cli->tree, fname);
1762         smbcli_deltree(cli->tree, BASEDIR);
1763
1764         return ret;
1765 }
1766
1767 /*
1768  * Show a close after write updates the write timestamp to
1769  * the close time, not the last write time.
1770  */
1771
1772 static bool test_delayed_write_update3b(struct torture_context *tctx,
1773                                         struct smbcli_state *cli,
1774                                         struct smbcli_state *cli2)
1775 {
1776         union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1777         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1778         const char *fname = BASEDIR "\\torture_file3b.txt";
1779         int fnum1 = -1;
1780         bool ret = true;
1781         ssize_t written;
1782         struct timeval start;
1783         struct timeval end;
1784         double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1785         int normal_delay = 2000000;
1786         double sec = ((double)used_delay) / ((double)normal_delay);
1787         int msec = 1000 * sec;
1788
1789         torture_comment(tctx, "\nRunning test_delayed_write_update3b\n");
1790
1791         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1792
1793         torture_comment(tctx, "Open the file handle\n");
1794         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1795         if (fnum1 == -1) {
1796                 ret = false;
1797                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1798                 goto done;
1799         }
1800
1801         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1802         finfo0.basic_info.in.file.fnum = fnum1;
1803         finfo1 = finfo0;
1804         finfo2 = finfo0;
1805         finfo3 = finfo0;
1806         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1807         pinfo0.basic_info.in.file.path = fname;
1808         pinfo1 = pinfo0;
1809         pinfo2 = pinfo0;
1810         pinfo3 = pinfo0;
1811         pinfo4 = pinfo0;
1812
1813         /* get the initial times */
1814         GET_INFO_BOTH(finfo0,pinfo0);
1815
1816         /*
1817          * sleep some time, to demonstrate the handling of write times
1818          * doesn't depend on the time since the open
1819          */
1820         smb_msleep(5 * msec);
1821
1822         /* get the initial times */
1823         GET_INFO_BOTH(finfo1,pinfo1);
1824         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1825
1826         /*
1827          * make sure the write time is updated 2 seconds later
1828          * calcuated from the first write
1829          * (but expect upto 5 seconds extra time for a busy server)
1830          */
1831         start = timeval_current();
1832         end = timeval_add(&start, 7 * sec, 0);
1833         while (!timeval_expired(&end)) {
1834                 /* do a write */
1835                 torture_comment(tctx, "Do a write on the file handle\n");
1836                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1837                 if (written != 1) {
1838                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1839                         ret = false;
1840                         goto done;
1841                 }
1842                 /* get the times after the write */
1843                 GET_INFO_FILE(finfo1);
1844
1845                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1846                         double diff = timeval_elapsed(&start);
1847                         if (diff < (used_delay / (double)1000000)) {
1848                                 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
1849                                                 "(expected > %.2f) (wrong!)\n",
1850                                                 diff, used_delay / (double)1000000);
1851                                 ret = false;
1852                                 break;
1853                         }
1854
1855                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
1856                                         "(write time update delay == %.2f) (correct)\n",
1857                                         diff, used_delay / (double)1000000);
1858                         break;
1859                 }
1860                 smb_msleep(0.5 * msec);
1861         }
1862
1863         GET_INFO_BOTH(finfo1,pinfo1);
1864         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1865
1866         /* sure any further write doesn't update the write time */
1867         start = timeval_current();
1868         end = timeval_add(&start, 15 * sec, 0);
1869         while (!timeval_expired(&end)) {
1870                 /* do a write */
1871                 torture_comment(tctx, "Do a write on the file handle\n");
1872                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1873                 if (written != 1) {
1874                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
1875                         ret = false;
1876                         goto done;
1877                 }
1878                 /* get the times after the write */
1879                 GET_INFO_BOTH(finfo2,pinfo2);
1880
1881                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
1882                         double diff = timeval_elapsed(&start);
1883                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
1884                                         "(wrong!)\n",
1885                                         diff);
1886                         ret = false;
1887                         break;
1888                 }
1889                 smb_msleep(1 * msec);
1890         }
1891
1892         GET_INFO_BOTH(finfo2,pinfo2);
1893         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1894         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1895                 torture_comment(tctx, "Server did not update write_time (correct)\n");
1896         }
1897
1898         /* sleep */
1899         smb_msleep(5 * msec);
1900
1901         GET_INFO_BOTH(finfo3,pinfo3);
1902         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1903
1904         /*
1905          * the close updates the write time to the time of the close
1906          * and not to the time of the last write!
1907          */
1908         torture_comment(tctx, "Close the file handle\n");
1909         smbcli_close(cli->tree, fnum1);
1910         fnum1 = -1;
1911
1912         GET_INFO_PATH(pinfo4);
1913         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1914
1915         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1916                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1917         }
1918
1919  done:
1920         if (fnum1 != -1)
1921                 smbcli_close(cli->tree, fnum1);
1922         smbcli_unlink(cli->tree, fname);
1923         smbcli_deltree(cli->tree, BASEDIR);
1924
1925         return ret;
1926 }
1927
1928 /*
1929  * Check that a write after a truncate write doesn't update
1930  * the timestamp, but a truncate write after a write does.
1931  * Also prove that a close after a truncate write updates the
1932  * timestamp to current, not the time of last write.
1933  */
1934
1935 static bool test_delayed_write_update3c(struct torture_context *tctx,
1936                                         struct smbcli_state *cli,
1937                                         struct smbcli_state *cli2)
1938 {
1939         union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1940         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1941         const char *fname = BASEDIR "\\torture_file3c.txt";
1942         int fnum1 = -1;
1943         bool ret = true;
1944         ssize_t written;
1945         int i;
1946         struct timeval start;
1947         struct timeval end;
1948         double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1949         int normal_delay = 2000000;
1950         double sec = ((double)used_delay) / ((double)normal_delay);
1951         int msec = 1000 * sec;
1952
1953         torture_comment(tctx, "\nRunning test_delayed_write_update3c\n");
1954
1955         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1956
1957         torture_comment(tctx, "Open the file handle\n");
1958         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1959         if (fnum1 == -1) {
1960                 ret = false;
1961                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
1962                 goto done;
1963         }
1964
1965         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1966         finfo0.basic_info.in.file.fnum = fnum1;
1967         finfo1 = finfo0;
1968         finfo2 = finfo0;
1969         finfo3 = finfo0;
1970         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1971         pinfo0.basic_info.in.file.path = fname;
1972         pinfo1 = pinfo0;
1973         pinfo2 = pinfo0;
1974         pinfo3 = pinfo0;
1975         pinfo4 = pinfo0;
1976
1977         /* get the initial times */
1978         GET_INFO_BOTH(finfo0,pinfo0);
1979
1980         /*
1981          * sleep some time, to demonstrate the handling of write times
1982          * doesn't depend on the time since the open
1983          */
1984         smb_msleep(5 * msec);
1985
1986         /* get the initial times */
1987         GET_INFO_BOTH(finfo1,pinfo1);
1988         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1989
1990         /*
1991          * demonstrate that a truncate write always
1992          * updates the write time immediately
1993          */
1994         for (i=0; i < 3; i++) {
1995                 smb_msleep(2 * msec);
1996                 /* do a write */
1997                 torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1998                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
1999                 if (written != 0) {
2000                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2001                         ret = false;
2002                         goto done;
2003                 }
2004                 /* get the times after the write */
2005                 GET_INFO_BOTH(finfo2,pinfo2);
2006                 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2007                 finfo1 = finfo2;
2008         }
2009
2010         start = timeval_current();
2011         end = timeval_add(&start, 7 * sec, 0);
2012         while (!timeval_expired(&end)) {
2013                 /* do a write */
2014                 torture_comment(tctx, "Do a write on the file handle\n");
2015                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2016                 if (written != 1) {
2017                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2018                         ret = false;
2019                         goto done;
2020                 }
2021                 /* get the times after the write */
2022                 GET_INFO_FILE(finfo2);
2023
2024                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2025                         double diff = timeval_elapsed(&start);
2026                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2027                                         "(wrong!)\n",
2028                                         diff);
2029                         ret = false;
2030                         break;
2031                 }
2032                 smb_msleep(1 * msec);
2033         }
2034
2035         GET_INFO_BOTH(finfo2,pinfo2);
2036         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2037         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2038                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2039         }
2040
2041         /* sleep */
2042         smb_msleep(5 * msec);
2043
2044         /* get the initial times */
2045         GET_INFO_BOTH(finfo1,pinfo1);
2046         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
2047
2048         /*
2049          * demonstrate that a truncate write always
2050          * updates the write time immediately
2051          */
2052         for (i=0; i < 3; i++) {
2053                 smb_msleep(2 * msec);
2054                 /* do a write */
2055                 torture_comment(tctx, "Do a truncate write [%d] on the file handle\n", i);
2056                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2057                 if (written != 0) {
2058                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 0", (int)written);
2059                         ret = false;
2060                         goto done;
2061                 }
2062                 /* get the times after the write */
2063                 GET_INFO_BOTH(finfo2,pinfo2);
2064                 COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2065                 finfo1 = finfo2;
2066         }
2067
2068         /* sleep */
2069         smb_msleep(5 * msec);
2070
2071         GET_INFO_BOTH(finfo2,pinfo2);
2072         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2073
2074         /* sure any further write doesn't update the write time */
2075         start = timeval_current();
2076         end = timeval_add(&start, 15 * sec, 0);
2077         while (!timeval_expired(&end)) {
2078                 /* do a write */
2079                 torture_comment(tctx, "Do a write on the file handle\n");
2080                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2081                 if (written != 1) {
2082                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2083                         ret = false;
2084                         goto done;
2085                 }
2086                 /* get the times after the write */
2087                 GET_INFO_BOTH(finfo2,pinfo2);
2088
2089                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2090                         double diff = timeval_elapsed(&start);
2091                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2092                                         "(wrong!)\n",
2093                                         diff);
2094                         ret = false;
2095                         break;
2096                 }
2097                 smb_msleep(1 * msec);
2098         }
2099
2100         GET_INFO_BOTH(finfo2,pinfo2);
2101         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2102         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2103                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2104         }
2105
2106         /* sleep */
2107         smb_msleep(5 * msec);
2108
2109         GET_INFO_BOTH(finfo3,pinfo3);
2110         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2111
2112         /*
2113          * the close updates the write time to the time of the close
2114          * and not to the time of the last write!
2115          */
2116         torture_comment(tctx, "Close the file handle\n");
2117         smbcli_close(cli->tree, fnum1);
2118         fnum1 = -1;
2119
2120         GET_INFO_PATH(pinfo4);
2121         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2122
2123         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2124                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2125         }
2126
2127  done:
2128         if (fnum1 != -1)
2129                 smbcli_close(cli->tree, fnum1);
2130         smbcli_unlink(cli->tree, fname);
2131         smbcli_deltree(cli->tree, BASEDIR);
2132
2133         return ret;
2134 }
2135
2136 /*
2137  * Show only the first write updates the timestamp, and a close
2138  * after writes updates to current (I think this is the same
2139  * as test 3b. JRA).
2140  */
2141
2142 static bool test_delayed_write_update4(struct torture_context *tctx,
2143                                        struct smbcli_state *cli,
2144                                        struct smbcli_state *cli2)
2145 {
2146         union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
2147         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
2148         const char *fname = BASEDIR "\\torture_file4.txt";
2149         int fnum1 = -1;
2150         bool ret = true;
2151         ssize_t written;
2152         struct timeval start;
2153         struct timeval end;
2154         double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2155         int normal_delay = 2000000;
2156         double sec = ((double)used_delay) / ((double)normal_delay);
2157         int msec = 1000 * sec;
2158
2159         torture_comment(tctx, "\nRunning test_delayed_write_update4\n");
2160
2161         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2162
2163         torture_comment(tctx, "Open the file handle\n");
2164         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2165         if (fnum1 == -1) {
2166                 ret = false;
2167                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2168                 goto done;
2169         }
2170
2171         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2172         finfo0.basic_info.in.file.fnum = fnum1;
2173         finfo1 = finfo0;
2174         finfo2 = finfo0;
2175         finfo3 = finfo0;
2176         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2177         pinfo0.basic_info.in.file.path = fname;
2178         pinfo1 = pinfo0;
2179         pinfo2 = pinfo0;
2180         pinfo3 = pinfo0;
2181         pinfo4 = pinfo0;
2182
2183         /* get the initial times */
2184         GET_INFO_BOTH(finfo0,pinfo0);
2185
2186         /* sleep a bit */
2187         smb_msleep(5 * msec);
2188
2189         /* do a write */
2190         torture_comment(tctx, "Do a write on the file handle\n");
2191         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2192         if (written != 1) {
2193                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2194                 ret = false;
2195                 goto done;
2196         }
2197
2198         GET_INFO_BOTH(finfo1,pinfo1);
2199         COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
2200
2201         /*
2202          * make sure the write time is updated 2 seconds later
2203          * calcuated from the first write
2204          * (but expect upto 3 seconds extra time for a busy server)
2205          */
2206         start = timeval_current();
2207         end = timeval_add(&start, 5 * sec, 0);
2208         while (!timeval_expired(&end)) {
2209                 /* get the times after the first write */
2210                 GET_INFO_FILE(finfo1);
2211
2212                 if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
2213                         double diff = timeval_elapsed(&start);
2214                         if (diff < (used_delay / (double)1000000)) {
2215                                 torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds"
2216                                                 "(expected > %.2f) (wrong!)\n",
2217                                                 diff, used_delay / (double)1000000);
2218                                 ret = false;
2219                                 break;
2220                         }
2221
2222                         torture_comment(tctx, "Server updated write_time after %.2f seconds "
2223                                         "(write time update delay == %.2f) (correct)\n",
2224                                         diff, used_delay / (double)1000000);
2225                         break;
2226                 }
2227                 smb_msleep(0.5 * msec);
2228         }
2229
2230         GET_INFO_BOTH(finfo1,pinfo1);
2231         COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
2232
2233         /* sure any further write doesn't update the write time */
2234         start = timeval_current();
2235         end = timeval_add(&start, 15 * sec, 0);
2236         while (!timeval_expired(&end)) {
2237                 /* do a write */
2238                 torture_comment(tctx, "Do a write on the file handle\n");
2239                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2240                 if (written != 1) {
2241                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2242                         ret = false;
2243                         goto done;
2244                 }
2245                 /* get the times after the write */
2246                 GET_INFO_BOTH(finfo2,pinfo2);
2247
2248                 if (finfo2.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
2249                         double diff = timeval_elapsed(&start);
2250                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2251                                         "(wrong!)\n",
2252                                         diff);
2253                         ret = false;
2254                         break;
2255                 }
2256                 smb_msleep(1 * msec);
2257         }
2258
2259         GET_INFO_BOTH(finfo2,pinfo2);
2260         COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2261         if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2262                 torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
2263         }
2264
2265         /* sleep */
2266         smb_msleep(5 * msec);
2267
2268         GET_INFO_BOTH(finfo3,pinfo3);
2269         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2270
2271         /*
2272          * the close updates the write time to the time of the close
2273          * and not to the time of the last write!
2274          */
2275         torture_comment(tctx, "Close the file handle\n");
2276         smbcli_close(cli->tree, fnum1);
2277         fnum1 = -1;
2278
2279         GET_INFO_PATH(pinfo4);
2280         COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2281
2282         if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2283                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2284         }
2285
2286  done:
2287         if (fnum1 != -1)
2288                 smbcli_close(cli->tree, fnum1);
2289         smbcli_unlink(cli->tree, fname);
2290         smbcli_deltree(cli->tree, BASEDIR);
2291
2292         return ret;
2293 }
2294
2295 /*
2296  * Show writes and closes have no effect on updating times once a SETWRITETIME is done.
2297  */
2298
2299 static bool test_delayed_write_update5(struct torture_context *tctx,
2300                                        struct smbcli_state *cli,
2301                                        struct smbcli_state *cli2)
2302 {
2303         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2304         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2305         const char *fname = BASEDIR "\\torture_file5.txt";
2306         int fnum1 = -1;
2307         bool ret = true;
2308         ssize_t written;
2309         struct timeval start;
2310         struct timeval end;
2311         double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2312         int normal_delay = 2000000;
2313         double sec = ((double)used_delay) / ((double)normal_delay);
2314         int msec = 1000 * sec;
2315
2316         torture_comment(tctx, "\nRunning test_delayed_write_update5\n");
2317
2318         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2319
2320         torture_comment(tctx, "Open the file handle\n");
2321         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2322         if (fnum1 == -1) {
2323                 ret = false;
2324                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2325                 goto done;
2326         }
2327
2328         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2329         finfo0.basic_info.in.file.fnum = fnum1;
2330         finfo1 = finfo0;
2331         finfo2 = finfo0;
2332         finfo3 = finfo0;
2333         finfo4 = finfo0;
2334         finfo5 = finfo0;
2335         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2336         pinfo0.basic_info.in.file.path = fname;
2337         pinfo1 = pinfo0;
2338         pinfo2 = pinfo0;
2339         pinfo3 = pinfo0;
2340         pinfo4 = pinfo0;
2341         pinfo5 = pinfo0;
2342         pinfo6 = pinfo0;
2343
2344         /* get the initial times */
2345         GET_INFO_BOTH(finfo0,pinfo0);
2346
2347         /* do a write */
2348         torture_comment(tctx, "Do a write on the file handle\n");
2349         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2350         if (written != 1) {
2351                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2352                 ret = false;
2353                 goto done;
2354         }
2355
2356         GET_INFO_BOTH(finfo1,pinfo1);
2357         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2358
2359         torture_comment(tctx, "Set write time in the future on the file handle\n");
2360         SET_INFO_FILE(finfo0, time(NULL) + 86400);
2361         GET_INFO_BOTH(finfo2,pinfo2);
2362         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2363
2364         torture_comment(tctx, "Set write time in the past on the file handle\n");
2365         SET_INFO_FILE(finfo0, time(NULL) - 86400);
2366         GET_INFO_BOTH(finfo2,pinfo2);
2367         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2368
2369         /* make sure the 2 second delay from the first write are canceled */
2370         start = timeval_current();
2371         end = timeval_add(&start, 15 * sec, 0);
2372         while (!timeval_expired(&end)) {
2373
2374                 /* get the times after the first write */
2375                 GET_INFO_BOTH(finfo3,pinfo3);
2376
2377                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2378                         double diff = timeval_elapsed(&start);
2379                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2380                                         "(wrong!)\n",
2381                                         diff);
2382                         ret = false;
2383                         break;
2384                 }
2385                 smb_msleep(1 * msec);
2386         }
2387
2388         GET_INFO_BOTH(finfo3,pinfo3);
2389         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2390         if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2391                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2392         }
2393
2394         /* sure any further write doesn't update the write time */
2395         start = timeval_current();
2396         end = timeval_add(&start, 15 * sec, 0);
2397         while (!timeval_expired(&end)) {
2398                 /* do a write */
2399                 torture_comment(tctx, "Do a write on the file handle\n");
2400                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2401                 if (written != 1) {
2402                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2403                         ret = false;
2404                         goto done;
2405                 }
2406                 /* get the times after the write */
2407                 GET_INFO_BOTH(finfo4,pinfo4);
2408
2409                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2410                         double diff = timeval_elapsed(&start);
2411                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2412                                         "(wrong!)\n",
2413                                         diff);
2414                         ret = false;
2415                         break;
2416                 }
2417                 smb_msleep(1 * msec);
2418         }
2419
2420         GET_INFO_BOTH(finfo4,pinfo4);
2421         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2422         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2423                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2424         }
2425
2426         /* sleep */
2427         smb_msleep(5 * msec);
2428
2429         GET_INFO_BOTH(finfo5,pinfo5);
2430         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2431
2432         /*
2433          * the close doesn't update the write time
2434          */
2435         torture_comment(tctx, "Close the file handle\n");
2436         smbcli_close(cli->tree, fnum1);
2437         fnum1 = -1;
2438
2439         GET_INFO_PATH(pinfo6);
2440         COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2441
2442         if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2443                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2444         }
2445
2446  done:
2447         if (fnum1 != -1)
2448                 smbcli_close(cli->tree, fnum1);
2449         smbcli_unlink(cli->tree, fname);
2450         smbcli_deltree(cli->tree, BASEDIR);
2451
2452         return ret;
2453 }
2454
2455 /*
2456  * Show truncate writes and closes have no effect on updating times once a SETWRITETIME is done.
2457  */
2458
2459 static bool test_delayed_write_update5b(struct torture_context *tctx,
2460                                         struct smbcli_state *cli,
2461                                         struct smbcli_state *cli2)
2462 {
2463         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2464         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2465         const char *fname = BASEDIR "\\torture_fileb.txt";
2466         int fnum1 = -1;
2467         bool ret = true;
2468         ssize_t written;
2469         struct timeval start;
2470         struct timeval end;
2471         double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2472         int normal_delay = 2000000;
2473         double sec = ((double)used_delay) / ((double)normal_delay);
2474         int msec = 1000 * sec;
2475
2476         torture_comment(tctx, "\nRunning test_delayed_write_update5b\n");
2477
2478         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2479
2480         torture_comment(tctx, "Open the file handle\n");
2481         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2482         if (fnum1 == -1) {
2483                 ret = false;
2484                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2485                 goto done;
2486         }
2487
2488         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2489         finfo0.basic_info.in.file.fnum = fnum1;
2490         finfo1 = finfo0;
2491         finfo2 = finfo0;
2492         finfo3 = finfo0;
2493         finfo4 = finfo0;
2494         finfo5 = finfo0;
2495         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2496         pinfo0.basic_info.in.file.path = fname;
2497         pinfo1 = pinfo0;
2498         pinfo2 = pinfo0;
2499         pinfo3 = pinfo0;
2500         pinfo4 = pinfo0;
2501         pinfo5 = pinfo0;
2502         pinfo6 = pinfo0;
2503
2504         /* get the initial times */
2505         GET_INFO_BOTH(finfo0,pinfo0);
2506
2507         /* do a write */
2508         torture_comment(tctx, "Do a write on the file handle\n");
2509         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2510         if (written != 1) {
2511                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2512                 ret = false;
2513                 goto done;
2514         }
2515
2516         GET_INFO_BOTH(finfo1,pinfo1);
2517         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2518
2519         torture_comment(tctx, "Set write time in the future on the file handle\n");
2520         SET_INFO_FILE(finfo0, time(NULL) + 86400);
2521         GET_INFO_BOTH(finfo2,pinfo2);
2522         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2523
2524         torture_comment(tctx, "Set write time in the past on the file handle\n");
2525         SET_INFO_FILE(finfo0, time(NULL) - 86400);
2526         GET_INFO_BOTH(finfo2,pinfo2);
2527         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2528
2529         /* make sure the 2 second delay from the first write are canceled */
2530         start = timeval_current();
2531         end = timeval_add(&start, 15 * sec, 0);
2532         while (!timeval_expired(&end)) {
2533
2534                 /* get the times after the first write */
2535                 GET_INFO_BOTH(finfo3,pinfo3);
2536
2537                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2538                         double diff = timeval_elapsed(&start);
2539                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2540                                         "(wrong!)\n",
2541                                         diff);
2542                         ret = false;
2543                         break;
2544                 }
2545                 smb_msleep(1 * msec);
2546         }
2547
2548         GET_INFO_BOTH(finfo3,pinfo3);
2549         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2550         if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2551                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2552         }
2553
2554         /* Do any further write (truncates) update the write time ? */
2555         start = timeval_current();
2556         end = timeval_add(&start, 15 * sec, 0);
2557         while (!timeval_expired(&end)) {
2558                 /* do a write */
2559                 torture_comment(tctx, "Do a truncate write on the file handle\n");
2560                 written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
2561                 if (written != 0) {
2562                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2563                         ret = false;
2564                         goto done;
2565                 }
2566                 /* get the times after the write */
2567                 GET_INFO_BOTH(finfo4,pinfo4);
2568
2569                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2570                         double diff = timeval_elapsed(&start);
2571                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2572                                         "(wrong!)\n",
2573                                         diff);
2574                         ret = false;
2575                         break;
2576                 }
2577                 smb_msleep(1 * msec);
2578         }
2579
2580         GET_INFO_BOTH(finfo4,pinfo4);
2581         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2582         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2583                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2584         }
2585
2586         /* sleep */
2587         smb_msleep(5 * msec);
2588
2589         GET_INFO_BOTH(finfo5,pinfo5);
2590         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2591
2592         /*
2593          * the close doesn't update the write time
2594          */
2595         torture_comment(tctx, "Close the file handle\n");
2596         smbcli_close(cli->tree, fnum1);
2597         fnum1 = -1;
2598
2599         GET_INFO_PATH(pinfo6);
2600         COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2601
2602         if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2603                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2604         }
2605
2606  done:
2607         if (fnum1 != -1)
2608                 smbcli_close(cli->tree, fnum1);
2609         smbcli_unlink(cli->tree, fname);
2610         smbcli_deltree(cli->tree, BASEDIR);
2611
2612         return ret;
2613 }
2614
2615 /*
2616  * Open 2 handles on a file. Write one one and then set the
2617  * WRITE TIME explicitly on the other. Ensure the write time
2618  * update is cancelled. Ensure the write time is updated to
2619  * the close time when the non-explicit set handle is closed.
2620  *
2621  */
2622
2623 static bool test_delayed_write_update6(struct torture_context *tctx,
2624                                        struct smbcli_state *cli,
2625                                        struct smbcli_state *cli2)
2626 {
2627         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2628         union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
2629         const char *fname = BASEDIR "\\torture_file6.txt";
2630         int fnum1 = -1;
2631         int fnum2 = -1;
2632         bool ret = true;
2633         ssize_t written;
2634         struct timeval start;
2635         struct timeval end;
2636         double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2637         int normal_delay = 2000000;
2638         double sec = ((double)used_delay) / ((double)normal_delay);
2639         int msec = 1000 * sec;
2640         bool first = true;
2641
2642         torture_comment(tctx, "\nRunning test_delayed_write_update6\n");
2643
2644         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2645 again:
2646         torture_comment(tctx, "Open the file handle\n");
2647         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2648         if (fnum1 == -1) {
2649                 ret = false;
2650                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2651                 goto done;
2652         }
2653
2654         if (fnum2 == -1) {
2655                 torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
2656                 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2657                 if (fnum2 == -1) {
2658                         ret = false;
2659                         torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
2660                         goto done;
2661                 }
2662         }
2663
2664         finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2665         finfo0.basic_info.in.file.fnum = fnum1;
2666         finfo1 = finfo0;
2667         finfo2 = finfo0;
2668         finfo3 = finfo0;
2669         finfo4 = finfo0;
2670         finfo5 = finfo0;
2671         pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2672         pinfo0.basic_info.in.file.path = fname;
2673         pinfo1 = pinfo0;
2674         pinfo2 = pinfo0;
2675         pinfo3 = pinfo0;
2676         pinfo4 = pinfo0;
2677         pinfo5 = pinfo0;
2678         pinfo6 = pinfo0;
2679         pinfo7 = pinfo0;
2680
2681         /* get the initial times */
2682         GET_INFO_BOTH(finfo0,pinfo0);
2683
2684         /* do a write */
2685         torture_comment(tctx, "Do a write on the file handle\n");
2686         written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2687         if (written != 1) {
2688                 torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2689                 ret = false;
2690                 goto done;
2691         }
2692
2693         GET_INFO_BOTH(finfo1,pinfo1);
2694         COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2695
2696         torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
2697         SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
2698         GET_INFO_BOTH(finfo2,pinfo2);
2699         COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2700
2701         torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
2702         SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
2703         GET_INFO_BOTH(finfo2,pinfo2);
2704         COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2705
2706         /* make sure the 2 second delay from the first write are canceled */
2707         start = timeval_current();
2708         end = timeval_add(&start, 10 * sec, 0);
2709         while (!timeval_expired(&end)) {
2710
2711                 /* get the times after the first write */
2712                 GET_INFO_BOTH(finfo3,pinfo3);
2713
2714                 if (finfo3.basic_info.out.write_time > finfo2.basic_info.out.write_time) {
2715                         double diff = timeval_elapsed(&start);
2716                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2717                                         "(wrong!)\n",
2718                                         diff);
2719                         ret = false;
2720                         break;
2721                 }
2722                 smb_msleep(1 * msec);
2723         }
2724
2725         GET_INFO_BOTH(finfo3,pinfo3);
2726         COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2727         if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2728                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2729         }
2730
2731         /* sure any further write doesn't update the write time */
2732         start = timeval_current();
2733         end = timeval_add(&start, 10 * sec, 0);
2734         while (!timeval_expired(&end)) {
2735                 /* do a write */
2736                 torture_comment(tctx, "Do a write on the file handle\n");
2737                 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2738                 if (written != 1) {
2739                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2740                         ret = false;
2741                         goto done;
2742                 }
2743                 /* get the times after the write */
2744                 GET_INFO_BOTH(finfo4,pinfo4);
2745
2746                 if (finfo4.basic_info.out.write_time > finfo3.basic_info.out.write_time) {
2747                         double diff = timeval_elapsed(&start);
2748                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2749                                         "(wrong!)\n",
2750                                         diff);
2751                         ret = false;
2752                         break;
2753                 }
2754                 smb_msleep(1 * msec);
2755         }
2756
2757         GET_INFO_BOTH(finfo4,pinfo4);
2758         COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2759         if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2760                 torture_comment(tctx, "Server did not update write_time (correct)\n");
2761         }
2762
2763         /* sleep */
2764         smb_msleep(5 * msec);
2765
2766         GET_INFO_BOTH(finfo5,pinfo5);
2767         COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2768
2769         /*
2770          * the close updates the write time to the time of the close
2771          * as the write time was set on the 2nd handle
2772          */
2773         torture_comment(tctx, "Close the file handle\n");
2774         smbcli_close(cli->tree, fnum1);
2775         fnum1 = -1;
2776
2777         GET_INFO_PATH(pinfo6);
2778         COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
2779
2780         if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
2781                 torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2782         }
2783
2784         /* See what the second write handle thinks the time is ? */
2785         finfo5.basic_info.in.file.fnum = fnum2;
2786         GET_INFO_FILE2(finfo5);
2787         COMPARE_WRITE_TIME_EQUAL(finfo5, pinfo6);
2788
2789         /* See if we have lost the sticky write time on handle2 */
2790         smb_msleep(3 * msec);
2791         torture_comment(tctx, "Have we lost the sticky write time ?\n");
2792
2793         /* Make sure any further normal write doesn't update the write time */
2794         start = timeval_current();
2795         end = timeval_add(&start, 10 * sec, 0);
2796         while (!timeval_expired(&end)) {
2797                 /* do a write */
2798                 torture_comment(tctx, "Do a write on the second file handle\n");
2799                 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
2800                 if (written != 1) {
2801                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2802                         ret = false;
2803                         goto done;
2804                 }
2805                 /* get the times after the write */
2806                 GET_INFO_FILE2(finfo5);
2807                 GET_INFO_PATH(pinfo6);
2808
2809                 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2810                         double diff = timeval_elapsed(&start);
2811                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2812                                         "(wrong!)\n",
2813                                         diff);
2814                         ret = false;
2815                         break;
2816                 }
2817                 smb_msleep(1 * msec);
2818         }
2819
2820         /* What about a truncate write ? */
2821         start = timeval_current();
2822         end = timeval_add(&start, 10 * sec, 0);
2823         while (!timeval_expired(&end)) {
2824                 /* do a write */
2825                 torture_comment(tctx, "Do a truncate write on the second file handle\n");
2826                 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 0);
2827                 if (written != 0) {
2828                         torture_result(tctx, TORTURE_FAIL, __location__": written gave %d - should have been 1", (int)written);
2829                         ret = false;
2830                         goto done;
2831                 }
2832                 /* get the times after the write */
2833                 GET_INFO_FILE2(finfo5);
2834                 GET_INFO_PATH(pinfo6);
2835
2836                 if (finfo5.basic_info.out.write_time > pinfo6.basic_info.out.write_time) {
2837                         double diff = timeval_elapsed(&start);
2838                         torture_result(tctx, TORTURE_FAIL, "Server updated write_time after %.2f seconds "
2839                                         "(wrong!)\n",
2840                                         diff);
2841                         ret = false;
2842                         break;
2843                 }
2844                 smb_msleep(1 * msec);
2845         }
2846
2847
2848         /* keep the 2nd handle open and rerun tests */
2849         if (first) {
2850                 first = false;
2851                 goto again;
2852         }
2853
2854         /*
2855          * closing the 2nd handle will cause no write time update
2856          * as the write time was explicit set on this handle
2857          */
2858         torture_comment(tctx, "Close the 2nd file handle\n");
2859         smbcli_close(cli2->tree, fnum2);
2860         fnum2 = -1;
2861
2862         GET_INFO_PATH(pinfo7);
2863         COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
2864
2865         if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
2866                 torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2867         }
2868
2869  done:
2870         if (fnum1 != -1)
2871                 smbcli_close(cli->tree, fnum1);
2872         if (fnum2 != -1)
2873                 smbcli_close(cli2->tree, fnum2);
2874         smbcli_unlink(cli->tree, fname);
2875         smbcli_deltree(cli->tree, BASEDIR);
2876
2877         return ret;
2878 }
2879
2880 static bool test_delayed_write_update7(struct torture_context *tctx, struct smbcli_state *cli)
2881 {
2882         union smb_open open_parms;
2883         union smb_fileinfo finfo1, finfo2, finfo3;
2884         const char *fname = BASEDIR "\\torture_file7.txt";
2885         NTSTATUS status;
2886         int fnum1 = -1;
2887         bool ret = true;
2888         TALLOC_CTX *mem_ctx; 
2889
2890         torture_comment(tctx, "\nRunning test_delayed_write_update7 (timestamp resolution test)\n");
2891
2892         mem_ctx = talloc_init("test_delayed_write_update7");
2893         if (!mem_ctx) return false;
2894
2895         ZERO_STRUCT(finfo1);
2896         ZERO_STRUCT(finfo2);
2897         ZERO_STRUCT(finfo3);
2898         ZERO_STRUCT(open_parms);
2899
2900         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2901
2902         /* Create the file. */
2903         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2904         if (fnum1 == -1) {
2905                 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
2906                 return false;
2907         }
2908
2909         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2910         finfo1.basic_info.in.file.fnum = fnum1;
2911         finfo2 = finfo1;
2912         finfo3 = finfo1;
2913
2914         /* Get the initial timestamps. */
2915         status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
2916
2917         torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
2918         
2919         /* Set the pending write time to a value with ns. */
2920         SET_INFO_FILE_NS(finfo, time(NULL) + 86400, 103, cli->tree, fnum1);
2921
2922         /* Get the current pending write time by fnum. */
2923         status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
2924
2925         torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
2926
2927         /* Ensure the time is actually different. */
2928         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2929                 torture_result(tctx, TORTURE_FAIL,
2930                         "setfileinfo time matches original fileinfo time");
2931                 ret = false;
2932         }
2933
2934         /* Get the current pending write time by path. */
2935         finfo3.basic_info.in.file.path = fname;
2936         status = smb_raw_pathinfo(cli->tree, tctx, &finfo3);
2937
2938         if (finfo2.basic_info.out.write_time != finfo3.basic_info.out.write_time) {
2939                 torture_result(tctx, TORTURE_FAIL, 
2940                         "qpathinfo time doesn't match fileinfo time");
2941                 ret = false;
2942         }
2943
2944         /* Now close the file. Re-open and check that the write
2945            time is identical to the one we wrote. */
2946
2947         smbcli_close(cli->tree, fnum1);
2948
2949         open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
2950         open_parms.ntcreatex.in.flags = 0;
2951         open_parms.ntcreatex.in.access_mask = SEC_GENERIC_READ;
2952         open_parms.ntcreatex.in.file_attr = 0;
2953         open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE|
2954                                         NTCREATEX_SHARE_ACCESS_READ|
2955                                         NTCREATEX_SHARE_ACCESS_WRITE;
2956         open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
2957         open_parms.ntcreatex.in.create_options = 0;
2958         open_parms.ntcreatex.in.fname = fname;
2959
2960         status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
2961         talloc_free(mem_ctx);
2962
2963         if (!NT_STATUS_IS_OK(status)) {
2964                 torture_result(tctx, TORTURE_FAIL,
2965                         "setfileinfo time matches original fileinfo time");
2966                 ret = false;
2967         }
2968
2969         fnum1 = open_parms.ntcreatex.out.file.fnum;
2970
2971         /* Check the returned time matches. */
2972         if (open_parms.ntcreatex.out.write_time != finfo2.basic_info.out.write_time) {
2973                 torture_result(tctx, TORTURE_FAIL,
2974                         "final open time does not match set time");
2975                 ret = false;
2976         }
2977
2978  done:
2979
2980         smbcli_close(cli->tree, fnum1);
2981
2982         smbcli_unlink(cli->tree, fname);
2983         smbcli_deltree(cli->tree, BASEDIR);
2984         return ret;
2985 }
2986
2987 /*
2988    Test if creating a file in a directory with an open handle updates the
2989    write timestamp (it should).
2990 */
2991 static bool test_directory_update8(struct torture_context *tctx, struct smbcli_state *cli)
2992 {
2993         union smb_fileinfo dir_info1, dir_info2;
2994         union smb_open open_parms;
2995         const char *fname = BASEDIR "\\torture_file.txt";
2996         NTSTATUS status;
2997         int fnum1 = -1;
2998         int fnum2 = -1;
2999         bool ret = true;
3000         double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
3001         int normal_delay = 2000000;
3002         double sec = ((double)used_delay) / ((double)normal_delay);
3003         int msec = 1000 * sec;
3004         TALLOC_CTX *mem_ctx = talloc_init("test_delayed_write_update8");
3005
3006         if (!mem_ctx) return false;
3007
3008         torture_comment(tctx, "\nRunning test directory write update\n");
3009
3010         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
3011
3012         /* Open a handle on the directory - and leave it open. */
3013         ZERO_STRUCT(open_parms);
3014         open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
3015         open_parms.ntcreatex.in.flags = 0;
3016         open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ;
3017         open_parms.ntcreatex.in.file_attr = 0;
3018         open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE|
3019                                         NTCREATEX_SHARE_ACCESS_READ|
3020                                         NTCREATEX_SHARE_ACCESS_WRITE;
3021         open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
3022         open_parms.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
3023         open_parms.ntcreatex.in.fname = BASEDIR;
3024
3025         status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
3026         talloc_free(mem_ctx);
3027
3028         if (!NT_STATUS_IS_OK(status)) {
3029                 torture_result(tctx, TORTURE_FAIL,
3030                         "failed to open directory handle");
3031                 ret = false;
3032                 goto done;
3033         }
3034
3035         fnum1 = open_parms.ntcreatex.out.file.fnum;
3036
3037         /* Store the returned write time. */
3038         ZERO_STRUCT(dir_info1);
3039         dir_info1.basic_info.out.write_time = open_parms.ntcreatex.out.write_time;
3040
3041         torture_comment(tctx, "Initial write time %s\n",
3042                nt_time_string(tctx, dir_info1.basic_info.out.write_time));
3043
3044         /* sleep */
3045         smb_msleep(3 * msec);
3046
3047         /* Now create a file within the directory. */
3048         fnum2 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
3049         if (fnum2 == -1) {
3050                 torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
3051                 ret = false;
3052                 goto done;
3053         }
3054         smbcli_close(cli->tree, fnum2);
3055
3056         /* Read the directory write time again. */
3057         ZERO_STRUCT(dir_info2);
3058         dir_info2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
3059         dir_info2.basic_info.in.file.fnum = fnum1;
3060
3061         status = smb_raw_fileinfo(cli->tree, tctx, &dir_info2);
3062
3063         torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
3064
3065         /* Ensure it's been incremented. */
3066         COMPARE_WRITE_TIME_GREATER(dir_info2, dir_info1);
3067
3068         torture_comment(tctx, "Updated write time %s\n",
3069                nt_time_string(tctx, dir_info2.basic_info.out.write_time));
3070
3071  done:
3072
3073         if (fnum1 != -1)
3074                 smbcli_close(cli->tree, fnum1);
3075         smbcli_unlink(cli->tree, fname);
3076         smbcli_deltree(cli->tree, BASEDIR);
3077
3078         return ret;
3079 }
3080
3081 #undef COMPARE_WRITE_TIME_CMP
3082 #undef COMPARE_ACCESS_TIME_CMP
3083
3084 #define COMPARE_TIME_CMP(given, gelem, correct, celem, cmp) do { \
3085         const uint64_t _r = 10*1000*1000; \
3086         NTTIME _g = (given).basic_info.out.gelem; \
3087         NTTIME _gr = (_g / _r) * _r; \
3088         NTTIME _c = (correct).basic_info.out.celem; \
3089         NTTIME _cr = (_c / _r) * _r; \
3090         bool _strict = torture_setting_bool(tctx, "strict mode", false); \
3091         const char *_err = NULL; \
3092         if (_strict && (_g cmp _c)) { \
3093                 _err = "strict"; \
3094         } else if ((_g cmp _c) && (_gr cmp _cr)) { \
3095                 /* handle filesystem without high resolution timestamps */ \
3096                 _err = "rounded"; \
3097         } \
3098         if (_err != NULL) { \
3099                 struct timeval _gtv; \
3100                 struct timeval _ctv; \
3101                 struct timeval_buf _gtvb; \
3102                 struct timeval_buf _ctvb; \
3103                 nttime_to_timeval(&_gtv, _g); \
3104                 nttime_to_timeval(&_ctv, _c); \
3105                 torture_result(tctx, TORTURE_FAIL, \
3106                                __location__": %s wrong (%s.%s)%s %s (%s.%s)%s", \
3107                                _err, \
3108                                #given, #gelem, \
3109                                timeval_str_buf(&_gtv, false, true, &_gtvb), \
3110                                #cmp, \
3111                                #correct, #celem, \
3112                                timeval_str_buf(&_ctv, false, true, &_ctvb)); \
3113                 ret = false; \
3114                 goto done; \
3115         } \
3116 } while (0)
3117 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
3118         COMPARE_TIME_CMP(given, write_time, correct, write_time, cmp); \
3119 } while (0)
3120 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
3121         COMPARE_WRITE_TIME_CMP(given,correct,!=)
3122 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
3123         COMPARE_WRITE_TIME_CMP(given,correct,<=)
3124
3125 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
3126         COMPARE_TIME_CMP(given, access_time, correct, access_time, cmp); \
3127 } while (0)
3128 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
3129         COMPARE_ACCESS_TIME_CMP(given,correct,!=)
3130 #define COMPARE_ACCESS_TIME_GREATER(given,correct) \
3131         COMPARE_ACCESS_TIME_CMP(given,correct,<=)
3132
3133 #define COMPARE_CHANGE_TIME_CMP(given, correct, cmp) do { \
3134         COMPARE_TIME_CMP(given, change_time, correct, change_time, cmp); \
3135 } while (0)
3136 #define COMPARE_CHANGE_TIME_EQUAL(given,correct) \
3137         COMPARE_CHANGE_TIME_CMP(given,correct,!=)
3138 #define COMPARE_CHANGE_TIME_GREATER(given,correct) \
3139         COMPARE_CHANGE_TIME_CMP(given,correct,<=)
3140
3141 #define COMPARE_CREATE_TIME_CMP(given, correct, cmp) do { \
3142         COMPARE_TIME_CMP(given, create_time, correct, create_time, cmp); \
3143 } while (0)
3144 #define COMPARE_CREATE_TIME_EQUAL(given,correct) \
3145         COMPARE_CREATE_TIME_CMP(given,correct,!=)
3146
3147 #define COMPARE_ALL_TIMES_EQUAL(given,correct) do { \
3148         COMPARE_WRITE_TIME_EQUAL(given,correct); \
3149         COMPARE_CHANGE_TIME_EQUAL(given,correct); \
3150         COMPARE_ACCESS_TIME_EQUAL(given,correct); \
3151         COMPARE_CREATE_TIME_EQUAL(given,correct); \
3152 } while (0)
3153
3154 #define COMPARE_TIMES_AFTER_WRITE(given,correct) do { \
3155         COMPARE_WRITE_TIME_GREATER(given,correct); \
3156         COMPARE_CHANGE_TIME_GREATER(given,correct); \
3157         COMPARE_ACCESS_TIME_EQUAL(given,correct); \
3158         COMPARE_CREATE_TIME_EQUAL(given,correct); \
3159         COMPARE_TIME_CMP(given, change_time, given, write_time, !=); \
3160 } while (0)
3161
3162 struct test_delaywrite_delaywrite1_state {
3163         struct torture_context *tctx;
3164         struct smbcli_state *cli1;
3165         struct smbcli_state *cli2;
3166         int fnum1;
3167         int fnum2;
3168         const char *fname;
3169 };
3170
3171 static bool test_delaywrite_delaywrite1_get_info(void *private_data,
3172                                                    union smb_fileinfo *finfo)
3173 {
3174         struct test_delaywrite_delaywrite1_state *state =
3175                 (struct test_delaywrite_delaywrite1_state *)private_data;
3176         struct torture_context *tctx = state->tctx;
3177         struct smbcli_state *cli = state->cli1;
3178         struct smbcli_state *cli2 = state->cli2;
3179         union smb_fileinfo t1finfo;
3180         union smb_fileinfo t2finfo;
3181         bool ret = true;
3182
3183         ZERO_STRUCTP(finfo);
3184
3185         ZERO_STRUCT(t1finfo);
3186         t1finfo.basic_info.level = RAW_FILEINFO_BASIC_INFORMATION;
3187         t1finfo.basic_info.in.file.fnum = state->fnum1;
3188
3189         ZERO_STRUCT(t2finfo);
3190         t2finfo.basic_info.level = RAW_FILEINFO_BASIC_INFORMATION;
3191         t2finfo.basic_info.in.file.fnum = state->fnum2;
3192
3193         GET_INFO_FILE2(t2finfo);
3194         GET_INFO_FILE(t1finfo);
3195         if (t1finfo.basic_info.out.write_time != t2finfo.basic_info.out.write_time) {
3196                 /*
3197                  * There was a race, get it again on handle 2,
3198                  * but then they have to match.
3199                  */
3200                 GET_INFO_FILE2(t2finfo);
3201         }
3202         COMPARE_ALL_TIMES_EQUAL(t1finfo, t2finfo);
3203
3204         finfo->basic_info.out = t1finfo.basic_info.out;
3205 done:
3206         return ret;
3207 }
3208
3209 static bool test_delaywrite_delaywrite1_write_data(void *private_data)
3210 {
3211         struct test_delaywrite_delaywrite1_state *state =
3212                 (struct test_delaywrite_delaywrite1_state *)private_data;
3213         struct torture_context *tctx = state->tctx;
3214         bool ret = true;
3215         ssize_t nwritten;
3216
3217         nwritten = smbcli_write(state->cli1->tree, state->fnum1, 0, "x", 0, 1);
3218         torture_assert_int_equal_goto(tctx, nwritten, 1,
3219                                       ret, done, "smbcli_write");
3220
3221 done:
3222         return ret;
3223 }
3224
3225 static bool test_delaywrite_delaywrite1_close(void *private_data,
3226                                               union smb_fileinfo *finfo)
3227 {
3228         struct test_delaywrite_delaywrite1_state *state =
3229                 (struct test_delaywrite_delaywrite1_state *)private_data;
3230         struct torture_context *tctx = state->tctx;
3231         struct smbcli_state *cli2 = state->cli2;
3232         union smb_fileinfo t2finfo;
3233         union smb_fileinfo t2pinfo;
3234         bool ret = true;
3235
3236         ZERO_STRUCTP(finfo);
3237
3238         /*
3239          * the close updates the write time to the time of the close
3240          * and not to the time of the last write!
3241          */
3242         torture_comment(tctx, "Close the file handle\n");
3243         smbcli_close(state->cli1->tree, state->fnum1);
3244         state->fnum1 = -1;
3245
3246         ZERO_STRUCT(t2finfo);
3247         t2finfo.basic_info.level = RAW_FILEINFO_BASIC_INFO;
3248         t2finfo.basic_info.in.file.fnum = state->fnum2;
3249         ZERO_STRUCT(t2pinfo);
3250         t2pinfo.basic_info.level = RAW_FILEINFO_BASIC_INFO;
3251         t2pinfo.basic_info.in.file.path = state->fname;
3252
3253         GET_INFO_FILE2(t2finfo);
3254
3255         smbcli_close(state->cli2->tree, state->fnum2);
3256         state->fnum2 = -1;
3257
3258         GET_INFO_PATH(t2pinfo);
3259         COMPARE_ALL_TIMES_EQUAL(t2pinfo, t2finfo);
3260
3261         finfo->basic_info.out = t2pinfo.basic_info.out;
3262
3263 done:
3264         return ret;
3265 }
3266
3267 static bool test_delaywrite_delaywrite1(struct torture_context *tctx,
3268                                         struct smbcli_state *cli,
3269                                         struct smbcli_state *cli2)
3270 {
3271         struct test_delaywrite_delaywrite1_state state = {
3272                 .tctx = tctx,
3273                 .cli1 = cli,
3274                 .cli2 = cli2,
3275                 .fnum1 = -1,
3276                 .fnum2 = -1,
3277         };
3278         const char *fname = BASEDIR "\\torture_file3.txt";
3279         bool ret = true;
3280         //double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
3281         //double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 1000000);
3282         double normal_delay = 1000000;
3283         double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", normal_delay);
3284         //double normal_delay = 1000000;
3285         //int normal_delay = 2000000;
3286         bool ok;
3287
3288         torture_comment(tctx, "\nRunning test_delayed_write_update3\n");
3289
3290         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
3291
3292         torture_comment(tctx, "Open the file handle\n");
3293         state.fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
3294         if (state.fnum1 == -1) {
3295                 ret = false;
3296                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
3297                 goto done;
3298         }
3299         state.fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
3300         if (state.fnum2 == -1) {
3301                 ret = false;
3302                 torture_result(tctx, TORTURE_FAIL, __location__": unable to open %s", fname);
3303                 goto done;
3304         }
3305
3306         state.fname = fname;
3307
3308         ok = test_delay_writetime1(tctx, used_delay, normal_delay,
3309                         "run1",
3310                         test_delaywrite_delaywrite1_get_info,
3311                         test_delaywrite_delaywrite1_write_data,
3312                         NULL, /* close_cb */
3313                         &state);
3314         torture_assert_goto(tctx, ok, ret, done, "test_delay_writetime1(1)");
3315         ok = test_delay_writetime1(tctx, used_delay, normal_delay,
3316                         "run2",
3317                         test_delaywrite_delaywrite1_get_info,
3318                         test_delaywrite_delaywrite1_write_data,
3319                         test_delaywrite_delaywrite1_close,
3320                         &state);
3321         torture_assert_goto(tctx, ok, ret, done, "test_delay_writetime1(2)");
3322
3323  done:
3324         if (state.fnum1 != -1) {
3325                 smbcli_close(cli->tree, state.fnum1);
3326         }
3327         if (state.fnum2 != -1) {
3328                 smbcli_close(cli2->tree, state.fnum2);
3329         }
3330         smbcli_unlink(cli->tree, fname);
3331         smbcli_deltree(cli->tree, BASEDIR);
3332
3333         return ret;
3334 }
3335
3336 /*
3337    testing of delayed update of write_time
3338 */
3339 struct torture_suite *torture_delay_write(TALLOC_CTX *ctx)
3340 {
3341         struct torture_suite *suite = torture_suite_create(ctx, "delaywrite");
3342
3343         torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
3344         torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
3345         torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate", test_delayed_write_update1);
3346         torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a);
3347         torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b);
3348         torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c);
3349         torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
3350         torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
3351         torture_suite_add_2smb_test(suite, "delayed update of write time 3a", test_delayed_write_update3a);
3352         torture_suite_add_2smb_test(suite, "delayed update of write time 3b", test_delayed_write_update3b);
3353         torture_suite_add_2smb_test(suite, "delayed update of write time 3c", test_delayed_write_update3c);
3354         torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
3355         torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
3356         torture_suite_add_2smb_test(suite, "delayed update of write time 5b", test_delayed_write_update5b);
3357         torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
3358         torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7);
3359         torture_suite_add_1smb_test(suite, "directory timestamp update test", test_directory_update8);
3360         torture_suite_add_2smb_test(suite, "delaywrite1", test_delaywrite_delaywrite1);
3361
3362         return suite;
3363 }