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