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