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