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 0 : static bool test_delayed_write_update(struct torture_context *tctx, struct smbcli_state *cli)
37 : {
38 : union smb_fileinfo finfo1, finfo2;
39 0 : const char *fname = BASEDIR "\\torture_file.txt";
40 : NTSTATUS status;
41 0 : int fnum1 = -1;
42 0 : bool ret = true;
43 : ssize_t written;
44 : struct timeval start;
45 : struct timeval end;
46 0 : double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
47 0 : int normal_delay = 2000000;
48 0 : double sec = ((double)used_delay) / ((double)normal_delay);
49 0 : int msec = 1000 * sec;
50 :
51 0 : torture_comment(tctx, "\nRunning test_delayed_write_update\n");
52 :
53 0 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
54 :
55 0 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
56 0 : torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
57 : "Failed to open %s", fname));
58 :
59 0 : finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
60 0 : finfo1.basic_info.in.file.fnum = fnum1;
61 0 : finfo2 = finfo1;
62 :
63 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
64 0 : torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
65 :
66 0 : torture_comment(tctx, "Initial write time %s\n",
67 : nt_time_string(tctx, finfo1.basic_info.out.write_time));
68 :
69 0 : written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
70 0 : torture_assert_int_equal(tctx, written, 1,
71 : "unexpected number of bytes written");
72 :
73 0 : start = timeval_current();
74 0 : end = timeval_add(&start, (120 * sec), 0);
75 0 : while (!timeval_expired(&end)) {
76 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
77 :
78 0 : torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
79 :
80 0 : torture_comment(tctx, "write time %s\n",
81 : nt_time_string(tctx, finfo2.basic_info.out.write_time));
82 :
83 0 : if (finfo1.basic_info.out.write_time !=
84 0 : finfo2.basic_info.out.write_time)
85 : {
86 0 : double diff = timeval_elapsed(&start);
87 :
88 0 : 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 0 : torture_comment(tctx, "Server updated write_time after %.2f seconds (correct)\n",
96 : diff);
97 0 : break;
98 : }
99 0 : fflush(stdout);
100 0 : smb_msleep(1 * msec);
101 : }
102 :
103 0 : 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 0 : if (fnum1 != -1)
110 0 : smbcli_close(cli->tree, fnum1);
111 0 : smbcli_unlink(cli->tree, fname);
112 0 : smbcli_deltree(cli->tree, BASEDIR);
113 :
114 0 : return ret;
115 : }
116 :
117 0 : static bool test_delayed_write_update1(struct torture_context *tctx, struct smbcli_state *cli)
118 : {
119 : union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
120 0 : const char *fname = BASEDIR "\\torture_file1.txt";
121 : NTSTATUS status;
122 0 : int fnum1 = -1;
123 0 : bool ret = true;
124 : ssize_t written;
125 : struct timeval start;
126 : struct timeval end;
127 0 : double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
128 0 : int normal_delay = 2000000;
129 0 : double sec = ((double)used_delay) / ((double)normal_delay);
130 0 : int msec = 1000 * sec;
131 : char buf[2048];
132 : bool first;
133 : bool updated;
134 :
135 0 : torture_comment(tctx, "\nRunning test_delayed_write_update1\n");
136 :
137 0 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
138 :
139 0 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
140 0 : torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
141 : "Failed to open %s", fname));
142 :
143 0 : memset(buf, 'x', 2048);
144 0 : 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 0 : smb_msleep(3 * msec);
149 :
150 0 : finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
151 0 : finfo1.all_info.in.file.fnum = fnum1;
152 0 : finfo2 = finfo1;
153 0 : finfo3 = finfo1;
154 0 : pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
155 0 : pinfo4.all_info.in.file.path = fname;
156 :
157 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
158 :
159 0 : torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
160 :
161 0 : torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048,
162 : "file size not as expected after write(2048)");
163 :
164 0 : 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 0 : smb_msleep(3 * msec);
170 :
171 : /* Do a zero length SMBwrite call to truncate. */
172 0 : written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
173 0 : torture_assert_int_equal(tctx, written, 0,
174 : "unexpected number of bytes written");
175 :
176 0 : start = timeval_current();
177 0 : end = timeval_add(&start, (120 * sec), 0);
178 0 : first = true;
179 0 : updated = false;
180 0 : while (!timeval_expired(&end)) {
181 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
182 :
183 0 : torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
184 :
185 0 : torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 1024,
186 : "file not truncated to expected size "
187 : "(1024)");
188 :
189 0 : torture_comment(tctx, "write time %s\n",
190 : nt_time_string(tctx, finfo2.all_info.out.write_time));
191 :
192 0 : if (finfo1.all_info.out.write_time !=
193 0 : finfo2.all_info.out.write_time)
194 : {
195 0 : updated = true;
196 0 : break;
197 : }
198 :
199 0 : fflush(stdout);
200 0 : smb_msleep(1 * msec);
201 0 : first = false;
202 : }
203 :
204 0 : torture_assert(tctx, updated,
205 : "Server did not update write time within 120 seconds");
206 :
207 0 : 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 0 : torture_comment(tctx, "Server updated write time immediately. Good!\n");
212 :
213 0 : fflush(stdout);
214 0 : smb_msleep(2 * msec);
215 :
216 : /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
217 0 : written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
218 0 : torture_assert_int_equal(tctx, written, 1,
219 : "unexpected number of bytes written");
220 :
221 0 : start = timeval_current();
222 0 : end = timeval_add(&start, (10*sec), 0);
223 0 : while (!timeval_expired(&end)) {
224 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
225 :
226 0 : torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
227 :
228 0 : torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 1024,
229 : "file not truncated to expected size "
230 : "(1024)");
231 :
232 0 : torture_comment(tctx, "write time %s\n",
233 : nt_time_string(tctx, finfo3.all_info.out.write_time));
234 :
235 0 : 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 0 : fflush(stdout);
244 0 : smb_msleep(1 * msec);
245 : }
246 :
247 0 : torture_comment(tctx, "Server did not update write time within 10 "
248 : "seconds. Good!\n");
249 :
250 0 : fflush(stdout);
251 0 : smb_msleep(2 * msec);
252 :
253 : /* the close should trigger an write time update */
254 0 : smbcli_close(cli->tree, fnum1);
255 0 : fnum1 = -1;
256 :
257 0 : status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
258 0 : torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
259 :
260 0 : 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 0 : 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 0 : torture_comment(tctx, "Server updated write time on close (correct)\n");
271 :
272 0 : if (fnum1 != -1)
273 0 : smbcli_close(cli->tree, fnum1);
274 0 : smbcli_unlink(cli->tree, fname);
275 0 : smbcli_deltree(cli->tree, BASEDIR);
276 :
277 0 : return ret;
278 : }
279 :
280 : /* Updating with a SMBwrite of zero length
281 : * changes the write time immediately - even on expand. */
282 :
283 0 : static bool test_delayed_write_update1a(struct torture_context *tctx, struct smbcli_state *cli)
284 : {
285 : union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
286 0 : const char *fname = BASEDIR "\\torture_file1a.txt";
287 : NTSTATUS status;
288 0 : int fnum1 = -1;
289 0 : bool ret = true;
290 : ssize_t written;
291 : struct timeval start;
292 : struct timeval end;
293 0 : double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
294 0 : int normal_delay = 2000000;
295 0 : double sec = ((double)used_delay) / ((double)normal_delay);
296 0 : int msec = 1000 * sec;
297 : char buf[2048];
298 : bool first;
299 : bool updated;
300 :
301 0 : torture_comment(tctx, "\nRunning test_delayed_write_update1a\n");
302 :
303 0 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
304 :
305 0 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
306 0 : torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
307 : "Failed to open %s", fname));
308 :
309 0 : memset(buf, 'x', 2048);
310 0 : 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 0 : smb_msleep(3 * msec);
315 :
316 0 : finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
317 0 : finfo1.all_info.in.file.fnum = fnum1;
318 0 : finfo2 = finfo1;
319 0 : finfo3 = finfo1;
320 0 : pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
321 0 : pinfo4.all_info.in.file.path = fname;
322 :
323 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
324 :
325 0 : torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
326 :
327 0 : torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048,
328 : "file size not as expected after write(2048)");
329 :
330 0 : 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 0 : written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
335 :
336 0 : torture_assert_int_equal(tctx, written, 0,
337 : "unexpected number of bytes written");
338 :
339 0 : start = timeval_current();
340 0 : end = timeval_add(&start, (120*sec), 0);
341 0 : first = true;
342 0 : updated = false;
343 0 : while (!timeval_expired(&end)) {
344 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
345 :
346 0 : torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
347 :
348 0 : torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 10240,
349 : "file not truncated to expected size "
350 : "(10240)");
351 :
352 0 : torture_comment(tctx, "write time %s\n",
353 : nt_time_string(tctx, finfo2.all_info.out.write_time));
354 :
355 0 : if (finfo1.all_info.out.write_time !=
356 0 : finfo2.all_info.out.write_time)
357 : {
358 0 : updated = true;
359 0 : break;
360 : }
361 :
362 0 : fflush(stdout);
363 0 : smb_msleep(1 * msec);
364 0 : first = false;
365 : }
366 :
367 0 : torture_assert(tctx, updated,
368 : "Server did not update write time within 120 seconds");
369 :
370 0 : 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 0 : torture_comment(tctx, "Server updated write time immediately. Good!\n");
375 :
376 0 : fflush(stdout);
377 0 : smb_msleep(2 * msec);
378 :
379 : /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
380 0 : written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
381 :
382 0 : torture_assert_int_equal(tctx, written, 1,
383 : "unexpected number of bytes written");
384 :
385 0 : start = timeval_current();
386 0 : end = timeval_add(&start, (10*sec), 0);
387 0 : while (!timeval_expired(&end)) {
388 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
389 :
390 0 : torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
391 :
392 0 : torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 10240,
393 : "file not truncated to expected size "
394 : "(10240)");
395 :
396 0 : torture_comment(tctx, "write time %s\n",
397 : nt_time_string(tctx, finfo3.all_info.out.write_time));
398 :
399 0 : 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 0 : fflush(stdout);
408 0 : smb_msleep(1 * msec);
409 : }
410 :
411 0 : 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 0 : smbcli_close(cli->tree, fnum1);
416 0 : fnum1 = -1;
417 :
418 0 : status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
419 0 : torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
420 :
421 0 : 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 0 : 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 0 : torture_comment(tctx, "Server updated write time on close (correct)\n");
432 :
433 0 : if (fnum1 != -1)
434 0 : smbcli_close(cli->tree, fnum1);
435 0 : smbcli_unlink(cli->tree, fname);
436 0 : smbcli_deltree(cli->tree, BASEDIR);
437 :
438 0 : 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 0 : static bool test_delayed_write_update1b(struct torture_context *tctx, struct smbcli_state *cli)
445 : {
446 : union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
447 0 : const char *fname = BASEDIR "\\torture_file1b.txt";
448 : NTSTATUS status;
449 0 : int fnum1 = -1;
450 0 : bool ret = true;
451 : ssize_t written;
452 : struct timeval start;
453 : struct timeval end;
454 0 : double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
455 0 : int normal_delay = 2000000;
456 0 : double sec = ((double)used_delay) / ((double)normal_delay);
457 0 : int msec = 1000 * sec;
458 : char buf[2048];
459 : bool first;
460 : bool updated;
461 :
462 0 : torture_comment(tctx, "\nRunning test_delayed_write_update1b\n");
463 :
464 0 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
465 :
466 0 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
467 0 : torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
468 : "Failed to open %s", fname));
469 :
470 0 : memset(buf, 'x', 2048);
471 0 : 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 0 : smb_msleep(3 * msec);
476 :
477 0 : finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
478 0 : finfo1.all_info.in.file.fnum = fnum1;
479 0 : finfo2 = finfo1;
480 0 : finfo3 = finfo1;
481 0 : pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
482 0 : pinfo4.all_info.in.file.path = fname;
483 :
484 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
485 :
486 0 : torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
487 :
488 0 : torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048,
489 : "file size not as expected after write(2048)");
490 :
491 0 : 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 0 : status = smbcli_ftruncate(cli->tree, fnum1, (uint64_t)10240);
496 :
497 0 : torture_assert_ntstatus_ok(tctx, status, "SET_END_OF_FILE failed");
498 :
499 0 : start = timeval_current();
500 0 : end = timeval_add(&start, (120*sec), 0);
501 0 : first = true;
502 0 : updated = false;
503 0 : while (!timeval_expired(&end)) {
504 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
505 :
506 0 : torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
507 :
508 0 : torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 10240,
509 : "file not truncated to expected size "
510 : "(10240)");
511 :
512 0 : torture_comment(tctx, "write time %s\n",
513 : nt_time_string(tctx, finfo2.all_info.out.write_time));
514 :
515 0 : if (finfo1.all_info.out.write_time !=
516 0 : finfo2.all_info.out.write_time)
517 : {
518 0 : updated = true;
519 0 : break;
520 : }
521 :
522 0 : fflush(stdout);
523 0 : smb_msleep(1 * msec);
524 0 : first = false;
525 : }
526 :
527 0 : torture_assert(tctx, updated,
528 : "Server did not update write time within 120 seconds");
529 :
530 0 : 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 0 : torture_comment(tctx, "Server updated write time immediately. Good!\n");
535 :
536 0 : fflush(stdout);
537 0 : smb_msleep(2 * msec);
538 :
539 : /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
540 0 : written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
541 :
542 0 : torture_assert_int_equal(tctx, written, 1,
543 : "unexpected number of bytes written");
544 :
545 0 : start = timeval_current();
546 0 : end = timeval_add(&start, (10*sec), 0);
547 0 : while (!timeval_expired(&end)) {
548 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
549 :
550 0 : torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
551 :
552 0 : torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 10240,
553 : "file not truncated to expected size "
554 : "(10240)");
555 :
556 0 : torture_comment(tctx, "write time %s\n",
557 : nt_time_string(tctx, finfo3.all_info.out.write_time));
558 :
559 0 : 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 0 : fflush(stdout);
568 0 : smb_msleep(1 * msec);
569 : }
570 :
571 0 : 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 0 : smbcli_close(cli->tree, fnum1);
576 0 : fnum1 = -1;
577 :
578 0 : status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
579 0 : torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
580 :
581 0 : 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 0 : 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 0 : torture_comment(tctx, "Server updated write time on close (correct)\n");
592 :
593 0 : if (fnum1 != -1)
594 0 : smbcli_close(cli->tree, fnum1);
595 0 : smbcli_unlink(cli->tree, fname);
596 0 : smbcli_deltree(cli->tree, BASEDIR);
597 :
598 0 : return ret;
599 : }
600 :
601 : /* Updating with a SET_ALLOCATION_INFO (truncate) does so immediately. */
602 :
603 0 : static bool test_delayed_write_update1c(struct torture_context *tctx, struct smbcli_state *cli)
604 : {
605 : union smb_setfileinfo parms;
606 : union smb_fileinfo finfo1, finfo2, finfo3, pinfo4;
607 0 : const char *fname = BASEDIR "\\torture_file1c.txt";
608 : NTSTATUS status;
609 0 : int fnum1 = -1;
610 0 : bool ret = true;
611 : ssize_t written;
612 : struct timeval start;
613 : struct timeval end;
614 0 : double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
615 0 : int normal_delay = 2000000;
616 0 : double sec = ((double)used_delay) / ((double)normal_delay);
617 0 : int msec = 1000 * sec;
618 : char buf[2048];
619 : bool first;
620 : bool updated;
621 :
622 0 : torture_comment(tctx, "\nRunning test_delayed_write_update1c\n");
623 :
624 0 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
625 :
626 0 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
627 0 : torture_assert_int_not_equal(tctx, fnum1, -1, talloc_asprintf(tctx,
628 : "Failed to open %s", fname));
629 :
630 0 : memset(buf, 'x', 2048);
631 0 : 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 0 : smb_msleep(3 * msec);
636 :
637 0 : finfo1.all_info.level = RAW_FILEINFO_ALL_INFO;
638 0 : finfo1.all_info.in.file.fnum = fnum1;
639 0 : finfo2 = finfo1;
640 0 : finfo3 = finfo1;
641 0 : pinfo4.all_info.level = RAW_FILEINFO_ALL_INFO;
642 0 : pinfo4.all_info.in.file.path = fname;
643 :
644 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
645 :
646 0 : torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
647 :
648 0 : torture_assert_u64_equal(tctx, finfo1.all_info.out.size, 2048,
649 : "file size not as expected after write(2048)");
650 :
651 0 : 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 0 : parms.allocation_info.level = RAW_SFILEINFO_ALLOCATION_INFO;
656 0 : parms.allocation_info.in.file.fnum = fnum1;
657 0 : parms.allocation_info.in.alloc_size = 0;
658 :
659 0 : status = smb_raw_setfileinfo(cli->tree, &parms);
660 :
661 0 : torture_assert_ntstatus_ok(tctx, status,
662 : "RAW_SFILEINFO_ALLOCATION_INFO failed");
663 :
664 0 : start = timeval_current();
665 0 : end = timeval_add(&start, (120*sec), 0);
666 0 : first = true;
667 0 : updated = false;
668 0 : while (!timeval_expired(&end)) {
669 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
670 :
671 0 : torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
672 :
673 0 : torture_assert_u64_equal(tctx, finfo2.all_info.out.size, 0,
674 : "file not truncated to expected size "
675 : "(0)");
676 :
677 0 : torture_comment(tctx, "write time %s\n",
678 : nt_time_string(tctx, finfo2.all_info.out.write_time));
679 :
680 0 : if (finfo1.all_info.out.write_time !=
681 0 : finfo2.all_info.out.write_time)
682 : {
683 0 : updated = true;
684 0 : break;
685 : }
686 :
687 0 : fflush(stdout);
688 0 : smb_msleep(1 * msec);
689 0 : first = false;
690 : }
691 :
692 0 : torture_assert(tctx, updated,
693 : "Server did not update write time within 120 seconds");
694 :
695 0 : 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 0 : torture_comment(tctx, "Server updated write time immediately. Good!\n");
700 :
701 0 : fflush(stdout);
702 0 : smb_msleep(2 * msec);
703 :
704 : /* Do a non-zero length SMBwrite and make sure it doesn't update the write time. */
705 0 : written = smbcli_smbwrite(cli->tree, fnum1, "x", 0, 1);
706 0 : torture_assert_int_equal(tctx, written, 1,
707 : "Unexpected number of bytes written");
708 :
709 0 : start = timeval_current();
710 0 : end = timeval_add(&start, (10*sec), 0);
711 0 : while (!timeval_expired(&end)) {
712 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
713 :
714 0 : torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
715 :
716 0 : torture_assert_u64_equal(tctx, finfo3.all_info.out.size, 1,
717 : "file not expaneded");
718 :
719 0 : torture_comment(tctx, "write time %s\n",
720 : nt_time_string(tctx, finfo3.all_info.out.write_time));
721 :
722 0 : 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 0 : fflush(stdout);
731 0 : smb_msleep(1 * msec);
732 : }
733 :
734 0 : 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 0 : smbcli_close(cli->tree, fnum1);
739 0 : fnum1 = -1;
740 :
741 0 : status = smb_raw_pathinfo(cli->tree, tctx, &pinfo4);
742 0 : torture_assert_ntstatus_ok(tctx, status, "pathinfo failed");
743 :
744 0 : 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 0 : 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 0 : if (fnum1 != -1)
755 0 : smbcli_close(cli->tree, fnum1);
756 0 : smbcli_unlink(cli->tree, fname);
757 0 : smbcli_deltree(cli->tree, BASEDIR);
758 :
759 0 : return ret;
760 : }
761 :
762 : /*
763 : * Do as above, but using 2 connections.
764 : */
765 :
766 0 : static bool test_delayed_write_update2(struct torture_context *tctx, struct smbcli_state *cli,
767 : struct smbcli_state *cli2)
768 : {
769 : union smb_fileinfo finfo1, finfo2;
770 0 : const char *fname = BASEDIR "\\torture_file.txt";
771 : NTSTATUS status;
772 0 : int fnum1 = -1;
773 0 : int fnum2 = -1;
774 0 : bool ret = true;
775 : ssize_t written;
776 : struct timeval start;
777 : struct timeval end;
778 0 : double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
779 0 : int normal_delay = 2000000;
780 0 : double sec = ((double)used_delay) / ((double)normal_delay);
781 0 : int msec = 1000 * sec;
782 : union smb_flush flsh;
783 :
784 0 : torture_comment(tctx, "\nRunning test_delayed_write_update2\n");
785 :
786 0 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
787 :
788 0 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
789 0 : if (fnum1 == -1) {
790 0 : torture_comment(tctx, "Failed to open %s\n", fname);
791 0 : return false;
792 : }
793 :
794 0 : finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
795 0 : finfo1.basic_info.in.file.fnum = fnum1;
796 0 : finfo2 = finfo1;
797 :
798 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
799 :
800 0 : torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
801 :
802 0 : 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 0 : smb_msleep(3 * msec);
808 :
809 : {
810 : /* Try using setfileinfo instead of write to update write time. */
811 : union smb_setfileinfo sfinfo;
812 0 : time_t t_set = time(NULL);
813 0 : sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
814 0 : sfinfo.basic_info.in.file.fnum = fnum1;
815 0 : sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
816 0 : 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 0 : 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 0 : sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
826 0 : sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
827 :
828 0 : status = smb_raw_setfileinfo(cli->tree, &sfinfo);
829 :
830 0 : torture_assert_ntstatus_ok(tctx, status, "sfileinfo failed");
831 : }
832 :
833 0 : finfo2.basic_info.in.file.path = fname;
834 :
835 0 : status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
836 :
837 0 : if (!NT_STATUS_IS_OK(status)) {
838 0 : DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
839 0 : return false;
840 : }
841 0 : torture_comment(tctx, "write time %s\n",
842 : nt_time_string(tctx, finfo2.basic_info.out.write_time));
843 :
844 0 : if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
845 0 : 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 0 : finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
854 0 : finfo1.basic_info.in.file.fnum = fnum1;
855 0 : finfo2 = finfo1;
856 :
857 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
858 :
859 0 : if (!NT_STATUS_IS_OK(status)) {
860 0 : DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
861 0 : return false;
862 : }
863 :
864 0 : torture_comment(tctx, "Modified write time %s\n",
865 : nt_time_string(tctx, finfo1.basic_info.out.write_time));
866 :
867 :
868 0 : torture_comment(tctx, "Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
869 :
870 0 : written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
871 :
872 0 : 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 0 : torture_comment(tctx, "Doing flush after write\n");
882 :
883 0 : flsh.flush.level = RAW_FLUSH_FLUSH;
884 0 : flsh.flush.in.file.fnum = fnum1;
885 0 : status = smb_raw_flush(cli->tree, &flsh);
886 0 : 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 0 : start = timeval_current();
894 0 : end = timeval_add(&start, (15*sec), 0);
895 0 : while (!timeval_expired(&end)) {
896 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
897 :
898 0 : 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 0 : torture_comment(tctx, "write time %s\n",
904 : nt_time_string(tctx, finfo2.basic_info.out.write_time));
905 0 : 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 0 : fflush(stdout);
914 0 : smb_msleep(1 * msec);
915 : }
916 :
917 0 : if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
918 0 : torture_comment(tctx, "Server did not update write time (correct)\n");
919 : }
920 :
921 0 : fflush(stdout);
922 0 : smb_msleep(2 * msec);
923 :
924 0 : fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
925 0 : if (fnum2 == -1) {
926 0 : torture_result(tctx, TORTURE_FAIL, "Failed to open %s\n", fname);
927 0 : return false;
928 : }
929 :
930 0 : 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 0 : written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
933 :
934 0 : 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 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
941 :
942 0 : if (!NT_STATUS_IS_OK(status)) {
943 0 : DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
944 0 : return false;
945 : }
946 0 : torture_comment(tctx, "write time %s\n",
947 : nt_time_string(tctx, finfo2.basic_info.out.write_time));
948 0 : 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 0 : torture_comment(tctx, "Closing the first fd to see if write time updated.\n");
954 0 : smbcli_close(cli->tree, fnum1);
955 0 : fnum1 = -1;
956 :
957 0 : 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 0 : written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
960 :
961 0 : 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 0 : finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
968 0 : finfo1.basic_info.in.file.fnum = fnum2;
969 0 : finfo2 = finfo1;
970 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
971 :
972 0 : if (!NT_STATUS_IS_OK(status)) {
973 0 : DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
974 0 : return false;
975 : }
976 0 : torture_comment(tctx, "write time %s\n",
977 : nt_time_string(tctx, finfo2.basic_info.out.write_time));
978 0 : 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 0 : start = timeval_current();
986 0 : end = timeval_add(&start, (15*sec), 0);
987 0 : while (!timeval_expired(&end)) {
988 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
989 :
990 0 : 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 0 : torture_comment(tctx, "write time %s\n",
996 : nt_time_string(tctx, finfo2.basic_info.out.write_time));
997 0 : 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 0 : fflush(stdout);
1006 0 : smb_msleep(1 * msec);
1007 : }
1008 :
1009 0 : if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
1010 0 : torture_comment(tctx, "Server did not update write time (correct)\n");
1011 : }
1012 :
1013 0 : torture_comment(tctx, "Closing second fd to see if write time updated.\n");
1014 :
1015 0 : smbcli_close(cli->tree, fnum2);
1016 0 : fnum2 = -1;
1017 :
1018 0 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
1019 0 : if (fnum1 == -1) {
1020 0 : torture_comment(tctx, "Failed to open %s\n", fname);
1021 0 : return false;
1022 : }
1023 :
1024 0 : finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1025 0 : finfo1.basic_info.in.file.fnum = fnum1;
1026 0 : finfo2 = finfo1;
1027 :
1028 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1029 :
1030 0 : if (!NT_STATUS_IS_OK(status)) {
1031 0 : DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1032 0 : return false;
1033 : }
1034 :
1035 0 : torture_comment(tctx, "Second open initial write time %s\n",
1036 : nt_time_string(tctx, finfo1.basic_info.out.write_time));
1037 :
1038 0 : smb_msleep(10 * msec);
1039 0 : torture_comment(tctx, "Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
1040 :
1041 0 : written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
1042 :
1043 0 : 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 0 : finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1050 0 : finfo1.basic_info.in.file.fnum = fnum1;
1051 0 : finfo2 = finfo1;
1052 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1053 :
1054 0 : if (!NT_STATUS_IS_OK(status)) {
1055 0 : DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
1056 0 : return false;
1057 : }
1058 0 : torture_comment(tctx, "write time %s\n",
1059 : nt_time_string(tctx, finfo2.basic_info.out.write_time));
1060 0 : 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 0 : start = timeval_current();
1067 0 : end = timeval_add(&start, (15*sec), 0);
1068 0 : while (!timeval_expired(&end)) {
1069 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
1070 :
1071 0 : 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 0 : torture_comment(tctx, "write time %s\n",
1077 : nt_time_string(tctx, finfo2.basic_info.out.write_time));
1078 0 : if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
1079 0 : double diff = timeval_elapsed(&start);
1080 0 : 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 0 : torture_comment(tctx, "Server updated write_time after %.2f seconds"
1089 : "(correct)\n",
1090 : diff);
1091 0 : break;
1092 : }
1093 0 : fflush(stdout);
1094 0 : smb_msleep(1*msec);
1095 : }
1096 :
1097 0 : 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 0 : if (fnum1 != -1)
1108 0 : smbcli_close(cli->tree, fnum1);
1109 0 : smbcli_unlink(cli->tree, fname);
1110 0 : smbcli_deltree(cli->tree, BASEDIR);
1111 :
1112 0 : 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 0 : static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_state *cli,
1127 : struct smbcli_state *cli2)
1128 : {
1129 : union smb_fileinfo finfo1, finfo2;
1130 0 : const char *fname = BASEDIR "\\torture_file.txt";
1131 : NTSTATUS status;
1132 0 : int fnum1 = -1;
1133 : int fnum2;
1134 0 : bool ret = true;
1135 : ssize_t written;
1136 0 : double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1137 0 : int normal_delay = 2000000;
1138 0 : double sec = ((double)used_delay) / ((double)normal_delay);
1139 0 : int msec = 1000 * sec;
1140 :
1141 0 : torture_comment(tctx, "\nRunning test_finfo_after_write\n");
1142 :
1143 0 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1144 :
1145 0 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1146 0 : 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 0 : finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1153 0 : finfo1.basic_info.in.file.fnum = fnum1;
1154 :
1155 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
1156 :
1157 0 : 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 0 : smb_msleep(1 * msec);
1164 :
1165 0 : written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1166 :
1167 0 : 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 0 : fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
1174 0 : 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 0 : written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
1182 :
1183 0 : 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 0 : finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1191 0 : finfo2.basic_info.in.file.path = fname;
1192 :
1193 0 : status = smb_raw_pathinfo(cli2->tree, tctx, &finfo2);
1194 :
1195 0 : 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 0 : if (finfo1.basic_info.out.create_time !=
1203 0 : 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 0 : if (finfo1.basic_info.out.access_time !=
1210 0 : 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 0 : if (finfo1.basic_info.out.write_time !=
1217 0 : 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 0 : if (finfo1.basic_info.out.change_time !=
1227 0 : 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 0 : smbcli_close(cli2->tree, fnum2);
1239 0 : cli2 = NULL;
1240 :
1241 : /* This call is only for the people looking at ethereal :-) */
1242 0 : finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1243 0 : finfo2.basic_info.in.file.path = fname;
1244 :
1245 0 : status = smb_raw_pathinfo(cli->tree, tctx, &finfo2);
1246 :
1247 0 : 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 0 : done:
1254 0 : if (fnum1 != -1)
1255 0 : smbcli_close(cli->tree, fnum1);
1256 0 : smbcli_unlink(cli->tree, fname);
1257 0 : smbcli_deltree(cli->tree, BASEDIR);
1258 :
1259 0 : 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 = finfo1.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 = finfo1.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 0 : static bool test_delayed_write_update3(struct torture_context *tctx,
1397 : struct smbcli_state *cli,
1398 : struct smbcli_state *cli2)
1399 : {
1400 : union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1401 : union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1402 0 : const char *fname = BASEDIR "\\torture_file3.txt";
1403 0 : int fnum1 = -1;
1404 0 : bool ret = true;
1405 : ssize_t written;
1406 : struct timeval start;
1407 : struct timeval end;
1408 0 : double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1409 0 : int normal_delay = 2000000;
1410 0 : double sec = ((double)used_delay) / ((double)normal_delay);
1411 0 : int msec = 1000 * sec;
1412 :
1413 0 : torture_comment(tctx, "\nRunning test_delayed_write_update3\n");
1414 :
1415 0 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1416 :
1417 0 : torture_comment(tctx, "Open the file handle\n");
1418 0 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1419 0 : 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 0 : finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1426 0 : finfo0.basic_info.in.file.fnum = fnum1;
1427 0 : finfo1 = finfo0;
1428 0 : finfo2 = finfo0;
1429 0 : finfo3 = finfo0;
1430 0 : pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1431 0 : pinfo0.basic_info.in.file.path = fname;
1432 0 : pinfo1 = pinfo0;
1433 0 : pinfo2 = pinfo0;
1434 0 : pinfo3 = pinfo0;
1435 0 : pinfo4 = pinfo0;
1436 :
1437 : /* get the initial times */
1438 0 : GET_INFO_BOTH(finfo0,pinfo0);
1439 :
1440 : /*
1441 : * make sure the write time is updated 2 seconds later
1442 : * calcuated from the first write
1443 : * (but expect up to 5 seconds extra time for a busy server)
1444 : */
1445 0 : start = timeval_current();
1446 0 : end = timeval_add(&start, 7 * sec, 0);
1447 0 : while (!timeval_expired(&end)) {
1448 : /* do a write */
1449 0 : torture_comment(tctx, "Do a write on the file handle\n");
1450 0 : written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1451 0 : 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 0 : GET_INFO_FILE(finfo1);
1458 :
1459 0 : if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1460 0 : double diff = timeval_elapsed(&start);
1461 0 : 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 0 : torture_comment(tctx, "Server updated write_time after %.2f seconds "
1470 : "(correct)\n",
1471 : diff);
1472 0 : break;
1473 : }
1474 0 : smb_msleep(0.5 * msec);
1475 : }
1476 :
1477 0 : GET_INFO_BOTH(finfo1,pinfo1);
1478 0 : COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1479 :
1480 : /* sure any further write doesn't update the write time */
1481 0 : start = timeval_current();
1482 0 : end = timeval_add(&start, 15 * sec, 0);
1483 0 : while (!timeval_expired(&end)) {
1484 : /* do a write */
1485 0 : torture_comment(tctx, "Do a write on the file handle\n");
1486 0 : written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1487 0 : 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 0 : GET_INFO_BOTH(finfo2,pinfo2);
1494 :
1495 0 : 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 0 : smb_msleep(1 * msec);
1504 : }
1505 :
1506 0 : GET_INFO_BOTH(finfo2,pinfo2);
1507 0 : COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1508 0 : if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1509 0 : torture_comment(tctx, "Server did not update write_time (correct)\n");
1510 : }
1511 :
1512 : /* sleep */
1513 0 : smb_msleep(5 * msec);
1514 :
1515 0 : GET_INFO_BOTH(finfo3,pinfo3);
1516 0 : 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 0 : torture_comment(tctx, "Close the file handle\n");
1523 0 : smbcli_close(cli->tree, fnum1);
1524 0 : fnum1 = -1;
1525 :
1526 0 : GET_INFO_PATH(pinfo4);
1527 0 : COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1528 :
1529 0 : if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1530 0 : torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1531 : }
1532 :
1533 0 : done:
1534 0 : if (fnum1 != -1)
1535 0 : smbcli_close(cli->tree, fnum1);
1536 0 : smbcli_unlink(cli->tree, fname);
1537 0 : smbcli_deltree(cli->tree, BASEDIR);
1538 :
1539 0 : 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 0 : static bool test_delayed_write_update3a(struct torture_context *tctx,
1548 : struct smbcli_state *cli,
1549 : struct smbcli_state *cli2)
1550 : {
1551 : union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1552 : union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1553 0 : const char *fname = BASEDIR "\\torture_file3a.txt";
1554 0 : int fnum1 = -1;
1555 0 : bool ret = true;
1556 : ssize_t written;
1557 : int i;
1558 : struct timeval start;
1559 : struct timeval end;
1560 0 : double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1561 0 : int normal_delay = 2000000;
1562 0 : double sec = ((double)used_delay) / ((double)normal_delay);
1563 0 : int msec = 1000 * sec;
1564 :
1565 0 : torture_comment(tctx, "\nRunning test_delayed_write_update3a\n");
1566 :
1567 0 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1568 :
1569 0 : torture_comment(tctx, "Open the file handle\n");
1570 0 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1571 0 : 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 0 : finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1578 0 : finfo0.basic_info.in.file.fnum = fnum1;
1579 0 : finfo1 = finfo0;
1580 0 : finfo2 = finfo0;
1581 0 : finfo3 = finfo0;
1582 0 : pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1583 0 : pinfo0.basic_info.in.file.path = fname;
1584 0 : pinfo1 = pinfo0;
1585 0 : pinfo2 = pinfo0;
1586 0 : pinfo3 = pinfo0;
1587 0 : pinfo4 = pinfo0;
1588 :
1589 : /* get the initial times */
1590 0 : 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 0 : smb_msleep(5 * msec);
1597 :
1598 : /* get the initial times */
1599 0 : GET_INFO_BOTH(finfo1,pinfo1);
1600 0 : COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1601 :
1602 : /*
1603 : * make sure the write time is updated 2 seconds later
1604 : * calcuated from the first write
1605 : * (but expect up to 5 seconds extra time for a busy server)
1606 : */
1607 0 : start = timeval_current();
1608 0 : end = timeval_add(&start, 7 * sec, 0);
1609 0 : while (!timeval_expired(&end)) {
1610 : /* do a write */
1611 0 : torture_comment(tctx, "Do a write on the file handle\n");
1612 0 : written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1613 0 : 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 0 : GET_INFO_FILE(finfo1);
1620 :
1621 0 : if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1622 0 : double diff = timeval_elapsed(&start);
1623 0 : 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 0 : torture_comment(tctx, "Server updated write_time after %.2f seconds "
1632 : "(correct)\n",
1633 : diff);
1634 0 : break;
1635 : }
1636 0 : smb_msleep(0.5 * msec);
1637 : }
1638 :
1639 0 : GET_INFO_BOTH(finfo1,pinfo1);
1640 0 : COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1641 :
1642 0 : smb_msleep(3 * msec);
1643 :
1644 : /*
1645 : * demonstrate that a truncate write always
1646 : * updates the write time immediately
1647 : */
1648 0 : for (i=0; i < 3; i++) {
1649 0 : smb_msleep(2 * msec);
1650 : /* do a write */
1651 0 : torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1652 0 : written = smbcli_smbwrite(cli->tree, fnum1, "x", 10240, 0);
1653 0 : 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 0 : GET_INFO_BOTH(finfo2,pinfo2);
1660 0 : COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1661 0 : finfo1 = finfo2;
1662 : }
1663 :
1664 0 : smb_msleep(3 * msec);
1665 :
1666 : /* sure any further write doesn't update the write time */
1667 0 : start = timeval_current();
1668 0 : end = timeval_add(&start, 15 * sec, 0);
1669 0 : while (!timeval_expired(&end)) {
1670 : /* do a write */
1671 0 : torture_comment(tctx, "Do a write on the file handle\n");
1672 0 : written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1673 0 : 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 0 : GET_INFO_BOTH(finfo2,pinfo2);
1680 :
1681 0 : 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 0 : smb_msleep(1 * msec);
1690 : }
1691 :
1692 0 : GET_INFO_BOTH(finfo2,pinfo2);
1693 0 : COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1694 0 : if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1695 0 : torture_comment(tctx, "Server did not update write_time (correct)\n");
1696 : }
1697 :
1698 : /* sleep */
1699 0 : smb_msleep(3 * msec);
1700 :
1701 : /* get the initial times */
1702 0 : GET_INFO_BOTH(finfo1,pinfo1);
1703 0 : COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
1704 :
1705 : /*
1706 : * demonstrate that a truncate write always
1707 : * updates the write time immediately
1708 : */
1709 0 : for (i=0; i < 3; i++) {
1710 0 : smb_msleep(2 * msec);
1711 : /* do a write */
1712 0 : torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1713 0 : written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
1714 0 : 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 0 : GET_INFO_BOTH(finfo2,pinfo2);
1721 0 : COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1722 0 : finfo1 = finfo2;
1723 : }
1724 :
1725 : /* sleep */
1726 0 : smb_msleep(3 * msec);
1727 :
1728 0 : GET_INFO_BOTH(finfo3,pinfo3);
1729 0 : COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
1730 :
1731 : /*
1732 : * the close doesn't update the write time
1733 : */
1734 0 : torture_comment(tctx, "Close the file handle\n");
1735 0 : smbcli_close(cli->tree, fnum1);
1736 0 : fnum1 = -1;
1737 :
1738 0 : GET_INFO_PATH(pinfo4);
1739 0 : COMPARE_WRITE_TIME_EQUAL(pinfo4, pinfo3);
1740 :
1741 0 : if (pinfo4.basic_info.out.write_time == pinfo3.basic_info.out.write_time) {
1742 0 : torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
1743 : }
1744 :
1745 0 : done:
1746 0 : if (fnum1 != -1)
1747 0 : smbcli_close(cli->tree, fnum1);
1748 0 : smbcli_unlink(cli->tree, fname);
1749 0 : smbcli_deltree(cli->tree, BASEDIR);
1750 :
1751 0 : 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 0 : static bool test_delayed_write_update3b(struct torture_context *tctx,
1760 : struct smbcli_state *cli,
1761 : struct smbcli_state *cli2)
1762 : {
1763 : union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1764 : union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1765 0 : const char *fname = BASEDIR "\\torture_file3b.txt";
1766 0 : int fnum1 = -1;
1767 0 : bool ret = true;
1768 : ssize_t written;
1769 : struct timeval start;
1770 : struct timeval end;
1771 0 : double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1772 0 : int normal_delay = 2000000;
1773 0 : double sec = ((double)used_delay) / ((double)normal_delay);
1774 0 : int msec = 1000 * sec;
1775 :
1776 0 : torture_comment(tctx, "\nRunning test_delayed_write_update3b\n");
1777 :
1778 0 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1779 :
1780 0 : torture_comment(tctx, "Open the file handle\n");
1781 0 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1782 0 : 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 0 : finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1789 0 : finfo0.basic_info.in.file.fnum = fnum1;
1790 0 : finfo1 = finfo0;
1791 0 : finfo2 = finfo0;
1792 0 : finfo3 = finfo0;
1793 0 : pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1794 0 : pinfo0.basic_info.in.file.path = fname;
1795 0 : pinfo1 = pinfo0;
1796 0 : pinfo2 = pinfo0;
1797 0 : pinfo3 = pinfo0;
1798 0 : pinfo4 = pinfo0;
1799 :
1800 : /* get the initial times */
1801 0 : 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 0 : smb_msleep(5 * msec);
1808 :
1809 : /* get the initial times */
1810 0 : GET_INFO_BOTH(finfo1,pinfo1);
1811 0 : COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1812 :
1813 : /*
1814 : * make sure the write time is updated 2 seconds later
1815 : * calcuated from the first write
1816 : * (but expect up to 5 seconds extra time for a busy server)
1817 : */
1818 0 : start = timeval_current();
1819 0 : end = timeval_add(&start, 7 * sec, 0);
1820 0 : while (!timeval_expired(&end)) {
1821 : /* do a write */
1822 0 : torture_comment(tctx, "Do a write on the file handle\n");
1823 0 : written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1824 0 : 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 0 : GET_INFO_FILE(finfo1);
1831 :
1832 0 : if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
1833 0 : double diff = timeval_elapsed(&start);
1834 0 : 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 0 : 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 0 : break;
1846 : }
1847 0 : smb_msleep(0.5 * msec);
1848 : }
1849 :
1850 0 : GET_INFO_BOTH(finfo1,pinfo1);
1851 0 : COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
1852 :
1853 : /* sure any further write doesn't update the write time */
1854 0 : start = timeval_current();
1855 0 : end = timeval_add(&start, 15 * sec, 0);
1856 0 : while (!timeval_expired(&end)) {
1857 : /* do a write */
1858 0 : torture_comment(tctx, "Do a write on the file handle\n");
1859 0 : written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
1860 0 : 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 0 : GET_INFO_BOTH(finfo2,pinfo2);
1867 :
1868 0 : 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 0 : smb_msleep(1 * msec);
1877 : }
1878 :
1879 0 : GET_INFO_BOTH(finfo2,pinfo2);
1880 0 : COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
1881 0 : if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
1882 0 : torture_comment(tctx, "Server did not update write_time (correct)\n");
1883 : }
1884 :
1885 : /* sleep */
1886 0 : smb_msleep(5 * msec);
1887 :
1888 0 : GET_INFO_BOTH(finfo3,pinfo3);
1889 0 : 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 0 : torture_comment(tctx, "Close the file handle\n");
1896 0 : smbcli_close(cli->tree, fnum1);
1897 0 : fnum1 = -1;
1898 :
1899 0 : GET_INFO_PATH(pinfo4);
1900 0 : COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
1901 :
1902 0 : if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
1903 0 : torture_comment(tctx, "Server updated the write_time on close (correct)\n");
1904 : }
1905 :
1906 0 : done:
1907 0 : if (fnum1 != -1)
1908 0 : smbcli_close(cli->tree, fnum1);
1909 0 : smbcli_unlink(cli->tree, fname);
1910 0 : smbcli_deltree(cli->tree, BASEDIR);
1911 :
1912 0 : 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 0 : static bool test_delayed_write_update3c(struct torture_context *tctx,
1923 : struct smbcli_state *cli,
1924 : struct smbcli_state *cli2)
1925 : {
1926 : union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
1927 : union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
1928 0 : const char *fname = BASEDIR "\\torture_file3c.txt";
1929 0 : int fnum1 = -1;
1930 0 : bool ret = true;
1931 : ssize_t written;
1932 : int i;
1933 : struct timeval start;
1934 : struct timeval end;
1935 0 : double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
1936 0 : int normal_delay = 2000000;
1937 0 : double sec = ((double)used_delay) / ((double)normal_delay);
1938 0 : int msec = 1000 * sec;
1939 :
1940 0 : torture_comment(tctx, "\nRunning test_delayed_write_update3c\n");
1941 :
1942 0 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1943 :
1944 0 : torture_comment(tctx, "Open the file handle\n");
1945 0 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
1946 0 : 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 0 : finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1953 0 : finfo0.basic_info.in.file.fnum = fnum1;
1954 0 : finfo1 = finfo0;
1955 0 : finfo2 = finfo0;
1956 0 : finfo3 = finfo0;
1957 0 : pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
1958 0 : pinfo0.basic_info.in.file.path = fname;
1959 0 : pinfo1 = pinfo0;
1960 0 : pinfo2 = pinfo0;
1961 0 : pinfo3 = pinfo0;
1962 0 : pinfo4 = pinfo0;
1963 :
1964 : /* get the initial times */
1965 0 : 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 0 : smb_msleep(5 * msec);
1972 :
1973 : /* get the initial times */
1974 0 : GET_INFO_BOTH(finfo1,pinfo1);
1975 0 : COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
1976 :
1977 : /*
1978 : * demonstrate that a truncate write always
1979 : * updates the write time immediately
1980 : */
1981 0 : for (i=0; i < 3; i++) {
1982 0 : smb_msleep(2 * msec);
1983 : /* do a write */
1984 0 : torture_comment(tctx, "Do a truncate SMBwrite [%d] on the file handle\n", i);
1985 0 : written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
1986 0 : 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 0 : GET_INFO_BOTH(finfo2,pinfo2);
1993 0 : COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
1994 0 : finfo1 = finfo2;
1995 : }
1996 :
1997 0 : start = timeval_current();
1998 0 : end = timeval_add(&start, 7 * sec, 0);
1999 0 : while (!timeval_expired(&end)) {
2000 : /* do a write */
2001 0 : torture_comment(tctx, "Do a write on the file handle\n");
2002 0 : written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2003 0 : 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 0 : GET_INFO_FILE(finfo2);
2010 :
2011 0 : 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 0 : smb_msleep(1 * msec);
2020 : }
2021 :
2022 0 : GET_INFO_BOTH(finfo2,pinfo2);
2023 0 : COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2024 0 : if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2025 0 : torture_comment(tctx, "Server did not update write_time (correct)\n");
2026 : }
2027 :
2028 : /* sleep */
2029 0 : smb_msleep(5 * msec);
2030 :
2031 : /* get the initial times */
2032 0 : GET_INFO_BOTH(finfo1,pinfo1);
2033 0 : COMPARE_WRITE_TIME_EQUAL(finfo1, finfo2);
2034 :
2035 : /*
2036 : * demonstrate that a truncate write always
2037 : * updates the write time immediately
2038 : */
2039 0 : for (i=0; i < 3; i++) {
2040 0 : smb_msleep(2 * msec);
2041 : /* do a write */
2042 0 : torture_comment(tctx, "Do a truncate write [%d] on the file handle\n", i);
2043 0 : written = smbcli_smbwrite(cli->tree, fnum1, "x", 512, 0);
2044 0 : 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 0 : GET_INFO_BOTH(finfo2,pinfo2);
2051 0 : COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2052 0 : finfo1 = finfo2;
2053 : }
2054 :
2055 : /* sleep */
2056 0 : smb_msleep(5 * msec);
2057 :
2058 0 : GET_INFO_BOTH(finfo2,pinfo2);
2059 0 : COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2060 :
2061 : /* sure any further write doesn't update the write time */
2062 0 : start = timeval_current();
2063 0 : end = timeval_add(&start, 15 * sec, 0);
2064 0 : while (!timeval_expired(&end)) {
2065 : /* do a write */
2066 0 : torture_comment(tctx, "Do a write on the file handle\n");
2067 0 : written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2068 0 : 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 0 : GET_INFO_BOTH(finfo2,pinfo2);
2075 :
2076 0 : 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 0 : smb_msleep(1 * msec);
2085 : }
2086 :
2087 0 : GET_INFO_BOTH(finfo2,pinfo2);
2088 0 : COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2089 0 : if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2090 0 : torture_comment(tctx, "Server did not update write_time (correct)\n");
2091 : }
2092 :
2093 : /* sleep */
2094 0 : smb_msleep(5 * msec);
2095 :
2096 0 : GET_INFO_BOTH(finfo3,pinfo3);
2097 0 : 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 0 : torture_comment(tctx, "Close the file handle\n");
2104 0 : smbcli_close(cli->tree, fnum1);
2105 0 : fnum1 = -1;
2106 :
2107 0 : GET_INFO_PATH(pinfo4);
2108 0 : COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2109 :
2110 0 : if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2111 0 : torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2112 : }
2113 :
2114 0 : done:
2115 0 : if (fnum1 != -1)
2116 0 : smbcli_close(cli->tree, fnum1);
2117 0 : smbcli_unlink(cli->tree, fname);
2118 0 : smbcli_deltree(cli->tree, BASEDIR);
2119 :
2120 0 : 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 0 : static bool test_delayed_write_update4(struct torture_context *tctx,
2130 : struct smbcli_state *cli,
2131 : struct smbcli_state *cli2)
2132 : {
2133 : union smb_fileinfo finfo0, finfo1, finfo2, finfo3;
2134 : union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4;
2135 0 : const char *fname = BASEDIR "\\torture_file4.txt";
2136 0 : int fnum1 = -1;
2137 0 : bool ret = true;
2138 : ssize_t written;
2139 : struct timeval start;
2140 : struct timeval end;
2141 0 : double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2142 0 : int normal_delay = 2000000;
2143 0 : double sec = ((double)used_delay) / ((double)normal_delay);
2144 0 : int msec = 1000 * sec;
2145 :
2146 0 : torture_comment(tctx, "\nRunning test_delayed_write_update4\n");
2147 :
2148 0 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2149 :
2150 0 : torture_comment(tctx, "Open the file handle\n");
2151 0 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2152 0 : 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 0 : finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2159 0 : finfo0.basic_info.in.file.fnum = fnum1;
2160 0 : finfo1 = finfo0;
2161 0 : finfo2 = finfo0;
2162 0 : finfo3 = finfo0;
2163 0 : pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2164 0 : pinfo0.basic_info.in.file.path = fname;
2165 0 : pinfo1 = pinfo0;
2166 0 : pinfo2 = pinfo0;
2167 0 : pinfo3 = pinfo0;
2168 0 : pinfo4 = pinfo0;
2169 :
2170 : /* get the initial times */
2171 0 : GET_INFO_BOTH(finfo0,pinfo0);
2172 :
2173 : /* sleep a bit */
2174 0 : smb_msleep(5 * msec);
2175 :
2176 : /* do a write */
2177 0 : torture_comment(tctx, "Do a write on the file handle\n");
2178 0 : written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2179 0 : 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 0 : GET_INFO_BOTH(finfo1,pinfo1);
2186 0 : COMPARE_WRITE_TIME_EQUAL(finfo1,finfo0);
2187 :
2188 : /*
2189 : * make sure the write time is updated 2 seconds later
2190 : * calcuated from the first write
2191 : * (but expect up to 3 seconds extra time for a busy server)
2192 : */
2193 0 : start = timeval_current();
2194 0 : end = timeval_add(&start, 5 * sec, 0);
2195 0 : while (!timeval_expired(&end)) {
2196 : /* get the times after the first write */
2197 0 : GET_INFO_FILE(finfo1);
2198 :
2199 0 : if (finfo1.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
2200 0 : double diff = timeval_elapsed(&start);
2201 0 : 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 0 : 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 0 : break;
2213 : }
2214 0 : smb_msleep(0.5 * msec);
2215 : }
2216 :
2217 0 : GET_INFO_BOTH(finfo1,pinfo1);
2218 0 : COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
2219 :
2220 : /* sure any further write doesn't update the write time */
2221 0 : start = timeval_current();
2222 0 : end = timeval_add(&start, 15 * sec, 0);
2223 0 : while (!timeval_expired(&end)) {
2224 : /* do a write */
2225 0 : torture_comment(tctx, "Do a write on the file handle\n");
2226 0 : written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2227 0 : 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 0 : GET_INFO_BOTH(finfo2,pinfo2);
2234 :
2235 0 : 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 0 : smb_msleep(1 * msec);
2244 : }
2245 :
2246 0 : GET_INFO_BOTH(finfo2,pinfo2);
2247 0 : COMPARE_WRITE_TIME_EQUAL(finfo2, finfo1);
2248 0 : if (finfo2.basic_info.out.write_time == finfo1.basic_info.out.write_time) {
2249 0 : torture_comment(tctx, "Server did not updatewrite_time (correct)\n");
2250 : }
2251 :
2252 : /* sleep */
2253 0 : smb_msleep(5 * msec);
2254 :
2255 0 : GET_INFO_BOTH(finfo3,pinfo3);
2256 0 : 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 0 : torture_comment(tctx, "Close the file handle\n");
2263 0 : smbcli_close(cli->tree, fnum1);
2264 0 : fnum1 = -1;
2265 :
2266 0 : GET_INFO_PATH(pinfo4);
2267 0 : COMPARE_WRITE_TIME_GREATER(pinfo4, pinfo3);
2268 :
2269 0 : if (pinfo4.basic_info.out.write_time > pinfo3.basic_info.out.write_time) {
2270 0 : torture_comment(tctx, "Server updated the write_time on close (correct)\n");
2271 : }
2272 :
2273 0 : done:
2274 0 : if (fnum1 != -1)
2275 0 : smbcli_close(cli->tree, fnum1);
2276 0 : smbcli_unlink(cli->tree, fname);
2277 0 : smbcli_deltree(cli->tree, BASEDIR);
2278 :
2279 0 : return ret;
2280 : }
2281 :
2282 : /*
2283 : * Show writes and closes have no effect on updating times once a SETWRITETIME is done.
2284 : */
2285 :
2286 0 : static bool test_delayed_write_update5(struct torture_context *tctx,
2287 : struct smbcli_state *cli,
2288 : struct smbcli_state *cli2)
2289 : {
2290 : union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2291 : union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2292 0 : const char *fname = BASEDIR "\\torture_file5.txt";
2293 0 : int fnum1 = -1;
2294 0 : bool ret = true;
2295 : ssize_t written;
2296 : struct timeval start;
2297 : struct timeval end;
2298 0 : double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2299 0 : int normal_delay = 2000000;
2300 0 : double sec = ((double)used_delay) / ((double)normal_delay);
2301 0 : int msec = 1000 * sec;
2302 :
2303 0 : torture_comment(tctx, "\nRunning test_delayed_write_update5\n");
2304 :
2305 0 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2306 :
2307 0 : torture_comment(tctx, "Open the file handle\n");
2308 0 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2309 0 : 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 0 : finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2316 0 : finfo0.basic_info.in.file.fnum = fnum1;
2317 0 : finfo1 = finfo0;
2318 0 : finfo2 = finfo0;
2319 0 : finfo3 = finfo0;
2320 0 : finfo4 = finfo0;
2321 0 : finfo5 = finfo0;
2322 0 : pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2323 0 : pinfo0.basic_info.in.file.path = fname;
2324 0 : pinfo1 = pinfo0;
2325 0 : pinfo2 = pinfo0;
2326 0 : pinfo3 = pinfo0;
2327 0 : pinfo4 = pinfo0;
2328 0 : pinfo5 = pinfo0;
2329 0 : pinfo6 = pinfo0;
2330 :
2331 : /* get the initial times */
2332 0 : GET_INFO_BOTH(finfo0,pinfo0);
2333 :
2334 : /* do a write */
2335 0 : torture_comment(tctx, "Do a write on the file handle\n");
2336 0 : written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2337 0 : 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 0 : GET_INFO_BOTH(finfo1,pinfo1);
2344 0 : COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2345 :
2346 0 : torture_comment(tctx, "Set write time in the future on the file handle\n");
2347 0 : SET_INFO_FILE(finfo0, time(NULL) + 86400);
2348 0 : GET_INFO_BOTH(finfo2,pinfo2);
2349 0 : COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2350 :
2351 0 : torture_comment(tctx, "Set write time in the past on the file handle\n");
2352 0 : SET_INFO_FILE(finfo0, time(NULL) - 86400);
2353 0 : GET_INFO_BOTH(finfo2,pinfo2);
2354 0 : COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2355 :
2356 : /* make sure the 2 second delay from the first write are canceled */
2357 0 : start = timeval_current();
2358 0 : end = timeval_add(&start, 15 * sec, 0);
2359 0 : while (!timeval_expired(&end)) {
2360 :
2361 : /* get the times after the first write */
2362 0 : GET_INFO_BOTH(finfo3,pinfo3);
2363 :
2364 0 : 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 0 : smb_msleep(1 * msec);
2373 : }
2374 :
2375 0 : GET_INFO_BOTH(finfo3,pinfo3);
2376 0 : COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2377 0 : if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2378 0 : 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 0 : start = timeval_current();
2383 0 : end = timeval_add(&start, 15 * sec, 0);
2384 0 : while (!timeval_expired(&end)) {
2385 : /* do a write */
2386 0 : torture_comment(tctx, "Do a write on the file handle\n");
2387 0 : written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2388 0 : 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 0 : GET_INFO_BOTH(finfo4,pinfo4);
2395 :
2396 0 : 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 0 : smb_msleep(1 * msec);
2405 : }
2406 :
2407 0 : GET_INFO_BOTH(finfo4,pinfo4);
2408 0 : COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2409 0 : if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2410 0 : torture_comment(tctx, "Server did not update write_time (correct)\n");
2411 : }
2412 :
2413 : /* sleep */
2414 0 : smb_msleep(5 * msec);
2415 :
2416 0 : GET_INFO_BOTH(finfo5,pinfo5);
2417 0 : COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2418 :
2419 : /*
2420 : * the close doesn't update the write time
2421 : */
2422 0 : torture_comment(tctx, "Close the file handle\n");
2423 0 : smbcli_close(cli->tree, fnum1);
2424 0 : fnum1 = -1;
2425 :
2426 0 : GET_INFO_PATH(pinfo6);
2427 0 : COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2428 :
2429 0 : if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2430 0 : torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2431 : }
2432 :
2433 0 : done:
2434 0 : if (fnum1 != -1)
2435 0 : smbcli_close(cli->tree, fnum1);
2436 0 : smbcli_unlink(cli->tree, fname);
2437 0 : smbcli_deltree(cli->tree, BASEDIR);
2438 :
2439 0 : 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 0 : static bool test_delayed_write_update5b(struct torture_context *tctx,
2447 : struct smbcli_state *cli,
2448 : struct smbcli_state *cli2)
2449 : {
2450 : union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2451 : union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6;
2452 0 : const char *fname = BASEDIR "\\torture_fileb.txt";
2453 0 : int fnum1 = -1;
2454 0 : bool ret = true;
2455 : ssize_t written;
2456 : struct timeval start;
2457 : struct timeval end;
2458 0 : double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2459 0 : int normal_delay = 2000000;
2460 0 : double sec = ((double)used_delay) / ((double)normal_delay);
2461 0 : int msec = 1000 * sec;
2462 :
2463 0 : torture_comment(tctx, "\nRunning test_delayed_write_update5b\n");
2464 :
2465 0 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2466 :
2467 0 : torture_comment(tctx, "Open the file handle\n");
2468 0 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2469 0 : 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 0 : finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2476 0 : finfo0.basic_info.in.file.fnum = fnum1;
2477 0 : finfo1 = finfo0;
2478 0 : finfo2 = finfo0;
2479 0 : finfo3 = finfo0;
2480 0 : finfo4 = finfo0;
2481 0 : finfo5 = finfo0;
2482 0 : pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2483 0 : pinfo0.basic_info.in.file.path = fname;
2484 0 : pinfo1 = pinfo0;
2485 0 : pinfo2 = pinfo0;
2486 0 : pinfo3 = pinfo0;
2487 0 : pinfo4 = pinfo0;
2488 0 : pinfo5 = pinfo0;
2489 0 : pinfo6 = pinfo0;
2490 :
2491 : /* get the initial times */
2492 0 : GET_INFO_BOTH(finfo0,pinfo0);
2493 :
2494 : /* do a write */
2495 0 : torture_comment(tctx, "Do a write on the file handle\n");
2496 0 : written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2497 0 : 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 0 : GET_INFO_BOTH(finfo1,pinfo1);
2504 0 : COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2505 :
2506 0 : torture_comment(tctx, "Set write time in the future on the file handle\n");
2507 0 : SET_INFO_FILE(finfo0, time(NULL) + 86400);
2508 0 : GET_INFO_BOTH(finfo2,pinfo2);
2509 0 : COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2510 :
2511 0 : torture_comment(tctx, "Set write time in the past on the file handle\n");
2512 0 : SET_INFO_FILE(finfo0, time(NULL) - 86400);
2513 0 : GET_INFO_BOTH(finfo2,pinfo2);
2514 0 : COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2515 :
2516 : /* make sure the 2 second delay from the first write are canceled */
2517 0 : start = timeval_current();
2518 0 : end = timeval_add(&start, 15 * sec, 0);
2519 0 : while (!timeval_expired(&end)) {
2520 :
2521 : /* get the times after the first write */
2522 0 : GET_INFO_BOTH(finfo3,pinfo3);
2523 :
2524 0 : 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 0 : smb_msleep(1 * msec);
2533 : }
2534 :
2535 0 : GET_INFO_BOTH(finfo3,pinfo3);
2536 0 : COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2537 0 : if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2538 0 : torture_comment(tctx, "Server did not update write_time (correct)\n");
2539 : }
2540 :
2541 : /* Do any further write (truncates) update the write time ? */
2542 0 : start = timeval_current();
2543 0 : end = timeval_add(&start, 15 * sec, 0);
2544 0 : while (!timeval_expired(&end)) {
2545 : /* do a write */
2546 0 : torture_comment(tctx, "Do a truncate write on the file handle\n");
2547 0 : written = smbcli_smbwrite(cli->tree, fnum1, "x", 1024, 0);
2548 0 : 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 0 : GET_INFO_BOTH(finfo4,pinfo4);
2555 :
2556 0 : 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 0 : smb_msleep(1 * msec);
2565 : }
2566 :
2567 0 : GET_INFO_BOTH(finfo4,pinfo4);
2568 0 : COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2569 0 : if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2570 0 : torture_comment(tctx, "Server did not update write_time (correct)\n");
2571 : }
2572 :
2573 : /* sleep */
2574 0 : smb_msleep(5 * msec);
2575 :
2576 0 : GET_INFO_BOTH(finfo5,pinfo5);
2577 0 : COMPARE_WRITE_TIME_EQUAL(finfo5, finfo4);
2578 :
2579 : /*
2580 : * the close doesn't update the write time
2581 : */
2582 0 : torture_comment(tctx, "Close the file handle\n");
2583 0 : smbcli_close(cli->tree, fnum1);
2584 0 : fnum1 = -1;
2585 :
2586 0 : GET_INFO_PATH(pinfo6);
2587 0 : COMPARE_WRITE_TIME_EQUAL(pinfo6, pinfo5);
2588 :
2589 0 : if (pinfo6.basic_info.out.write_time == pinfo5.basic_info.out.write_time) {
2590 0 : torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2591 : }
2592 :
2593 0 : done:
2594 0 : if (fnum1 != -1)
2595 0 : smbcli_close(cli->tree, fnum1);
2596 0 : smbcli_unlink(cli->tree, fname);
2597 0 : smbcli_deltree(cli->tree, BASEDIR);
2598 :
2599 0 : 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 0 : static bool test_delayed_write_update6(struct torture_context *tctx,
2611 : struct smbcli_state *cli,
2612 : struct smbcli_state *cli2)
2613 : {
2614 : union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfo4, finfo5;
2615 : union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5, pinfo6, pinfo7;
2616 0 : const char *fname = BASEDIR "\\torture_file6.txt";
2617 0 : int fnum1 = -1;
2618 0 : int fnum2 = -1;
2619 0 : bool ret = true;
2620 : ssize_t written;
2621 : struct timeval start;
2622 : struct timeval end;
2623 0 : double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2624 0 : int normal_delay = 2000000;
2625 0 : double sec = ((double)used_delay) / ((double)normal_delay);
2626 0 : int msec = 1000 * sec;
2627 0 : bool first = true;
2628 :
2629 0 : torture_comment(tctx, "\nRunning test_delayed_write_update6\n");
2630 :
2631 0 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2632 0 : again:
2633 0 : torture_comment(tctx, "Open the file handle\n");
2634 0 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2635 0 : 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 0 : if (fnum2 == -1) {
2642 0 : torture_comment(tctx, "Open the 2nd file handle on 2nd connection\n");
2643 0 : fnum2 = smbcli_open(cli2->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2644 0 : 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 0 : finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2652 0 : finfo0.basic_info.in.file.fnum = fnum1;
2653 0 : finfo1 = finfo0;
2654 0 : finfo2 = finfo0;
2655 0 : finfo3 = finfo0;
2656 0 : finfo4 = finfo0;
2657 0 : finfo5 = finfo0;
2658 0 : pinfo0.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2659 0 : pinfo0.basic_info.in.file.path = fname;
2660 0 : pinfo1 = pinfo0;
2661 0 : pinfo2 = pinfo0;
2662 0 : pinfo3 = pinfo0;
2663 0 : pinfo4 = pinfo0;
2664 0 : pinfo5 = pinfo0;
2665 0 : pinfo6 = pinfo0;
2666 0 : pinfo7 = pinfo0;
2667 :
2668 : /* get the initial times */
2669 0 : GET_INFO_BOTH(finfo0,pinfo0);
2670 :
2671 : /* do a write */
2672 0 : torture_comment(tctx, "Do a write on the file handle\n");
2673 0 : written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2674 0 : 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 0 : GET_INFO_BOTH(finfo1,pinfo1);
2681 0 : COMPARE_WRITE_TIME_EQUAL(finfo1, finfo0);
2682 :
2683 0 : torture_comment(tctx, "Set write time in the future on the 2nd file handle\n");
2684 0 : SET_INFO_FILE_EX(finfo0, time(NULL) + 86400, cli2->tree, fnum2);
2685 0 : GET_INFO_BOTH(finfo2,pinfo2);
2686 0 : COMPARE_WRITE_TIME_GREATER(finfo2, finfo1);
2687 :
2688 0 : torture_comment(tctx, "Set write time in the past on the 2nd file handle\n");
2689 0 : SET_INFO_FILE_EX(finfo0, time(NULL) - 86400, cli2->tree, fnum2);
2690 0 : GET_INFO_BOTH(finfo2,pinfo2);
2691 0 : COMPARE_WRITE_TIME_LESS(finfo2, finfo1);
2692 :
2693 : /* make sure the 2 second delay from the first write are canceled */
2694 0 : start = timeval_current();
2695 0 : end = timeval_add(&start, 10 * sec, 0);
2696 0 : while (!timeval_expired(&end)) {
2697 :
2698 : /* get the times after the first write */
2699 0 : GET_INFO_BOTH(finfo3,pinfo3);
2700 :
2701 0 : 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 0 : smb_msleep(1 * msec);
2710 : }
2711 :
2712 0 : GET_INFO_BOTH(finfo3,pinfo3);
2713 0 : COMPARE_WRITE_TIME_EQUAL(finfo3, finfo2);
2714 0 : if (finfo3.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2715 0 : 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 0 : start = timeval_current();
2720 0 : end = timeval_add(&start, 10 * sec, 0);
2721 0 : while (!timeval_expired(&end)) {
2722 : /* do a write */
2723 0 : torture_comment(tctx, "Do a write on the file handle\n");
2724 0 : written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
2725 0 : 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 0 : GET_INFO_BOTH(finfo4,pinfo4);
2732 :
2733 0 : 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 0 : smb_msleep(1 * msec);
2742 : }
2743 :
2744 0 : GET_INFO_BOTH(finfo4,pinfo4);
2745 0 : COMPARE_WRITE_TIME_EQUAL(finfo4, finfo3);
2746 0 : if (finfo4.basic_info.out.write_time == finfo3.basic_info.out.write_time) {
2747 0 : torture_comment(tctx, "Server did not update write_time (correct)\n");
2748 : }
2749 :
2750 : /* sleep */
2751 0 : smb_msleep(5 * msec);
2752 :
2753 0 : GET_INFO_BOTH(finfo5,pinfo5);
2754 0 : 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 0 : torture_comment(tctx, "Close the file handle\n");
2761 0 : smbcli_close(cli->tree, fnum1);
2762 0 : fnum1 = -1;
2763 :
2764 0 : GET_INFO_PATH(pinfo6);
2765 0 : COMPARE_WRITE_TIME_GREATER(pinfo6, pinfo5);
2766 :
2767 0 : if (pinfo6.basic_info.out.write_time > pinfo5.basic_info.out.write_time) {
2768 0 : 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 0 : finfo5.basic_info.in.file.fnum = fnum2;
2773 0 : GET_INFO_FILE2(finfo5);
2774 0 : COMPARE_WRITE_TIME_EQUAL(finfo5, pinfo6);
2775 :
2776 : /* See if we have lost the sticky write time on handle2 */
2777 0 : smb_msleep(3 * msec);
2778 0 : 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 0 : start = timeval_current();
2782 0 : end = timeval_add(&start, 10 * sec, 0);
2783 0 : while (!timeval_expired(&end)) {
2784 : /* do a write */
2785 0 : torture_comment(tctx, "Do a write on the second file handle\n");
2786 0 : written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
2787 0 : 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 0 : GET_INFO_FILE2(finfo5);
2794 0 : GET_INFO_PATH(pinfo6);
2795 :
2796 0 : 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 0 : smb_msleep(1 * msec);
2805 : }
2806 :
2807 : /* What about a truncate write ? */
2808 0 : start = timeval_current();
2809 0 : end = timeval_add(&start, 10 * sec, 0);
2810 0 : while (!timeval_expired(&end)) {
2811 : /* do a write */
2812 0 : torture_comment(tctx, "Do a truncate write on the second file handle\n");
2813 0 : written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 0);
2814 0 : 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 0 : GET_INFO_FILE2(finfo5);
2821 0 : GET_INFO_PATH(pinfo6);
2822 :
2823 0 : 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 0 : smb_msleep(1 * msec);
2832 : }
2833 :
2834 :
2835 : /* keep the 2nd handle open and rerun tests */
2836 0 : if (first) {
2837 0 : first = false;
2838 0 : 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 0 : torture_comment(tctx, "Close the 2nd file handle\n");
2846 0 : smbcli_close(cli2->tree, fnum2);
2847 0 : fnum2 = -1;
2848 :
2849 0 : GET_INFO_PATH(pinfo7);
2850 0 : COMPARE_WRITE_TIME_EQUAL(pinfo7, pinfo6);
2851 :
2852 0 : if (pinfo7.basic_info.out.write_time == pinfo6.basic_info.out.write_time) {
2853 0 : torture_comment(tctx, "Server did not update the write_time on close (correct)\n");
2854 : }
2855 :
2856 0 : done:
2857 0 : if (fnum1 != -1)
2858 0 : smbcli_close(cli->tree, fnum1);
2859 0 : if (fnum2 != -1)
2860 0 : smbcli_close(cli2->tree, fnum2);
2861 0 : smbcli_unlink(cli->tree, fname);
2862 0 : smbcli_deltree(cli->tree, BASEDIR);
2863 :
2864 0 : return ret;
2865 : }
2866 :
2867 0 : static bool test_delayed_write_update7(struct torture_context *tctx, struct smbcli_state *cli)
2868 : {
2869 : union smb_open open_parms;
2870 : union smb_fileinfo finfo1, finfo2, finfo3;
2871 0 : const char *fname = BASEDIR "\\torture_file7.txt";
2872 : NTSTATUS status;
2873 0 : int fnum1 = -1;
2874 0 : bool ret = true;
2875 : TALLOC_CTX *mem_ctx;
2876 :
2877 0 : torture_comment(tctx, "\nRunning test_delayed_write_update7 (timestamp resolution test)\n");
2878 :
2879 0 : mem_ctx = talloc_init("test_delayed_write_update7");
2880 0 : if (!mem_ctx) return false;
2881 :
2882 0 : ZERO_STRUCT(finfo1);
2883 0 : ZERO_STRUCT(finfo2);
2884 0 : ZERO_STRUCT(finfo3);
2885 0 : ZERO_STRUCT(open_parms);
2886 :
2887 0 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2888 :
2889 : /* Create the file. */
2890 0 : fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
2891 0 : if (fnum1 == -1) {
2892 0 : torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
2893 0 : return false;
2894 : }
2895 :
2896 0 : finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
2897 0 : finfo1.basic_info.in.file.fnum = fnum1;
2898 0 : finfo2 = finfo1;
2899 0 : finfo3 = finfo1;
2900 :
2901 : /* Get the initial timestamps. */
2902 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo1);
2903 :
2904 0 : torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
2905 :
2906 : /* Set the pending write time to a value with ns. */
2907 0 : SET_INFO_FILE_NS(finfo, time(NULL) + 86400, 103, cli->tree, fnum1);
2908 :
2909 : /* Get the current pending write time by fnum. */
2910 0 : status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
2911 :
2912 0 : torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
2913 :
2914 : /* Ensure the time is actually different. */
2915 0 : if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
2916 0 : torture_result(tctx, TORTURE_FAIL,
2917 : "setfileinfo time matches original fileinfo time");
2918 0 : ret = false;
2919 : }
2920 :
2921 : /* Get the current pending write time by path. */
2922 0 : finfo3.basic_info.in.file.path = fname;
2923 0 : status = smb_raw_pathinfo(cli->tree, tctx, &finfo3);
2924 :
2925 0 : if (finfo2.basic_info.out.write_time != finfo3.basic_info.out.write_time) {
2926 0 : torture_result(tctx, TORTURE_FAIL,
2927 : "qpathinfo time doesn't match fileinfo time");
2928 0 : ret = false;
2929 : }
2930 :
2931 : /* Now close the file. Re-open and check that the write
2932 : time is identical to the one we wrote. */
2933 :
2934 0 : smbcli_close(cli->tree, fnum1);
2935 :
2936 0 : open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
2937 0 : open_parms.ntcreatex.in.flags = 0;
2938 0 : open_parms.ntcreatex.in.access_mask = SEC_GENERIC_READ;
2939 0 : open_parms.ntcreatex.in.file_attr = 0;
2940 0 : open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE|
2941 : NTCREATEX_SHARE_ACCESS_READ|
2942 : NTCREATEX_SHARE_ACCESS_WRITE;
2943 0 : open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
2944 0 : open_parms.ntcreatex.in.create_options = 0;
2945 0 : open_parms.ntcreatex.in.fname = fname;
2946 :
2947 0 : status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
2948 0 : talloc_free(mem_ctx);
2949 :
2950 0 : if (!NT_STATUS_IS_OK(status)) {
2951 0 : torture_result(tctx, TORTURE_FAIL,
2952 : "setfileinfo time matches original fileinfo time");
2953 0 : ret = false;
2954 : }
2955 :
2956 0 : fnum1 = open_parms.ntcreatex.out.file.fnum;
2957 :
2958 : /* Check the returned time matches. */
2959 0 : if (open_parms.ntcreatex.out.write_time != finfo2.basic_info.out.write_time) {
2960 0 : torture_result(tctx, TORTURE_FAIL,
2961 : "final open time does not match set time");
2962 0 : ret = false;
2963 : }
2964 :
2965 0 : done:
2966 :
2967 0 : smbcli_close(cli->tree, fnum1);
2968 :
2969 0 : smbcli_unlink(cli->tree, fname);
2970 0 : smbcli_deltree(cli->tree, BASEDIR);
2971 0 : return ret;
2972 : }
2973 :
2974 : /*
2975 : Test if creating a file in a directory with an open handle updates the
2976 : write timestamp (it should).
2977 : */
2978 0 : static bool test_directory_update8(struct torture_context *tctx, struct smbcli_state *cli)
2979 : {
2980 : union smb_fileinfo dir_info1, dir_info2;
2981 : union smb_open open_parms;
2982 0 : const char *fname = BASEDIR "\\torture_file.txt";
2983 : NTSTATUS status;
2984 0 : int fnum1 = -1;
2985 0 : int fnum2 = -1;
2986 0 : bool ret = true;
2987 0 : double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
2988 0 : int normal_delay = 2000000;
2989 0 : double sec = ((double)used_delay) / ((double)normal_delay);
2990 0 : int msec = 1000 * sec;
2991 0 : TALLOC_CTX *mem_ctx = talloc_init("test_delayed_write_update8");
2992 :
2993 0 : if (!mem_ctx) return false;
2994 :
2995 0 : torture_comment(tctx, "\nRunning test directory write update\n");
2996 :
2997 0 : torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
2998 :
2999 : /* Open a handle on the directory - and leave it open. */
3000 0 : ZERO_STRUCT(open_parms);
3001 0 : open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
3002 0 : open_parms.ntcreatex.in.flags = 0;
3003 0 : open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ;
3004 0 : open_parms.ntcreatex.in.file_attr = 0;
3005 0 : open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE|
3006 : NTCREATEX_SHARE_ACCESS_READ|
3007 : NTCREATEX_SHARE_ACCESS_WRITE;
3008 0 : open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
3009 0 : open_parms.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
3010 0 : open_parms.ntcreatex.in.fname = BASEDIR;
3011 :
3012 0 : status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
3013 0 : talloc_free(mem_ctx);
3014 :
3015 0 : if (!NT_STATUS_IS_OK(status)) {
3016 0 : torture_result(tctx, TORTURE_FAIL,
3017 : "failed to open directory handle");
3018 0 : ret = false;
3019 0 : goto done;
3020 : }
3021 :
3022 0 : fnum1 = open_parms.ntcreatex.out.file.fnum;
3023 :
3024 : /* Store the returned write time. */
3025 0 : ZERO_STRUCT(dir_info1);
3026 0 : dir_info1.basic_info.out.write_time = open_parms.ntcreatex.out.write_time;
3027 :
3028 0 : torture_comment(tctx, "Initial write time %s\n",
3029 : nt_time_string(tctx, dir_info1.basic_info.out.write_time));
3030 :
3031 : /* sleep */
3032 0 : smb_msleep(3 * msec);
3033 :
3034 : /* Now create a file within the directory. */
3035 0 : fnum2 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
3036 0 : if (fnum2 == -1) {
3037 0 : torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname);
3038 0 : ret = false;
3039 0 : goto done;
3040 : }
3041 0 : smbcli_close(cli->tree, fnum2);
3042 :
3043 : /* Read the directory write time again. */
3044 0 : ZERO_STRUCT(dir_info2);
3045 0 : dir_info2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
3046 0 : dir_info2.basic_info.in.file.fnum = fnum1;
3047 :
3048 0 : status = smb_raw_fileinfo(cli->tree, tctx, &dir_info2);
3049 :
3050 0 : torture_assert_ntstatus_ok(tctx, status, "fileinfo failed");
3051 :
3052 : /* Ensure it's been incremented. */
3053 0 : COMPARE_WRITE_TIME_GREATER(dir_info2, dir_info1);
3054 :
3055 0 : torture_comment(tctx, "Updated write time %s\n",
3056 : nt_time_string(tctx, dir_info2.basic_info.out.write_time));
3057 :
3058 0 : done:
3059 :
3060 0 : if (fnum1 != -1)
3061 0 : smbcli_close(cli->tree, fnum1);
3062 0 : smbcli_unlink(cli->tree, fname);
3063 0 : smbcli_deltree(cli->tree, BASEDIR);
3064 :
3065 0 : return ret;
3066 : }
3067 :
3068 : /*
3069 : testing of delayed update of write_time
3070 : */
3071 964 : struct torture_suite *torture_delay_write(TALLOC_CTX *ctx)
3072 : {
3073 964 : struct torture_suite *suite = torture_suite_create(ctx, "delaywrite");
3074 :
3075 964 : torture_suite_add_2smb_test(suite, "finfo update on close", test_finfo_after_write);
3076 964 : torture_suite_add_1smb_test(suite, "delayed update of write time", test_delayed_write_update);
3077 964 : torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate", test_delayed_write_update1);
3078 964 : torture_suite_add_1smb_test(suite, "update of write time and SMBwrite truncate expand", test_delayed_write_update1a);
3079 964 : torture_suite_add_1smb_test(suite, "update of write time using SET_END_OF_FILE", test_delayed_write_update1b);
3080 964 : torture_suite_add_1smb_test(suite, "update of write time using SET_ALLOCATION_SIZE", test_delayed_write_update1c);
3081 964 : torture_suite_add_2smb_test(suite, "delayed update of write time using 2 connections", test_delayed_write_update2);
3082 964 : torture_suite_add_2smb_test(suite, "delayed update of write time 3", test_delayed_write_update3);
3083 964 : torture_suite_add_2smb_test(suite, "delayed update of write time 3a", test_delayed_write_update3a);
3084 964 : torture_suite_add_2smb_test(suite, "delayed update of write time 3b", test_delayed_write_update3b);
3085 964 : torture_suite_add_2smb_test(suite, "delayed update of write time 3c", test_delayed_write_update3c);
3086 964 : torture_suite_add_2smb_test(suite, "delayed update of write time 4", test_delayed_write_update4);
3087 964 : torture_suite_add_2smb_test(suite, "delayed update of write time 5", test_delayed_write_update5);
3088 964 : torture_suite_add_2smb_test(suite, "delayed update of write time 5b", test_delayed_write_update5b);
3089 964 : torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6);
3090 964 : torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7);
3091 964 : torture_suite_add_1smb_test(suite, "directory timestamp update test", test_directory_update8);
3092 :
3093 964 : return suite;
3094 : }
|