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