libads: Make ads_cldap_netlogon() static
[samba4-gss.git] / source4 / torture / smb2 / timestamps.c
bloba9416481c61adfae0176ecbe2e972cf54baa0db4
1 /*
2 Unix SMB/CIFS implementation.
4 test timestamps
6 Copyright (C) Ralph Boehme 2019
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.
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.
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/>.
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"
29 #define BASEDIR "smb2-timestamps"
30 #define FNAME "testfile.dat"
32 static bool test_close_no_attrib(struct torture_context *tctx,
33 struct smb2_tree *tree)
35 const char *filename = BASEDIR "/" FNAME;
36 struct smb2_create cr;
37 struct smb2_handle handle = {{0}};
38 struct smb2_handle testdirh = {{0}};
39 struct smb2_close c;
40 NTSTATUS status;
41 bool ret = true;
43 smb2_deltree(tree, BASEDIR);
45 status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
46 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
47 "torture_smb2_testdir failed\n");
48 smb2_util_close(tree, testdirh);
50 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,
59 status = smb2_create(tree, tctx, &cr);
60 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
61 "smb2_create failed\n");
62 handle = cr.out.file.handle;
64 c = (struct smb2_close) {
65 .in.file.handle = handle,
68 status = smb2_close(tree, &c);
69 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
70 "close failed\n");
71 ZERO_STRUCT(handle);
73 torture_assert_u64_equal_goto(tctx, c.out.create_time, NTTIME_OMIT,
74 ret, done, "Unexpected create time\n");
75 torture_assert_u64_equal_goto(tctx, c.out.access_time, NTTIME_OMIT,
76 ret, done, "Unexpected access time\n");
77 torture_assert_u64_equal_goto(tctx, c.out.write_time, NTTIME_OMIT,
78 ret, done, "Unexpected write time\n");
79 torture_assert_u64_equal_goto(tctx, c.out.change_time, NTTIME_OMIT,
80 ret, done, "Unexpected change time\n");
81 torture_assert_u64_equal_goto(tctx, c.out.size, 0,
82 ret, done, "Unexpected size\n");
83 torture_assert_u64_equal_goto(tctx, c.out.file_attr, 0,
84 ret, done, "Unexpected attributes\n");
86 done:
87 if (!smb2_util_handle_empty(handle)) {
88 smb2_util_close(tree, handle);
90 smb2_deltree(tree, BASEDIR);
91 return ret;
94 static bool test_time_t(struct torture_context *tctx,
95 struct smb2_tree *tree,
96 const char *fname,
97 time_t t)
99 char *filename = NULL;
100 struct smb2_create cr;
101 struct smb2_handle handle = {{0}};
102 struct smb2_handle testdirh = {{0}};
103 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 bool ret = true;
113 smb2_deltree(tree, BASEDIR);
115 status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
116 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
117 "torture_smb2_testdir failed\n");
119 filename = talloc_asprintf(tctx, "%s\\%s", BASEDIR, fname);
120 torture_assert_not_null_goto(tctx, filename, ret, done,
121 "talloc_asprintf failed\n");
123 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,
132 status = smb2_create(tree, tctx, &cr);
133 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
134 "smb2_create failed\n");
135 handle = cr.out.file.handle;
137 si = (union smb_setfileinfo) {
138 .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION,
139 .basic_info.in.file.handle = handle,
142 nttime = full_timespec_to_nt_time(&ts);
143 si.basic_info.in.create_time = nttime;
144 si.basic_info.in.write_time = nttime;
145 si.basic_info.in.change_time = nttime;
147 status = smb2_setinfo_file(tree, &si);
148 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
149 "smb2_setinfo_file failed\n");
151 gi = (union smb_fileinfo) {
152 .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
153 .generic.in.file.handle = handle,
156 status = smb2_getinfo_file(tree, tctx, &gi);
157 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
158 "smb2_getinfo_file failed\n");
160 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));
165 torture_assert_u64_equal_goto(tctx,
166 gi.basic_info.out.create_time,
167 nttime,
168 ret, done,
169 "Wrong create time\n");
170 torture_assert_u64_equal_goto(tctx,
171 gi.basic_info.out.write_time,
172 nttime,
173 ret, done,
174 "Wrong write time\n");
175 torture_assert_u64_equal_goto(tctx,
176 gi.basic_info.out.change_time,
177 nttime,
178 ret, done,
179 "Wrong change time\n");
181 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,
188 status = smb2_find_level(tree, tree, &find, &count, &d);
189 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
190 "smb2_find_level failed\n");
192 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 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 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");
208 status = smb2_util_close(tree, handle);
209 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
210 "smb2_util_close failed\n");
211 ZERO_STRUCT(handle);
213 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,
222 status = smb2_create(tree, tctx, &cr);
223 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
224 "smb2_create failed\n");
225 handle = cr.out.file.handle;
227 gi = (union smb_fileinfo) {
228 .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
229 .generic.in.file.handle = handle,
232 status = smb2_getinfo_file(tree, tctx, &gi);
233 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
234 "smb2_getinfo_file failed\n");
236 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));
241 torture_assert_u64_equal_goto(tctx,
242 gi.basic_info.out.create_time,
243 nttime,
244 ret, done,
245 "Wrong create time\n");
246 torture_assert_u64_equal_goto(tctx,
247 gi.basic_info.out.write_time,
248 nttime,
249 ret, done,
250 "Wrong write time\n");
251 torture_assert_u64_equal_goto(tctx,
252 gi.basic_info.out.change_time,
253 nttime,
254 ret, done,
255 "Wrong change time\n");
257 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,
265 status = smb2_find_level(tree, tree, &find, &count, &d);
266 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
267 "smb2_find_level failed\n");
269 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 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 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");
285 status = smb2_util_close(tree, handle);
286 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
287 "smb2_util_close failed\n");
288 ZERO_STRUCT(handle);
290 done:
291 if (!smb2_util_handle_empty(handle)) {
292 smb2_util_close(tree, handle);
294 if (!smb2_util_handle_empty(testdirh)) {
295 smb2_util_close(tree, testdirh);
297 smb2_deltree(tree, BASEDIR);
298 return ret;
301 static bool test_time_t_15032385535(struct torture_context *tctx,
302 struct smb2_tree *tree)
304 return test_time_t(tctx, tree, "test_time_t_15032385535.txt",
305 15032385535 /* >> INT32_MAX, limit on ext */);
308 static bool test_time_t_10000000000(struct torture_context *tctx,
309 struct smb2_tree *tree)
311 return test_time_t(tctx, tree, "test_time_t_10000000000.txt",
312 10000000000 /* >> INT32_MAX */);
315 static bool test_time_t_4294967295(struct torture_context *tctx,
316 struct smb2_tree *tree)
318 return test_time_t(tctx, tree, "test_time_t_4294967295.txt",
319 4294967295 /* INT32_MAX */);
322 static bool test_time_t_1(struct torture_context *tctx,
323 struct smb2_tree *tree)
325 return test_time_t(tctx, tree, "test_time_t_1.txt", 1);
328 static bool test_time_t_0(struct torture_context *tctx,
329 struct smb2_tree *tree)
331 return test_time_t(tctx, tree, "test_time_t_0.txt", 0);
334 static bool test_time_t_minus_1(struct torture_context *tctx,
335 struct smb2_tree *tree)
337 return test_time_t(tctx, tree, "test_time_t_-1.txt", -1);
340 static bool test_time_t_minus_2(struct torture_context *tctx,
341 struct smb2_tree *tree)
343 return test_time_t(tctx, tree, "test_time_t_-2.txt", -2);
346 static bool test_time_t_1968(struct torture_context *tctx,
347 struct smb2_tree *tree)
349 return test_time_t(tctx, tree, "test_time_t_1968.txt",
350 -63158400 /* 1968 */);
353 static bool test_freeze_thaw(struct torture_context *tctx,
354 struct smb2_tree *tree)
356 const char *filename = BASEDIR "\\test_freeze_thaw";
357 struct smb2_create cr;
358 struct smb2_handle handle = {{0}};
359 struct smb2_handle testdirh = {{0}};
360 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 bool ret = true;
367 smb2_deltree(tree, BASEDIR);
369 status = torture_smb2_testdir(tree, BASEDIR, &testdirh);
370 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
371 "torture_smb2_testdir failed\n");
373 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,
382 status = smb2_create(tree, tctx, &cr);
383 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
384 "smb2_create failed\n");
385 handle = cr.out.file.handle;
387 si = (union smb_setfileinfo) {
388 .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION,
389 .basic_info.in.file.handle = handle,
393 * Step 1:
394 * First set timestamps of testfile to current time
397 nttime = full_timespec_to_nt_time(&ts);
398 si.basic_info.in.create_time = nttime;
399 si.basic_info.in.write_time = nttime;
400 si.basic_info.in.change_time = nttime;
402 status = smb2_setinfo_file(tree, &si);
403 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
404 "smb2_setinfo_file failed\n");
406 gi = (union smb_fileinfo) {
407 .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
408 .generic.in.file.handle = handle,
412 * Step 2:
413 * Verify timestamps are indeed set to the value in "nttime".
416 status = smb2_getinfo_file(tree, tctx, &gi);
417 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
418 "smb2_getinfo_file failed\n");
420 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));
425 torture_assert_u64_equal_goto(tctx,
426 gi.basic_info.out.create_time,
427 nttime,
428 ret, done,
429 "Wrong create time\n");
430 torture_assert_u64_equal_goto(tctx,
431 gi.basic_info.out.write_time,
432 nttime,
433 ret, done,
434 "Wrong write time\n");
435 torture_assert_u64_equal_goto(tctx,
436 gi.basic_info.out.change_time,
437 nttime,
438 ret, done,
439 "Wrong change time\n");
442 * Step 3:
443 * First set timestamps with NTTIME_FREEZE, must not change any
444 * timestamp value.
447 si.basic_info.in.create_time = NTTIME_FREEZE;
448 si.basic_info.in.write_time = NTTIME_FREEZE;
449 si.basic_info.in.change_time = NTTIME_FREEZE;
451 status = smb2_setinfo_file(tree, &si);
452 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
453 "smb2_setinfo_file failed\n");
455 gi = (union smb_fileinfo) {
456 .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
457 .generic.in.file.handle = handle,
461 * Step 4:
462 * Verify timestamps are unmodified from step 2.
465 gi = (union smb_fileinfo) {
466 .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
467 .generic.in.file.handle = handle,
470 status = smb2_getinfo_file(tree, tctx, &gi);
471 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
472 "smb2_getinfo_file failed\n");
474 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));
479 torture_assert_u64_equal_goto(tctx,
480 gi.basic_info.out.create_time,
481 nttime,
482 ret, done,
483 "Wrong create time\n");
484 torture_assert_u64_equal_goto(tctx,
485 gi.basic_info.out.write_time,
486 nttime,
487 ret, done,
488 "Wrong write time\n");
489 torture_assert_u64_equal_goto(tctx,
490 gi.basic_info.out.change_time,
491 nttime,
492 ret, done,
493 "Wrong change time\n");
496 * Step 5:
497 * First set timestamps with NTTIME_THAW, must not change any timestamp
498 * value.
501 si.basic_info.in.create_time = NTTIME_THAW;
502 si.basic_info.in.write_time = NTTIME_THAW;
503 si.basic_info.in.change_time = NTTIME_THAW;
505 status = smb2_setinfo_file(tree, &si);
506 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
507 "smb2_setinfo_file failed\n");
509 gi = (union smb_fileinfo) {
510 .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
511 .generic.in.file.handle = handle,
515 * Step 6:
516 * Verify timestamps are unmodified from step 2.
519 gi = (union smb_fileinfo) {
520 .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
521 .generic.in.file.handle = handle,
524 status = smb2_getinfo_file(tree, tctx, &gi);
525 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
526 "smb2_getinfo_file failed\n");
528 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));
533 torture_assert_u64_equal_goto(tctx,
534 gi.basic_info.out.create_time,
535 nttime,
536 ret, done,
537 "Wrong create time\n");
538 torture_assert_u64_equal_goto(tctx,
539 gi.basic_info.out.write_time,
540 nttime,
541 ret, done,
542 "Wrong write time\n");
543 torture_assert_u64_equal_goto(tctx,
544 gi.basic_info.out.change_time,
545 nttime,
546 ret, done,
547 "Wrong change time\n");
549 done:
550 if (!smb2_util_handle_empty(handle)) {
551 smb2_util_close(tree, handle);
553 if (!smb2_util_handle_empty(testdirh)) {
554 smb2_util_close(tree, testdirh);
556 smb2_deltree(tree, BASEDIR);
557 return ret;
560 static bool test_delayed_write_vs_seteof(struct torture_context *tctx,
561 struct smb2_tree *tree)
563 struct smb2_create cr;
564 struct smb2_handle h1 = {{0}};
565 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 bool ret = true;
574 smb2_deltree(tree, BASEDIR);
575 status = torture_smb2_testdir(tree, BASEDIR, &h1);
576 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
577 "create failed\n");
578 status = smb2_util_close(tree, h1);
579 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
580 "close failed\n");
582 torture_comment(tctx, "Open file-handle 1\n");
584 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,
590 status = smb2_create(tree, tctx, &cr);
591 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
592 "create failed\n");
593 h1 = cr.out.file.handle;
594 create_time = cr.out.create_time;
595 sleep(1);
597 torture_comment(tctx, "Write to file-handle 1\n");
599 status = smb2_util_write(tree, h1, "s", 0, 1);
600 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
601 "write failed\n");
603 torture_comment(tctx, "Check writetime hasn't been updated\n");
605 finfo = (union smb_fileinfo) {
606 .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
607 .generic.in.file.handle = h1,
609 status = smb2_getinfo_file(tree, tree, &finfo);
610 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
611 "getinfo failed\n");
613 torture_assert_nttime_equal(tctx,
614 finfo.all_info.out.write_time,
615 create_time,
616 "Writetime != set_time (wrong!)\n");
618 torture_comment(tctx, "Setinfo EOF on file-handle 1,"
619 " should flush pending writetime update\n");
621 setinfo = (union smb_setfileinfo) {
622 .generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION,
624 setinfo.end_of_file_info.in.file.handle = h1;
625 setinfo.end_of_file_info.in.size = 1; /* same size! */
627 status = smb2_setinfo_file(tree, &setinfo);
628 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
629 "close failed\n");
631 torture_comment(tctx, "Check writetime has been updated "
632 "by the setinfo EOF\n");
634 finfo = (union smb_fileinfo) {
635 .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
636 .generic.in.file.handle = h1,
638 status = smb2_getinfo_file(tree, tree, &finfo);
639 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
640 "getinfo failed\n");
641 if (!(finfo.all_info.out.write_time > create_time)) {
642 ret = false;
643 torture_fail_goto(tctx, done, "setinfo EOF hasn't updated writetime\n");
646 torture_comment(tctx, "Open file-handle 2\n");
648 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,
654 status = smb2_create(tree, tctx, &cr);
655 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
656 "create failed\n");
657 h2 = cr.out.file.handle;
659 torture_comment(tctx, "Set write time on file-handle 2\n");
661 setinfo = (union smb_setfileinfo) {
662 .generic.level = SMB_QFILEINFO_BASIC_INFORMATION,
664 setinfo.generic.in.file.handle = h2;
665 unix_to_nt_time(&set_time, time(NULL) + 86400);
666 setinfo.basic_info.in.write_time = set_time;
668 status = smb2_setinfo_file(tree, &setinfo);
669 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
670 "close failed\n");
672 status = smb2_util_close(tree, h2);
673 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
674 "close failed\n");
675 ZERO_STRUCT(h2);
677 torture_comment(tctx, "Close file-handle 1, write-time should not be updated\n");
679 c = (struct smb2_close) {
680 .in.file.handle = h1,
681 .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
684 status = smb2_close(tree, &c);
685 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
686 "close failed\n");
687 ZERO_STRUCT(h1);
689 torture_assert_nttime_equal(tctx,
690 c.out.write_time,
691 set_time,
692 "Writetime != set_time (wrong!)\n");
694 done:
695 if (!smb2_util_handle_empty(h1)) {
696 smb2_util_close(tree, h1);
698 if (!smb2_util_handle_empty(h2)) {
699 smb2_util_close(tree, h2);
701 smb2_deltree(tree, BASEDIR);
702 return ret;
705 static bool test_delayed_write_vs_flush(struct torture_context *tctx,
706 struct smb2_tree *tree)
708 struct smb2_create cr;
709 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 bool ret = true;
718 smb2_deltree(tree, BASEDIR);
719 status = torture_smb2_testdir(tree, BASEDIR, &h1);
720 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
721 "create failed\n");
722 status = smb2_util_close(tree, h1);
723 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
724 "close failed\n");
726 torture_comment(tctx, "Open file-handle 1\n");
728 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,
734 status = smb2_create(tree, tctx, &cr);
735 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
736 "create failed\n");
737 h1 = cr.out.file.handle;
738 create_time = cr.out.create_time;
739 sleep(1);
741 torture_comment(tctx, "Write to file-handle 1\n");
743 status = smb2_util_write(tree, h1, "s", 0, 1);
744 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
745 "write failed\n");
747 torture_comment(tctx, "Check writetime hasn't been updated\n");
749 finfo = (union smb_fileinfo) {
750 .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
751 .generic.in.file.handle = h1,
753 status = smb2_getinfo_file(tree, tree, &finfo);
754 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
755 "getinfo failed\n");
757 torture_assert_nttime_equal(tctx,
758 finfo.all_info.out.write_time,
759 create_time,
760 "Writetime != create_time (wrong!)\n");
762 torture_comment(tctx, "Flush file, "
763 "should flush pending writetime update\n");
765 f = (struct smb2_flush) {
766 .in.file.handle = h1,
769 status = smb2_flush(tree, &f);
770 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
771 "flush failed\n");
773 torture_comment(tctx, "Check writetime has been updated "
774 "by the setinfo EOF\n");
776 finfo = (union smb_fileinfo) {
777 .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
778 .generic.in.file.handle = h1,
780 status = smb2_getinfo_file(tree, tree, &finfo);
781 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
782 "getinfo failed\n");
784 flush_time = finfo.all_info.out.write_time;
785 if (!(flush_time > create_time)) {
786 ret = false;
787 torture_fail_goto(tctx, done, "flush hasn't updated writetime\n");
790 torture_comment(tctx, "Close file-handle 1, write-time should not be updated\n");
792 c = (struct smb2_close) {
793 .in.file.handle = h1,
794 .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
797 status = smb2_close(tree, &c);
798 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
799 "close failed\n");
800 ZERO_STRUCT(h1);
802 torture_assert_nttime_equal(tctx,
803 c.out.write_time,
804 flush_time,
805 "writetime != flushtime (wrong!)\n");
807 done:
808 if (!smb2_util_handle_empty(h1)) {
809 smb2_util_close(tree, h1);
811 smb2_deltree(tree, BASEDIR);
812 return ret;
815 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)
820 char *path = NULL;
821 struct smb2_create cr;
822 struct smb2_handle h1 = {{0}};
823 NTTIME create_time;
824 union smb_fileinfo finfo;
825 NTSTATUS status;
826 bool ret = true;
828 torture_comment(tctx, "Create testfile\n");
830 path = talloc_asprintf(tree, BASEDIR "\\" FNAME ".%" PRIu32,
831 generate_random());
832 torture_assert_not_null_goto(tctx, path, ret, done, "OOM\n");
834 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,
840 status = smb2_create(tree, tctx, &cr);
841 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
842 "create failed\n");
843 h1 = cr.out.file.handle;
844 create_time = cr.out.create_time;
846 torture_comment(tctx, "Write to file\n");
848 status = smb2_util_write(tree, h1, "s", 0, 1);
849 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
850 "write failed\n");
852 torture_comment(tctx, "Get timestamps\n");
854 finfo = (union smb_fileinfo) {
855 .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
856 .generic.in.file.handle = h1,
858 status = smb2_getinfo_file(tree, tree, &finfo);
859 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
860 "getinfo failed\n");
862 torture_assert_nttime_equal(tctx,
863 finfo.all_info.out.write_time,
864 create_time,
865 "Writetime != create_time (wrong!)\n");
867 torture_comment(tctx, "Set timestamps\n");
869 setinfo->end_of_file_info.in.file.handle = h1;
870 status = smb2_setinfo_file(tree, setinfo);
871 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
872 "close failed\n");
874 torture_comment(tctx, "Check timestamps\n");
876 finfo = (union smb_fileinfo) {
877 .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
878 .generic.in.file.handle = h1,
880 status = smb2_getinfo_file(tree, tree, &finfo);
881 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
882 "getinfo failed\n");
884 if (expect_update) {
885 if (!(finfo.all_info.out.write_time > create_time)) {
886 ret = false;
887 torture_fail_goto(tctx, done, "setinfo basicinfo "
888 "hasn't updated writetime\n");
890 } else {
891 if (finfo.all_info.out.write_time != create_time) {
892 ret = false;
893 torture_fail_goto(tctx, done, "setinfo basicinfo "
894 "hasn't updated writetime\n");
898 status = smb2_util_close(tree, h1);
899 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
900 "close failed\n");
901 ZERO_STRUCT(h1);
903 status = smb2_util_unlink(tree, path);
904 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
905 "close failed\n");
907 done:
908 TALLOC_FREE(path);
909 if (!smb2_util_handle_empty(h1)) {
910 smb2_util_close(tree, h1);
912 return ret;
915 static bool test_delayed_write_vs_setbasic(struct torture_context *tctx,
916 struct smb2_tree *tree)
918 struct smb2_handle h1 = {{0}};
919 union smb_setfileinfo setinfo;
920 time_t t = time(NULL) - 86400;
921 NTSTATUS status;
922 bool ret = true;
924 smb2_deltree(tree, BASEDIR);
925 status = torture_smb2_testdir(tree, BASEDIR, &h1);
926 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
927 "create failed\n");
928 status = smb2_util_close(tree, h1);
929 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
930 "close failed\n");
933 * Yes, this is correct, tested against Windows 2016: even if all
934 * timestamp fields are 0, a pending write time is flushed.
936 torture_comment(tctx, "Test: setting all-0 timestamps flushes?\n");
938 setinfo = (union smb_setfileinfo) {
939 .generic.level = RAW_SFILEINFO_BASIC_INFORMATION,
941 ret = test_delayed_write_vs_setbasic_do(tctx, tree, &setinfo, true);
942 if (ret != true) {
943 goto done;
946 torture_comment(tctx, "Test: setting create_time flushes?\n");
947 unix_to_nt_time(&setinfo.basic_info.in.create_time, t);
948 ret = test_delayed_write_vs_setbasic_do(tctx, tree, &setinfo, true);
949 if (ret != true) {
950 goto done;
953 torture_comment(tctx, "Test: setting access_time flushes?\n");
954 unix_to_nt_time(&setinfo.basic_info.in.access_time, t);
955 ret = test_delayed_write_vs_setbasic_do(tctx, tree, &setinfo, true);
956 if (ret != true) {
957 goto done;
960 torture_comment(tctx, "Test: setting change_time flushes?\n");
961 unix_to_nt_time(&setinfo.basic_info.in.change_time, t);
962 ret = test_delayed_write_vs_setbasic_do(tctx, tree, &setinfo, true);
963 if (ret != true) {
964 goto done;
967 done:
968 smb2_deltree(tree, BASEDIR);
969 return ret;
972 static bool test_delayed_1write(struct torture_context *tctx,
973 struct smb2_tree *tree)
975 struct smb2_create cr;
976 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 bool ret = true;
985 smb2_deltree(tree, BASEDIR);
986 status = torture_smb2_testdir(tree, BASEDIR, &h1);
987 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
988 "create failed\n");
989 status = smb2_util_close(tree, h1);
990 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
991 "close failed\n");
993 torture_comment(tctx, "Open file-handle 1\n");
995 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,
1001 status = smb2_create(tree, tctx, &cr);
1002 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1003 "create failed\n");
1004 h1 = cr.out.file.handle;
1005 create_time = cr.out.create_time;
1006 sleep(1);
1008 torture_comment(tctx, "Write to file-handle 1\n");
1010 status = smb2_util_write(tree, h1, "s", 0, 1);
1011 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1012 "write failed\n");
1013 sleep(3);
1015 torture_comment(tctx, "Check writetime has been updated\n");
1017 finfo = (union smb_fileinfo) {
1018 .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
1019 .generic.in.file.handle = h1,
1021 status = smb2_getinfo_file(tree, tree, &finfo);
1022 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1023 "getinfo failed\n");
1024 write_time = finfo.all_info.out.write_time;
1026 if (!(write_time > create_time)) {
1027 ret = false;
1028 torture_fail_goto(tctx, done,
1029 "Write-time not updated (wrong!)\n");
1032 torture_comment(tctx, "Close file-handle 1\n");
1033 sleep(1);
1035 c = (struct smb2_close) {
1036 .in.file.handle = h1,
1037 .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
1040 status = smb2_close(tree, &c);
1041 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1042 "close failed\n");
1043 ZERO_STRUCT(h1);
1044 close_time = c.out.write_time;
1046 torture_assert_nttime_equal(tctx, close_time, write_time,
1047 "Writetime != close_time (wrong!)\n");
1049 done:
1050 if (!smb2_util_handle_empty(h1)) {
1051 smb2_util_close(tree, h1);
1053 smb2_deltree(tree, BASEDIR);
1054 return ret;
1057 static bool test_delayed_2write(struct torture_context *tctx,
1058 struct smb2_tree *tree)
1060 struct smb2_create cr;
1061 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 NTTIME close_time;
1068 NTSTATUS status;
1069 bool ret = true;
1071 smb2_deltree(tree, BASEDIR);
1072 status = torture_smb2_testdir(tree, BASEDIR, &h1);
1073 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1074 "create failed\n");
1075 status = smb2_util_close(tree, h1);
1076 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1077 "close failed\n");
1079 torture_comment(tctx, "Open file\n");
1081 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,
1087 status = smb2_create(tree, tctx, &cr);
1088 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1089 "create failed\n");
1090 h1 = cr.out.file.handle;
1091 create_time = cr.out.create_time;
1092 sleep(1);
1094 torture_comment(tctx, "Write to file\n");
1096 status = smb2_util_write(tree, h1, "s", 0, 1);
1097 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1098 "write failed\n");
1099 sleep(3);
1101 torture_comment(tctx, "Check writetime has been updated\n");
1103 finfo = (union smb_fileinfo) {
1104 .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
1105 .generic.in.file.handle = h1,
1107 status = smb2_getinfo_file(tree, tree, &finfo);
1108 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1109 "getinfo failed\n");
1110 write_time = finfo.all_info.out.write_time;
1112 if (!(write_time > create_time)) {
1113 ret = false;
1114 torture_fail_goto(tctx, done,
1115 "Write-time not updated (wrong!)\n");
1118 torture_comment(tctx, "Write a second time\n");
1120 status = smb2_util_write(tree, h1, "s", 0, 1);
1121 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1122 "write failed\n");
1123 sleep(3);
1125 torture_comment(tctx, "Check writetime has NOT been updated\n");
1127 finfo = (union smb_fileinfo) {
1128 .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
1129 .generic.in.file.handle = h1,
1131 status = smb2_getinfo_file(tree, tree, &finfo);
1132 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1133 "getinfo failed\n");
1134 write_time2 = finfo.all_info.out.write_time;
1136 torture_assert_nttime_equal(tctx, write_time2, write_time,
1137 "second write updated write-time (wrong!)\n");
1139 torture_comment(tctx, "Close file-handle 1\n");
1140 sleep(2);
1142 torture_comment(tctx, "Check writetime has been updated\n");
1144 c = (struct smb2_close) {
1145 .in.file.handle = h1,
1146 .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
1149 status = smb2_close(tree, &c);
1150 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1151 "close failed\n");
1152 ZERO_STRUCT(h1);
1153 close_time = c.out.write_time;
1155 if (!(close_time > write_time)) {
1156 ret = false;
1157 torture_fail_goto(tctx, done,
1158 "Write-time not updated (wrong!)\n");
1161 done:
1162 if (!smb2_util_handle_empty(h1)) {
1163 smb2_util_close(tree, h1);
1165 smb2_deltree(tree, BASEDIR);
1166 return ret;
1170 basic testing of SMB2 timestamps
1172 struct torture_suite *torture_smb2_timestamps_init(TALLOC_CTX *ctx)
1174 struct torture_suite *suite = torture_suite_create(ctx, "timestamps");
1176 torture_suite_add_1smb2_test(suite, "test_close_not_attrib", test_close_no_attrib);
1177 torture_suite_add_1smb2_test(suite, "time_t_15032385535", test_time_t_15032385535);
1178 torture_suite_add_1smb2_test(suite, "time_t_10000000000", test_time_t_10000000000);
1179 torture_suite_add_1smb2_test(suite, "time_t_4294967295", test_time_t_4294967295);
1180 torture_suite_add_1smb2_test(suite, "time_t_1", test_time_t_1);
1181 torture_suite_add_1smb2_test(suite, "time_t_0", test_time_t_0);
1182 torture_suite_add_1smb2_test(suite, "time_t_-1", test_time_t_minus_1);
1183 torture_suite_add_1smb2_test(suite, "time_t_-2", test_time_t_minus_2);
1184 torture_suite_add_1smb2_test(suite, "time_t_1968", test_time_t_1968);
1185 torture_suite_add_1smb2_test(suite, "freeze-thaw", test_freeze_thaw);
1188 * Testing of delayed write-time updates
1190 torture_suite_add_1smb2_test(suite, "delayed-write-vs-seteof", test_delayed_write_vs_seteof);
1191 torture_suite_add_1smb2_test(suite, "delayed-write-vs-flush", test_delayed_write_vs_flush);
1192 torture_suite_add_1smb2_test(suite, "delayed-write-vs-setbasic", test_delayed_write_vs_setbasic);
1193 torture_suite_add_1smb2_test(suite, "delayed-1write", test_delayed_1write);
1194 torture_suite_add_1smb2_test(suite, "delayed-2write", test_delayed_2write);
1196 suite->description = talloc_strdup(suite, "SMB2 timestamp tests");
1198 return suite;
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.
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.
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.
1214 * See also: https://lists.samba.org/archive/cifs-protocol/2019-December/003358.html
1216 static bool test_timestamp_resolution1(struct torture_context *tctx,
1217 struct smb2_tree *tree)
1219 union smb_fileinfo finfo1;
1220 const char *fname = BASEDIR "\\" FNAME;
1221 struct smb2_create cr;
1222 struct smb2_handle h = {{0}};
1223 struct smb2_close cl;
1224 NTSTATUS status;
1225 bool ret = true;
1227 smb2_deltree(tree, BASEDIR);
1228 status = torture_smb2_testdir(tree, BASEDIR, &h);
1229 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1230 "create failed\n");
1231 status = smb2_util_close(tree, h );
1232 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1233 "close failed\n");
1235 torture_comment(tctx, "Write without delay, expect no "
1236 "write-time change\n");
1238 smb2_generic_create(&cr, NULL, false, fname,
1239 NTCREATEX_DISP_CREATE,
1240 smb2_util_oplock_level(""), 0, 0);
1241 status = smb2_create(tree, tree, &cr);
1242 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1243 "create failed\n");
1244 h = cr.out.file.handle;
1246 finfo1 = (union smb_fileinfo) {
1247 .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
1248 .generic.in.file.handle = h,
1250 status = smb2_getinfo_file(tree, tree, &finfo1);
1251 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1252 "getinfo failed\n");
1254 status = smb2_util_write(tree, h, "123456789", 0, 9);
1255 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1256 "write failed\n");
1258 cl = (struct smb2_close) {
1259 .in.file.handle = h,
1260 .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
1263 status = smb2_close(tree, &cl);
1264 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1265 "close failed\n");
1266 ZERO_STRUCT(h);
1268 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));
1272 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");
1278 torture_comment(tctx, "Write with 20 ms delay, expect "
1279 "write-time change\n");
1281 smb2_generic_create(&cr, NULL, false, fname,
1282 NTCREATEX_DISP_OPEN,
1283 smb2_util_oplock_level(""), 0, 0);
1284 status = smb2_create(tree, tree, &cr);
1285 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1286 "create failed\n");
1287 h = cr.out.file.handle;
1289 finfo1 = (union smb_fileinfo) {
1290 .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
1291 .generic.in.file.handle = h,
1293 status = smb2_getinfo_file(tree, tree, &finfo1);
1294 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1295 "getinfo failed\n");
1297 smb_msleep(20);
1299 status = smb2_util_write(tree, h, "123456789", 0, 9);
1300 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1301 "write failed\n");
1303 cl = (struct smb2_close) {
1304 .in.file.handle = h,
1305 .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
1308 status = smb2_close(tree, &cl);
1309 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1310 "close failed\n");
1311 ZERO_STRUCT(h);
1313 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));
1317 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");
1324 done:
1325 if (!smb2_util_handle_empty(h)) {
1326 smb2_util_close(tree, h);
1328 smb2_deltree(tree, BASEDIR);
1329 return ret;
1333 basic testing of SMB2 timestamps
1335 struct torture_suite *torture_smb2_timestamp_resolution_init(TALLOC_CTX *ctx)
1337 struct torture_suite *suite = torture_suite_create(ctx, "timestamp_resolution");
1339 torture_suite_add_1smb2_test(suite, "resolution1", test_timestamp_resolution1);
1341 suite->description = talloc_strdup(suite, "SMB2 timestamp tests");
1343 return suite;