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