LCOV - code coverage report
Current view: top level - source4/torture/smb2 - timestamps.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 447 578 77.3 %
Date: 2024-04-21 15:09:00 Functions: 19 20 95.0 %

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

Generated by: LCOV version 1.14