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