LCOV - code coverage report
Current view: top level - source4/torture/smb2 - durable_open.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 1288 1564 82.4 %
Date: 2024-04-21 15:09:00 Functions: 27 28 96.4 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    test suite for SMB2 durable opens
       5             : 
       6             :    Copyright (C) Stefan Metzmacher 2008
       7             :    Copyright (C) Michael Adam 2011-2012
       8             : 
       9             :    This program is free software; you can redistribute it and/or modify
      10             :    it under the terms of the GNU General Public License as published by
      11             :    the Free Software Foundation; either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    This program is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU General Public License
      20             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      21             : */
      22             : 
      23             : #include "includes.h"
      24             : #include "libcli/smb2/smb2.h"
      25             : #include "libcli/smb2/smb2_calls.h"
      26             : #include "../libcli/smb/smbXcli_base.h"
      27             : #include "torture/torture.h"
      28             : #include "torture/smb2/proto.h"
      29             : #include "../libcli/smb/smbXcli_base.h"
      30             : 
      31             : #define CHECK_VAL(v, correct) do { \
      32             :         if ((v) != (correct)) { \
      33             :                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should be 0x%llx\n", \
      34             :                                 __location__, #v, (unsigned long long)v, (unsigned long long)correct); \
      35             :                 ret = false; \
      36             :         }} while (0)
      37             : 
      38             : #define CHECK_NOT_VAL(v, incorrect) do { \
      39             :         if ((v) == (incorrect)) { \
      40             :                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should not be 0x%llx\n", \
      41             :                                 __location__, #v, (unsigned long long)v, (unsigned long long)incorrect); \
      42             :                 ret = false; \
      43             :         }} while (0)
      44             : 
      45             : #define CHECK_NOT_NULL(p) do { \
      46             :         if ((p) == NULL) { \
      47             :                 torture_result(tctx, TORTURE_FAIL, "(%s): %s is NULL but it should not be.\n", \
      48             :                                 __location__, #p); \
      49             :                 ret = false; \
      50             :         }} while (0)
      51             : 
      52             : #define CHECK_STATUS(status, correct) do { \
      53             :         if (!NT_STATUS_EQUAL(status, correct)) { \
      54             :                 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
      55             :                        nt_errstr(status), nt_errstr(correct)); \
      56             :                 ret = false; \
      57             :                 goto done; \
      58             :         }} while (0)
      59             : 
      60             : #define CHECK_CREATED(__io, __created, __attribute)                     \
      61             :         do {                                                            \
      62             :                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
      63             :                 CHECK_VAL((__io)->out.size, 0);                              \
      64             :                 CHECK_VAL((__io)->out.file_attr, (__attribute));     \
      65             :                 CHECK_VAL((__io)->out.reserved2, 0);                 \
      66             :         } while(0)
      67             : 
      68             : #define CHECK_CREATED_SIZE(__io, __created, __attribute, __alloc_size, __size)  \
      69             :         do {                                                                    \
      70             :                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
      71             :                 if (__alloc_size != 0) {                                        \
      72             :                         CHECK_VAL((__io)->out.alloc_size, (__alloc_size));   \
      73             :                 }                                                               \
      74             :                 CHECK_VAL((__io)->out.size, (__size));                               \
      75             :                 CHECK_VAL((__io)->out.file_attr, (__attribute));             \
      76             :                 CHECK_VAL((__io)->out.reserved2, 0);                         \
      77             :         } while(0)
      78             : 
      79             : 
      80             : 
      81             : /**
      82             :  * basic durable_open test.
      83             :  * durable state should only be granted when requested
      84             :  * along with a batch oplock or a handle lease.
      85             :  *
      86             :  * This test tests durable open with all possible oplock types.
      87             :  */
      88             : 
      89             : struct durable_open_vs_oplock {
      90             :         const char *level;
      91             :         const char *share_mode;
      92             :         bool expected;
      93             : };
      94             : 
      95             : #define NUM_OPLOCK_TYPES 4
      96             : #define NUM_SHARE_MODES 8
      97             : #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
      98             : static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
      99             : {
     100             :         { "", "", false },
     101             :         { "", "R", false },
     102             :         { "", "W", false },
     103             :         { "", "D", false },
     104             :         { "", "RD", false },
     105             :         { "", "RW", false },
     106             :         { "", "WD", false },
     107             :         { "", "RWD", false },
     108             : 
     109             :         { "s", "", false },
     110             :         { "s", "R", false },
     111             :         { "s", "W", false },
     112             :         { "s", "D", false },
     113             :         { "s", "RD", false },
     114             :         { "s", "RW", false },
     115             :         { "s", "WD", false },
     116             :         { "s", "RWD", false },
     117             : 
     118             :         { "x", "", false },
     119             :         { "x", "R", false },
     120             :         { "x", "W", false },
     121             :         { "x", "D", false },
     122             :         { "x", "RD", false },
     123             :         { "x", "RW", false },
     124             :         { "x", "WD", false },
     125             :         { "x", "RWD", false },
     126             : 
     127             :         { "b", "", true },
     128             :         { "b", "R", true },
     129             :         { "b", "W", true },
     130             :         { "b", "D", true },
     131             :         { "b", "RD", true },
     132             :         { "b", "RW", true },
     133             :         { "b", "WD", true },
     134             :         { "b", "RWD", true },
     135             : };
     136             : 
     137         128 : static bool test_one_durable_open_open_oplock(struct torture_context *tctx,
     138             :                                               struct smb2_tree *tree,
     139             :                                               const char *fname,
     140             :                                               struct durable_open_vs_oplock test)
     141             : {
     142           0 :         NTSTATUS status;
     143         128 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
     144           0 :         struct smb2_handle _h;
     145         128 :         struct smb2_handle *h = NULL;
     146         128 :         bool ret = true;
     147           0 :         struct smb2_create io;
     148             : 
     149         128 :         smb2_util_unlink(tree, fname);
     150             : 
     151         128 :         smb2_oplock_create_share(&io, fname,
     152             :                                  smb2_util_share_access(test.share_mode),
     153         128 :                                  smb2_util_oplock_level(test.level));
     154         128 :         io.in.durable_open = true;
     155             : 
     156         128 :         status = smb2_create(tree, mem_ctx, &io);
     157         128 :         CHECK_STATUS(status, NT_STATUS_OK);
     158         128 :         _h = io.out.file.handle;
     159         128 :         h = &_h;
     160         128 :         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
     161         128 :         CHECK_VAL(io.out.durable_open, test.expected);
     162         128 :         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
     163             : 
     164         128 : done:
     165         128 :         if (h != NULL) {
     166         128 :                 smb2_util_close(tree, *h);
     167             :         }
     168         128 :         smb2_util_unlink(tree, fname);
     169         128 :         talloc_free(mem_ctx);
     170             : 
     171         128 :         return ret;
     172             : }
     173             : 
     174           4 : static bool test_durable_open_open_oplock(struct torture_context *tctx,
     175             :                                           struct smb2_tree *tree)
     176             : {
     177           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
     178           0 :         char fname[256];
     179           4 :         bool ret = true;
     180           0 :         int i;
     181             : 
     182             :         /* Choose a random name in case the state is left a little funky. */
     183           4 :         snprintf(fname, 256, "durable_open_open_oplock_%s.dat", generate_random_str(tctx, 8));
     184             : 
     185           4 :         smb2_util_unlink(tree, fname);
     186             : 
     187             :         /* test various oplock levels with durable open */
     188             : 
     189         132 :         for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
     190         128 :                 ret = test_one_durable_open_open_oplock(tctx,
     191             :                                                         tree,
     192             :                                                         fname,
     193             :                                                         durable_open_vs_oplock_table[i]);
     194         128 :                 if (ret == false) {
     195           0 :                         goto done;
     196             :                 }
     197             :         }
     198             : 
     199           4 : done:
     200           4 :         smb2_util_unlink(tree, fname);
     201           4 :         talloc_free(tree);
     202           4 :         talloc_free(mem_ctx);
     203             : 
     204           4 :         return ret;
     205             : }
     206             : 
     207             : /**
     208             :  * basic durable_open test.
     209             :  * durable state should only be granted when requested
     210             :  * along with a batch oplock or a handle lease.
     211             :  *
     212             :  * This test tests durable open with all valid lease types.
     213             :  */
     214             : 
     215             : struct durable_open_vs_lease {
     216             :         const char *type;
     217             :         const char *share_mode;
     218             :         bool expected;
     219             : };
     220             : 
     221             : #define NUM_LEASE_TYPES 5
     222             : #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
     223             : static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
     224             : {
     225             :         { "", "", false },
     226             :         { "", "R", false },
     227             :         { "", "W", false },
     228             :         { "", "D", false },
     229             :         { "", "RW", false },
     230             :         { "", "RD", false },
     231             :         { "", "WD", false },
     232             :         { "", "RWD", false },
     233             : 
     234             :         { "R", "", false },
     235             :         { "R", "R", false },
     236             :         { "R", "W", false },
     237             :         { "R", "D", false },
     238             :         { "R", "RW", false },
     239             :         { "R", "RD", false },
     240             :         { "R", "DW", false },
     241             :         { "R", "RWD", false },
     242             : 
     243             :         { "RW", "", false },
     244             :         { "RW", "R", false },
     245             :         { "RW", "W", false },
     246             :         { "RW", "D", false },
     247             :         { "RW", "RW", false },
     248             :         { "RW", "RD", false },
     249             :         { "RW", "WD", false },
     250             :         { "RW", "RWD", false },
     251             : 
     252             :         { "RH", "", true },
     253             :         { "RH", "R", true },
     254             :         { "RH", "W", true },
     255             :         { "RH", "D", true },
     256             :         { "RH", "RW", true },
     257             :         { "RH", "RD", true },
     258             :         { "RH", "WD", true },
     259             :         { "RH", "RWD", true },
     260             : 
     261             :         { "RHW", "", true },
     262             :         { "RHW", "R", true },
     263             :         { "RHW", "W", true },
     264             :         { "RHW", "D", true },
     265             :         { "RHW", "RW", true },
     266             :         { "RHW", "RD", true },
     267             :         { "RHW", "WD", true },
     268             :         { "RHW", "RWD", true },
     269             : };
     270             : 
     271          80 : static bool test_one_durable_open_open_lease(struct torture_context *tctx,
     272             :                                              struct smb2_tree *tree,
     273             :                                              const char *fname,
     274             :                                              struct durable_open_vs_lease test)
     275             : {
     276           0 :         NTSTATUS status;
     277          80 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
     278           0 :         struct smb2_handle _h;
     279          80 :         struct smb2_handle *h = NULL;
     280          80 :         bool ret = true;
     281           0 :         struct smb2_create io;
     282           0 :         struct smb2_lease ls;
     283           0 :         uint64_t lease;
     284           0 :         uint32_t caps;
     285             : 
     286          80 :         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
     287          80 :         if (!(caps & SMB2_CAP_LEASING)) {
     288           0 :                 torture_skip(tctx, "leases are not supported");
     289             :         }
     290             : 
     291          80 :         smb2_util_unlink(tree, fname);
     292             : 
     293          80 :         lease = random();
     294             : 
     295          80 :         smb2_lease_create_share(&io, &ls, false /* dir */, fname,
     296             :                                 smb2_util_share_access(test.share_mode),
     297             :                                 lease,
     298             :                                 smb2_util_lease_state(test.type));
     299          80 :         io.in.durable_open = true;
     300             : 
     301          80 :         status = smb2_create(tree, mem_ctx, &io);
     302          80 :         CHECK_STATUS(status, NT_STATUS_OK);
     303          80 :         _h = io.out.file.handle;
     304          80 :         h = &_h;
     305          80 :         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
     306          80 :         CHECK_VAL(io.out.durable_open, test.expected);
     307          80 :         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
     308          80 :         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
     309          80 :         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
     310          80 :         CHECK_VAL(io.out.lease_response.lease_state,
     311             :                   smb2_util_lease_state(test.type));
     312          80 : done:
     313          80 :         if (h != NULL) {
     314          80 :                 smb2_util_close(tree, *h);
     315             :         }
     316          80 :         smb2_util_unlink(tree, fname);
     317          80 :         talloc_free(mem_ctx);
     318             : 
     319          80 :         return ret;
     320             : }
     321             : 
     322           4 : static bool test_durable_open_open_lease(struct torture_context *tctx,
     323             :                                          struct smb2_tree *tree)
     324             : {
     325           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
     326           0 :         char fname[256];
     327           4 :         bool ret = true;
     328           0 :         int i;
     329           0 :         uint32_t caps;
     330             : 
     331           4 :         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
     332           4 :         if (!(caps & SMB2_CAP_LEASING)) {
     333           2 :                 torture_skip(tctx, "leases are not supported");
     334             :         }
     335             : 
     336             :         /* Choose a random name in case the state is left a little funky. */
     337           2 :         snprintf(fname, 256, "durable_open_open_lease_%s.dat", generate_random_str(tctx, 8));
     338             : 
     339           2 :         smb2_util_unlink(tree, fname);
     340             : 
     341             : 
     342             :         /* test various oplock levels with durable open */
     343             : 
     344          82 :         for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
     345          80 :                 ret = test_one_durable_open_open_lease(tctx,
     346             :                                                        tree,
     347             :                                                        fname,
     348             :                                                        durable_open_vs_lease_table[i]);
     349          80 :                 if (ret == false) {
     350           0 :                         goto done;
     351             :                 }
     352             :         }
     353             : 
     354           2 : done:
     355           2 :         smb2_util_unlink(tree, fname);
     356           2 :         talloc_free(tree);
     357           2 :         talloc_free(mem_ctx);
     358             : 
     359           2 :         return ret;
     360             : }
     361             : 
     362             : /**
     363             :  * basic test for doing a durable open
     364             :  * and do a durable reopen on the same connection
     365             :  * while the first open is still active (fails)
     366             :  */
     367           4 : static bool test_durable_open_reopen1(struct torture_context *tctx,
     368             :                                       struct smb2_tree *tree)
     369             : {
     370           0 :         NTSTATUS status;
     371           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
     372           0 :         char fname[256];
     373           0 :         struct smb2_handle _h;
     374           4 :         struct smb2_handle *h = NULL;
     375           0 :         struct smb2_create io1, io2;
     376           4 :         bool ret = true;
     377             : 
     378             :         /* Choose a random name in case the state is left a little funky. */
     379           4 :         snprintf(fname, 256, "durable_open_reopen1_%s.dat",
     380             :                  generate_random_str(tctx, 8));
     381             : 
     382           4 :         smb2_util_unlink(tree, fname);
     383             : 
     384           4 :         smb2_oplock_create_share(&io1, fname,
     385             :                                  smb2_util_share_access(""),
     386           4 :                                  smb2_util_oplock_level("b"));
     387           4 :         io1.in.durable_open = true;
     388             : 
     389           4 :         status = smb2_create(tree, mem_ctx, &io1);
     390           4 :         CHECK_STATUS(status, NT_STATUS_OK);
     391           4 :         _h = io1.out.file.handle;
     392           4 :         h = &_h;
     393           4 :         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
     394           4 :         CHECK_VAL(io1.out.durable_open, true);
     395           4 :         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
     396             : 
     397             :         /* try a durable reconnect while the file is still open */
     398           4 :         ZERO_STRUCT(io2);
     399           4 :         io2.in.fname = fname;
     400           4 :         io2.in.durable_handle = h;
     401             : 
     402           4 :         status = smb2_create(tree, mem_ctx, &io2);
     403           4 :         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
     404             : 
     405           4 : done:
     406           4 :         if (h != NULL) {
     407           4 :                 smb2_util_close(tree, *h);
     408             :         }
     409             : 
     410           4 :         smb2_util_unlink(tree, fname);
     411             : 
     412           4 :         talloc_free(tree);
     413             : 
     414           4 :         talloc_free(mem_ctx);
     415             : 
     416           4 :         return ret;
     417             : }
     418             : 
     419             : /**
     420             :  * Basic test for doing a durable open
     421             :  * and do a session reconnect while the first
     422             :  * session is still active and the handle is
     423             :  * still open in the client.
     424             :  * This closes the original session and  a
     425             :  * durable reconnect on the new session succeeds.
     426             :  */
     427           4 : static bool test_durable_open_reopen1a(struct torture_context *tctx,
     428             :                                        struct smb2_tree *tree)
     429             : {
     430           0 :         NTSTATUS status;
     431           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
     432           0 :         char fname[256];
     433           0 :         struct smb2_handle _h;
     434           4 :         struct smb2_handle *h = NULL;
     435           0 :         struct smb2_create io;
     436           4 :         bool ret = true;
     437           4 :         struct smb2_tree *tree2 = NULL;
     438           4 :         struct smb2_tree *tree3 = NULL;
     439           0 :         uint64_t previous_session_id;
     440           0 :         struct smbcli_options options;
     441           0 :         struct GUID orig_client_guid;
     442             : 
     443           4 :         options = tree->session->transport->options;
     444           4 :         orig_client_guid = options.client_guid;
     445             : 
     446             :         /* Choose a random name in case the state is left a little funky. */
     447           4 :         snprintf(fname, 256, "durable_open_reopen1a_%s.dat",
     448             :                  generate_random_str(tctx, 8));
     449             : 
     450           4 :         smb2_util_unlink(tree, fname);
     451             : 
     452           4 :         smb2_oplock_create_share(&io, fname,
     453             :                                  smb2_util_share_access(""),
     454           4 :                                  smb2_util_oplock_level("b"));
     455           4 :         io.in.durable_open = true;
     456             : 
     457           4 :         status = smb2_create(tree, mem_ctx, &io);
     458           4 :         CHECK_STATUS(status, NT_STATUS_OK);
     459           4 :         _h = io.out.file.handle;
     460           4 :         h = &_h;
     461           4 :         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
     462           4 :         CHECK_VAL(io.out.durable_open, true);
     463           4 :         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
     464             : 
     465             :         /*
     466             :          * a session reconnect on a second tcp connection
     467             :          */
     468             : 
     469           4 :         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
     470             : 
     471             :         /* for oplocks, the client guid can be different: */
     472           4 :         options.client_guid = GUID_random();
     473             : 
     474           4 :         ret = torture_smb2_connection_ext(tctx, previous_session_id,
     475             :                                           &options, &tree2);
     476           4 :         torture_assert_goto(tctx, ret, ret, done, "could not reconnect");
     477             : 
     478             :         /*
     479             :          * check that this has deleted the old session
     480             :          */
     481             : 
     482           4 :         ZERO_STRUCT(io);
     483           4 :         io.in.fname = fname;
     484           4 :         io.in.durable_handle = h;
     485             : 
     486           4 :         status = smb2_create(tree, mem_ctx, &io);
     487           4 :         CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
     488             : 
     489           4 :         TALLOC_FREE(tree);
     490             : 
     491             :         /*
     492             :          * but a durable reconnect on the new session succeeds:
     493             :          */
     494             : 
     495           4 :         ZERO_STRUCT(io);
     496           4 :         io.in.fname = fname;
     497           4 :         io.in.durable_handle = h;
     498             : 
     499           4 :         status = smb2_create(tree2, mem_ctx, &io);
     500           4 :         CHECK_STATUS(status, NT_STATUS_OK);
     501           4 :         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
     502           4 :         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
     503           4 :         _h = io.out.file.handle;
     504           4 :         h = &_h;
     505             : 
     506             :         /*
     507             :          * a session reconnect on a second tcp connection
     508             :          */
     509             : 
     510           4 :         previous_session_id = smb2cli_session_current_id(tree2->session->smbXcli);
     511             : 
     512             :         /* the original client_guid works just the same */
     513           4 :         options.client_guid = orig_client_guid;
     514             : 
     515           4 :         ret = torture_smb2_connection_ext(tctx, previous_session_id,
     516             :                                           &options, &tree3);
     517           4 :         torture_assert_goto(tctx, ret, ret, done, "could not reconnect");
     518             : 
     519             :         /*
     520             :          * check that this has deleted the old session
     521             :          */
     522             : 
     523           4 :         ZERO_STRUCT(io);
     524           4 :         io.in.fname = fname;
     525           4 :         io.in.durable_handle = h;
     526             : 
     527           4 :         status = smb2_create(tree2, mem_ctx, &io);
     528           4 :         CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
     529             : 
     530           4 :         TALLOC_FREE(tree2);
     531             : 
     532             :         /*
     533             :          * but a durable reconnect on the new session succeeds:
     534             :          */
     535             : 
     536           4 :         ZERO_STRUCT(io);
     537           4 :         io.in.fname = fname;
     538           4 :         io.in.durable_handle = h;
     539             : 
     540           4 :         status = smb2_create(tree3, mem_ctx, &io);
     541           4 :         CHECK_STATUS(status, NT_STATUS_OK);
     542           4 :         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
     543           4 :         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
     544           4 :         _h = io.out.file.handle;
     545           4 :         h = &_h;
     546             : 
     547           4 : done:
     548           4 :         if (tree == NULL) {
     549           4 :                 tree = tree2;
     550             :         }
     551             : 
     552           4 :         if (tree == NULL) {
     553           4 :                 tree = tree3;
     554             :         }
     555             : 
     556           4 :         if (tree != NULL) {
     557           4 :                 if (h != NULL) {
     558           4 :                         smb2_util_close(tree, *h);
     559           4 :                         h = NULL;
     560             :                 }
     561           4 :                 smb2_util_unlink(tree, fname);
     562             : 
     563           4 :                 talloc_free(tree);
     564             :         }
     565             : 
     566           4 :         talloc_free(mem_ctx);
     567             : 
     568           4 :         return ret;
     569             : }
     570             : 
     571             : /**
     572             :  * lease variant of reopen1a
     573             :  *
     574             :  * Basic test for doing a durable open and doing a session
     575             :  * reconnect while the first session is still active and the
     576             :  * handle is still open in the client.
     577             :  * This closes the original session and  a durable reconnect on
     578             :  * the new session succeeds depending on the client guid:
     579             :  *
     580             :  * Durable reconnect on a session with a different client guid fails.
     581             :  * Durable reconnect on a session with the original client guid succeeds.
     582             :  */
     583           4 : bool test_durable_open_reopen1a_lease(struct torture_context *tctx,
     584             :                                       struct smb2_tree *tree)
     585             : {
     586           0 :         NTSTATUS status;
     587           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
     588           0 :         char fname[256];
     589           0 :         struct smb2_handle _h;
     590           4 :         struct smb2_handle *h = NULL;
     591           0 :         struct smb2_create io;
     592           0 :         struct smb2_lease ls;
     593           0 :         uint64_t lease_key;
     594           4 :         bool ret = true;
     595           4 :         struct smb2_tree *tree2 = NULL;
     596           4 :         struct smb2_tree *tree3 = NULL;
     597           0 :         uint64_t previous_session_id;
     598           0 :         struct smbcli_options options;
     599           0 :         struct GUID orig_client_guid;
     600             : 
     601           4 :         options = tree->session->transport->options;
     602           4 :         orig_client_guid = options.client_guid;
     603             : 
     604             :         /* Choose a random name in case the state is left a little funky. */
     605           4 :         snprintf(fname, 256, "durable_v2_open_reopen1a_lease_%s.dat",
     606             :                  generate_random_str(tctx, 8));
     607             : 
     608           4 :         smb2_util_unlink(tree, fname);
     609             : 
     610           4 :         lease_key = random();
     611           4 :         smb2_lease_create(&io, &ls, false /* dir */, fname,
     612             :                           lease_key, smb2_util_lease_state("RWH"));
     613           4 :         io.in.durable_open = true;
     614             : 
     615           4 :         status = smb2_create(tree, mem_ctx, &io);
     616           4 :         CHECK_STATUS(status, NT_STATUS_OK);
     617           4 :         _h = io.out.file.handle;
     618           4 :         h = &_h;
     619           4 :         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
     620           4 :         CHECK_VAL(io.out.durable_open, true);
     621           4 :         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
     622           4 :         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
     623           4 :         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
     624           4 :         CHECK_VAL(io.out.lease_response.lease_state,
     625             :                   smb2_util_lease_state("RWH"));
     626           4 :         CHECK_VAL(io.out.lease_response.lease_flags, 0);
     627           4 :         CHECK_VAL(io.out.lease_response.lease_duration, 0);
     628             : 
     629           4 :         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
     630             : 
     631             :         /*
     632             :          * a session reconnect on a second tcp connection
     633             :          * with a different client_guid does not allow
     634             :          * the durable reconnect.
     635             :          */
     636             : 
     637           4 :         options.client_guid = GUID_random();
     638             : 
     639           4 :         ret = torture_smb2_connection_ext(tctx, previous_session_id,
     640             :                                           &options, &tree2);
     641           4 :         torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
     642             : 
     643             :         /*
     644             :          * check that this has deleted the old session
     645             :          */
     646             : 
     647           4 :         ZERO_STRUCT(io);
     648           4 :         io.in.fname = fname;
     649           4 :         io.in.durable_handle = h;
     650           4 :         io.in.lease_request = &ls;
     651           4 :         status = smb2_create(tree, mem_ctx, &io);
     652           4 :         CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
     653           4 :         TALLOC_FREE(tree);
     654             : 
     655             : 
     656             :         /*
     657             :          * but a durable reconnect on the new session with the wrong
     658             :          * client guid fails
     659             :          */
     660             : 
     661           4 :         ZERO_STRUCT(io);
     662           4 :         io.in.fname = fname;
     663           4 :         io.in.durable_handle = h;
     664           4 :         io.in.lease_request = &ls;
     665           4 :         status = smb2_create(tree2, mem_ctx, &io);
     666           4 :         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
     667             : 
     668             :         /*
     669             :          * now a session reconnect on a second tcp connection
     670             :          * with original client_guid allows the durable reconnect.
     671             :          */
     672             : 
     673           4 :         options.client_guid = orig_client_guid;
     674             : 
     675           4 :         ret = torture_smb2_connection_ext(tctx, previous_session_id,
     676             :                                           &options, &tree3);
     677           4 :         torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
     678             : 
     679             :         /*
     680             :          * check that this has deleted the old session
     681             :          * In this case, a durable reconnect attempt with the
     682             :          * correct client_guid yields a different error code.
     683             :          */
     684             : 
     685           4 :         ZERO_STRUCT(io);
     686           4 :         io.in.fname = fname;
     687           4 :         io.in.durable_handle = h;
     688           4 :         io.in.lease_request = &ls;
     689           4 :         status = smb2_create(tree2, mem_ctx, &io);
     690           4 :         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
     691           4 :         TALLOC_FREE(tree2);
     692             : 
     693             :         /*
     694             :          * but a durable reconnect on the new session succeeds:
     695             :          */
     696             : 
     697           4 :         ZERO_STRUCT(io);
     698           4 :         io.in.fname = fname;
     699           4 :         io.in.durable_handle = h;
     700           4 :         io.in.lease_request = &ls;
     701           4 :         status = smb2_create(tree3, mem_ctx, &io);
     702           4 :         CHECK_STATUS(status, NT_STATUS_OK);
     703           2 :         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
     704           2 :         CHECK_VAL(io.out.durable_open, false); /* no dh response context... */
     705           2 :         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
     706           2 :         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
     707           2 :         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
     708           2 :         CHECK_VAL(io.out.lease_response.lease_state,
     709             :                   smb2_util_lease_state("RWH"));
     710           2 :         CHECK_VAL(io.out.lease_response.lease_flags, 0);
     711           2 :         CHECK_VAL(io.out.lease_response.lease_duration, 0);
     712           2 :         _h = io.out.file.handle;
     713           2 :         h = &_h;
     714             : 
     715           4 : done:
     716           4 :         if (tree == NULL) {
     717           4 :                 tree = tree2;
     718             :         }
     719             : 
     720           4 :         if (tree == NULL) {
     721           4 :                 tree = tree3;
     722             :         }
     723             : 
     724           4 :         if (tree != NULL) {
     725           4 :                 if (h != NULL) {
     726           4 :                         smb2_util_close(tree, *h);
     727             :                 }
     728             : 
     729           4 :                 smb2_util_unlink(tree, fname);
     730             : 
     731           4 :                 talloc_free(tree);
     732             :         }
     733             : 
     734           4 :         talloc_free(mem_ctx);
     735             : 
     736           4 :         return ret;
     737             : }
     738             : 
     739             : 
     740             : /**
     741             :  * basic test for doing a durable open
     742             :  * tcp disconnect, reconnect, do a durable reopen (succeeds)
     743             :  */
     744           4 : static bool test_durable_open_reopen2(struct torture_context *tctx,
     745             :                                       struct smb2_tree *tree)
     746             : {
     747           0 :         NTSTATUS status;
     748           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
     749           0 :         char fname[256];
     750           0 :         struct smb2_handle _h;
     751           4 :         struct smb2_handle *h = NULL;
     752           0 :         struct smb2_create io;
     753           4 :         bool ret = true;
     754             : 
     755             :         /* Choose a random name in case the state is left a little funky. */
     756           4 :         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
     757             :                  generate_random_str(tctx, 8));
     758             : 
     759           4 :         smb2_util_unlink(tree, fname);
     760             : 
     761           4 :         smb2_oplock_create_share(&io, fname,
     762             :                                  smb2_util_share_access(""),
     763           4 :                                  smb2_util_oplock_level("b"));
     764           4 :         io.in.durable_open = true;
     765             : 
     766           4 :         status = smb2_create(tree, mem_ctx, &io);
     767           4 :         CHECK_STATUS(status, NT_STATUS_OK);
     768           4 :         _h = io.out.file.handle;
     769           4 :         h = &_h;
     770           4 :         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
     771           4 :         CHECK_VAL(io.out.durable_open, true);
     772           4 :         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
     773             : 
     774             :         /* disconnect, leaving the durable in place */
     775           4 :         TALLOC_FREE(tree);
     776             : 
     777           4 :         if (!torture_smb2_connection(tctx, &tree)) {
     778           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
     779           0 :                 ret = false;
     780           0 :                 goto done;
     781             :         }
     782             : 
     783           4 :         ZERO_STRUCT(io);
     784             :         /* the path name is ignored by the server */
     785           4 :         io.in.fname = fname;
     786           4 :         io.in.durable_handle = h; /* durable v1 reconnect request */
     787           4 :         h = NULL;
     788             : 
     789           4 :         status = smb2_create(tree, mem_ctx, &io);
     790           4 :         CHECK_STATUS(status, NT_STATUS_OK);
     791           4 :         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
     792           4 :         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
     793           4 :         _h = io.out.file.handle;
     794           4 :         h = &_h;
     795             : 
     796             :         /* disconnect again, leaving the durable in place */
     797           4 :         TALLOC_FREE(tree);
     798             : 
     799           4 :         if (!torture_smb2_connection(tctx, &tree)) {
     800           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
     801           0 :                 ret = false;
     802           0 :                 goto done;
     803             :         }
     804             : 
     805             :         /*
     806             :          * show that the filename and many other fields
     807             :          * are ignored. only the reconnect request blob
     808             :          * is important.
     809             :          */
     810           4 :         ZERO_STRUCT(io);
     811             :         /* the path name is ignored by the server */
     812           4 :         io.in.security_flags = 0x78;
     813           4 :         io.in.oplock_level = 0x78;
     814           4 :         io.in.impersonation_level = 0x12345678;
     815           4 :         io.in.create_flags = 0x12345678;
     816           4 :         io.in.reserved = 0x12345678;
     817           4 :         io.in.desired_access = 0x12345678;
     818           4 :         io.in.file_attributes = 0x12345678;
     819           4 :         io.in.share_access = 0x12345678;
     820           4 :         io.in.create_disposition = 0x12345678;
     821           4 :         io.in.create_options = 0x12345678;
     822           4 :         io.in.fname = "__non_existing_fname__";
     823           4 :         io.in.durable_handle = h; /* durable v1 reconnect request */
     824           4 :         h = NULL;
     825             : 
     826           4 :         status = smb2_create(tree, mem_ctx, &io);
     827           4 :         CHECK_STATUS(status, NT_STATUS_OK);
     828           4 :         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
     829           4 :         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
     830           4 :         _h = io.out.file.handle;
     831           4 :         h = &_h;
     832             : 
     833             :         /* disconnect, leaving the durable in place */
     834           4 :         TALLOC_FREE(tree);
     835             : 
     836           4 :         if (!torture_smb2_connection(tctx, &tree)) {
     837           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
     838           0 :                 ret = false;
     839           0 :                 goto done;
     840             :         }
     841             : 
     842             :         /*
     843             :          * show that an additionally specified durable v1 request
     844             :          * is ignored by the server.
     845             :          * See MS-SMB2, 3.3.5.9.7
     846             :          * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context
     847             :          */
     848           4 :         ZERO_STRUCT(io);
     849             :         /* the path name is ignored by the server */
     850           4 :         io.in.fname = fname;
     851           4 :         io.in.durable_handle = h;  /* durable v1 reconnect request */
     852           4 :         io.in.durable_open = true; /* durable v1 handle request */
     853           4 :         h = NULL;
     854             : 
     855           4 :         status = smb2_create(tree, mem_ctx, &io);
     856           4 :         CHECK_STATUS(status, NT_STATUS_OK);
     857           4 :         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
     858           4 :         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
     859           4 :         _h = io.out.file.handle;
     860           4 :         h = &_h;
     861             : 
     862           4 : done:
     863           4 :         if (tree != NULL) {
     864           4 :                 if (h != NULL) {
     865           4 :                         smb2_util_close(tree, *h);
     866             :                 }
     867             : 
     868           4 :                 smb2_util_unlink(tree, fname);
     869             : 
     870           4 :                 talloc_free(tree);
     871             :         }
     872             : 
     873           4 :         talloc_free(mem_ctx);
     874             : 
     875           4 :         return ret;
     876             : }
     877             : 
     878             : /**
     879             :  * lease variant of reopen2
     880             :  * basic test for doing a durable open
     881             :  * tcp disconnect, reconnect, do a durable reopen (succeeds)
     882             :  */
     883           4 : static bool test_durable_open_reopen2_lease(struct torture_context *tctx,
     884             :                                             struct smb2_tree *tree)
     885             : {
     886           0 :         NTSTATUS status;
     887           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
     888           0 :         char fname[256];
     889           0 :         struct smb2_handle _h;
     890           4 :         struct smb2_handle *h = NULL;
     891           0 :         struct smb2_create io;
     892           0 :         struct smb2_lease ls;
     893           0 :         uint64_t lease_key;
     894           4 :         bool ret = true;
     895           0 :         struct smbcli_options options;
     896           0 :         uint32_t caps;
     897             : 
     898           4 :         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
     899           4 :         if (!(caps & SMB2_CAP_LEASING)) {
     900           2 :                 torture_skip(tctx, "leases are not supported");
     901             :         }
     902             : 
     903           2 :         options = tree->session->transport->options;
     904             : 
     905             :         /* Choose a random name in case the state is left a little funky. */
     906           2 :         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
     907             :                  generate_random_str(tctx, 8));
     908             : 
     909           2 :         smb2_util_unlink(tree, fname);
     910             : 
     911           2 :         lease_key = random();
     912           2 :         smb2_lease_create(&io, &ls, false /* dir */, fname, lease_key,
     913             :                           smb2_util_lease_state("RWH"));
     914           2 :         io.in.durable_open = true;
     915             : 
     916           2 :         status = smb2_create(tree, mem_ctx, &io);
     917           2 :         CHECK_STATUS(status, NT_STATUS_OK);
     918           2 :         _h = io.out.file.handle;
     919           2 :         h = &_h;
     920           2 :         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
     921             : 
     922           2 :         CHECK_VAL(io.out.durable_open, true);
     923           2 :         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
     924           2 :         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
     925           2 :         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
     926           2 :         CHECK_VAL(io.out.lease_response.lease_state,
     927             :                   smb2_util_lease_state("RWH"));
     928           2 :         CHECK_VAL(io.out.lease_response.lease_flags, 0);
     929           2 :         CHECK_VAL(io.out.lease_response.lease_duration, 0);
     930             : 
     931             :         /* disconnect, reconnect and then do durable reopen */
     932           2 :         TALLOC_FREE(tree);
     933             : 
     934           2 :         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
     935           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
     936           0 :                 ret = false;
     937           0 :                 goto done;
     938             :         }
     939             : 
     940             : 
     941             :         /* a few failure tests: */
     942             : 
     943             :         /*
     944             :          * several attempts without lease attached:
     945             :          * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
     946             :          * irrespective of file name provided
     947             :          */
     948             : 
     949           2 :         ZERO_STRUCT(io);
     950           2 :         io.in.fname = "";
     951           2 :         io.in.durable_handle = h;
     952           2 :         status = smb2_create(tree, mem_ctx, &io);
     953           2 :         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
     954             : 
     955           2 :         ZERO_STRUCT(io);
     956           2 :         io.in.fname = "__non_existing_fname__";
     957           2 :         io.in.durable_handle = h;
     958           2 :         status = smb2_create(tree, mem_ctx, &io);
     959           2 :         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
     960             : 
     961           2 :         ZERO_STRUCT(io);
     962           2 :         io.in.fname = fname;
     963           2 :         io.in.durable_handle = h;
     964           2 :         status = smb2_create(tree, mem_ctx, &io);
     965           2 :         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
     966             : 
     967             :         /*
     968             :          * attempt with lease provided, but
     969             :          * with a changed lease key. => fails
     970             :          */
     971           2 :         ZERO_STRUCT(io);
     972           2 :         io.in.fname = fname;
     973           2 :         io.in.durable_open = false;
     974           2 :         io.in.durable_handle = h;
     975           2 :         io.in.lease_request = &ls;
     976           2 :         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
     977             :         /* a wrong lease key lets the request fail */
     978           2 :         ls.lease_key.data[0]++;
     979             : 
     980           2 :         status = smb2_create(tree, mem_ctx, &io);
     981           2 :         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
     982             : 
     983             :         /* restore the correct lease key */
     984           2 :         ls.lease_key.data[0]--;
     985             : 
     986             :         /*
     987             :          * this last failing attempt is almost correct:
     988             :          * only problem is: we use the wrong filename...
     989             :          * Note that this gives INVALID_PARAMETER.
     990             :          * This is different from oplocks!
     991             :          */
     992           2 :         ZERO_STRUCT(io);
     993           2 :         io.in.fname = "__non_existing_fname__";
     994           2 :         io.in.durable_open = false;
     995           2 :         io.in.durable_handle = h;
     996           2 :         io.in.lease_request = &ls;
     997           2 :         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
     998             : 
     999           2 :         status = smb2_create(tree, mem_ctx, &io);
    1000           2 :         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
    1001             : 
    1002             :         /*
    1003             :          * Now for a succeeding reconnect:
    1004             :          */
    1005             : 
    1006           2 :         ZERO_STRUCT(io);
    1007           2 :         io.in.fname = fname;
    1008           2 :         io.in.durable_open = false;
    1009           2 :         io.in.durable_handle = h;
    1010           2 :         io.in.lease_request = &ls;
    1011           2 :         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
    1012             : 
    1013             :         /* the requested lease state is irrelevant */
    1014           2 :         ls.lease_state = smb2_util_lease_state("");
    1015             : 
    1016           2 :         h = NULL;
    1017             : 
    1018           2 :         status = smb2_create(tree, mem_ctx, &io);
    1019           2 :         CHECK_STATUS(status, NT_STATUS_OK);
    1020             : 
    1021           2 :         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
    1022           2 :         CHECK_VAL(io.out.durable_open, false);
    1023           2 :         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
    1024           2 :         CHECK_VAL(io.out.persistent_open, false);
    1025           2 :         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
    1026           2 :         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
    1027           2 :         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
    1028           2 :         CHECK_VAL(io.out.lease_response.lease_state,
    1029             :                   smb2_util_lease_state("RWH"));
    1030           2 :         CHECK_VAL(io.out.lease_response.lease_flags, 0);
    1031           2 :         CHECK_VAL(io.out.lease_response.lease_duration, 0);
    1032           2 :         _h = io.out.file.handle;
    1033           2 :         h = &_h;
    1034             : 
    1035             :         /* disconnect one more time */
    1036           2 :         TALLOC_FREE(tree);
    1037             : 
    1038           2 :         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
    1039           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
    1040           0 :                 ret = false;
    1041           0 :                 goto done;
    1042             :         }
    1043             : 
    1044             :         /*
    1045             :          * demonstrate that various parameters are ignored
    1046             :          * in the reconnect
    1047             :          */
    1048             : 
    1049           2 :         ZERO_STRUCT(io);
    1050             :         /*
    1051             :          * These are completely ignored by the server
    1052             :          */
    1053           2 :         io.in.security_flags = 0x78;
    1054           2 :         io.in.oplock_level = 0x78;
    1055           2 :         io.in.impersonation_level = 0x12345678;
    1056           2 :         io.in.create_flags = 0x12345678;
    1057           2 :         io.in.reserved = 0x12345678;
    1058           2 :         io.in.desired_access = 0x12345678;
    1059           2 :         io.in.file_attributes = 0x12345678;
    1060           2 :         io.in.share_access = 0x12345678;
    1061           2 :         io.in.create_disposition = 0x12345678;
    1062           2 :         io.in.create_options = 0x12345678;
    1063             : 
    1064             :         /*
    1065             :          * only these are checked:
    1066             :          * - io.in.fname
    1067             :          * - io.in.durable_handle,
    1068             :          * - io.in.lease_request->lease_key
    1069             :          */
    1070             : 
    1071           2 :         io.in.fname = fname;
    1072           2 :         io.in.durable_open_v2 = false;
    1073           2 :         io.in.durable_handle_v2 = h;
    1074           2 :         io.in.lease_request = &ls;
    1075             : 
    1076             :         /* the requested lease state is irrelevant */
    1077           2 :         ls.lease_state = smb2_util_lease_state("");
    1078             : 
    1079           2 :         h = NULL;
    1080             : 
    1081           2 :         status = smb2_create(tree, mem_ctx, &io);
    1082           2 :         CHECK_STATUS(status, NT_STATUS_OK);
    1083             : 
    1084           2 :         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
    1085           2 :         CHECK_VAL(io.out.durable_open, false);
    1086           2 :         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
    1087           2 :         CHECK_VAL(io.out.persistent_open, false);
    1088           2 :         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
    1089           2 :         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
    1090           2 :         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
    1091           2 :         CHECK_VAL(io.out.lease_response.lease_state,
    1092             :                   smb2_util_lease_state("RWH"));
    1093           2 :         CHECK_VAL(io.out.lease_response.lease_flags, 0);
    1094           2 :         CHECK_VAL(io.out.lease_response.lease_duration, 0);
    1095             : 
    1096           2 :         _h = io.out.file.handle;
    1097           2 :         h = &_h;
    1098             : 
    1099           2 : done:
    1100           2 :         if (tree != NULL) {
    1101           2 :                 if (h != NULL) {
    1102           2 :                         smb2_util_close(tree, *h);
    1103             :                 }
    1104             : 
    1105           2 :                 smb2_util_unlink(tree, fname);
    1106             : 
    1107           2 :                 talloc_free(tree);
    1108             :         }
    1109             : 
    1110           2 :         talloc_free(mem_ctx);
    1111             : 
    1112           2 :         return ret;
    1113             : }
    1114             : 
    1115             : /**
    1116             :  * lease v2 variant of reopen2
    1117             :  * basic test for doing a durable open
    1118             :  * tcp disconnect, reconnect, do a durable reopen (succeeds)
    1119             :  */
    1120           4 : static bool test_durable_open_reopen2_lease_v2(struct torture_context *tctx,
    1121             :                                                struct smb2_tree *tree)
    1122             : {
    1123           0 :         NTSTATUS status;
    1124           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
    1125           0 :         char fname[256];
    1126           0 :         struct smb2_handle _h;
    1127           4 :         struct smb2_handle *h = NULL;
    1128           0 :         struct smb2_create io;
    1129           0 :         struct smb2_lease ls;
    1130           0 :         uint64_t lease_key;
    1131           4 :         bool ret = true;
    1132           0 :         struct smbcli_options options;
    1133           0 :         uint32_t caps;
    1134             : 
    1135           4 :         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
    1136           4 :         if (!(caps & SMB2_CAP_LEASING)) {
    1137           2 :                 torture_skip(tctx, "leases are not supported");
    1138             :         }
    1139             : 
    1140           2 :         options = tree->session->transport->options;
    1141             : 
    1142             :         /* Choose a random name in case the state is left a little funky. */
    1143           2 :         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
    1144             :                  generate_random_str(tctx, 8));
    1145             : 
    1146           2 :         smb2_util_unlink(tree, fname);
    1147             : 
    1148           2 :         lease_key = random();
    1149           2 :         smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
    1150             :                              lease_key, 0, /* parent lease key */
    1151             :                              smb2_util_lease_state("RWH"), 0 /* lease epoch */);
    1152           2 :         io.in.durable_open = true;
    1153             : 
    1154           2 :         status = smb2_create(tree, mem_ctx, &io);
    1155           2 :         CHECK_STATUS(status, NT_STATUS_OK);
    1156           2 :         _h = io.out.file.handle;
    1157           2 :         h = &_h;
    1158           2 :         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
    1159             : 
    1160           2 :         CHECK_VAL(io.out.durable_open, true);
    1161           2 :         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
    1162           2 :         CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
    1163           2 :         CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
    1164           2 :         CHECK_VAL(io.out.lease_response_v2.lease_state,
    1165             :                   smb2_util_lease_state("RWH"));
    1166           2 :         CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
    1167           2 :         CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
    1168             : 
    1169             :         /* disconnect, reconnect and then do durable reopen */
    1170           2 :         TALLOC_FREE(tree);
    1171             : 
    1172           2 :         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
    1173           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
    1174           0 :                 ret = false;
    1175           0 :                 goto done;
    1176             :         }
    1177             : 
    1178             :         /* a few failure tests: */
    1179             : 
    1180             :         /*
    1181             :          * several attempts without lease attached:
    1182             :          * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
    1183             :          * irrespective of file name provided
    1184             :          */
    1185             : 
    1186           2 :         ZERO_STRUCT(io);
    1187           2 :         io.in.fname = "";
    1188           2 :         io.in.durable_handle = h;
    1189           2 :         status = smb2_create(tree, mem_ctx, &io);
    1190           2 :         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
    1191             : 
    1192           2 :         ZERO_STRUCT(io);
    1193           2 :         io.in.fname = "__non_existing_fname__";
    1194           2 :         io.in.durable_handle = h;
    1195           2 :         status = smb2_create(tree, mem_ctx, &io);
    1196           2 :         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
    1197             : 
    1198           2 :         ZERO_STRUCT(io);
    1199           2 :         io.in.fname = fname;
    1200           2 :         io.in.durable_handle = h;
    1201           2 :         status = smb2_create(tree, mem_ctx, &io);
    1202           2 :         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
    1203             : 
    1204             :         /*
    1205             :          * attempt with lease provided, but
    1206             :          * with a changed lease key. => fails
    1207             :          */
    1208           2 :         ZERO_STRUCT(io);
    1209           2 :         io.in.fname = fname;
    1210           2 :         io.in.durable_open = false;
    1211           2 :         io.in.durable_handle = h;
    1212           2 :         io.in.lease_request_v2 = &ls;
    1213           2 :         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
    1214             :         /* a wrong lease key lets the request fail */
    1215           2 :         ls.lease_key.data[0]++;
    1216             : 
    1217           2 :         status = smb2_create(tree, mem_ctx, &io);
    1218           2 :         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
    1219             : 
    1220             :         /* restore the correct lease key */
    1221           2 :         ls.lease_key.data[0]--;
    1222             : 
    1223             :         /*
    1224             :          * this last failing attempt is almost correct:
    1225             :          * only problem is: we use the wrong filename...
    1226             :          * Note that this gives INVALID_PARAMETER.
    1227             :          * This is different from oplocks!
    1228             :          */
    1229           2 :         ZERO_STRUCT(io);
    1230           2 :         io.in.fname = "__non_existing_fname__";
    1231           2 :         io.in.durable_open = false;
    1232           2 :         io.in.durable_handle = h;
    1233           2 :         io.in.lease_request_v2 = &ls;
    1234           2 :         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
    1235             : 
    1236           2 :         status = smb2_create(tree, mem_ctx, &io);
    1237           2 :         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
    1238             : 
    1239             :         /*
    1240             :          * Now for a succeeding reconnect:
    1241             :          */
    1242             : 
    1243           2 :         ZERO_STRUCT(io);
    1244           2 :         io.in.fname = fname;
    1245           2 :         io.in.durable_open = false;
    1246           2 :         io.in.durable_handle = h;
    1247           2 :         io.in.lease_request_v2 = &ls;
    1248           2 :         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
    1249             : 
    1250             :         /* the requested lease state is irrelevant */
    1251           2 :         ls.lease_state = smb2_util_lease_state("");
    1252             : 
    1253           2 :         h = NULL;
    1254             : 
    1255           2 :         status = smb2_create(tree, mem_ctx, &io);
    1256           2 :         CHECK_STATUS(status, NT_STATUS_OK);
    1257             : 
    1258           2 :         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
    1259           2 :         CHECK_VAL(io.out.durable_open, false);
    1260           2 :         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
    1261           2 :         CHECK_VAL(io.out.persistent_open, false);
    1262           2 :         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
    1263           2 :         CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
    1264           2 :         CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
    1265           2 :         CHECK_VAL(io.out.lease_response_v2.lease_state,
    1266             :                   smb2_util_lease_state("RWH"));
    1267           2 :         CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
    1268           2 :         CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
    1269           2 :         _h = io.out.file.handle;
    1270           2 :         h = &_h;
    1271             : 
    1272             :         /* disconnect one more time */
    1273           2 :         TALLOC_FREE(tree);
    1274             : 
    1275           2 :         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
    1276           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
    1277           0 :                 ret = false;
    1278           0 :                 goto done;
    1279             :         }
    1280             : 
    1281             :         /*
    1282             :          * demonstrate that various parameters are ignored
    1283             :          * in the reconnect
    1284             :          */
    1285             : 
    1286           2 :         ZERO_STRUCT(io);
    1287             :         /*
    1288             :          * These are completely ignored by the server
    1289             :          */
    1290           2 :         io.in.security_flags = 0x78;
    1291           2 :         io.in.oplock_level = 0x78;
    1292           2 :         io.in.impersonation_level = 0x12345678;
    1293           2 :         io.in.create_flags = 0x12345678;
    1294           2 :         io.in.reserved = 0x12345678;
    1295           2 :         io.in.desired_access = 0x12345678;
    1296           2 :         io.in.file_attributes = 0x12345678;
    1297           2 :         io.in.share_access = 0x12345678;
    1298           2 :         io.in.create_disposition = 0x12345678;
    1299           2 :         io.in.create_options = 0x12345678;
    1300             : 
    1301             :         /*
    1302             :          * only these are checked:
    1303             :          * - io.in.fname
    1304             :          * - io.in.durable_handle,
    1305             :          * - io.in.lease_request->lease_key
    1306             :          */
    1307             : 
    1308           2 :         io.in.fname = fname;
    1309           2 :         io.in.durable_open_v2 = false;
    1310           2 :         io.in.durable_handle_v2 = h;
    1311           2 :         io.in.lease_request_v2 = &ls;
    1312             : 
    1313             :         /* the requested lease state is irrelevant */
    1314           2 :         ls.lease_state = smb2_util_lease_state("");
    1315             : 
    1316           2 :         h = NULL;
    1317             : 
    1318           2 :         status = smb2_create(tree, mem_ctx, &io);
    1319           2 :         CHECK_STATUS(status, NT_STATUS_OK);
    1320             : 
    1321           2 :         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
    1322           2 :         CHECK_VAL(io.out.durable_open, false);
    1323           2 :         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
    1324           2 :         CHECK_VAL(io.out.persistent_open, false);
    1325           2 :         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
    1326           2 :         CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
    1327           2 :         CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
    1328           2 :         CHECK_VAL(io.out.lease_response_v2.lease_state,
    1329             :                   smb2_util_lease_state("RWH"));
    1330           2 :         CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
    1331           2 :         CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
    1332             : 
    1333           2 :         _h = io.out.file.handle;
    1334           2 :         h = &_h;
    1335             : 
    1336           2 : done:
    1337           2 :         if (tree != NULL) {
    1338           2 :                 if (h != NULL) {
    1339           2 :                         smb2_util_close(tree, *h);
    1340             :                 }
    1341             : 
    1342           2 :                 smb2_util_unlink(tree, fname);
    1343             : 
    1344           2 :                 talloc_free(tree);
    1345             :         }
    1346             : 
    1347           2 :         talloc_free(mem_ctx);
    1348             : 
    1349           2 :         return ret;
    1350             : }
    1351             : 
    1352             : /**
    1353             :  * basic test for doing a durable open
    1354             :  * tcp disconnect, reconnect with a session reconnect and
    1355             :  * do a durable reopen (succeeds)
    1356             :  */
    1357           4 : static bool test_durable_open_reopen2a(struct torture_context *tctx,
    1358             :                                        struct smb2_tree *tree)
    1359             : {
    1360           0 :         NTSTATUS status;
    1361           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
    1362           0 :         char fname[256];
    1363           0 :         struct smb2_handle _h;
    1364           4 :         struct smb2_handle *h = NULL;
    1365           0 :         struct smb2_create io1, io2;
    1366           0 :         uint64_t previous_session_id;
    1367           4 :         bool ret = true;
    1368           0 :         struct smbcli_options options;
    1369             : 
    1370           4 :         options = tree->session->transport->options;
    1371             : 
    1372             :         /* Choose a random name in case the state is left a little funky. */
    1373           4 :         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
    1374             :                  generate_random_str(tctx, 8));
    1375             : 
    1376           4 :         smb2_util_unlink(tree, fname);
    1377             : 
    1378           4 :         smb2_oplock_create_share(&io1, fname,
    1379             :                                  smb2_util_share_access(""),
    1380           4 :                                  smb2_util_oplock_level("b"));
    1381           4 :         io1.in.durable_open = true;
    1382             : 
    1383           4 :         status = smb2_create(tree, mem_ctx, &io1);
    1384           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1385           4 :         _h = io1.out.file.handle;
    1386           4 :         h = &_h;
    1387           4 :         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
    1388           4 :         CHECK_VAL(io1.out.durable_open, true);
    1389           4 :         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
    1390             : 
    1391             :         /* disconnect, reconnect and then do durable reopen */
    1392           4 :         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
    1393           4 :         talloc_free(tree);
    1394           4 :         tree = NULL;
    1395             : 
    1396           4 :         if (!torture_smb2_connection_ext(tctx, previous_session_id,
    1397             :                                          &options, &tree))
    1398             :         {
    1399           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
    1400           0 :                 ret = false;
    1401           0 :                 goto done;
    1402             :         }
    1403             : 
    1404           4 :         ZERO_STRUCT(io2);
    1405           4 :         io2.in.fname = fname;
    1406           4 :         io2.in.durable_handle = h;
    1407           4 :         h = NULL;
    1408             : 
    1409           4 :         status = smb2_create(tree, mem_ctx, &io2);
    1410           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1411           4 :         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
    1412           4 :         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
    1413           4 :         _h = io2.out.file.handle;
    1414           4 :         h = &_h;
    1415             : 
    1416           4 : done:
    1417           4 :         if (tree != NULL) {
    1418           4 :                 if (h != NULL) {
    1419           4 :                         smb2_util_close(tree, *h);
    1420             :                 }
    1421             : 
    1422           4 :                 smb2_util_unlink(tree, fname);
    1423             : 
    1424           4 :                 talloc_free(tree);
    1425             :         }
    1426             : 
    1427           4 :         talloc_free(mem_ctx);
    1428             : 
    1429           4 :         return ret;
    1430             : }
    1431             : 
    1432             : 
    1433             : /**
    1434             :  * basic test for doing a durable open:
    1435             :  * tdis, new tcon, try durable reopen (fails)
    1436             :  */
    1437           4 : static bool test_durable_open_reopen3(struct torture_context *tctx,
    1438             :                                       struct smb2_tree *tree)
    1439             : {
    1440           0 :         NTSTATUS status;
    1441           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
    1442           0 :         char fname[256];
    1443           0 :         struct smb2_handle _h;
    1444           4 :         struct smb2_handle *h = NULL;
    1445           0 :         struct smb2_create io1, io2;
    1446           4 :         bool ret = true;
    1447           0 :         struct smb2_tree *tree2;
    1448             : 
    1449             :         /* Choose a random name in case the state is left a little funky. */
    1450           4 :         snprintf(fname, 256, "durable_open_reopen3_%s.dat",
    1451             :                  generate_random_str(tctx, 8));
    1452             : 
    1453           4 :         smb2_util_unlink(tree, fname);
    1454             : 
    1455           4 :         smb2_oplock_create_share(&io1, fname,
    1456             :                                  smb2_util_share_access(""),
    1457           4 :                                  smb2_util_oplock_level("b"));
    1458           4 :         io1.in.durable_open = true;
    1459             : 
    1460           4 :         status = smb2_create(tree, mem_ctx, &io1);
    1461           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1462           4 :         _h = io1.out.file.handle;
    1463           4 :         h = &_h;
    1464           4 :         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
    1465           4 :         CHECK_VAL(io1.out.durable_open, true);
    1466           4 :         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
    1467             : 
    1468             :         /* disconnect, reconnect and then do durable reopen */
    1469           4 :         status = smb2_tdis(tree);
    1470           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1471             : 
    1472           4 :         if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
    1473           0 :                 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
    1474           0 :                 ret = false;
    1475           0 :                 goto done;
    1476             :         }
    1477             : 
    1478             : 
    1479           4 :         ZERO_STRUCT(io2);
    1480           4 :         io2.in.fname = fname;
    1481           4 :         io2.in.durable_handle = h;
    1482             : 
    1483           4 :         status = smb2_create(tree2, mem_ctx, &io2);
    1484           4 :         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
    1485             : 
    1486           4 : done:
    1487           4 :         if (tree != NULL) {
    1488           4 :                 if (h != NULL) {
    1489           4 :                         smb2_util_close(tree, *h);
    1490             :                 }
    1491             : 
    1492           4 :                 smb2_util_unlink(tree2, fname);
    1493             : 
    1494           4 :                 talloc_free(tree);
    1495             :         }
    1496             : 
    1497           4 :         talloc_free(mem_ctx);
    1498             : 
    1499           4 :         return ret;
    1500             : }
    1501             : 
    1502             : /**
    1503             :  * basic test for doing a durable open:
    1504             :  * logoff, create a new session, do a durable reopen (succeeds)
    1505             :  */
    1506           4 : static bool test_durable_open_reopen4(struct torture_context *tctx,
    1507             :                                       struct smb2_tree *tree)
    1508             : {
    1509           0 :         NTSTATUS status;
    1510           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
    1511           0 :         char fname[256];
    1512           0 :         struct smb2_handle _h;
    1513           4 :         struct smb2_handle *h = NULL;
    1514           0 :         struct smb2_create io1, io2;
    1515           4 :         bool ret = true;
    1516           0 :         struct smb2_transport *transport;
    1517           0 :         struct smb2_session *session2;
    1518           0 :         struct smb2_tree *tree2;
    1519             : 
    1520             :         /* Choose a random name in case the state is left a little funky. */
    1521           4 :         snprintf(fname, 256, "durable_open_reopen4_%s.dat",
    1522             :                  generate_random_str(tctx, 8));
    1523             : 
    1524           4 :         smb2_util_unlink(tree, fname);
    1525             : 
    1526           4 :         smb2_oplock_create_share(&io1, fname,
    1527             :                                  smb2_util_share_access(""),
    1528           4 :                                  smb2_util_oplock_level("b"));
    1529           4 :         io1.in.durable_open = true;
    1530             : 
    1531           4 :         status = smb2_create(tree, mem_ctx, &io1);
    1532           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1533           4 :         _h = io1.out.file.handle;
    1534           4 :         h = &_h;
    1535           4 :         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
    1536           4 :         CHECK_VAL(io1.out.durable_open, true);
    1537           4 :         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
    1538             : 
    1539             :         /*
    1540             :          * do a session logoff, establish a new session and tree
    1541             :          * connect on the same transport, and try a durable reopen
    1542             :          */
    1543           4 :         transport = tree->session->transport;
    1544           4 :         status = smb2_logoff(tree->session);
    1545           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1546             : 
    1547           4 :         if (!torture_smb2_session_setup(tctx, transport,
    1548             :                                         0, /* previous_session_id */
    1549             :                                         mem_ctx, &session2))
    1550             :         {
    1551           0 :                 torture_warning(tctx, "session setup failed.\n");
    1552           0 :                 ret = false;
    1553           0 :                 goto done;
    1554             :         }
    1555             : 
    1556             :         /*
    1557             :          * the session setup has talloc-stolen the transport,
    1558             :          * so we can safely free the old tree+session for clarity
    1559             :          */
    1560           4 :         TALLOC_FREE(tree);
    1561             : 
    1562           4 :         if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
    1563           0 :                 torture_warning(tctx, "tree connect failed.\n");
    1564           0 :                 ret = false;
    1565           0 :                 goto done;
    1566             :         }
    1567             : 
    1568           4 :         ZERO_STRUCT(io2);
    1569           4 :         io2.in.fname = fname;
    1570           4 :         io2.in.durable_handle = h;
    1571           4 :         h = NULL;
    1572             : 
    1573           4 :         status = smb2_create(tree2, mem_ctx, &io2);
    1574           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1575             : 
    1576           4 :         _h = io2.out.file.handle;
    1577           4 :         h = &_h;
    1578           4 :         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
    1579           4 :         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
    1580             : 
    1581           4 : done:
    1582           4 :         if (tree != NULL) {
    1583           0 :                 if (h != NULL) {
    1584           0 :                         smb2_util_close(tree2, *h);
    1585             :                 }
    1586             : 
    1587           0 :                 smb2_util_unlink(tree2, fname);
    1588             : 
    1589           0 :                 talloc_free(tree);
    1590             :         }
    1591             : 
    1592           4 :         talloc_free(mem_ctx);
    1593             : 
    1594           4 :         return ret;
    1595             : }
    1596             : 
    1597           4 : static bool test_durable_open_delete_on_close1(struct torture_context *tctx,
    1598             :                                                struct smb2_tree *tree)
    1599             : {
    1600           0 :         NTSTATUS status;
    1601           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
    1602           0 :         char fname[256];
    1603           0 :         struct smb2_handle _h;
    1604           4 :         struct smb2_handle *h = NULL;
    1605           0 :         struct smb2_create io1, io2;
    1606           4 :         bool ret = true;
    1607           4 :         uint8_t b = 0;
    1608             : 
    1609             :         /* Choose a random name in case the state is left a little funky. */
    1610           4 :         snprintf(fname, 256, "durable_open_delete_on_close1_%s.dat",
    1611             :                  generate_random_str(tctx, 8));
    1612             : 
    1613           4 :         smb2_util_unlink(tree, fname);
    1614             : 
    1615           4 :         smb2_oplock_create_share(&io1, fname,
    1616             :                                  smb2_util_share_access(""),
    1617           4 :                                  smb2_util_oplock_level("b"));
    1618           4 :         io1.in.durable_open = true;
    1619           4 :         io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
    1620             : 
    1621           4 :         status = smb2_create(tree, mem_ctx, &io1);
    1622           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1623           4 :         _h = io1.out.file.handle;
    1624           4 :         h = &_h;
    1625           4 :         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
    1626           4 :         CHECK_VAL(io1.out.durable_open, true);
    1627           4 :         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
    1628             : 
    1629           4 :         status = smb2_util_write(tree, *h, &b, 0, 1);
    1630           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1631             : 
    1632             :         /* disconnect, leaving the durable handle in place */
    1633           4 :         TALLOC_FREE(tree);
    1634             : 
    1635           4 :         if (!torture_smb2_connection(tctx, &tree)) {
    1636           0 :                 torture_warning(tctx, "could not reconnect, bailing\n");
    1637           0 :                 ret = false;
    1638           0 :                 goto done;
    1639             :         }
    1640             : 
    1641             :         /*
    1642             :          * Open the file on the new connection again
    1643             :          * and check that it has been newly created,
    1644             :          * i.e. delete on close was effective on the disconnected handle.
    1645             :          * Also check that the file is really empty,
    1646             :          * the previously written byte gone.
    1647             :          */
    1648           4 :         smb2_oplock_create_share(&io2, fname,
    1649             :                                  smb2_util_share_access(""),
    1650           4 :                                  smb2_util_oplock_level("b"));
    1651           4 :         io2.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
    1652             : 
    1653           4 :         status = smb2_create(tree, mem_ctx, &io2);
    1654           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1655           4 :         _h = io2.out.file.handle;
    1656           4 :         h = &_h;
    1657           4 :         CHECK_CREATED_SIZE(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
    1658           4 :         CHECK_VAL(io2.out.durable_open, false);
    1659           4 :         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
    1660             : 
    1661           4 : done:
    1662           4 :         if (tree != NULL) {
    1663           4 :                 if (h != NULL) {
    1664           4 :                         smb2_util_close(tree, *h);
    1665             :                 }
    1666             : 
    1667           4 :                 smb2_util_unlink(tree, fname);
    1668             : 
    1669           4 :                 talloc_free(tree);
    1670             :         }
    1671             : 
    1672           4 :         talloc_free(mem_ctx);
    1673             : 
    1674           4 :         return ret;
    1675             : }
    1676             : 
    1677             : 
    1678           4 : static bool test_durable_open_delete_on_close2(struct torture_context *tctx,
    1679             :                                                struct smb2_tree *tree)
    1680             : {
    1681           0 :         NTSTATUS status;
    1682           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
    1683           0 :         char fname[256];
    1684           0 :         struct smb2_handle _h;
    1685           4 :         struct smb2_handle *h = NULL;
    1686           0 :         struct smb2_create io;
    1687           4 :         bool ret = true;
    1688           4 :         uint8_t b = 0;
    1689           0 :         uint64_t previous_session_id;
    1690           0 :         uint64_t alloc_size_step;
    1691           0 :         struct smbcli_options options;
    1692             : 
    1693           4 :         options = tree->session->transport->options;
    1694             : 
    1695             :         /* Choose a random name in case the state is left a little funky. */
    1696           4 :         snprintf(fname, 256, "durable_open_delete_on_close2_%s.dat",
    1697             :                  generate_random_str(tctx, 8));
    1698             : 
    1699           4 :         smb2_util_unlink(tree, fname);
    1700             : 
    1701           4 :         smb2_oplock_create_share(&io, fname,
    1702             :                                  smb2_util_share_access(""),
    1703           4 :                                  smb2_util_oplock_level("b"));
    1704           4 :         io.in.durable_open = true;
    1705           4 :         io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
    1706             : 
    1707           4 :         status = smb2_create(tree, mem_ctx, &io);
    1708           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1709           4 :         _h = io.out.file.handle;
    1710           4 :         h = &_h;
    1711           4 :         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
    1712           4 :         CHECK_VAL(io.out.durable_open, true);
    1713           4 :         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
    1714             : 
    1715           4 :         status = smb2_util_write(tree, *h, &b, 0, 1);
    1716           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1717             : 
    1718           4 :         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
    1719             : 
    1720             :         /* disconnect, leaving the durable handle in place */
    1721           4 :         TALLOC_FREE(tree);
    1722             : 
    1723           4 :         if (!torture_smb2_connection_ext(tctx, previous_session_id,
    1724             :                                          &options, &tree))
    1725             :         {
    1726           0 :                 torture_warning(tctx, "could not reconnect, bailing\n");
    1727           0 :                 ret = false;
    1728           0 :                 goto done;
    1729             :         }
    1730             : 
    1731           4 :         ZERO_STRUCT(io);
    1732           4 :         io.in.fname = fname;
    1733           4 :         io.in.durable_handle = h;
    1734             : 
    1735           4 :         status = smb2_create(tree, mem_ctx, &io);
    1736           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1737           0 :         _h = io.out.file.handle;
    1738           0 :         h = &_h;
    1739           0 :         alloc_size_step = io.out.alloc_size;
    1740           0 :         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE, alloc_size_step, 1);
    1741           0 :         CHECK_VAL(io.out.durable_open, false);
    1742           0 :         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
    1743             : 
    1744             :         /* close the file, thereby deleting it */
    1745           0 :         smb2_util_close(tree, *h);
    1746           0 :         status = smb2_logoff(tree->session);
    1747           0 :         TALLOC_FREE(tree);
    1748             : 
    1749           0 :         if (!torture_smb2_connection(tctx, &tree)) {
    1750           0 :                 torture_warning(tctx, "could not reconnect, bailing\n");
    1751           0 :                 ret = false;
    1752           0 :                 goto done;
    1753             :         }
    1754             : 
    1755             :         /*
    1756             :          * Open the file on the new connection again
    1757             :          * and check that it has been newly created,
    1758             :          * i.e. delete on close was effective on the reconnected handle.
    1759             :          * Also check that the file is really empty,
    1760             :          * the previously written byte gone.
    1761             :          */
    1762           0 :         smb2_oplock_create_share(&io, fname,
    1763             :                                  smb2_util_share_access(""),
    1764           0 :                                  smb2_util_oplock_level("b"));
    1765           0 :         io.in.durable_open = true;
    1766           0 :         io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
    1767             : 
    1768           0 :         status = smb2_create(tree, mem_ctx, &io);
    1769           0 :         CHECK_STATUS(status, NT_STATUS_OK);
    1770           0 :         _h = io.out.file.handle;
    1771           0 :         h = &_h;
    1772           0 :         CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
    1773           0 :         CHECK_VAL(io.out.durable_open, true);
    1774           0 :         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
    1775             : 
    1776           0 : done:
    1777           4 :         if (tree != NULL) {
    1778           4 :                 if (h != NULL) {
    1779           4 :                         smb2_util_close(tree, *h);
    1780             :                 }
    1781             : 
    1782           4 :                 smb2_util_unlink(tree, fname);
    1783             : 
    1784           4 :                 talloc_free(tree);
    1785             :         }
    1786             : 
    1787           4 :         talloc_free(mem_ctx);
    1788             : 
    1789           4 :         return ret;
    1790             : }
    1791             : 
    1792             : /*
    1793             :    basic testing of SMB2 durable opens
    1794             :    regarding the position information on the handle
    1795             : */
    1796           4 : static bool test_durable_open_file_position(struct torture_context *tctx,
    1797             :                                             struct smb2_tree *tree)
    1798             : {
    1799           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
    1800           0 :         struct smb2_handle h;
    1801           0 :         struct smb2_create io;
    1802           0 :         NTSTATUS status;
    1803           4 :         const char *fname = "durable_open_position.dat";
    1804           0 :         union smb_fileinfo qfinfo;
    1805           0 :         union smb_setfileinfo sfinfo;
    1806           4 :         bool ret = true;
    1807           0 :         uint64_t pos;
    1808           0 :         uint64_t previous_session_id;
    1809           0 :         struct smbcli_options options;
    1810             : 
    1811           4 :         options = tree->session->transport->options;
    1812             : 
    1813           4 :         smb2_util_unlink(tree, fname);
    1814             : 
    1815           4 :         smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
    1816           4 :         io.in.durable_open = true;
    1817             : 
    1818           4 :         status = smb2_create(tree, mem_ctx, &io);
    1819           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1820           4 :         h = io.out.file.handle;
    1821           4 :         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
    1822           4 :         CHECK_VAL(io.out.durable_open, true);
    1823           4 :         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
    1824             : 
    1825             :         /* TODO: check extra blob content */
    1826             : 
    1827           4 :         ZERO_STRUCT(qfinfo);
    1828           4 :         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
    1829           4 :         qfinfo.generic.in.file.handle = h;
    1830           4 :         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
    1831           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1832           4 :         CHECK_VAL(qfinfo.position_information.out.position, 0);
    1833           4 :         pos = qfinfo.position_information.out.position;
    1834           4 :         torture_comment(tctx, "position: %llu\n",
    1835             :                         (unsigned long long)pos);
    1836             : 
    1837           4 :         ZERO_STRUCT(sfinfo);
    1838           4 :         sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
    1839           4 :         sfinfo.generic.in.file.handle = h;
    1840           4 :         sfinfo.position_information.in.position = 0x1000;
    1841           4 :         status = smb2_setinfo_file(tree, &sfinfo);
    1842           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1843             : 
    1844           4 :         ZERO_STRUCT(qfinfo);
    1845           4 :         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
    1846           4 :         qfinfo.generic.in.file.handle = h;
    1847           4 :         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
    1848           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1849           4 :         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
    1850           4 :         pos = qfinfo.position_information.out.position;
    1851           4 :         torture_comment(tctx, "position: %llu\n",
    1852             :                         (unsigned long long)pos);
    1853             : 
    1854           4 :         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
    1855             : 
    1856             :         /* tcp disconnect */
    1857           4 :         talloc_free(tree);
    1858           4 :         tree = NULL;
    1859             : 
    1860             :         /* do a session reconnect */
    1861           4 :         if (!torture_smb2_connection_ext(tctx, previous_session_id,
    1862             :                                          &options, &tree))
    1863             :         {
    1864           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
    1865           0 :                 ret = false;
    1866           0 :                 goto done;
    1867             :         }
    1868             : 
    1869           4 :         ZERO_STRUCT(qfinfo);
    1870           4 :         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
    1871           4 :         qfinfo.generic.in.file.handle = h;
    1872           4 :         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
    1873           4 :         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
    1874             : 
    1875           4 :         ZERO_STRUCT(io);
    1876           4 :         io.in.fname = fname;
    1877           4 :         io.in.durable_handle = &h;
    1878             : 
    1879           4 :         status = smb2_create(tree, mem_ctx, &io);
    1880           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1881           4 :         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
    1882           4 :         CHECK_VAL(io.out.reserved, 0x00);
    1883           4 :         CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
    1884           4 :         CHECK_VAL(io.out.size, 0);
    1885           4 :         CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
    1886           4 :         CHECK_VAL(io.out.reserved2, 0);
    1887             : 
    1888           4 :         h = io.out.file.handle;
    1889             : 
    1890           4 :         ZERO_STRUCT(qfinfo);
    1891           4 :         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
    1892           4 :         qfinfo.generic.in.file.handle = h;
    1893           4 :         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
    1894           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1895           4 :         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
    1896           4 :         pos = qfinfo.position_information.out.position;
    1897           4 :         torture_comment(tctx, "position: %llu\n",
    1898             :                         (unsigned long long)pos);
    1899             : 
    1900           4 :         smb2_util_close(tree, h);
    1901             : 
    1902           4 :         talloc_free(mem_ctx);
    1903             : 
    1904           4 :         smb2_util_unlink(tree, fname);
    1905             : 
    1906           4 : done:
    1907           4 :         talloc_free(tree);
    1908             : 
    1909           4 :         return ret;
    1910             : }
    1911             : 
    1912             : /*
    1913             :   Open, disconnect, oplock break, reconnect.
    1914             : */
    1915           4 : static bool test_durable_open_oplock(struct torture_context *tctx,
    1916             :                                      struct smb2_tree *tree1,
    1917             :                                      struct smb2_tree *tree2)
    1918             : {
    1919           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
    1920           0 :         struct smb2_create io1, io2;
    1921           4 :         struct smb2_handle h1 = {{0}};
    1922           4 :         struct smb2_handle h2 = {{0}};
    1923           0 :         NTSTATUS status;
    1924           0 :         char fname[256];
    1925           4 :         bool ret = true;
    1926             : 
    1927             :         /* Choose a random name in case the state is left a little funky. */
    1928           4 :         snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
    1929             : 
    1930             :         /* Clean slate */
    1931           4 :         smb2_util_unlink(tree1, fname);
    1932             : 
    1933             :         /* Create with batch oplock */
    1934           4 :         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
    1935           4 :         io1.in.durable_open = true;
    1936             : 
    1937           4 :         io2 = io1;
    1938           4 :         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
    1939             : 
    1940           4 :         status = smb2_create(tree1, mem_ctx, &io1);
    1941           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1942           4 :         h1 = io1.out.file.handle;
    1943           4 :         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
    1944           4 :         CHECK_VAL(io1.out.durable_open, true);
    1945           4 :         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
    1946             : 
    1947             :         /* Disconnect after getting the batch */
    1948           4 :         talloc_free(tree1);
    1949           4 :         tree1 = NULL;
    1950             : 
    1951             :         /*
    1952             :          * Windows7 (build 7000) will break a batch oplock immediately if the
    1953             :          * original client is gone. (ZML: This seems like a bug. It should give
    1954             :          * some time for the client to reconnect!)
    1955             :          */
    1956           4 :         status = smb2_create(tree2, mem_ctx, &io2);
    1957           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    1958           4 :         h2 = io2.out.file.handle;
    1959           4 :         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
    1960           4 :         CHECK_VAL(io2.out.durable_open, true);
    1961           4 :         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
    1962             : 
    1963             :         /* What if tree1 tries to come back and reclaim? */
    1964           4 :         if (!torture_smb2_connection(tctx, &tree1)) {
    1965           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
    1966           0 :                 ret = false;
    1967           0 :                 goto done;
    1968             :         }
    1969             : 
    1970           4 :         ZERO_STRUCT(io1);
    1971           4 :         io1.in.fname = fname;
    1972           4 :         io1.in.durable_handle = &h1;
    1973             : 
    1974           4 :         status = smb2_create(tree1, mem_ctx, &io1);
    1975           4 :         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
    1976             : 
    1977           4 :  done:
    1978           4 :         smb2_util_close(tree2, h2);
    1979           4 :         smb2_util_unlink(tree2, fname);
    1980             : 
    1981           4 :         talloc_free(tree1);
    1982           4 :         talloc_free(tree2);
    1983             : 
    1984           4 :         return ret;
    1985             : }
    1986             : 
    1987             : /*
    1988             :   Open, disconnect, lease break, reconnect.
    1989             : */
    1990           4 : static bool test_durable_open_lease(struct torture_context *tctx,
    1991             :                                     struct smb2_tree *tree1,
    1992             :                                     struct smb2_tree *tree2)
    1993             : {
    1994           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
    1995           0 :         struct smb2_create io1, io2;
    1996           0 :         struct smb2_lease ls1, ls2;
    1997           4 :         struct smb2_handle h1 = {{0}};
    1998           4 :         struct smb2_handle h2 = {{0}};
    1999           0 :         NTSTATUS status;
    2000           0 :         char fname[256];
    2001           4 :         bool ret = true;
    2002           0 :         uint64_t lease1, lease2;
    2003           0 :         uint32_t caps;
    2004             : 
    2005           4 :         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
    2006           4 :         if (!(caps & SMB2_CAP_LEASING)) {
    2007           2 :                 torture_skip(tctx, "leases are not supported");
    2008             :         }
    2009             : 
    2010             :         /*
    2011             :          * Choose a random name and random lease in case the state is left a
    2012             :          * little funky.
    2013             :          */
    2014           2 :         lease1 = random();
    2015           2 :         lease2 = random();
    2016           2 :         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
    2017             : 
    2018             :         /* Clean slate */
    2019           2 :         smb2_util_unlink(tree1, fname);
    2020             : 
    2021             :         /* Create with lease */
    2022           2 :         smb2_lease_create(&io1, &ls1, false /* dir */, fname,
    2023             :                           lease1, smb2_util_lease_state("RHW"));
    2024           2 :         io1.in.durable_open = true;
    2025             : 
    2026           2 :         smb2_lease_create(&io2, &ls2, false /* dir */, fname,
    2027             :                           lease2, smb2_util_lease_state("RHW"));
    2028           2 :         io2.in.durable_open = true;
    2029           2 :         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
    2030             : 
    2031           2 :         status = smb2_create(tree1, mem_ctx, &io1);
    2032           2 :         CHECK_STATUS(status, NT_STATUS_OK);
    2033           2 :         h1 = io1.out.file.handle;
    2034           2 :         CHECK_VAL(io1.out.durable_open, true);
    2035           2 :         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
    2036             : 
    2037           2 :         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
    2038           2 :         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
    2039           2 :         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
    2040           2 :         CHECK_VAL(io1.out.lease_response.lease_state,
    2041             :             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
    2042             : 
    2043             :         /* Disconnect after getting the lease */
    2044           2 :         talloc_free(tree1);
    2045           2 :         tree1 = NULL;
    2046             : 
    2047             :         /*
    2048             :          * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
    2049             :          * even if the original client is gone. (ZML: This seems like a bug. It
    2050             :          * should give some time for the client to reconnect! And why RH?)
    2051             :          * 
    2052             :          * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
    2053             :          * Test is adapted accordingly.
    2054             :          */
    2055           2 :         status = smb2_create(tree2, mem_ctx, &io2);
    2056           2 :         CHECK_STATUS(status, NT_STATUS_OK);
    2057           2 :         h2 = io2.out.file.handle;
    2058           2 :         CHECK_VAL(io2.out.durable_open, true);
    2059           2 :         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
    2060             : 
    2061           2 :         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
    2062           2 :         CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
    2063           2 :         CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
    2064           2 :         CHECK_VAL(io2.out.lease_response.lease_state,
    2065             :             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
    2066             : 
    2067             :         /* What if tree1 tries to come back and reclaim? */
    2068           2 :         if (!torture_smb2_connection(tctx, &tree1)) {
    2069           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
    2070           0 :                 ret = false;
    2071           0 :                 goto done;
    2072             :         }
    2073             : 
    2074           2 :         ZERO_STRUCT(io1);
    2075           2 :         io1.in.fname = fname;
    2076           2 :         io1.in.durable_handle = &h1;
    2077           2 :         io1.in.lease_request = &ls1;
    2078             : 
    2079           2 :         status = smb2_create(tree1, mem_ctx, &io1);
    2080           2 :         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
    2081             : 
    2082           2 :  done:
    2083           2 :         smb2_util_close(tree2, h2);
    2084           2 :         smb2_util_unlink(tree2, fname);
    2085             : 
    2086           2 :         talloc_free(tree1);
    2087           2 :         talloc_free(tree2);
    2088             : 
    2089           2 :         return ret;
    2090             : }
    2091             : 
    2092           4 : static bool test_durable_open_lock_oplock(struct torture_context *tctx,
    2093             :                                           struct smb2_tree *tree)
    2094             : {
    2095           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
    2096           0 :         struct smb2_create io;
    2097           4 :         struct smb2_handle h = {{0}};
    2098           0 :         struct smb2_lock lck;
    2099           0 :         struct smb2_lock_element el[2];
    2100           0 :         NTSTATUS status;
    2101           0 :         char fname[256];
    2102           4 :         bool ret = true;
    2103             : 
    2104             :         /*
    2105             :          */
    2106           4 :         snprintf(fname, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx, 8));
    2107             : 
    2108             :         /* Clean slate */
    2109           4 :         smb2_util_unlink(tree, fname);
    2110             : 
    2111             :         /* Create with oplock */
    2112             : 
    2113           4 :         smb2_oplock_create_share(&io, fname,
    2114             :                                  smb2_util_share_access(""),
    2115           4 :                                  smb2_util_oplock_level("b"));
    2116           4 :         io.in.durable_open = true;
    2117             : 
    2118           4 :         status = smb2_create(tree, mem_ctx, &io);
    2119           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    2120           4 :         h = io.out.file.handle;
    2121           4 :         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
    2122             : 
    2123           4 :         CHECK_VAL(io.out.durable_open, true);
    2124           4 :         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
    2125             : 
    2126           4 :         ZERO_STRUCT(lck);
    2127           4 :         ZERO_STRUCT(el);
    2128           4 :         lck.in.locks            = el;
    2129           4 :         lck.in.lock_count       = 0x0001;
    2130           4 :         lck.in.lock_sequence    = 0x00000000;
    2131           4 :         lck.in.file.handle      = h;
    2132           4 :         el[0].offset            = 0;
    2133           4 :         el[0].length            = 1;
    2134           4 :         el[0].reserved          = 0x00000000;
    2135           4 :         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
    2136           4 :         status = smb2_lock(tree, &lck);
    2137           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    2138             : 
    2139             :         /* Disconnect/Reconnect. */
    2140           4 :         talloc_free(tree);
    2141           4 :         tree = NULL;
    2142             : 
    2143           4 :         if (!torture_smb2_connection(tctx, &tree)) {
    2144           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
    2145           0 :                 ret = false;
    2146           0 :                 goto done;
    2147             :         }
    2148             : 
    2149           4 :         ZERO_STRUCT(io);
    2150           4 :         io.in.fname = fname;
    2151           4 :         io.in.durable_handle = &h;
    2152             : 
    2153           4 :         status = smb2_create(tree, mem_ctx, &io);
    2154           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    2155           4 :         h = io.out.file.handle;
    2156             : 
    2157           4 :         lck.in.file.handle      = h;
    2158           4 :         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
    2159           4 :         status = smb2_lock(tree, &lck);
    2160           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    2161             : 
    2162           4 :  done:
    2163           4 :         smb2_util_close(tree, h);
    2164           4 :         smb2_util_unlink(tree, fname);
    2165           4 :         talloc_free(tree);
    2166             : 
    2167           4 :         return ret;
    2168             : }
    2169             : 
    2170             : /*
    2171             :   Open, take BRL, disconnect, reconnect.
    2172             : */
    2173           4 : static bool test_durable_open_lock_lease(struct torture_context *tctx,
    2174             :                                          struct smb2_tree *tree)
    2175             : {
    2176           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
    2177           0 :         struct smb2_create io;
    2178           0 :         struct smb2_lease ls;
    2179           4 :         struct smb2_handle h = {{0}};
    2180           0 :         struct smb2_lock lck;
    2181           0 :         struct smb2_lock_element el[2];
    2182           0 :         NTSTATUS status;
    2183           0 :         char fname[256];
    2184           4 :         bool ret = true;
    2185           0 :         uint64_t lease;
    2186           0 :         uint32_t caps;
    2187           0 :         struct smbcli_options options;
    2188             : 
    2189           4 :         options = tree->session->transport->options;
    2190             : 
    2191           4 :         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
    2192           4 :         if (!(caps & SMB2_CAP_LEASING)) {
    2193           2 :                 torture_skip(tctx, "leases are not supported");
    2194             :         }
    2195             : 
    2196             :         /*
    2197             :          * Choose a random name and random lease in case the state is left a
    2198             :          * little funky.
    2199             :          */
    2200           2 :         lease = random();
    2201           2 :         snprintf(fname, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx, 8));
    2202             : 
    2203             :         /* Clean slate */
    2204           2 :         smb2_util_unlink(tree, fname);
    2205             : 
    2206             :         /* Create with lease */
    2207             : 
    2208           2 :         smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
    2209             :                           smb2_util_lease_state("RWH"));
    2210           2 :         io.in.durable_open              = true;
    2211             : 
    2212           2 :         status = smb2_create(tree, mem_ctx, &io);
    2213           2 :         CHECK_STATUS(status, NT_STATUS_OK);
    2214           2 :         h = io.out.file.handle;
    2215           2 :         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
    2216             : 
    2217           2 :         CHECK_VAL(io.out.durable_open, true);
    2218           2 :         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
    2219           2 :         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
    2220           2 :         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
    2221           2 :         CHECK_VAL(io.out.lease_response.lease_state,
    2222             :             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
    2223             : 
    2224           2 :         ZERO_STRUCT(lck);
    2225           2 :         ZERO_STRUCT(el);
    2226           2 :         lck.in.locks            = el;
    2227           2 :         lck.in.lock_count       = 0x0001;
    2228           2 :         lck.in.lock_sequence    = 0x00000000;
    2229           2 :         lck.in.file.handle      = h;
    2230           2 :         el[0].offset            = 0;
    2231           2 :         el[0].length            = 1;
    2232           2 :         el[0].reserved          = 0x00000000;
    2233           2 :         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
    2234           2 :         status = smb2_lock(tree, &lck);
    2235           2 :         CHECK_STATUS(status, NT_STATUS_OK);
    2236             : 
    2237             :         /* Disconnect/Reconnect. */
    2238           2 :         talloc_free(tree);
    2239           2 :         tree = NULL;
    2240             : 
    2241           2 :         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
    2242           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
    2243           0 :                 ret = false;
    2244           0 :                 goto done;
    2245             :         }
    2246             : 
    2247           2 :         ZERO_STRUCT(io);
    2248           2 :         io.in.fname = fname;
    2249           2 :         io.in.durable_handle = &h;
    2250           2 :         io.in.lease_request = &ls;
    2251             : 
    2252           2 :         status = smb2_create(tree, mem_ctx, &io);
    2253           2 :         CHECK_STATUS(status, NT_STATUS_OK);
    2254           2 :         h = io.out.file.handle;
    2255             : 
    2256           2 :         lck.in.file.handle      = h;
    2257           2 :         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
    2258           2 :         status = smb2_lock(tree, &lck);
    2259           2 :         CHECK_STATUS(status, NT_STATUS_OK);
    2260             : 
    2261           2 :  done:
    2262           2 :         smb2_util_close(tree, h);
    2263           2 :         smb2_util_unlink(tree, fname);
    2264           2 :         talloc_free(tree);
    2265             : 
    2266           2 :         return ret;
    2267             : }
    2268             : 
    2269             : /**
    2270             :  * Open with a RH lease, disconnect, open in another tree, reconnect.
    2271             :  *
    2272             :  * This test actually demonstrates a minimum level of respect for the durable
    2273             :  * open in the face of another open. As long as this test shows an inability to
    2274             :  * reconnect after an open, the oplock/lease tests above will certainly
    2275             :  * demonstrate an error on reconnect.
    2276             :  */
    2277           4 : static bool test_durable_open_open2_lease(struct torture_context *tctx,
    2278             :                                           struct smb2_tree *tree1,
    2279             :                                           struct smb2_tree *tree2)
    2280             : {
    2281           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
    2282           0 :         struct smb2_create io1, io2;
    2283           0 :         struct smb2_lease ls;
    2284           4 :         struct smb2_handle h1 = {{0}};
    2285           4 :         struct smb2_handle h2 = {{0}};
    2286           0 :         NTSTATUS status;
    2287           0 :         char fname[256];
    2288           4 :         bool ret = true;
    2289           0 :         uint64_t lease;
    2290           0 :         uint32_t caps;
    2291           0 :         struct smbcli_options options;
    2292             : 
    2293           4 :         options = tree1->session->transport->options;
    2294             : 
    2295           4 :         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
    2296           4 :         if (!(caps & SMB2_CAP_LEASING)) {
    2297           2 :                 torture_skip(tctx, "leases are not supported");
    2298             :         }
    2299             : 
    2300             :         /*
    2301             :          * Choose a random name and random lease in case the state is left a
    2302             :          * little funky.
    2303             :          */
    2304           2 :         lease = random();
    2305           2 :         snprintf(fname, 256, "durable_open_open2_lease_%s.dat",
    2306             :                  generate_random_str(tctx, 8));
    2307             : 
    2308             :         /* Clean slate */
    2309           2 :         smb2_util_unlink(tree1, fname);
    2310             : 
    2311             :         /* Create with lease */
    2312           2 :         smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
    2313             :                                 smb2_util_share_access(""),
    2314             :                                 lease,
    2315             :                                 smb2_util_lease_state("RH"));
    2316           2 :         io1.in.durable_open = true;
    2317             : 
    2318           2 :         status = smb2_create(tree1, mem_ctx, &io1);
    2319           2 :         CHECK_STATUS(status, NT_STATUS_OK);
    2320           2 :         h1 = io1.out.file.handle;
    2321           2 :         CHECK_VAL(io1.out.durable_open, true);
    2322           2 :         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
    2323             : 
    2324           2 :         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
    2325           2 :         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
    2326           2 :         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
    2327           2 :         CHECK_VAL(io1.out.lease_response.lease_state,
    2328             :                   smb2_util_lease_state("RH"));
    2329             : 
    2330             :         /* Disconnect */
    2331           2 :         talloc_free(tree1);
    2332           2 :         tree1 = NULL;
    2333             : 
    2334             :         /* Open the file in tree2 */
    2335           2 :         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
    2336             : 
    2337           2 :         status = smb2_create(tree2, mem_ctx, &io2);
    2338           2 :         CHECK_STATUS(status, NT_STATUS_OK);
    2339           2 :         h2 = io2.out.file.handle;
    2340           2 :         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
    2341             : 
    2342             :         /* Reconnect */
    2343           2 :         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree1)) {
    2344           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
    2345           0 :                 ret = false;
    2346           0 :                 goto done;
    2347             :         }
    2348             : 
    2349           2 :         ZERO_STRUCT(io1);
    2350           2 :         io1.in.fname = fname;
    2351           2 :         io1.in.durable_handle = &h1;
    2352           2 :         io1.in.lease_request = &ls;
    2353             : 
    2354             :         /*
    2355             :          * Windows7 (build 7000) will give away an open immediately if the
    2356             :          * original client is gone. (ZML: This seems like a bug. It should give
    2357             :          * some time for the client to reconnect!)
    2358             :          */
    2359           2 :         status = smb2_create(tree1, mem_ctx, &io1);
    2360           2 :         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
    2361           2 :         h1 = io1.out.file.handle;
    2362             : 
    2363           2 :  done:
    2364           2 :         if (tree1 != NULL){
    2365           2 :                 smb2_util_close(tree1, h1);
    2366           2 :                 smb2_util_unlink(tree1, fname);
    2367           2 :                 talloc_free(tree1);
    2368             :         }
    2369             : 
    2370           2 :         smb2_util_close(tree2, h2);
    2371           2 :         smb2_util_unlink(tree2, fname);
    2372           2 :         talloc_free(tree2);
    2373             : 
    2374           2 :         return ret;
    2375             : }
    2376             : 
    2377             : /**
    2378             :  * Open with a batch oplock, disconnect, open in another tree, reconnect.
    2379             :  *
    2380             :  * This test actually demonstrates a minimum level of respect for the durable
    2381             :  * open in the face of another open. As long as this test shows an inability to
    2382             :  * reconnect after an open, the oplock/lease tests above will certainly
    2383             :  * demonstrate an error on reconnect.
    2384             :  */
    2385           4 : static bool test_durable_open_open2_oplock(struct torture_context *tctx,
    2386             :                                            struct smb2_tree *tree1,
    2387             :                                            struct smb2_tree *tree2)
    2388             : {
    2389           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
    2390           0 :         struct smb2_create io1, io2;
    2391           4 :         struct smb2_handle h1 = {{0}};
    2392           4 :         struct smb2_handle h2 = {{0}};
    2393           0 :         NTSTATUS status;
    2394           0 :         char fname[256];
    2395           4 :         bool ret = true;
    2396             : 
    2397             :         /*
    2398             :          * Choose a random name and random lease in case the state is left a
    2399             :          * little funky.
    2400             :          */
    2401           4 :         snprintf(fname, 256, "durable_open_open2_oplock_%s.dat",
    2402             :                  generate_random_str(tctx, 8));
    2403             : 
    2404             :         /* Clean slate */
    2405           4 :         smb2_util_unlink(tree1, fname);
    2406             : 
    2407             :         /* Create with batch oplock */
    2408           4 :         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
    2409           4 :         io1.in.durable_open = true;
    2410             : 
    2411           4 :         status = smb2_create(tree1, mem_ctx, &io1);
    2412           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    2413           4 :         h1 = io1.out.file.handle;
    2414           4 :         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
    2415           4 :         CHECK_VAL(io1.out.durable_open, true);
    2416           4 :         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
    2417             : 
    2418             :         /* Disconnect */
    2419           4 :         talloc_free(tree1);
    2420           4 :         tree1 = NULL;
    2421             : 
    2422             :         /* Open the file in tree2 */
    2423           4 :         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
    2424             : 
    2425           4 :         status = smb2_create(tree2, mem_ctx, &io2);
    2426           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    2427           4 :         h2 = io2.out.file.handle;
    2428           4 :         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
    2429             : 
    2430             :         /* Reconnect */
    2431           4 :         if (!torture_smb2_connection(tctx, &tree1)) {
    2432           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
    2433           0 :                 ret = false;
    2434           0 :                 goto done;
    2435             :         }
    2436             : 
    2437           4 :         ZERO_STRUCT(io1);
    2438           4 :         io1.in.fname = fname;
    2439           4 :         io1.in.durable_handle = &h1;
    2440             : 
    2441           4 :         status = smb2_create(tree1, mem_ctx, &io1);
    2442           4 :         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
    2443           4 :         h1 = io1.out.file.handle;
    2444             : 
    2445           4 :  done:
    2446           4 :         smb2_util_close(tree2, h2);
    2447           4 :         smb2_util_unlink(tree2, fname);
    2448           4 :         if (tree1 != NULL) {
    2449           4 :                 smb2_util_close(tree1, h1);
    2450           4 :                 smb2_util_unlink(tree1, fname);
    2451             :         }
    2452             : 
    2453           4 :         talloc_free(tree1);
    2454           4 :         talloc_free(tree2);
    2455             : 
    2456           4 :         return ret;
    2457             : }
    2458             : 
    2459             : /**
    2460             :  * test behaviour with initial allocation size
    2461             :  */
    2462           4 : static bool test_durable_open_alloc_size(struct torture_context *tctx,
    2463             :                                          struct smb2_tree *tree)
    2464             : {
    2465           0 :         NTSTATUS status;
    2466           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
    2467           0 :         char fname[256];
    2468           0 :         struct smb2_handle _h;
    2469           4 :         struct smb2_handle *h = NULL;
    2470           0 :         struct smb2_create io;
    2471           4 :         bool ret = true;
    2472           0 :         uint64_t previous_session_id;
    2473           0 :         uint64_t alloc_size_step;
    2474           4 :         uint64_t initial_alloc_size = 0x1000;
    2475           4 :         const uint8_t *b = NULL;
    2476           0 :         struct smbcli_options options;
    2477             : 
    2478           4 :         options = tree->session->transport->options;
    2479             : 
    2480             :         /* Choose a random name in case the state is left a little funky. */
    2481           4 :         snprintf(fname, 256, "durable_open_alloc_size_%s.dat",
    2482             :                  generate_random_str(tctx, 8));
    2483             : 
    2484           4 :         smb2_util_unlink(tree, fname);
    2485             : 
    2486           4 :         smb2_oplock_create_share(&io, fname,
    2487             :                                  smb2_util_share_access(""),
    2488           4 :                                  smb2_util_oplock_level("b"));
    2489           4 :         io.in.durable_open = true;
    2490           4 :         io.in.alloc_size = initial_alloc_size;
    2491             : 
    2492           4 :         status = smb2_create(tree, mem_ctx, &io);
    2493           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    2494           4 :         _h = io.out.file.handle;
    2495           4 :         h = &_h;
    2496           4 :         CHECK_NOT_VAL(io.out.alloc_size, 0);
    2497           4 :         alloc_size_step = io.out.alloc_size;
    2498           4 :         CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE,
    2499             :                            alloc_size_step, 0);
    2500           4 :         CHECK_VAL(io.out.durable_open, true);
    2501           4 :         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
    2502             : 
    2503             :         /* prepare buffer */
    2504           4 :         b = talloc_zero_size(mem_ctx, alloc_size_step);
    2505           4 :         CHECK_NOT_NULL(b);
    2506             : 
    2507           4 :         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
    2508             : 
    2509             :         /* disconnect, reconnect and then do durable reopen */
    2510           4 :         talloc_free(tree);
    2511           4 :         tree = NULL;
    2512             : 
    2513           4 :         if (!torture_smb2_connection_ext(tctx, previous_session_id,
    2514             :                                          &options, &tree))
    2515             :         {
    2516           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
    2517           0 :                 ret = false;
    2518           0 :                 goto done;
    2519             :         }
    2520             : 
    2521           4 :         ZERO_STRUCT(io);
    2522           4 :         io.in.fname = fname;
    2523           4 :         io.in.durable_handle = h;
    2524           4 :         h = NULL;
    2525             : 
    2526           4 :         status = smb2_create(tree, mem_ctx, &io);
    2527           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    2528           4 :         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
    2529             :                            alloc_size_step, 0);
    2530           4 :         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
    2531           4 :         _h = io.out.file.handle;
    2532           4 :         h = &_h;
    2533             : 
    2534           4 :         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
    2535             : 
    2536             :         /* write one byte */
    2537           4 :         status = smb2_util_write(tree, *h, b, 0, 1);
    2538           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    2539             : 
    2540             :         /* disconnect, reconnect and then do durable reopen */
    2541           4 :         talloc_free(tree);
    2542           4 :         tree = NULL;
    2543             : 
    2544           4 :         if (!torture_smb2_connection_ext(tctx, previous_session_id,
    2545             :                                          &options, &tree))
    2546             :         {
    2547           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
    2548           0 :                 ret = false;
    2549           0 :                 goto done;
    2550             :         }
    2551             : 
    2552           4 :         ZERO_STRUCT(io);
    2553           4 :         io.in.fname = fname;
    2554           4 :         io.in.durable_handle = h;
    2555           4 :         h = NULL;
    2556             : 
    2557           4 :         status = smb2_create(tree, mem_ctx, &io);
    2558           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    2559           4 :         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
    2560             :                            alloc_size_step, 1);
    2561           4 :         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
    2562           4 :         _h = io.out.file.handle;
    2563           4 :         h = &_h;
    2564             : 
    2565           4 :         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
    2566             : 
    2567             :         /* write more byte than initial allocation size */
    2568           4 :         status = smb2_util_write(tree, *h, b, 1, alloc_size_step);
    2569             : 
    2570             :         /* disconnect, reconnect and then do durable reopen */
    2571           4 :         talloc_free(tree);
    2572           4 :         tree = NULL;
    2573             : 
    2574           4 :         if (!torture_smb2_connection_ext(tctx, previous_session_id,
    2575             :                                          &options, &tree))
    2576             :         {
    2577           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
    2578           0 :                 ret = false;
    2579           0 :                 goto done;
    2580             :         }
    2581             : 
    2582           4 :         ZERO_STRUCT(io);
    2583           4 :         io.in.fname = fname;
    2584           4 :         io.in.durable_handle = h;
    2585           4 :         h = NULL;
    2586             : 
    2587           4 :         status = smb2_create(tree, mem_ctx, &io);
    2588           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    2589           4 :         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
    2590             :                            alloc_size_step * 2, alloc_size_step + 1);
    2591           4 :         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
    2592           4 :         _h = io.out.file.handle;
    2593           4 :         h = &_h;
    2594             : 
    2595           4 : done:
    2596           4 :         if (h != NULL) {
    2597           4 :                 smb2_util_close(tree, *h);
    2598             :         }
    2599             : 
    2600           4 :         smb2_util_unlink(tree, fname);
    2601             : 
    2602           4 :         talloc_free(tree);
    2603             : 
    2604           4 :         talloc_free(mem_ctx);
    2605             : 
    2606           4 :         return ret;
    2607             : }
    2608             : 
    2609             : /**
    2610             :  * test behaviour when a disconnect happens while creating a read-only file
    2611             :  */
    2612           4 : static bool test_durable_open_read_only(struct torture_context *tctx,
    2613             :                                         struct smb2_tree *tree)
    2614             : {
    2615           0 :         NTSTATUS status;
    2616           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
    2617           0 :         char fname[256];
    2618           0 :         struct smb2_handle _h;
    2619           4 :         struct smb2_handle *h = NULL;
    2620           0 :         struct smb2_create io;
    2621           4 :         bool ret = true;
    2622           0 :         uint64_t previous_session_id;
    2623           4 :         const uint8_t b = 0;
    2624           4 :         uint64_t alloc_size = 0;
    2625           0 :         struct smbcli_options options;
    2626             : 
    2627           4 :         options = tree->session->transport->options;
    2628             : 
    2629             :         /* Choose a random name in case the state is left a little funky. */
    2630           4 :         snprintf(fname, 256, "durable_open_initial_alloc_%s.dat",
    2631             :                  generate_random_str(tctx, 8));
    2632             : 
    2633           4 :         smb2_util_unlink(tree, fname);
    2634             : 
    2635           4 :         smb2_oplock_create_share(&io, fname,
    2636             :                                  smb2_util_share_access(""),
    2637           4 :                                  smb2_util_oplock_level("b"));
    2638           4 :         io.in.durable_open = true;
    2639           4 :         io.in.file_attributes = FILE_ATTRIBUTE_READONLY;
    2640             : 
    2641           4 :         status = smb2_create(tree, mem_ctx, &io);
    2642           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    2643           4 :         _h = io.out.file.handle;
    2644           4 :         h = &_h;
    2645           4 :         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE);
    2646           4 :         CHECK_VAL(io.out.durable_open, true);
    2647           4 :         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
    2648             : 
    2649           4 :         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
    2650             : 
    2651             :         /* write one byte */
    2652           4 :         status = smb2_util_write(tree, *h, &b, 0, 1);
    2653           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    2654             : 
    2655             :         /* disconnect, reconnect and then do durable reopen */
    2656           4 :         talloc_free(tree);
    2657           4 :         tree = NULL;
    2658             : 
    2659           4 :         if (!torture_smb2_connection_ext(tctx, previous_session_id,
    2660             :                                          &options, &tree))
    2661             :         {
    2662           0 :                 torture_warning(tctx, "couldn't reconnect, bailing\n");
    2663           0 :                 ret = false;
    2664           0 :                 goto done;
    2665             :         }
    2666             : 
    2667           4 :         ZERO_STRUCT(io);
    2668           4 :         io.in.fname = fname;
    2669           4 :         io.in.durable_handle = h;
    2670           4 :         h = NULL;
    2671             : 
    2672           4 :         status = smb2_create(tree, mem_ctx, &io);
    2673           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    2674           4 :         alloc_size = io.out.alloc_size;
    2675           4 :         CHECK_CREATED_SIZE(&io, EXISTED,
    2676             :                            FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE,
    2677             :                            alloc_size, 1);
    2678           4 :         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
    2679           4 :         _h = io.out.file.handle;
    2680           4 :         h = &_h;
    2681             : 
    2682             :         /* write one byte */
    2683           4 :         status = smb2_util_write(tree, *h, &b, 1, 1);
    2684           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    2685             : 
    2686           4 : done:
    2687           4 :         if (h != NULL) {
    2688           0 :                 union smb_setfileinfo sfinfo;
    2689             : 
    2690           4 :                 ZERO_STRUCT(sfinfo);
    2691           4 :                 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
    2692           4 :                 sfinfo.basic_info.in.file.handle = *h;
    2693           4 :                 sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
    2694           4 :                 smb2_setinfo_file(tree, &sfinfo);
    2695             : 
    2696           4 :                 smb2_util_close(tree, *h);
    2697             :         }
    2698             : 
    2699           4 :         smb2_util_unlink(tree, fname);
    2700             : 
    2701           4 :         talloc_free(tree);
    2702             : 
    2703           4 :         talloc_free(mem_ctx);
    2704             : 
    2705           4 :         return ret;
    2706             : }
    2707             : 
    2708             : /**
    2709             :  * durable open with oplock, disconnect, exit
    2710             :  */
    2711           0 : static bool test_durable_open_oplock_disconnect(struct torture_context *tctx,
    2712             :                                                 struct smb2_tree *tree)
    2713             : {
    2714           0 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
    2715           0 :         struct smb2_create io;
    2716           0 :         struct smb2_handle _h;
    2717           0 :         struct smb2_handle *h = NULL;
    2718           0 :         NTSTATUS status;
    2719           0 :         char fname[256];
    2720           0 :         bool ret = true;
    2721             : 
    2722           0 :         snprintf(fname, 256, "durable_open_oplock_disconnect_%s.dat",
    2723             :                  generate_random_str(mem_ctx, 8));
    2724             : 
    2725           0 :         smb2_util_unlink(tree, fname);
    2726             : 
    2727           0 :         smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
    2728           0 :         io.in.durable_open = true;
    2729             : 
    2730           0 :         status = smb2_create(tree, mem_ctx, &io);
    2731           0 :         CHECK_STATUS(status, NT_STATUS_OK);
    2732             : 
    2733           0 :         _h = io.out.file.handle;
    2734           0 :         h = &_h;
    2735             : 
    2736           0 :         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
    2737           0 :         CHECK_VAL(io.out.durable_open, true);
    2738           0 :         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
    2739             : 
    2740             :         /* disconnect */
    2741           0 :         talloc_free(tree);
    2742           0 :         tree = NULL;
    2743             : 
    2744           0 : done:
    2745           0 :         if (tree != NULL) {
    2746           0 :                 if (h != NULL) {
    2747           0 :                         smb2_util_close(tree, *h);
    2748             :                 }
    2749           0 :                 smb2_util_unlink(tree, fname);
    2750             :         }
    2751           0 :         talloc_free(mem_ctx);
    2752           0 :         return ret;
    2753             : }
    2754             : 
    2755             : /**
    2756             :  * durable stat open with lease.
    2757             :  */
    2758           4 : static bool test_durable_open_stat_open(struct torture_context *tctx,
    2759             :                                         struct smb2_tree *tree)
    2760             : {
    2761           4 :         TALLOC_CTX *mem_ctx = talloc_new(tctx);
    2762           0 :         struct smb2_create io;
    2763           0 :         struct smb2_handle _h;
    2764           4 :         struct smb2_handle *h = NULL;
    2765           0 :         struct smb2_lease ls;
    2766           0 :         NTSTATUS status;
    2767           0 :         char fname[256];
    2768           4 :         bool ret = true;
    2769           0 :         uint64_t lease;
    2770             : 
    2771           4 :         snprintf(fname, 256, "durable_open_stat_open_%s.dat",
    2772             :                  generate_random_str(mem_ctx, 8));
    2773             : 
    2774             :         /* Ensure file doesn't exist. */
    2775           4 :         smb2_util_unlink(tree, fname);
    2776             : 
    2777             :         /* Create a normal file. */
    2778           4 :         smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_NONE);
    2779           4 :         status = smb2_create(tree, mem_ctx, &io);
    2780           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    2781           4 :         _h = io.out.file.handle;
    2782           4 :         h = &_h;
    2783           4 :         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
    2784             :         /* Close. */
    2785           4 :         smb2_util_close(tree, *h);
    2786           4 :         h = NULL;
    2787             : 
    2788             :         /* Now try a leased, durable handle stat open. */
    2789           4 :         lease = random();
    2790             :         /* Create with lease */
    2791           4 :         smb2_lease_create(&io,
    2792             :                           &ls,
    2793             :                           false /* dir */,
    2794             :                           fname,
    2795             :                           lease,
    2796             :                           smb2_util_lease_state("RH"));
    2797           4 :         io.in.durable_open = true;
    2798           4 :         io.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
    2799           4 :         io.in.create_disposition = NTCREATEX_DISP_OPEN;
    2800             : 
    2801           4 :         status = smb2_create(tree, mem_ctx, &io);
    2802           4 :         CHECK_STATUS(status, NT_STATUS_OK);
    2803           4 :         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
    2804           4 :         CHECK_VAL(io.out.durable_open, true);
    2805           4 :         _h = io.out.file.handle;
    2806           4 :         h = &_h;
    2807             : 
    2808           4 : done:
    2809           4 :         if (h != NULL) {
    2810           4 :                 smb2_util_close(tree, *h);
    2811             :         }
    2812           4 :         smb2_util_unlink(tree, fname);
    2813           4 :         talloc_free(mem_ctx);
    2814           4 :         return ret;
    2815             : }
    2816             : 
    2817        2354 : struct torture_suite *torture_smb2_durable_open_init(TALLOC_CTX *ctx)
    2818             : {
    2819         125 :         struct torture_suite *suite =
    2820        2354 :             torture_suite_create(ctx, "durable-open");
    2821             : 
    2822        2354 :         torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock);
    2823        2354 :         torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
    2824        2354 :         torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
    2825        2354 :         torture_suite_add_1smb2_test(suite, "reopen1a", test_durable_open_reopen1a);
    2826        2354 :         torture_suite_add_1smb2_test(suite, "reopen1a-lease", test_durable_open_reopen1a_lease);
    2827        2354 :         torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
    2828        2354 :         torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_open_reopen2_lease);
    2829        2354 :         torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_open_reopen2_lease_v2);
    2830        2354 :         torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
    2831        2354 :         torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
    2832        2354 :         torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
    2833        2354 :         torture_suite_add_1smb2_test(suite, "delete_on_close1",
    2834             :                                      test_durable_open_delete_on_close1);
    2835        2354 :         torture_suite_add_1smb2_test(suite, "delete_on_close2",
    2836             :                                      test_durable_open_delete_on_close2);
    2837        2354 :         torture_suite_add_1smb2_test(suite, "file-position",
    2838             :             test_durable_open_file_position);
    2839        2354 :         torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
    2840        2354 :         torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
    2841        2354 :         torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_open_lock_oplock);
    2842        2354 :         torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease);
    2843        2354 :         torture_suite_add_2smb2_test(suite, "open2-lease",
    2844             :                                      test_durable_open_open2_lease);
    2845        2354 :         torture_suite_add_2smb2_test(suite, "open2-oplock",
    2846             :                                      test_durable_open_open2_oplock);
    2847        2354 :         torture_suite_add_1smb2_test(suite, "alloc-size",
    2848             :                                      test_durable_open_alloc_size);
    2849        2354 :         torture_suite_add_1smb2_test(suite, "read-only",
    2850             :                                      test_durable_open_read_only);
    2851        2354 :         torture_suite_add_1smb2_test(suite, "stat-open",
    2852             :                                      test_durable_open_stat_open);
    2853             : 
    2854        2354 :         suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
    2855             : 
    2856        2354 :         return suite;
    2857             : }
    2858             : 
    2859        2354 : struct torture_suite *torture_smb2_durable_open_disconnect_init(TALLOC_CTX *ctx)
    2860             : {
    2861         125 :         struct torture_suite *suite =
    2862        2354 :             torture_suite_create(ctx,
    2863             :                                  "durable-open-disconnect");
    2864             : 
    2865        2354 :         torture_suite_add_1smb2_test(suite, "open-oplock-disconnect",
    2866             :                                      test_durable_open_oplock_disconnect);
    2867             : 
    2868        2354 :         suite->description = talloc_strdup(suite,
    2869             :                                         "SMB2-DURABLE-OPEN-DISCONNECT tests");
    2870             : 
    2871        2354 :         return suite;
    2872             : }

Generated by: LCOV version 1.14