Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : test timestamps
5 :
6 : Copyright (C) Ralph Boehme 2019
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include "includes.h"
23 : #include "libcli/smb2/smb2.h"
24 : #include "libcli/smb2/smb2_calls.h"
25 : #include "torture/torture.h"
26 : #include "torture/util.h"
27 : #include "torture/smb2/proto.h"
28 :
29 : #define BASEDIR "smb2-timestamps"
30 : #define FNAME "testfile.dat"
31 :
32 0 : static bool test_close_no_attrib(struct torture_context *tctx,
33 : struct smb2_tree *tree)
34 : {
35 0 : const char *filename = BASEDIR "/" FNAME;
36 : struct smb2_create cr;
37 0 : struct smb2_handle handle = {{0}};
38 0 : struct smb2_handle testdirh = {{0}};
39 : struct smb2_close c;
40 : NTSTATUS status;
41 0 : bool ret = true;
42 :
43 0 : smb2_deltree(tree, BASEDIR);
44 :
45 0 : status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
46 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
47 : "torture_smb2_testdir failed\n");
48 0 : smb2_util_close(tree, testdirh);
49 :
50 0 : cr = (struct smb2_create) {
51 : .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED,
52 : .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
53 : .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
54 : .in.create_disposition = NTCREATEX_DISP_OPEN_IF,
55 : .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS,
56 : .in.fname = filename,
57 : };
58 :
59 0 : status = smb2_create(tree, tctx, &cr);
60 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
61 : "smb2_create failed\n");
62 0 : handle = cr.out.file.handle;
63 :
64 0 : c = (struct smb2_close) {
65 : .in.file.handle = handle,
66 : };
67 :
68 0 : status = smb2_close(tree, &c);
69 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
70 : "close failed\n");
71 0 : ZERO_STRUCT(handle);
72 :
73 0 : torture_assert_u64_equal_goto(tctx, c.out.create_time, NTTIME_OMIT,
74 : ret, done, "Unexpected create time\n");
75 0 : torture_assert_u64_equal_goto(tctx, c.out.access_time, NTTIME_OMIT,
76 : ret, done, "Unexpected access time\n");
77 0 : torture_assert_u64_equal_goto(tctx, c.out.write_time, NTTIME_OMIT,
78 : ret, done, "Unexpected write time\n");
79 0 : torture_assert_u64_equal_goto(tctx, c.out.change_time, NTTIME_OMIT,
80 : ret, done, "Unexpected change time\n");
81 0 : torture_assert_u64_equal_goto(tctx, c.out.alloc_size, 0,
82 : ret, done, "Unexpected allocation size\n");
83 0 : torture_assert_u64_equal_goto(tctx, c.out.size, 0,
84 : ret, done, "Unexpected size\n");
85 0 : torture_assert_u64_equal_goto(tctx, c.out.file_attr, 0,
86 : ret, done, "Unexpected attributes\n");
87 :
88 0 : done:
89 0 : if (!smb2_util_handle_empty(handle)) {
90 0 : smb2_util_close(tree, handle);
91 : }
92 0 : smb2_deltree(tree, BASEDIR);
93 0 : return ret;
94 : }
95 :
96 0 : static bool test_time_t(struct torture_context *tctx,
97 : struct smb2_tree *tree,
98 : const char *fname,
99 : time_t t)
100 : {
101 0 : char *filename = NULL;
102 : struct smb2_create cr;
103 0 : struct smb2_handle handle = {{0}};
104 0 : struct smb2_handle testdirh = {{0}};
105 0 : struct timespec ts = { .tv_sec = t };
106 : uint64_t nttime;
107 : union smb_fileinfo gi;
108 : union smb_setfileinfo si;
109 : struct smb2_find find;
110 : unsigned int count;
111 : union smb_search_data *d;
112 : NTSTATUS status;
113 0 : bool ret = true;
114 :
115 0 : smb2_deltree(tree, BASEDIR);
116 :
117 0 : status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
118 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
119 : "torture_smb2_testdir failed\n");
120 :
121 0 : filename = talloc_asprintf(tctx, "%s\\%s", BASEDIR, fname);
122 0 : torture_assert_not_null_goto(tctx, filename, ret, done,
123 : "talloc_asprintf failed\n");
124 :
125 0 : cr = (struct smb2_create) {
126 : .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED,
127 : .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
128 : .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
129 : .in.create_disposition = NTCREATEX_DISP_OPEN_IF,
130 : .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS,
131 : .in.fname = filename,
132 : };
133 :
134 0 : status = smb2_create(tree, tctx, &cr);
135 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
136 : "smb2_create failed\n");
137 0 : handle = cr.out.file.handle;
138 :
139 0 : si = (union smb_setfileinfo) {
140 : .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION,
141 : .basic_info.in.file.handle = handle,
142 : };
143 :
144 0 : nttime = full_timespec_to_nt_time(&ts);
145 0 : si.basic_info.in.create_time = nttime;
146 0 : si.basic_info.in.write_time = nttime;
147 0 : si.basic_info.in.change_time = nttime;
148 :
149 0 : status = smb2_setinfo_file(tree, &si);
150 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
151 : "smb2_setinfo_file failed\n");
152 :
153 0 : gi = (union smb_fileinfo) {
154 : .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
155 : .generic.in.file.handle = handle,
156 : };
157 :
158 0 : status = smb2_getinfo_file(tree, tctx, &gi);
159 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
160 : "smb2_getinfo_file failed\n");
161 :
162 0 : torture_comment(tctx, "Got: create: %s, write: %s, change: %s\n",
163 : nt_time_string(tctx, gi.basic_info.out.create_time),
164 : nt_time_string(tctx, gi.basic_info.out.write_time),
165 : nt_time_string(tctx, gi.basic_info.out.change_time));
166 :
167 0 : torture_assert_u64_equal_goto(tctx,
168 : nttime,
169 : gi.basic_info.out.create_time,
170 : ret, done,
171 : "Wrong create time\n");
172 0 : torture_assert_u64_equal_goto(tctx,
173 : nttime,
174 : gi.basic_info.out.write_time,
175 : ret, done,
176 : "Wrong write time\n");
177 0 : torture_assert_u64_equal_goto(tctx,
178 : nttime,
179 : gi.basic_info.out.change_time,
180 : ret, done,
181 : "Wrong change time\n");
182 :
183 0 : find = (struct smb2_find) {
184 : .in.file.handle = testdirh,
185 : .in.pattern = fname,
186 : .in.max_response_size = 0x1000,
187 : .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO,
188 : };
189 :
190 0 : status = smb2_find_level(tree, tree, &find, &count, &d);
191 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
192 : "smb2_find_level failed\n");
193 :
194 0 : torture_assert_u64_equal_goto(tctx,
195 : nttime,
196 : d[0].id_both_directory_info.create_time,
197 : ret, done,
198 : "Wrong create time\n");
199 0 : torture_assert_u64_equal_goto(tctx,
200 : nttime,
201 : d[0].id_both_directory_info.write_time,
202 : ret, done,
203 : "Wrong write time\n");
204 0 : torture_assert_u64_equal_goto(tctx,
205 : nttime,
206 : d[0].id_both_directory_info.change_time,
207 : ret, done,
208 : "Wrong change time\n");
209 :
210 0 : status = smb2_util_close(tree, handle);
211 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
212 : "smb2_util_close failed\n");
213 0 : ZERO_STRUCT(handle);
214 :
215 0 : cr = (struct smb2_create) {
216 : .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED,
217 : .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
218 : .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
219 : .in.create_disposition = NTCREATEX_DISP_OPEN,
220 : .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS,
221 : .in.fname = filename,
222 : };
223 :
224 0 : status = smb2_create(tree, tctx, &cr);
225 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
226 : "smb2_create failed\n");
227 0 : handle = cr.out.file.handle;
228 :
229 0 : gi = (union smb_fileinfo) {
230 : .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
231 : .generic.in.file.handle = handle,
232 : };
233 :
234 0 : status = smb2_getinfo_file(tree, tctx, &gi);
235 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
236 : "smb2_getinfo_file failed\n");
237 :
238 0 : torture_comment(tctx, "Got: create: %s, write: %s, change: %s\n",
239 : nt_time_string(tctx, gi.basic_info.out.create_time),
240 : nt_time_string(tctx, gi.basic_info.out.write_time),
241 : nt_time_string(tctx, gi.basic_info.out.change_time));
242 :
243 0 : torture_assert_u64_equal_goto(tctx,
244 : nttime,
245 : gi.basic_info.out.create_time,
246 : ret, done,
247 : "Wrong create time\n");
248 0 : torture_assert_u64_equal_goto(tctx,
249 : nttime,
250 : gi.basic_info.out.write_time,
251 : ret, done,
252 : "Wrong write time\n");
253 0 : torture_assert_u64_equal_goto(tctx,
254 : nttime,
255 : gi.basic_info.out.change_time,
256 : ret, done,
257 : "Wrong change time\n");
258 :
259 0 : find = (struct smb2_find) {
260 : .in.continue_flags = SMB2_CONTINUE_FLAG_RESTART,
261 : .in.file.handle = testdirh,
262 : .in.pattern = fname,
263 : .in.max_response_size = 0x1000,
264 : .in.level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO,
265 : };
266 :
267 0 : status = smb2_find_level(tree, tree, &find, &count, &d);
268 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
269 : "smb2_find_level failed\n");
270 :
271 0 : torture_assert_u64_equal_goto(tctx,
272 : nttime,
273 : d[0].id_both_directory_info.create_time,
274 : ret, done,
275 : "Wrong create time\n");
276 0 : torture_assert_u64_equal_goto(tctx,
277 : nttime,
278 : d[0].id_both_directory_info.write_time,
279 : ret, done,
280 : "Wrong write time\n");
281 0 : torture_assert_u64_equal_goto(tctx,
282 : nttime,
283 : d[0].id_both_directory_info.change_time,
284 : ret, done,
285 : "Wrong change time\n");
286 :
287 0 : status = smb2_util_close(tree, handle);
288 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
289 : "smb2_util_close failed\n");
290 0 : ZERO_STRUCT(handle);
291 :
292 0 : done:
293 0 : if (!smb2_util_handle_empty(handle)) {
294 0 : smb2_util_close(tree, handle);
295 : }
296 0 : if (!smb2_util_handle_empty(testdirh)) {
297 0 : smb2_util_close(tree, testdirh);
298 : }
299 0 : smb2_deltree(tree, BASEDIR);
300 0 : return ret;
301 : }
302 :
303 0 : static bool test_time_t_15032385535(struct torture_context *tctx,
304 : struct smb2_tree *tree)
305 : {
306 0 : return test_time_t(tctx, tree, "test_time_t_15032385535.txt",
307 : 15032385535 /* >> INT32_MAX, limit on ext */);
308 : }
309 :
310 0 : static bool test_time_t_10000000000(struct torture_context *tctx,
311 : struct smb2_tree *tree)
312 : {
313 0 : return test_time_t(tctx, tree, "test_time_t_10000000000.txt",
314 : 10000000000 /* >> INT32_MAX */);
315 : }
316 :
317 0 : static bool test_time_t_4294967295(struct torture_context *tctx,
318 : struct smb2_tree *tree)
319 : {
320 0 : return test_time_t(tctx, tree, "test_time_t_4294967295.txt",
321 : 4294967295 /* INT32_MAX */);
322 : }
323 :
324 0 : static bool test_time_t_1(struct torture_context *tctx,
325 : struct smb2_tree *tree)
326 : {
327 0 : return test_time_t(tctx, tree, "test_time_t_1.txt", 1);
328 : }
329 :
330 0 : static bool test_time_t_0(struct torture_context *tctx,
331 : struct smb2_tree *tree)
332 : {
333 0 : return test_time_t(tctx, tree, "test_time_t_0.txt", 0);
334 : }
335 :
336 0 : static bool test_time_t_minus_1(struct torture_context *tctx,
337 : struct smb2_tree *tree)
338 : {
339 0 : return test_time_t(tctx, tree, "test_time_t_-1.txt", -1);
340 : }
341 :
342 0 : static bool test_time_t_minus_2(struct torture_context *tctx,
343 : struct smb2_tree *tree)
344 : {
345 0 : return test_time_t(tctx, tree, "test_time_t_-2.txt", -2);
346 : }
347 :
348 0 : static bool test_time_t_1968(struct torture_context *tctx,
349 : struct smb2_tree *tree)
350 : {
351 0 : return test_time_t(tctx, tree, "test_time_t_1968.txt",
352 : -63158400 /* 1968 */);
353 : }
354 :
355 0 : static bool test_freeze_thaw(struct torture_context *tctx,
356 : struct smb2_tree *tree)
357 : {
358 0 : const char *filename = BASEDIR "\\test_freeze_thaw";
359 : struct smb2_create cr;
360 0 : struct smb2_handle handle = {{0}};
361 0 : struct smb2_handle testdirh = {{0}};
362 0 : struct timespec ts = { .tv_sec = time(NULL) };
363 : uint64_t nttime;
364 : union smb_fileinfo gi;
365 : union smb_setfileinfo si;
366 : NTSTATUS status;
367 0 : bool ret = true;
368 :
369 0 : smb2_deltree(tree, BASEDIR);
370 :
371 0 : status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
372 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
373 : "torture_smb2_testdir failed\n");
374 :
375 0 : cr = (struct smb2_create) {
376 : .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED,
377 : .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
378 : .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
379 : .in.create_disposition = NTCREATEX_DISP_OPEN_IF,
380 : .in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS,
381 : .in.fname = filename,
382 : };
383 :
384 0 : status = smb2_create(tree, tctx, &cr);
385 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
386 : "smb2_create failed\n");
387 0 : handle = cr.out.file.handle;
388 :
389 0 : si = (union smb_setfileinfo) {
390 : .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION,
391 : .basic_info.in.file.handle = handle,
392 : };
393 :
394 : /*
395 : * Step 1:
396 : * First set timestamps of testfile to current time
397 : */
398 :
399 0 : nttime = full_timespec_to_nt_time(&ts);
400 0 : si.basic_info.in.create_time = nttime;
401 0 : si.basic_info.in.write_time = nttime;
402 0 : si.basic_info.in.change_time = nttime;
403 :
404 0 : status = smb2_setinfo_file(tree, &si);
405 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
406 : "smb2_setinfo_file failed\n");
407 :
408 0 : gi = (union smb_fileinfo) {
409 : .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
410 : .generic.in.file.handle = handle,
411 : };
412 :
413 : /*
414 : * Step 2:
415 : * Verify timestamps are indeed set to the value in "nttime".
416 : */
417 :
418 0 : status = smb2_getinfo_file(tree, tctx, &gi);
419 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
420 : "smb2_getinfo_file failed\n");
421 :
422 0 : torture_comment(tctx, "Got: create: %s, write: %s, change: %s\n",
423 : nt_time_string(tctx, gi.basic_info.out.create_time),
424 : nt_time_string(tctx, gi.basic_info.out.write_time),
425 : nt_time_string(tctx, gi.basic_info.out.change_time));
426 :
427 0 : torture_assert_u64_equal_goto(tctx,
428 : nttime,
429 : gi.basic_info.out.create_time,
430 : ret, done,
431 : "Wrong create time\n");
432 0 : torture_assert_u64_equal_goto(tctx,
433 : nttime,
434 : gi.basic_info.out.write_time,
435 : ret, done,
436 : "Wrong write time\n");
437 0 : torture_assert_u64_equal_goto(tctx,
438 : nttime,
439 : gi.basic_info.out.change_time,
440 : ret, done,
441 : "Wrong change time\n");
442 :
443 : /*
444 : * Step 3:
445 : * First set timestamps with NTTIME_FREEZE, must not change any
446 : * timestamp value.
447 : */
448 :
449 0 : si.basic_info.in.create_time = NTTIME_FREEZE;
450 0 : si.basic_info.in.write_time = NTTIME_FREEZE;
451 0 : si.basic_info.in.change_time = NTTIME_FREEZE;
452 :
453 0 : status = smb2_setinfo_file(tree, &si);
454 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
455 : "smb2_setinfo_file failed\n");
456 :
457 0 : gi = (union smb_fileinfo) {
458 : .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
459 : .generic.in.file.handle = handle,
460 : };
461 :
462 : /*
463 : * Step 4:
464 : * Verify timestamps are unmodified from step 2.
465 : */
466 :
467 0 : gi = (union smb_fileinfo) {
468 : .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
469 : .generic.in.file.handle = handle,
470 : };
471 :
472 0 : status = smb2_getinfo_file(tree, tctx, &gi);
473 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
474 : "smb2_getinfo_file failed\n");
475 :
476 0 : torture_comment(tctx, "Got: create: %s, write: %s, change: %s\n",
477 : nt_time_string(tctx, gi.basic_info.out.create_time),
478 : nt_time_string(tctx, gi.basic_info.out.write_time),
479 : nt_time_string(tctx, gi.basic_info.out.change_time));
480 :
481 0 : torture_assert_u64_equal_goto(tctx,
482 : nttime,
483 : gi.basic_info.out.create_time,
484 : ret, done,
485 : "Wrong create time\n");
486 0 : torture_assert_u64_equal_goto(tctx,
487 : nttime,
488 : gi.basic_info.out.write_time,
489 : ret, done,
490 : "Wrong write time\n");
491 0 : torture_assert_u64_equal_goto(tctx,
492 : nttime,
493 : gi.basic_info.out.change_time,
494 : ret, done,
495 : "Wrong change time\n");
496 :
497 : /*
498 : * Step 5:
499 : * First set timestamps with NTTIME_THAW, must not change any timestamp
500 : * value.
501 : */
502 :
503 0 : si.basic_info.in.create_time = NTTIME_THAW;
504 0 : si.basic_info.in.write_time = NTTIME_THAW;
505 0 : si.basic_info.in.change_time = NTTIME_THAW;
506 :
507 0 : status = smb2_setinfo_file(tree, &si);
508 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
509 : "smb2_setinfo_file failed\n");
510 :
511 0 : gi = (union smb_fileinfo) {
512 : .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
513 : .generic.in.file.handle = handle,
514 : };
515 :
516 : /*
517 : * Step 6:
518 : * Verify timestamps are unmodified from step 2.
519 : */
520 :
521 0 : gi = (union smb_fileinfo) {
522 : .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
523 : .generic.in.file.handle = handle,
524 : };
525 :
526 0 : status = smb2_getinfo_file(tree, tctx, &gi);
527 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
528 : "smb2_getinfo_file failed\n");
529 :
530 0 : torture_comment(tctx, "Got: create: %s, write: %s, change: %s\n",
531 : nt_time_string(tctx, gi.basic_info.out.create_time),
532 : nt_time_string(tctx, gi.basic_info.out.write_time),
533 : nt_time_string(tctx, gi.basic_info.out.change_time));
534 :
535 0 : torture_assert_u64_equal_goto(tctx,
536 : nttime,
537 : gi.basic_info.out.create_time,
538 : ret, done,
539 : "Wrong create time\n");
540 0 : torture_assert_u64_equal_goto(tctx,
541 : nttime,
542 : gi.basic_info.out.write_time,
543 : ret, done,
544 : "Wrong write time\n");
545 0 : torture_assert_u64_equal_goto(tctx,
546 : nttime,
547 : gi.basic_info.out.change_time,
548 : ret, done,
549 : "Wrong change time\n");
550 :
551 0 : done:
552 0 : if (!smb2_util_handle_empty(handle)) {
553 0 : smb2_util_close(tree, handle);
554 : }
555 0 : if (!smb2_util_handle_empty(testdirh)) {
556 0 : smb2_util_close(tree, testdirh);
557 : }
558 0 : smb2_deltree(tree, BASEDIR);
559 0 : return ret;
560 : }
561 :
562 0 : static bool test_delayed_write_vs_seteof(struct torture_context *tctx,
563 : struct smb2_tree *tree)
564 : {
565 : struct smb2_create cr;
566 0 : struct smb2_handle h1 = {{0}};
567 0 : struct smb2_handle h2 = {{0}};
568 : NTTIME create_time;
569 : NTTIME set_time;
570 : union smb_fileinfo finfo;
571 : union smb_setfileinfo setinfo;
572 : struct smb2_close c;
573 : NTSTATUS status;
574 0 : bool ret = true;
575 :
576 0 : smb2_deltree(tree, BASEDIR);
577 0 : status = torture_smb2_testdir(tree, BASEDIR, &h1);
578 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
579 : "create failed\n");
580 0 : status = smb2_util_close(tree, h1);
581 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
582 : "close failed\n");
583 :
584 0 : torture_comment(tctx, "Open file-handle 1\n");
585 :
586 0 : cr = (struct smb2_create) {
587 : .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED,
588 : .in.create_disposition = NTCREATEX_DISP_OPEN_IF,
589 : .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
590 : .in.fname = BASEDIR "\\" FNAME,
591 : };
592 0 : status = smb2_create(tree, tctx, &cr);
593 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
594 : "create failed\n");
595 0 : h1 = cr.out.file.handle;
596 0 : create_time = cr.out.create_time;
597 0 : sleep(1);
598 :
599 0 : torture_comment(tctx, "Write to file-handle 1\n");
600 :
601 0 : status = smb2_util_write(tree, h1, "s", 0, 1);
602 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
603 : "write failed\n");
604 :
605 0 : torture_comment(tctx, "Check writetime hasn't been updated\n");
606 :
607 0 : finfo = (union smb_fileinfo) {
608 : .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
609 : .generic.in.file.handle = h1,
610 : };
611 0 : status = smb2_getinfo_file(tree, tree, &finfo);
612 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
613 : "getinfo failed\n");
614 :
615 0 : torture_assert_nttime_equal(tctx,
616 : finfo.all_info.out.write_time,
617 : create_time,
618 : "Writetime != set_time (wrong!)\n");
619 :
620 0 : torture_comment(tctx, "Setinfo EOF on file-handle 1,"
621 : " should flush pending writetime update\n");
622 :
623 0 : setinfo = (union smb_setfileinfo) {
624 : .generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION,
625 : };
626 0 : setinfo.end_of_file_info.in.file.handle = h1;
627 0 : setinfo.end_of_file_info.in.size = 1; /* same size! */
628 :
629 0 : status = smb2_setinfo_file(tree, &setinfo);
630 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
631 : "close failed\n");
632 :
633 0 : torture_comment(tctx, "Check writetime has been updated "
634 : "by the setinfo EOF\n");
635 :
636 0 : finfo = (union smb_fileinfo) {
637 : .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
638 : .generic.in.file.handle = h1,
639 : };
640 0 : status = smb2_getinfo_file(tree, tree, &finfo);
641 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
642 : "getinfo failed\n");
643 0 : if (!(finfo.all_info.out.write_time > create_time)) {
644 0 : ret = false;
645 0 : torture_fail_goto(tctx, done, "setinfo EOF hasn't updated writetime\n");
646 : }
647 :
648 0 : torture_comment(tctx, "Open file-handle 2\n");
649 :
650 0 : cr = (struct smb2_create) {
651 : .in.desired_access = SEC_FILE_WRITE_ATTRIBUTE,
652 : .in.create_disposition = NTCREATEX_DISP_OPEN,
653 : .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
654 : .in.fname = BASEDIR "\\" FNAME,
655 : };
656 0 : status = smb2_create(tree, tctx, &cr);
657 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
658 : "create failed\n");
659 0 : h2 = cr.out.file.handle;
660 :
661 0 : torture_comment(tctx, "Set write time on file-handle 2\n");
662 :
663 0 : setinfo = (union smb_setfileinfo) {
664 : .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
665 : };
666 0 : setinfo.generic.in.file.handle = h2;
667 0 : unix_to_nt_time(&set_time, time(NULL) + 86400);
668 0 : setinfo.basic_info.in.write_time = set_time;
669 :
670 0 : status = smb2_setinfo_file(tree, &setinfo);
671 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
672 : "close failed\n");
673 :
674 0 : status = smb2_util_close(tree, h2);
675 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
676 : "close failed\n");
677 0 : ZERO_STRUCT(h2);
678 :
679 0 : torture_comment(tctx, "Close file-handle 1, write-time should not be updated\n");
680 :
681 0 : c = (struct smb2_close) {
682 : .in.file.handle = h1,
683 : .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
684 : };
685 :
686 0 : status = smb2_close(tree, &c);
687 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
688 : "close failed\n");
689 0 : ZERO_STRUCT(h1);
690 :
691 0 : torture_assert_nttime_equal(tctx,
692 : c.out.write_time,
693 : set_time,
694 : "Writetime != set_time (wrong!)\n");
695 :
696 0 : done:
697 0 : if (!smb2_util_handle_empty(h1)) {
698 0 : smb2_util_close(tree, h1);
699 : }
700 0 : if (!smb2_util_handle_empty(h2)) {
701 0 : smb2_util_close(tree, h2);
702 : }
703 0 : smb2_deltree(tree, BASEDIR);
704 0 : return ret;
705 : }
706 :
707 0 : static bool test_delayed_write_vs_flush(struct torture_context *tctx,
708 : struct smb2_tree *tree)
709 : {
710 : struct smb2_create cr;
711 0 : struct smb2_handle h1 = {{0}};
712 : union smb_fileinfo finfo;
713 : struct smb2_flush f;
714 : struct smb2_close c;
715 : NTTIME create_time;
716 : NTTIME flush_time;
717 : NTSTATUS status;
718 0 : bool ret = true;
719 :
720 0 : smb2_deltree(tree, BASEDIR);
721 0 : status = torture_smb2_testdir(tree, BASEDIR, &h1);
722 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
723 : "create failed\n");
724 0 : status = smb2_util_close(tree, h1);
725 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
726 : "close failed\n");
727 :
728 0 : torture_comment(tctx, "Open file-handle 1\n");
729 :
730 0 : cr = (struct smb2_create) {
731 : .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED,
732 : .in.create_disposition = NTCREATEX_DISP_OPEN_IF,
733 : .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
734 : .in.fname = BASEDIR "\\" FNAME,
735 : };
736 0 : status = smb2_create(tree, tctx, &cr);
737 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
738 : "create failed\n");
739 0 : h1 = cr.out.file.handle;
740 0 : create_time = cr.out.create_time;
741 0 : sleep(1);
742 :
743 0 : torture_comment(tctx, "Write to file-handle 1\n");
744 :
745 0 : status = smb2_util_write(tree, h1, "s", 0, 1);
746 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
747 : "write failed\n");
748 :
749 0 : torture_comment(tctx, "Check writetime hasn't been updated\n");
750 :
751 0 : finfo = (union smb_fileinfo) {
752 : .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
753 : .generic.in.file.handle = h1,
754 : };
755 0 : status = smb2_getinfo_file(tree, tree, &finfo);
756 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
757 : "getinfo failed\n");
758 :
759 0 : torture_assert_nttime_equal(tctx,
760 : finfo.all_info.out.write_time,
761 : create_time,
762 : "Writetime != create_time (wrong!)\n");
763 :
764 0 : torture_comment(tctx, "Flush file, "
765 : "should flush pending writetime update\n");
766 :
767 0 : f = (struct smb2_flush) {
768 : .in.file.handle = h1,
769 : };
770 :
771 0 : status = smb2_flush(tree, &f);
772 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
773 : "flush failed\n");
774 :
775 0 : torture_comment(tctx, "Check writetime has been updated "
776 : "by the setinfo EOF\n");
777 :
778 0 : finfo = (union smb_fileinfo) {
779 : .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
780 : .generic.in.file.handle = h1,
781 : };
782 0 : status = smb2_getinfo_file(tree, tree, &finfo);
783 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
784 : "getinfo failed\n");
785 :
786 0 : flush_time = finfo.all_info.out.write_time;
787 0 : if (!(flush_time > create_time)) {
788 0 : ret = false;
789 0 : torture_fail_goto(tctx, done, "flush hasn't updated writetime\n");
790 : }
791 :
792 0 : torture_comment(tctx, "Close file-handle 1, write-time should not be updated\n");
793 :
794 0 : c = (struct smb2_close) {
795 : .in.file.handle = h1,
796 : .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
797 : };
798 :
799 0 : status = smb2_close(tree, &c);
800 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
801 : "close failed\n");
802 0 : ZERO_STRUCT(h1);
803 :
804 0 : torture_assert_nttime_equal(tctx,
805 : c.out.write_time,
806 : flush_time,
807 : "writetime != flushtime (wrong!)\n");
808 :
809 0 : done:
810 0 : if (!smb2_util_handle_empty(h1)) {
811 0 : smb2_util_close(tree, h1);
812 : }
813 0 : smb2_deltree(tree, BASEDIR);
814 0 : return ret;
815 : }
816 :
817 0 : static bool test_delayed_write_vs_setbasic_do(struct torture_context *tctx,
818 : struct smb2_tree *tree,
819 : union smb_setfileinfo *setinfo,
820 : bool expect_update)
821 : {
822 0 : char *path = NULL;
823 : struct smb2_create cr;
824 0 : struct smb2_handle h1 = {{0}};
825 : NTTIME create_time;
826 : union smb_fileinfo finfo;
827 : NTSTATUS status;
828 0 : bool ret = true;
829 :
830 0 : torture_comment(tctx, "Create testfile\n");
831 :
832 0 : path = talloc_asprintf(tree, BASEDIR "\\" FNAME ".%" PRIu32,
833 : generate_random());
834 0 : torture_assert_not_null_goto(tctx, path, ret, done, "OOM\n");
835 :
836 0 : cr = (struct smb2_create) {
837 : .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED,
838 : .in.create_disposition = NTCREATEX_DISP_CREATE,
839 : .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
840 : .in.fname = path,
841 : };
842 0 : status = smb2_create(tree, tctx, &cr);
843 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
844 : "create failed\n");
845 0 : h1 = cr.out.file.handle;
846 0 : create_time = cr.out.create_time;
847 :
848 0 : torture_comment(tctx, "Write to file\n");
849 :
850 0 : status = smb2_util_write(tree, h1, "s", 0, 1);
851 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
852 : "write failed\n");
853 :
854 0 : torture_comment(tctx, "Get timestamps\n");
855 :
856 0 : finfo = (union smb_fileinfo) {
857 : .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
858 : .generic.in.file.handle = h1,
859 : };
860 0 : status = smb2_getinfo_file(tree, tree, &finfo);
861 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
862 : "getinfo failed\n");
863 :
864 0 : torture_assert_nttime_equal(tctx,
865 : finfo.all_info.out.write_time,
866 : create_time,
867 : "Writetime != create_time (wrong!)\n");
868 :
869 0 : torture_comment(tctx, "Set timestamps\n");
870 :
871 0 : setinfo->end_of_file_info.in.file.handle = h1;
872 0 : status = smb2_setinfo_file(tree, setinfo);
873 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
874 : "close failed\n");
875 :
876 0 : torture_comment(tctx, "Check timestamps\n");
877 :
878 0 : finfo = (union smb_fileinfo) {
879 : .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
880 : .generic.in.file.handle = h1,
881 : };
882 0 : status = smb2_getinfo_file(tree, tree, &finfo);
883 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
884 : "getinfo failed\n");
885 :
886 0 : if (expect_update) {
887 0 : if (!(finfo.all_info.out.write_time > create_time)) {
888 0 : ret = false;
889 0 : torture_fail_goto(tctx, done, "setinfo basicinfo "
890 : "hasn't updated writetime\n");
891 : }
892 : } else {
893 0 : if (finfo.all_info.out.write_time != create_time) {
894 0 : ret = false;
895 0 : torture_fail_goto(tctx, done, "setinfo basicinfo "
896 : "hasn't updated writetime\n");
897 : }
898 : }
899 :
900 0 : status = smb2_util_close(tree, h1);
901 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
902 : "close failed\n");
903 0 : ZERO_STRUCT(h1);
904 :
905 0 : status = smb2_util_unlink(tree, path);
906 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
907 : "close failed\n");
908 :
909 0 : done:
910 0 : TALLOC_FREE(path);
911 0 : if (!smb2_util_handle_empty(h1)) {
912 0 : smb2_util_close(tree, h1);
913 : }
914 0 : return ret;
915 : }
916 :
917 0 : static bool test_delayed_write_vs_setbasic(struct torture_context *tctx,
918 : struct smb2_tree *tree)
919 : {
920 0 : struct smb2_handle h1 = {{0}};
921 : union smb_setfileinfo setinfo;
922 0 : time_t t = time(NULL) - 86400;
923 : NTSTATUS status;
924 0 : bool ret = true;
925 :
926 0 : smb2_deltree(tree, BASEDIR);
927 0 : status = torture_smb2_testdir(tree, BASEDIR, &h1);
928 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
929 : "create failed\n");
930 0 : status = smb2_util_close(tree, h1);
931 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
932 : "close failed\n");
933 :
934 : /*
935 : * Yes, this is correct, tested against Windows 2016: even if all
936 : * timestamp fields are 0, a pending write time is flushed.
937 : */
938 0 : torture_comment(tctx, "Test: setting all-0 timestamps flushes?\n");
939 :
940 0 : setinfo = (union smb_setfileinfo) {
941 : .generic.level = RAW_SFILEINFO_BASIC_INFORMATION,
942 : };
943 0 : ret = test_delayed_write_vs_setbasic_do(tctx, tree, &setinfo, true);
944 0 : if (ret != true) {
945 0 : goto done;
946 : }
947 :
948 0 : torture_comment(tctx, "Test: setting create_time flushes?\n");
949 0 : unix_to_nt_time(&setinfo.basic_info.in.create_time, t);
950 0 : ret = test_delayed_write_vs_setbasic_do(tctx, tree, &setinfo, true);
951 0 : if (ret != true) {
952 0 : goto done;
953 : }
954 :
955 0 : torture_comment(tctx, "Test: setting access_time flushes?\n");
956 0 : unix_to_nt_time(&setinfo.basic_info.in.access_time, t);
957 0 : ret = test_delayed_write_vs_setbasic_do(tctx, tree, &setinfo, true);
958 0 : if (ret != true) {
959 0 : goto done;
960 : }
961 :
962 0 : torture_comment(tctx, "Test: setting change_time flushes?\n");
963 0 : unix_to_nt_time(&setinfo.basic_info.in.change_time, t);
964 0 : ret = test_delayed_write_vs_setbasic_do(tctx, tree, &setinfo, true);
965 0 : if (ret != true) {
966 0 : goto done;
967 : }
968 :
969 0 : done:
970 0 : smb2_deltree(tree, BASEDIR);
971 0 : return ret;
972 : }
973 :
974 0 : static bool test_delayed_1write(struct torture_context *tctx,
975 : struct smb2_tree *tree)
976 : {
977 : struct smb2_create cr;
978 0 : struct smb2_handle h1 = {{0}};
979 : union smb_fileinfo finfo;
980 : struct smb2_close c;
981 : NTTIME create_time;
982 : NTTIME write_time;
983 : NTTIME close_time;
984 : NTSTATUS status;
985 0 : bool ret = true;
986 :
987 0 : smb2_deltree(tree, BASEDIR);
988 0 : status = torture_smb2_testdir(tree, BASEDIR, &h1);
989 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
990 : "create failed\n");
991 0 : status = smb2_util_close(tree, h1);
992 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
993 : "close failed\n");
994 :
995 0 : torture_comment(tctx, "Open file-handle 1\n");
996 :
997 0 : cr = (struct smb2_create) {
998 : .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED,
999 : .in.create_disposition = NTCREATEX_DISP_OPEN_IF,
1000 : .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
1001 : .in.fname = BASEDIR "\\" FNAME,
1002 : };
1003 0 : status = smb2_create(tree, tctx, &cr);
1004 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1005 : "create failed\n");
1006 0 : h1 = cr.out.file.handle;
1007 0 : create_time = cr.out.create_time;
1008 0 : sleep(1);
1009 :
1010 0 : torture_comment(tctx, "Write to file-handle 1\n");
1011 :
1012 0 : status = smb2_util_write(tree, h1, "s", 0, 1);
1013 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1014 : "write failed\n");
1015 0 : sleep(3);
1016 :
1017 0 : torture_comment(tctx, "Check writetime has been updated\n");
1018 :
1019 0 : finfo = (union smb_fileinfo) {
1020 : .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
1021 : .generic.in.file.handle = h1,
1022 : };
1023 0 : status = smb2_getinfo_file(tree, tree, &finfo);
1024 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1025 : "getinfo failed\n");
1026 0 : write_time = finfo.all_info.out.write_time;
1027 :
1028 0 : if (!(write_time > create_time)) {
1029 0 : ret = false;
1030 0 : torture_fail_goto(tctx, done,
1031 : "Write-time not updated (wrong!)\n");
1032 : }
1033 :
1034 0 : torture_comment(tctx, "Close file-handle 1\n");
1035 0 : sleep(1);
1036 :
1037 0 : c = (struct smb2_close) {
1038 : .in.file.handle = h1,
1039 : .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
1040 : };
1041 :
1042 0 : status = smb2_close(tree, &c);
1043 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1044 : "close failed\n");
1045 0 : ZERO_STRUCT(h1);
1046 0 : close_time = c.out.write_time;
1047 :
1048 0 : torture_assert_nttime_equal(tctx, close_time, write_time,
1049 : "Writetime != close_time (wrong!)\n");
1050 :
1051 0 : done:
1052 0 : if (!smb2_util_handle_empty(h1)) {
1053 0 : smb2_util_close(tree, h1);
1054 : }
1055 0 : smb2_deltree(tree, BASEDIR);
1056 0 : return ret;
1057 : }
1058 :
1059 0 : static bool test_delayed_2write(struct torture_context *tctx,
1060 : struct smb2_tree *tree)
1061 : {
1062 : struct smb2_create cr;
1063 0 : struct smb2_handle h1 = {{0}};
1064 : union smb_fileinfo finfo;
1065 : struct smb2_close c;
1066 : NTTIME create_time;
1067 : NTTIME write_time;
1068 : NTTIME write_time2;
1069 : struct timespec now;
1070 : NTTIME send_close_time;
1071 : NTTIME close_time;
1072 : NTSTATUS status;
1073 0 : bool ret = true;
1074 :
1075 0 : smb2_deltree(tree, BASEDIR);
1076 0 : status = torture_smb2_testdir(tree, BASEDIR, &h1);
1077 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1078 : "create failed\n");
1079 0 : status = smb2_util_close(tree, h1);
1080 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1081 : "close failed\n");
1082 :
1083 0 : torture_comment(tctx, "Open file\n");
1084 :
1085 0 : cr = (struct smb2_create) {
1086 : .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED,
1087 : .in.create_disposition = NTCREATEX_DISP_OPEN_IF,
1088 : .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
1089 : .in.fname = BASEDIR "\\" FNAME,
1090 : };
1091 0 : status = smb2_create(tree, tctx, &cr);
1092 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1093 : "create failed\n");
1094 0 : h1 = cr.out.file.handle;
1095 0 : create_time = cr.out.create_time;
1096 0 : sleep(1);
1097 :
1098 0 : torture_comment(tctx, "Write to file\n");
1099 :
1100 0 : status = smb2_util_write(tree, h1, "s", 0, 1);
1101 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1102 : "write failed\n");
1103 0 : sleep(3);
1104 :
1105 0 : torture_comment(tctx, "Check writetime has been updated\n");
1106 :
1107 0 : finfo = (union smb_fileinfo) {
1108 : .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
1109 : .generic.in.file.handle = h1,
1110 : };
1111 0 : status = smb2_getinfo_file(tree, tree, &finfo);
1112 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1113 : "getinfo failed\n");
1114 0 : write_time = finfo.all_info.out.write_time;
1115 :
1116 0 : if (!(write_time > create_time)) {
1117 0 : ret = false;
1118 0 : torture_fail_goto(tctx, done,
1119 : "Write-time not updated (wrong!)\n");
1120 : }
1121 :
1122 0 : torture_comment(tctx, "Write a second time\n");
1123 :
1124 0 : status = smb2_util_write(tree, h1, "s", 0, 1);
1125 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1126 : "write failed\n");
1127 0 : sleep(3);
1128 :
1129 0 : torture_comment(tctx, "Check writetime has NOT been updated\n");
1130 :
1131 0 : finfo = (union smb_fileinfo) {
1132 : .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
1133 : .generic.in.file.handle = h1,
1134 : };
1135 0 : status = smb2_getinfo_file(tree, tree, &finfo);
1136 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1137 : "getinfo failed\n");
1138 0 : write_time2 = finfo.all_info.out.write_time;
1139 :
1140 0 : torture_assert_nttime_equal(tctx, write_time2, write_time,
1141 : "second write updated write-time (wrong!)\n");
1142 :
1143 0 : torture_comment(tctx, "Close file-handle 1\n");
1144 0 : sleep(2);
1145 :
1146 0 : now = timespec_current();
1147 0 : send_close_time = full_timespec_to_nt_time(&now);
1148 :
1149 0 : c = (struct smb2_close) {
1150 : .in.file.handle = h1,
1151 : .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
1152 : };
1153 :
1154 0 : status = smb2_close(tree, &c);
1155 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1156 : "close failed\n");
1157 0 : ZERO_STRUCT(h1);
1158 0 : close_time = c.out.write_time;
1159 :
1160 0 : if (!(close_time > send_close_time)) {
1161 0 : ret = false;
1162 0 : torture_fail_goto(tctx, done,
1163 : "Write-time not updated (wrong!)\n");
1164 : }
1165 :
1166 0 : done:
1167 0 : if (!smb2_util_handle_empty(h1)) {
1168 0 : smb2_util_close(tree, h1);
1169 : }
1170 0 : smb2_deltree(tree, BASEDIR);
1171 0 : return ret;
1172 : }
1173 :
1174 : /*
1175 : basic testing of SMB2 timestamps
1176 : */
1177 964 : struct torture_suite *torture_smb2_timestamps_init(TALLOC_CTX *ctx)
1178 : {
1179 964 : struct torture_suite *suite = torture_suite_create(ctx, "timestamps");
1180 :
1181 964 : torture_suite_add_1smb2_test(suite, "test_close_not_attrib", test_close_no_attrib);
1182 964 : torture_suite_add_1smb2_test(suite, "time_t_15032385535", test_time_t_15032385535);
1183 964 : torture_suite_add_1smb2_test(suite, "time_t_10000000000", test_time_t_10000000000);
1184 964 : torture_suite_add_1smb2_test(suite, "time_t_4294967295", test_time_t_4294967295);
1185 964 : torture_suite_add_1smb2_test(suite, "time_t_1", test_time_t_1);
1186 964 : torture_suite_add_1smb2_test(suite, "time_t_0", test_time_t_0);
1187 964 : torture_suite_add_1smb2_test(suite, "time_t_-1", test_time_t_minus_1);
1188 964 : torture_suite_add_1smb2_test(suite, "time_t_-2", test_time_t_minus_2);
1189 964 : torture_suite_add_1smb2_test(suite, "time_t_1968", test_time_t_1968);
1190 964 : torture_suite_add_1smb2_test(suite, "freeze-thaw", test_freeze_thaw);
1191 :
1192 : /*
1193 : * Testing of delayed write-time udpates
1194 : */
1195 964 : torture_suite_add_1smb2_test(suite, "delayed-write-vs-seteof", test_delayed_write_vs_seteof);
1196 964 : torture_suite_add_1smb2_test(suite, "delayed-write-vs-flush", test_delayed_write_vs_flush);
1197 964 : torture_suite_add_1smb2_test(suite, "delayed-write-vs-setbasic", test_delayed_write_vs_setbasic);
1198 964 : torture_suite_add_1smb2_test(suite, "delayed-1write", test_delayed_1write);
1199 964 : torture_suite_add_1smb2_test(suite, "delayed-2write", test_delayed_2write);
1200 :
1201 964 : suite->description = talloc_strdup(suite, "SMB2 timestamp tests");
1202 :
1203 964 : return suite;
1204 : }
1205 :
1206 : /*
1207 : * This test shows that Windows has a timestamp resolution of ~15ms. When so
1208 : * when a smaller amount of time than that has passed it's not necessarily
1209 : * detectable on a Windows 2019 and newer who implement immediate timestamp
1210 : * updates.
1211 : *
1212 : * Note that this test relies on a low latency SMB connection. Even with a low
1213 : * latency connection of eg 1m there's a chance of 1/15 that the first part of
1214 : * the test expecting no timestamp change fails as the writetime is updated.
1215 : *
1216 : * Due to this timing dependency this test is skipped in Samba CI, but it is
1217 : * preserved here for future SMB2 timestamps behaviour archealogists.
1218 : *
1219 : * See also: https://lists.samba.org/archive/cifs-protocol/2019-December/003358.html
1220 : */
1221 0 : static bool test_timestamp_resolution1(struct torture_context *tctx,
1222 : struct smb2_tree *tree)
1223 : {
1224 : union smb_fileinfo finfo1;
1225 0 : const char *fname = BASEDIR "\\" FNAME;
1226 : struct smb2_create cr;
1227 0 : struct smb2_handle h = {{0}};
1228 : struct smb2_close cl;
1229 : NTSTATUS status;
1230 0 : bool ret = true;
1231 :
1232 0 : smb2_deltree(tree, BASEDIR);
1233 0 : status = torture_smb2_testdir(tree, BASEDIR, &h);
1234 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1235 : "create failed\n");
1236 0 : status = smb2_util_close(tree, h );
1237 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1238 : "close failed\n");
1239 :
1240 0 : torture_comment(tctx, "Write without delay, expect no "
1241 : "write-time change\n");
1242 :
1243 0 : smb2_generic_create(&cr, NULL, false, fname,
1244 : NTCREATEX_DISP_CREATE,
1245 0 : smb2_util_oplock_level(""), 0, 0);
1246 0 : status = smb2_create(tree, tree, &cr);
1247 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1248 : "create failed\n");
1249 0 : h = cr.out.file.handle;
1250 :
1251 0 : finfo1 = (union smb_fileinfo) {
1252 : .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
1253 : .generic.in.file.handle = h,
1254 : };
1255 0 : status = smb2_getinfo_file(tree, tree, &finfo1);
1256 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1257 : "getinfo failed\n");
1258 :
1259 0 : status = smb2_util_write(tree, h, "123456789", 0, 9);
1260 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1261 : "write failed\n");
1262 :
1263 0 : cl = (struct smb2_close) {
1264 : .in.file.handle = h,
1265 : .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
1266 : };
1267 :
1268 0 : status = smb2_close(tree, &cl);
1269 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1270 : "close failed\n");
1271 0 : ZERO_STRUCT(h);
1272 :
1273 0 : torture_comment(tctx, "Initial: %s\nClose: %s\n",
1274 : nt_time_string(tctx, finfo1.basic_info.out.write_time),
1275 : nt_time_string(tctx, cl.out.write_time));
1276 :
1277 0 : torture_assert_u64_equal_goto(tctx,
1278 : finfo1.basic_info.out.write_time,
1279 : cl.out.write_time,
1280 : ret, done,
1281 : "Write time changed (wrong!)\n");
1282 :
1283 0 : torture_comment(tctx, "Write with 20 ms delay, expect "
1284 : "write-time change\n");
1285 :
1286 0 : smb2_generic_create(&cr, NULL, false, fname,
1287 : NTCREATEX_DISP_OPEN,
1288 0 : smb2_util_oplock_level(""), 0, 0);
1289 0 : status = smb2_create(tree, tree, &cr);
1290 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1291 : "create failed\n");
1292 0 : h = cr.out.file.handle;
1293 :
1294 0 : finfo1 = (union smb_fileinfo) {
1295 : .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
1296 : .generic.in.file.handle = h,
1297 : };
1298 0 : status = smb2_getinfo_file(tree, tree, &finfo1);
1299 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1300 : "getinfo failed\n");
1301 :
1302 0 : smb_msleep(20);
1303 :
1304 0 : status = smb2_util_write(tree, h, "123456789", 0, 9);
1305 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1306 : "write failed\n");
1307 :
1308 0 : cl = (struct smb2_close) {
1309 : .in.file.handle = h,
1310 : .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
1311 : };
1312 :
1313 0 : status = smb2_close(tree, &cl);
1314 0 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1315 : "close failed\n");
1316 0 : ZERO_STRUCT(h);
1317 :
1318 0 : torture_comment(tctx, "Initial: %s\nClose: %s\n",
1319 : nt_time_string(tctx, finfo1.basic_info.out.write_time),
1320 : nt_time_string(tctx, cl.out.write_time));
1321 :
1322 0 : torture_assert_u64_not_equal_goto(
1323 : tctx,
1324 : finfo1.basic_info.out.write_time,
1325 : cl.out.write_time,
1326 : ret, done,
1327 : "Write time did not change (wrong!)\n");
1328 :
1329 0 : done:
1330 0 : if (!smb2_util_handle_empty(h)) {
1331 0 : smb2_util_close(tree, h);
1332 : }
1333 0 : smb2_deltree(tree, BASEDIR);
1334 0 : return ret;
1335 : }
1336 :
1337 : /*
1338 : basic testing of SMB2 timestamps
1339 : */
1340 964 : struct torture_suite *torture_smb2_timestamp_resolution_init(TALLOC_CTX *ctx)
1341 : {
1342 964 : struct torture_suite *suite = torture_suite_create(ctx, "timestamp_resolution");
1343 :
1344 964 : torture_suite_add_1smb2_test(suite, "resolution1", test_timestamp_resolution1);
1345 :
1346 964 : suite->description = talloc_strdup(suite, "SMB2 timestamp tests");
1347 :
1348 964 : return suite;
1349 : }
|