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

Generated by: LCOV version 1.14