• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt/router/samba-3.5.8/source4/torture/basic/
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
39static 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		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
132static 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	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	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		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	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		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	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
323static 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	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		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	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		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
505static 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	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		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	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		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
679static 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	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		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	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		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
860static 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	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		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	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		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	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		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
1222static 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	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
1494static 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		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		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	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
1649static 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	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		msleep(0.5 * msec);
1743	}
1744
1745	GET_INFO_BOTH(finfo1,pinfo1);
1746	COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1747
1748	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		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	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		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	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		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	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
1865static 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	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		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		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	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
2032static 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	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		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		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	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		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	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		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	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
2243static 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	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		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		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	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
2404static 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		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		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	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
2566static 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		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		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	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
2732static 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	}
2756again:
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		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		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	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	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		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		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
2991static 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*/
3103struct 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}
3127