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