2 Unix SMB/CIFS implementation.
4 test suite for SMB2 version two of durable opens
6 Copyright (C) Michael Adam 2012
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25 #include "../libcli/smb/smbXcli_base.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "librpc/ndr/libndr.h"
30 #define CHECK_VAL(v, correct) do { \
31 if ((v) != (correct)) { \
32 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
33 __location__, #v, (int)v, (int)correct); \
37 #define CHECK_STATUS(status, correct) do { \
38 if (!NT_STATUS_EQUAL(status, correct)) { \
39 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
40 nt_errstr(status), nt_errstr(correct)); \
45 #define CHECK_CREATED(__io, __created, __attribute) \
47 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
48 CHECK_VAL((__io)->out.alloc_size, 0); \
49 CHECK_VAL((__io)->out.size, 0); \
50 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
51 CHECK_VAL((__io)->out.reserved2, 0); \
55 * basic durable_open test.
56 * durable state should only be granted when requested
57 * along with a batch oplock or a handle lease.
59 * This test tests durable open with all possible oplock types.
62 struct durable_open_vs_oplock
{
64 const char *share_mode
;
69 #define NUM_OPLOCK_TYPES 4
70 #define NUM_SHARE_MODES 8
71 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
72 static struct durable_open_vs_oplock durable_open_vs_oplock_table
[NUM_OPLOCK_OPEN_TESTS
] =
74 { "", "", false, false },
75 { "", "R", false, false },
76 { "", "W", false, false },
77 { "", "D", false, false },
78 { "", "RD", false, false },
79 { "", "RW", false, false },
80 { "", "WD", false, false },
81 { "", "RWD", false, false },
83 { "s", "", false, false },
84 { "s", "R", false, false },
85 { "s", "W", false, false },
86 { "s", "D", false, false },
87 { "s", "RD", false, false },
88 { "s", "RW", false, false },
89 { "s", "WD", false, false },
90 { "s", "RWD", false, false },
92 { "x", "", false, false },
93 { "x", "R", false, false },
94 { "x", "W", false, false },
95 { "x", "D", false, false },
96 { "x", "RD", false, false },
97 { "x", "RW", false, false },
98 { "x", "WD", false, false },
99 { "x", "RWD", false, false },
101 { "b", "", true, false },
102 { "b", "R", true, false },
103 { "b", "W", true, false },
104 { "b", "D", true, false },
105 { "b", "RD", true, false },
106 { "b", "RW", true, false },
107 { "b", "WD", true, false },
108 { "b", "RWD", true, false },
111 static bool test_one_durable_v2_open_oplock(struct torture_context
*tctx
,
112 struct smb2_tree
*tree
,
114 bool request_persistent
,
115 struct durable_open_vs_oplock test
)
118 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
119 struct smb2_handle _h
;
120 struct smb2_handle
*h
= NULL
;
122 struct smb2_create io
;
124 smb2_util_unlink(tree
, fname
);
126 smb2_oplock_create_share(&io
, fname
,
127 smb2_util_share_access(test
.share_mode
),
128 smb2_util_oplock_level(test
.level
));
129 io
.in
.durable_open
= false;
130 io
.in
.durable_open_v2
= true;
131 io
.in
.persistent_open
= request_persistent
;
132 io
.in
.create_guid
= GUID_random();
134 status
= smb2_create(tree
, mem_ctx
, &io
);
135 CHECK_STATUS(status
, NT_STATUS_OK
);
136 _h
= io
.out
.file
.handle
;
138 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
139 CHECK_VAL(io
.out
.durable_open
, false);
140 CHECK_VAL(io
.out
.durable_open_v2
, test
.durable
);
141 CHECK_VAL(io
.out
.persistent_open
, test
.persistent
);
142 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level(test
.level
));
146 smb2_util_close(tree
, *h
);
148 smb2_util_unlink(tree
, fname
);
149 talloc_free(mem_ctx
);
154 static bool test_durable_v2_open_oplock_table(struct torture_context
*tctx
,
155 struct smb2_tree
*tree
,
157 bool request_persistent
,
158 struct durable_open_vs_oplock
*table
,
164 smb2_util_unlink(tree
, fname
);
166 for (i
= 0; i
< num_tests
; i
++) {
167 ret
= test_one_durable_v2_open_oplock(tctx
,
178 smb2_util_unlink(tree
, fname
);
183 bool test_durable_v2_open_oplock(struct torture_context
*tctx
,
184 struct smb2_tree
*tree
)
189 /* Choose a random name in case the state is left a little funky. */
190 snprintf(fname
, 256, "durable_open_oplock_%s.dat",
191 generate_random_str(tctx
, 8));
193 ret
= test_durable_v2_open_oplock_table(tctx
, tree
, fname
,
194 false, /* request_persistent */
195 durable_open_vs_oplock_table
,
196 NUM_OPLOCK_OPEN_TESTS
);
204 * basic durable handle open test.
205 * persistent state should only be granted when requested
206 * along with a batch oplock or a handle lease.
208 * This test tests persistent open with all valid lease types.
211 struct durable_open_vs_lease
{
213 const char *share_mode
;
218 #define NUM_LEASE_TYPES 5
219 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
220 static struct durable_open_vs_lease durable_open_vs_lease_table
[NUM_LEASE_OPEN_TESTS
] =
222 { "", "", false, false },
223 { "", "R", false, false },
224 { "", "W", false, false },
225 { "", "D", false, false },
226 { "", "RW", false, false },
227 { "", "RD", false, false },
228 { "", "WD", false, false },
229 { "", "RWD", false, false },
231 { "R", "", false, false },
232 { "R", "R", false, false },
233 { "R", "W", false, false },
234 { "R", "D", false, false },
235 { "R", "RW", false, false },
236 { "R", "RD", false, false },
237 { "R", "DW", false, false },
238 { "R", "RWD", false, false },
240 { "RW", "", false, false },
241 { "RW", "R", false, false },
242 { "RW", "W", false, false },
243 { "RW", "D", false, false },
244 { "RW", "RW", false, false },
245 { "RW", "RD", false, false },
246 { "RW", "WD", false, false },
247 { "RW", "RWD", false, false },
249 { "RH", "", true, false },
250 { "RH", "R", true, false },
251 { "RH", "W", true, false },
252 { "RH", "D", true, false },
253 { "RH", "RW", true, false },
254 { "RH", "RD", true, false },
255 { "RH", "WD", true, false },
256 { "RH", "RWD", true, false },
258 { "RHW", "", true, false },
259 { "RHW", "R", true, false },
260 { "RHW", "W", true, false },
261 { "RHW", "D", true, false },
262 { "RHW", "RW", true, false },
263 { "RHW", "RD", true, false },
264 { "RHW", "WD", true, false },
265 { "RHW", "RWD", true, false },
268 static bool test_one_durable_v2_open_lease(struct torture_context
*tctx
,
269 struct smb2_tree
*tree
,
271 bool request_persistent
,
272 struct durable_open_vs_lease test
)
275 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
276 struct smb2_handle _h
;
277 struct smb2_handle
*h
= NULL
;
279 struct smb2_create io
;
280 struct smb2_lease ls
;
283 smb2_util_unlink(tree
, fname
);
287 smb2_lease_create_share(&io
, &ls
, false /* dir */, fname
,
288 smb2_util_share_access(test
.share_mode
),
290 smb2_util_lease_state(test
.type
));
291 io
.in
.durable_open
= false;
292 io
.in
.durable_open_v2
= true;
293 io
.in
.persistent_open
= request_persistent
;
294 io
.in
.create_guid
= GUID_random();
296 status
= smb2_create(tree
, mem_ctx
, &io
);
297 CHECK_STATUS(status
, NT_STATUS_OK
);
298 _h
= io
.out
.file
.handle
;
300 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
301 CHECK_VAL(io
.out
.durable_open
, false);
302 CHECK_VAL(io
.out
.durable_open_v2
, test
.durable
);
303 CHECK_VAL(io
.out
.persistent_open
, test
.persistent
);
304 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
305 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease
);
306 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease
);
307 CHECK_VAL(io
.out
.lease_response
.lease_state
,
308 smb2_util_lease_state(test
.type
));
311 smb2_util_close(tree
, *h
);
313 smb2_util_unlink(tree
, fname
);
314 talloc_free(mem_ctx
);
319 static bool test_durable_v2_open_lease_table(struct torture_context
*tctx
,
320 struct smb2_tree
*tree
,
322 bool request_persistent
,
323 struct durable_open_vs_lease
*table
,
329 smb2_util_unlink(tree
, fname
);
331 for (i
= 0; i
< num_tests
; i
++) {
332 ret
= test_one_durable_v2_open_lease(tctx
,
343 smb2_util_unlink(tree
, fname
);
348 bool test_durable_v2_open_lease(struct torture_context
*tctx
,
349 struct smb2_tree
*tree
)
355 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
356 if (!(caps
& SMB2_CAP_LEASING
)) {
357 torture_skip(tctx
, "leases are not supported");
360 /* Choose a random name in case the state is left a little funky. */
361 snprintf(fname
, 256, "durable_open_lease_%s.dat", generate_random_str(tctx
, 8));
363 ret
= test_durable_v2_open_lease_table(tctx
, tree
, fname
,
364 false, /* request_persistent */
365 durable_open_vs_lease_table
,
366 NUM_LEASE_OPEN_TESTS
);
374 * basic persistent open test.
376 * This test tests durable open with all possible oplock types.
379 struct durable_open_vs_oplock persistent_open_oplock_ca_table
[NUM_OPLOCK_OPEN_TESTS
] =
381 { "", "", true, true },
382 { "", "R", true, true },
383 { "", "W", true, true },
384 { "", "D", true, true },
385 { "", "RD", true, true },
386 { "", "RW", true, true },
387 { "", "WD", true, true },
388 { "", "RWD", true, true },
390 { "s", "", true, true },
391 { "s", "R", true, true },
392 { "s", "W", true, true },
393 { "s", "D", true, true },
394 { "s", "RD", true, true },
395 { "s", "RW", true, true },
396 { "s", "WD", true, true },
397 { "s", "RWD", true, true },
399 { "x", "", true, true },
400 { "x", "R", true, true },
401 { "x", "W", true, true },
402 { "x", "D", true, true },
403 { "x", "RD", true, true },
404 { "x", "RW", true, true },
405 { "x", "WD", true, true },
406 { "x", "RWD", true, true },
408 { "b", "", true, true },
409 { "b", "R", true, true },
410 { "b", "W", true, true },
411 { "b", "D", true, true },
412 { "b", "RD", true, true },
413 { "b", "RW", true, true },
414 { "b", "WD", true, true },
415 { "b", "RWD", true, true },
418 bool test_persistent_open_oplock(struct torture_context
*tctx
,
419 struct smb2_tree
*tree
)
423 bool share_is_ca
= false;
424 struct durable_open_vs_oplock
*table
;
426 /* Choose a random name in case the state is left a little funky. */
427 snprintf(fname
, 256, "persistent_open_oplock_%s.dat", generate_random_str(tctx
, 8));
429 share_is_ca
= tree
->capabilities
& SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY
;
432 table
= persistent_open_oplock_ca_table
;
434 table
= durable_open_vs_oplock_table
;
437 ret
= test_durable_v2_open_oplock_table(tctx
, tree
, fname
,
438 true, /* request_persistent */
440 NUM_OPLOCK_OPEN_TESTS
);
448 * basic persistent handle open test.
449 * persistent state should only be granted when requested
450 * along with a batch oplock or a handle lease.
452 * This test tests persistent open with all valid lease types.
455 struct durable_open_vs_lease persistent_open_lease_ca_table
[NUM_LEASE_OPEN_TESTS
] =
457 { "", "", true, true },
458 { "", "R", true, true },
459 { "", "W", true, true },
460 { "", "D", true, true },
461 { "", "RW", true, true },
462 { "", "RD", true, true },
463 { "", "WD", true, true },
464 { "", "RWD", true, true },
466 { "R", "", true, true },
467 { "R", "R", true, true },
468 { "R", "W", true, true },
469 { "R", "D", true, true },
470 { "R", "RW", true, true },
471 { "R", "RD", true, true },
472 { "R", "DW", true, true },
473 { "R", "RWD", true, true },
475 { "RW", "", true, true },
476 { "RW", "R", true, true },
477 { "RW", "W", true, true },
478 { "RW", "D", true, true },
479 { "RW", "RW", true, true },
480 { "RW", "RD", true, true },
481 { "RW", "WD", true, true },
482 { "RW", "RWD", true, true },
484 { "RH", "", true, true },
485 { "RH", "R", true, true },
486 { "RH", "W", true, true },
487 { "RH", "D", true, true },
488 { "RH", "RW", true, true },
489 { "RH", "RD", true, true },
490 { "RH", "WD", true, true },
491 { "RH", "RWD", true, true },
493 { "RHW", "", true, true },
494 { "RHW", "R", true, true },
495 { "RHW", "W", true, true },
496 { "RHW", "D", true, true },
497 { "RHW", "RW", true, true },
498 { "RHW", "RD", true, true },
499 { "RHW", "WD", true, true },
500 { "RHW", "RWD", true, true },
503 bool test_persistent_open_lease(struct torture_context
*tctx
,
504 struct smb2_tree
*tree
)
510 struct durable_open_vs_lease
*table
;
512 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
513 if (!(caps
& SMB2_CAP_LEASING
)) {
514 torture_skip(tctx
, "leases are not supported");
517 /* Choose a random name in case the state is left a little funky. */
518 snprintf(fname
, 256, "persistent_open_lease_%s.dat", generate_random_str(tctx
, 8));
520 share_is_ca
= tree
->capabilities
& SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY
;
523 table
= persistent_open_lease_ca_table
;
525 table
= durable_open_vs_lease_table
;
528 ret
= test_durable_v2_open_lease_table(tctx
, tree
, fname
,
529 true, /* request_persistent */
531 NUM_LEASE_OPEN_TESTS
);
538 struct torture_suite
*torture_smb2_durable_v2_open_init(void)
540 struct torture_suite
*suite
=
541 torture_suite_create(talloc_autofree_context(), "durable-v2-open");
543 torture_suite_add_1smb2_test(suite
, "open-oplock", test_durable_v2_open_oplock
);
544 torture_suite_add_1smb2_test(suite
, "open-lease", test_durable_v2_open_lease
);
545 torture_suite_add_1smb2_test(suite
, "persistent-open-oplock", test_persistent_open_oplock
);
546 torture_suite_add_1smb2_test(suite
, "persistent-open-lease", test_persistent_open_lease
);
548 suite
->description
= talloc_strdup(suite
, "SMB2-DURABLE-V2-OPEN tests");