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