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