LCOV - code coverage report
Current view: top level - source4/torture/smb2 - timestamps.c (source / functions) Hit Total Coverage
Test: coverage report for master 6248eab5 Lines: 388 464 83.6 %
Date: 2021-08-25 13:27:56 Functions: 18 19 94.7 %

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

Generated by: LCOV version 1.13