• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt/router/samba-3.5.8/source4/torture/smb2/
1/*
2   Unix SMB/CIFS implementation.
3
4   test suite for SMB2 leases
5
6   Copyright (C) Zachary Loafman 2009
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 3 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "includes.h"
23#include "lib/events/events.h"
24#include "librpc/gen_ndr/security.h"
25#include "libcli/smb2/smb2.h"
26#include "libcli/smb2/smb2_calls.h"
27#include "torture/torture.h"
28#include "torture/smb2/proto.h"
29
30static inline uint32_t lease(const char *ls) {
31	uint32_t val = 0;
32	int i;
33
34	for (i = 0; i < strlen(ls); i++) {
35		switch (ls[i]) {
36		case 'R':
37			val |= SMB2_LEASE_READ;
38			break;
39		case 'H':
40			val |= SMB2_LEASE_HANDLE;
41			break;
42		case 'W':
43			val |= SMB2_LEASE_WRITE;
44			break;
45		}
46	}
47
48	return val;
49}
50
51#define CHECK_VAL(v, correct) do { \
52	if ((v) != (correct)) { \
53		torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
54				__location__, #v, (int)(v), (int)(correct)); \
55		ret = false; \
56	}} while (0)
57
58#define CHECK_STATUS(status, correct) do { \
59	if (!NT_STATUS_EQUAL(status, correct)) { \
60		torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
61		       nt_errstr(status), nt_errstr(correct)); \
62		ret = false; \
63		goto done; \
64	}} while (0)
65
66static void smb2_generic_create(struct smb2_create *io, struct smb2_lease *ls,
67                                bool dir, const char *name, uint32_t disposition,
68                                uint32_t oplock, uint64_t leasekey,
69                                uint32_t leasestate)
70{
71	ZERO_STRUCT(*io);
72	io->in.security_flags		= 0x00;
73	io->in.oplock_level		= oplock;
74	io->in.impersonation_level	= NTCREATEX_IMPERSONATION_IMPERSONATION;
75	io->in.create_flags		= 0x00000000;
76	io->in.reserved			= 0x00000000;
77	io->in.desired_access		= SEC_RIGHTS_FILE_ALL;
78	io->in.file_attributes		= FILE_ATTRIBUTE_NORMAL;
79	io->in.share_access		= NTCREATEX_SHARE_ACCESS_READ |
80					  NTCREATEX_SHARE_ACCESS_WRITE |
81					  NTCREATEX_SHARE_ACCESS_DELETE;
82	io->in.create_disposition	= disposition;
83	io->in.create_options		= NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
84					  NTCREATEX_OPTIONS_ASYNC_ALERT	|
85					  NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
86					  0x00200000;
87	io->in.fname			= name;
88
89	if (dir) {
90		io->in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
91		io->in.share_access &= ~NTCREATEX_SHARE_ACCESS_DELETE;
92		io->in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
93		io->in.create_disposition = NTCREATEX_DISP_CREATE;
94	}
95
96	if (ls) {
97		ZERO_STRUCT(*ls);
98		ls->lease_key.data[0] = leasekey;
99		ls->lease_key.data[1] = ~leasekey;
100		ls->lease_state = leasestate;
101		io->in.lease_request = ls;
102	}
103}
104
105static void smb2_lease_create(struct smb2_create *io, struct smb2_lease *ls,
106                              bool dir, const char *name, uint64_t leasekey,
107                              uint32_t leasestate)
108{
109	smb2_generic_create(io, ls, dir, name, NTCREATEX_DISP_OPEN_IF,
110	    SMB2_OPLOCK_LEVEL_LEASE, leasekey, leasestate);
111}
112
113static void smb2_oplock_create(struct smb2_create *io, const char *name,
114                               uint32_t oplock)
115{
116	smb2_generic_create(io, NULL, false, name, NTCREATEX_DISP_OPEN_IF,
117	    oplock, 0, 0);
118}
119
120#define CHECK_CREATED(__io, __created, __attribute)			\
121	do {								\
122		CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
123		CHECK_VAL((__io)->out.alloc_size, 0);			\
124		CHECK_VAL((__io)->out.size, 0);				\
125		CHECK_VAL((__io)->out.file_attr, (__attribute));	\
126		CHECK_VAL((__io)->out.reserved2, 0);			\
127	} while(0)
128
129#define CHECK_LEASE(__io, __state, __oplevel, __key)			\
130	do {								\
131		if (__oplevel) {					\
132			CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
133			CHECK_VAL((__io)->out.lease_response.lease_key.data[0], (__key)); \
134			CHECK_VAL((__io)->out.lease_response.lease_key.data[1], ~(__key)); \
135			CHECK_VAL((__io)->out.lease_response.lease_state, lease(__state)); \
136		} else {						\
137			CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
138			CHECK_VAL((__io)->out.lease_response.lease_key.data[0], 0); \
139			CHECK_VAL((__io)->out.lease_response.lease_key.data[1], 0); \
140			CHECK_VAL((__io)->out.lease_response.lease_state, 0); \
141		}							\
142									\
143		CHECK_VAL((__io)->out.lease_response.lease_flags, 0);	\
144		CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \
145	} while(0)							\
146
147static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull;
148static const uint64_t LEASE2 = 0xDEADBEEFFEEDBEADull;
149static const uint64_t LEASE3 = 0xDAD0FFEDD00DF00Dull;
150
151#define NREQUEST_RESULTS 8
152static const char *request_results[NREQUEST_RESULTS][2] = {
153	{ "", "" },
154	{ "R", "R" },
155	{ "H", "" },
156	{ "W", "" },
157	{ "RH", "RH" },
158	{ "RW", "RW" },
159	{ "HW", "" },
160	{ "RHW", "RHW" },
161};
162
163static bool test_lease_request(struct torture_context *tctx,
164	                       struct smb2_tree *tree)
165{
166	TALLOC_CTX *mem_ctx = talloc_new(tctx);
167	struct smb2_create io;
168	struct smb2_lease ls;
169	struct smb2_handle h1, h2;
170	NTSTATUS status;
171	const char *fname = "lease.dat";
172	const char *fname2 = "lease2.dat";
173	const char *sname = "lease.dat:stream";
174	const char *dname = "lease.dir";
175	bool ret = true;
176	int i;
177
178	smb2_util_unlink(tree, fname);
179	smb2_util_unlink(tree, fname2);
180	smb2_util_rmdir(tree, dname);
181
182	/* Win7 is happy to grant RHW leases on files. */
183	smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
184	status = smb2_create(tree, mem_ctx, &io);
185	CHECK_STATUS(status, NT_STATUS_OK);
186	h1 = io.out.file.handle;
187	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
188	CHECK_LEASE(&io, "RHW", true, LEASE1);
189
190	/* But will reject leases on directories. */
191	smb2_lease_create(&io, &ls, true, dname, LEASE2, lease("RHW"));
192	status = smb2_create(tree, mem_ctx, &io);
193	CHECK_STATUS(status, NT_STATUS_OK);
194	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
195	CHECK_LEASE(&io, "", false, 0);
196	smb2_util_close(tree, io.out.file.handle);
197
198	/* Also rejects multiple files leased under the same key. */
199	smb2_lease_create(&io, &ls, true, fname2, LEASE1, lease("RHW"));
200	status = smb2_create(tree, mem_ctx, &io);
201	CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
202
203	/* And grants leases on streams (with separate leasekey). */
204	smb2_lease_create(&io, &ls, false, sname, LEASE2, lease("RHW"));
205	status = smb2_create(tree, mem_ctx, &io);
206	h2 = io.out.file.handle;
207	CHECK_STATUS(status, NT_STATUS_OK);
208	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
209	CHECK_LEASE(&io, "RHW", true, LEASE2);
210	smb2_util_close(tree, h2);
211
212	smb2_util_close(tree, h1);
213
214	/* Now see what combos are actually granted. */
215	for (i = 0; i < NREQUEST_RESULTS; i++) {
216		torture_comment(tctx, "Requesting lease type %s(%x),"
217		    " expecting %s(%x)\n",
218		    request_results[i][0], lease(request_results[i][0]),
219		    request_results[i][1], lease(request_results[i][1]));
220		smb2_lease_create(&io, &ls, false, fname, LEASE1,
221		    lease(request_results[i][0]));
222		status = smb2_create(tree, mem_ctx, &io);
223		h2 = io.out.file.handle;
224		CHECK_STATUS(status, NT_STATUS_OK);
225		CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
226		CHECK_LEASE(&io, request_results[i][1], true, LEASE1);
227		smb2_util_close(tree, io.out.file.handle);
228	}
229
230 done:
231	smb2_util_close(tree, h1);
232	smb2_util_close(tree, h2);
233
234	smb2_util_unlink(tree, fname);
235	smb2_util_unlink(tree, fname2);
236	smb2_util_rmdir(tree, dname);
237
238	talloc_free(mem_ctx);
239
240	return ret;
241}
242
243static bool test_lease_upgrade(struct torture_context *tctx,
244                               struct smb2_tree *tree)
245{
246	TALLOC_CTX *mem_ctx = talloc_new(tctx);
247	struct smb2_create io;
248	struct smb2_lease ls;
249	struct smb2_handle h, hnew;
250	NTSTATUS status;
251	const char *fname = "lease.dat";
252	bool ret = true;
253
254	smb2_util_unlink(tree, fname);
255
256	/* Grab a RH lease. */
257	smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
258	status = smb2_create(tree, mem_ctx, &io);
259	CHECK_STATUS(status, NT_STATUS_OK);
260	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
261	CHECK_LEASE(&io, "RH", true, LEASE1);
262	h = io.out.file.handle;
263
264	/* Upgrades (sidegrades?) to RW leave us with an RH. */
265	smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RW"));
266	status = smb2_create(tree, mem_ctx, &io);
267	CHECK_STATUS(status, NT_STATUS_OK);
268	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
269	CHECK_LEASE(&io, "RH", true, LEASE1);
270	hnew = io.out.file.handle;
271
272	smb2_util_close(tree, hnew);
273
274	/* Upgrade to RHW lease. */
275	smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
276	status = smb2_create(tree, mem_ctx, &io);
277	CHECK_STATUS(status, NT_STATUS_OK);
278	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
279	CHECK_LEASE(&io, "RHW", true, LEASE1);
280	hnew = io.out.file.handle;
281
282	smb2_util_close(tree, h);
283	h = hnew;
284
285	/* Attempt to downgrade - original lease state is maintained. */
286	smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
287	status = smb2_create(tree, mem_ctx, &io);
288	CHECK_STATUS(status, NT_STATUS_OK);
289	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
290	CHECK_LEASE(&io, "RHW", true, LEASE1);
291	hnew = io.out.file.handle;
292
293	smb2_util_close(tree, hnew);
294
295 done:
296	smb2_util_close(tree, h);
297	smb2_util_close(tree, hnew);
298
299	smb2_util_unlink(tree, fname);
300
301	talloc_free(mem_ctx);
302
303	return ret;
304}
305
306#define CHECK_LEASE_BREAK(__lb, __oldstate, __state, __key)		\
307	do {								\
308		CHECK_VAL((__lb)->new_lease_state, lease(__state));	\
309		CHECK_VAL((__lb)->current_lease.lease_state, lease(__oldstate)); \
310		CHECK_VAL((__lb)->current_lease.lease_key.data[0], (__key)); \
311		CHECK_VAL((__lb)->current_lease.lease_key.data[1], ~(__key)); \
312	} while(0)
313
314#define CHECK_LEASE_BREAK_ACK(__lba, __state, __key)			\
315	do {								\
316		CHECK_VAL((__lba)->out.reserved, 0);			\
317		CHECK_VAL((__lba)->out.lease.lease_key.data[0], (__key)); \
318		CHECK_VAL((__lba)->out.lease.lease_key.data[1], ~(__key)); \
319		CHECK_VAL((__lba)->out.lease.lease_state, lease(__state)); \
320		CHECK_VAL((__lba)->out.lease.lease_flags, 0);		\
321		CHECK_VAL((__lba)->out.lease.lease_duration, 0);	\
322	} while(0)
323
324static struct {
325	struct smb2_lease_break lease_break;
326	struct smb2_lease_break_ack lease_break_ack;
327	int count;
328	int failures;
329
330	struct smb2_handle oplock_handle;
331	int held_oplock_level;
332	int oplock_level;
333	int oplock_count;
334	int oplock_failures;
335} break_info;
336
337#define CHECK_BREAK_INFO(__oldstate, __state, __key)			\
338	do {								\
339		CHECK_VAL(break_info.failures, 0);			\
340		CHECK_VAL(break_info.count, 1);				\
341		CHECK_LEASE_BREAK(&break_info.lease_break, (__oldstate), \
342		    (__state), (__key));				\
343		if (break_info.lease_break.break_flags &		\
344		    SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {	\
345			CHECK_LEASE_BREAK_ACK(&break_info.lease_break_ack, \
346				              (__state), (__key));	\
347		}							\
348	} while(0)
349
350static void torture_lease_break_callback(struct smb2_request *req)
351{
352	NTSTATUS status;
353
354	status = smb2_lease_break_ack_recv(req, &break_info.lease_break_ack);
355	if (!NT_STATUS_IS_OK(status))
356		break_info.failures++;
357
358	return;
359}
360
361/* a lease break request handler */
362static bool torture_lease_handler(struct smb2_transport *transport,
363				  const struct smb2_lease_break *lb,
364				  void *private_data)
365{
366	struct smb2_tree *tree = private_data;
367	struct smb2_lease_break_ack io;
368	struct smb2_request *req;
369
370	break_info.lease_break = *lb;
371	break_info.count++;
372
373	if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {
374		ZERO_STRUCT(io);
375		io.in.lease.lease_key = lb->current_lease.lease_key;
376		io.in.lease.lease_state = lb->new_lease_state;
377
378		req = smb2_lease_break_ack_send(tree, &io);
379		req->async.fn = torture_lease_break_callback;
380		req->async.private_data = NULL;
381	}
382
383	return true;
384}
385
386/*
387  break_results should be read as "held lease, new lease, hold broken to, new
388  grant", i.e. { "RH", "RW", "RH", "R" } means that if key1 holds RH and key2
389  tries for RW, key1 will be broken to RH (in this case, not broken at all)
390  and key2 will be granted R.
391
392  Note: break_results only includes things that Win7 will actually grant (see
393  request_results above).
394 */
395#define NBREAK_RESULTS 16
396static const char *break_results[NBREAK_RESULTS][4] = {
397	{"R",	"R",	"R",	"R"},
398	{"R",	"RH",	"R",	"RH"},
399	{"R",	"RW",	"R",	"R"},
400	{"R",	"RHW",	"R",	"RH"},
401
402	{"RH",	"R",	"RH",	"R"},
403	{"RH",	"RH",	"RH",	"RH"},
404	{"RH",	"RW",	"RH",	"R"},
405	{"RH",	"RHW",	"RH",	"RH"},
406
407	{"RW",	"R",	"R",	"R"},
408	{"RW",	"RH",	"R",	"RH"},
409	{"RW",	"RW",	"R",	"R"},
410	{"RW",	"RHW",	"R",	"RH"},
411
412	{"RHW",	"R",	"RH",	"R"},
413	{"RHW",	"RH",	"RH",	"RH"},
414	{"RHW",	"RW",	"RH",	"R"},
415	{"RHW", "RHW",	"RH",	"RH"},
416};
417
418static bool test_lease_break(struct torture_context *tctx,
419                               struct smb2_tree *tree)
420{
421	TALLOC_CTX *mem_ctx = talloc_new(tctx);
422	struct smb2_create io;
423	struct smb2_lease ls;
424	struct smb2_handle h, h2, h3;
425	NTSTATUS status;
426	const char *fname = "lease.dat";
427	bool ret = true;
428	int i;
429
430	tree->session->transport->lease.handler	= torture_lease_handler;
431	tree->session->transport->lease.private_data = tree;
432
433	smb2_util_unlink(tree, fname);
434
435	for (i = 0; i < NBREAK_RESULTS; i++) {
436		const char *held = break_results[i][0];
437		const char *contend = break_results[i][1];
438		const char *brokento = break_results[i][2];
439		const char *granted = break_results[i][3];
440		torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
441		    "expecting break to %s(%x) and grant of %s(%x)\n",
442		    held, lease(held), contend, lease(contend),
443		    brokento, lease(brokento), granted, lease(granted));
444
445		ZERO_STRUCT(break_info);
446
447		/* Grab lease. */
448		smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(held));
449		status = smb2_create(tree, mem_ctx, &io);
450		CHECK_STATUS(status, NT_STATUS_OK);
451		h = io.out.file.handle;
452		CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
453		CHECK_LEASE(&io, held, true, LEASE1);
454
455		/* Possibly contend lease. */
456		smb2_lease_create(&io, &ls, false, fname, LEASE2, lease(contend));
457		status = smb2_create(tree, mem_ctx, &io);
458		CHECK_STATUS(status, NT_STATUS_OK);
459		h2 = io.out.file.handle;
460		CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
461		CHECK_LEASE(&io, granted, true, LEASE2);
462
463		if (lease(held) != lease(brokento)) {
464			CHECK_BREAK_INFO(held, brokento, LEASE1);
465		} else {
466			CHECK_VAL(break_info.count, 0);
467			CHECK_VAL(break_info.failures, 0);
468		}
469
470		ZERO_STRUCT(break_info);
471
472		/*
473		  Now verify that an attempt to upgrade LEASE1 results in no
474		  break and no change in LEASE1.
475		 */
476		smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
477		status = smb2_create(tree, mem_ctx, &io);
478		CHECK_STATUS(status, NT_STATUS_OK);
479		h3 = io.out.file.handle;
480		CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
481		CHECK_LEASE(&io, brokento, true, LEASE1);
482		CHECK_VAL(break_info.count, 0);
483		CHECK_VAL(break_info.failures, 0);
484
485		smb2_util_close(tree, h);
486		smb2_util_close(tree, h2);
487		smb2_util_close(tree, h3);
488
489		status = smb2_util_unlink(tree, fname);
490		CHECK_STATUS(status, NT_STATUS_OK);
491	}
492
493 done:
494	smb2_util_close(tree, h);
495	smb2_util_close(tree, h2);
496
497	smb2_util_unlink(tree, fname);
498
499	talloc_free(mem_ctx);
500
501	return ret;
502}
503
504static void torture_oplock_break_callback(struct smb2_request *req)
505{
506	NTSTATUS status;
507	struct smb2_break br;
508
509	ZERO_STRUCT(br);
510	status = smb2_break_recv(req, &br);
511	if (!NT_STATUS_IS_OK(status))
512		break_info.oplock_failures++;
513
514	return;
515}
516
517/* a oplock break request handler */
518static bool torture_oplock_handler(struct smb2_transport *transport,
519				   const struct smb2_handle *handle,
520				   uint8_t level, void *private_data)
521{
522	struct smb2_tree *tree = private_data;
523	struct smb2_request *req;
524	struct smb2_break br;
525
526	break_info.oplock_handle = *handle;
527	break_info.oplock_level	= level;
528	break_info.oplock_count++;
529
530	ZERO_STRUCT(br);
531	br.in.file.handle = *handle;
532	br.in.oplock_level = level;
533
534	if (break_info.held_oplock_level > SMB2_OPLOCK_LEVEL_II) {
535		req = smb2_break_send(tree, &br);
536		req->async.fn = torture_oplock_break_callback;
537		req->async.private_data = NULL;
538	}
539	break_info.held_oplock_level = level;
540
541	return true;
542}
543
544static inline uint32_t oplock(const char *op) {
545	uint32_t val = SMB2_OPLOCK_LEVEL_NONE;
546	int i;
547
548	for (i = 0; i < strlen(op); i++) {
549		switch (op[i]) {
550		case 's':
551			return SMB2_OPLOCK_LEVEL_II;
552		case 'x':
553			return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
554		case 'b':
555			return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
556		default:
557			continue;
558		}
559	}
560
561	return val;
562}
563
564#define NOPLOCK_RESULTS 12
565static const char *oplock_results[NOPLOCK_RESULTS][4] = {
566	{"R",	"s",	"R",	"s"},
567	{"R",	"x",	"R",	"s"},
568	{"R",	"b",	"R",	"s"},
569
570	{"RH",	"s",	"RH",	""},
571	{"RH",	"x",	"RH",	""},
572	{"RH",	"b",	"RH",	""},
573
574	{"RW",	"s",	"R",	"s"},
575	{"RW",	"x",	"R",	"s"},
576	{"RW",	"b",	"R",	"s"},
577
578	{"RHW",	"s",	"RH",	""},
579	{"RHW",	"x",	"RH",	""},
580	{"RHW",	"b",	"RH",	""},
581};
582
583static const char *oplock_results_2[NOPLOCK_RESULTS][4] = {
584	{"s",	"R",	"s",	"R"},
585	{"s",	"RH",	"s",	"R"},
586	{"s",	"RW",	"s",	"R"},
587	{"s",	"RHW",	"s",	"R"},
588
589	{"x",	"R",	"s",	"R"},
590	{"x",	"RH",	"s",	"R"},
591	{"x",	"RW",	"s",	"R"},
592	{"x",	"RHW",	"s",	"R"},
593
594	{"b",	"R",	"s",	"R"},
595	{"b",	"RH",	"s",	"R"},
596	{"b",	"RW",	"s",	"R"},
597	{"b",	"RHW",	"s",	"R"},
598};
599
600static bool test_lease_oplock(struct torture_context *tctx,
601                              struct smb2_tree *tree)
602{
603	TALLOC_CTX *mem_ctx = talloc_new(tctx);
604	struct smb2_create io;
605	struct smb2_lease ls;
606	struct smb2_handle h, h2;
607	NTSTATUS status;
608	const char *fname = "lease.dat";
609	bool ret = true;
610	int i;
611
612	tree->session->transport->lease.handler	= torture_lease_handler;
613	tree->session->transport->lease.private_data = tree;
614	tree->session->transport->oplock.handler = torture_oplock_handler;
615	tree->session->transport->oplock.private_data = tree;
616
617	smb2_util_unlink(tree, fname);
618
619	for (i = 0; i < NOPLOCK_RESULTS; i++) {
620		const char *held = oplock_results[i][0];
621		const char *contend = oplock_results[i][1];
622		const char *brokento = oplock_results[i][2];
623		const char *granted = oplock_results[i][3];
624		torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
625		    "expecting break to %s(%x) and grant of %s(%x)\n",
626		    held, lease(held), contend, oplock(contend),
627		    brokento, lease(brokento), granted, oplock(granted));
628
629		ZERO_STRUCT(break_info);
630
631		/* Grab lease. */
632		smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(held));
633		status = smb2_create(tree, mem_ctx, &io);
634		CHECK_STATUS(status, NT_STATUS_OK);
635		h = io.out.file.handle;
636		CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
637		CHECK_LEASE(&io, held, true, LEASE1);
638
639		/* Does an oplock contend the lease? */
640		smb2_oplock_create(&io, fname, oplock(contend));
641		status = smb2_create(tree, mem_ctx, &io);
642		CHECK_STATUS(status, NT_STATUS_OK);
643		h2 = io.out.file.handle;
644		CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
645		CHECK_VAL(io.out.oplock_level, oplock(granted));
646		break_info.held_oplock_level = io.out.oplock_level;
647
648		if (lease(held) != lease(brokento)) {
649			CHECK_BREAK_INFO(held, brokento, LEASE1);
650		} else {
651			CHECK_VAL(break_info.count, 0);
652			CHECK_VAL(break_info.failures, 0);
653		}
654
655		smb2_util_close(tree, h);
656		smb2_util_close(tree, h2);
657
658		status = smb2_util_unlink(tree, fname);
659		CHECK_STATUS(status, NT_STATUS_OK);
660	}
661
662	for (i = 0; i < NOPLOCK_RESULTS; i++) {
663		const char *held = oplock_results_2[i][0];
664		const char *contend = oplock_results_2[i][1];
665		const char *brokento = oplock_results_2[i][2];
666		const char *granted = oplock_results_2[i][3];
667		torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
668		    "expecting break to %s(%x) and grant of %s(%x)\n",
669		    held, oplock(held), contend, lease(contend),
670		    brokento, oplock(brokento), granted, lease(granted));
671
672		ZERO_STRUCT(break_info);
673
674		/* Grab an oplock. */
675		smb2_oplock_create(&io, fname, oplock(held));
676		status = smb2_create(tree, mem_ctx, &io);
677		CHECK_STATUS(status, NT_STATUS_OK);
678		h = io.out.file.handle;
679		CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
680		CHECK_VAL(io.out.oplock_level, oplock(held));
681		break_info.held_oplock_level = io.out.oplock_level;
682
683		/* Grab lease. */
684		smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(contend));
685		status = smb2_create(tree, mem_ctx, &io);
686		CHECK_STATUS(status, NT_STATUS_OK);
687		h2 = io.out.file.handle;
688		CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
689		CHECK_LEASE(&io, granted, true, LEASE1);
690
691		if (oplock(held) != oplock(brokento)) {
692			CHECK_VAL(break_info.oplock_count, 1);
693			CHECK_VAL(break_info.oplock_failures, 0);
694			CHECK_VAL(break_info.oplock_level, oplock(brokento));
695			break_info.held_oplock_level = break_info.oplock_level;
696		} else {
697			CHECK_VAL(break_info.oplock_count, 0);
698			CHECK_VAL(break_info.oplock_failures, 0);
699		}
700
701		smb2_util_close(tree, h);
702		smb2_util_close(tree, h2);
703
704		status = smb2_util_unlink(tree, fname);
705		CHECK_STATUS(status, NT_STATUS_OK);
706	}
707
708 done:
709	smb2_util_close(tree, h);
710	smb2_util_close(tree, h2);
711
712	smb2_util_unlink(tree, fname);
713
714	talloc_free(mem_ctx);
715
716	return ret;
717}
718
719static bool test_lease_multibreak(struct torture_context *tctx,
720                                  struct smb2_tree *tree)
721{
722	TALLOC_CTX *mem_ctx = talloc_new(tctx);
723	struct smb2_create io;
724	struct smb2_lease ls;
725	struct smb2_handle h, h2, h3;
726	struct smb2_write w;
727	NTSTATUS status;
728	const char *fname = "lease.dat";
729	bool ret = true;
730
731	tree->session->transport->lease.handler	= torture_lease_handler;
732	tree->session->transport->lease.private_data = tree;
733	tree->session->transport->oplock.handler = torture_oplock_handler;
734	tree->session->transport->oplock.private_data = tree;
735
736	smb2_util_unlink(tree, fname);
737
738	ZERO_STRUCT(break_info);
739
740	/* Grab lease, upgrade to RHW .. */
741	smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
742	status = smb2_create(tree, mem_ctx, &io);
743	CHECK_STATUS(status, NT_STATUS_OK);
744	h = io.out.file.handle;
745	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
746	CHECK_LEASE(&io, "RH", true, LEASE1);
747
748	smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
749	status = smb2_create(tree, mem_ctx, &io);
750	CHECK_STATUS(status, NT_STATUS_OK);
751	h2 = io.out.file.handle;
752	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
753	CHECK_LEASE(&io, "RHW", true, LEASE1);
754
755	/* Contend with LEASE2. */
756	smb2_lease_create(&io, &ls, false, fname, LEASE2, lease("RHW"));
757	status = smb2_create(tree, mem_ctx, &io);
758	CHECK_STATUS(status, NT_STATUS_OK);
759	h3 = io.out.file.handle;
760	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
761	CHECK_LEASE(&io, "RH", true, LEASE2);
762
763	/* Verify that we were only sent one break. */
764	CHECK_BREAK_INFO("RHW", "RH", LEASE1);
765
766	/* Drop LEASE1 / LEASE2 */
767	status = smb2_util_close(tree, h);
768	CHECK_STATUS(status, NT_STATUS_OK);
769	status = smb2_util_close(tree, h2);
770	CHECK_STATUS(status, NT_STATUS_OK);
771	status = smb2_util_close(tree, h3);
772	CHECK_STATUS(status, NT_STATUS_OK);
773
774	ZERO_STRUCT(break_info);
775
776	/* Grab an R lease. */
777	smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("R"));
778	status = smb2_create(tree, mem_ctx, &io);
779	CHECK_STATUS(status, NT_STATUS_OK);
780	h = io.out.file.handle;
781	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
782	CHECK_LEASE(&io, "R", true, LEASE1);
783
784	/* Grab a level-II oplock. */
785	smb2_oplock_create(&io, fname, oplock("s"));
786	status = smb2_create(tree, mem_ctx, &io);
787	CHECK_STATUS(status, NT_STATUS_OK);
788	h2 = io.out.file.handle;
789	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
790	CHECK_VAL(io.out.oplock_level, oplock("s"));
791	break_info.held_oplock_level = io.out.oplock_level;
792
793	/* Verify no breaks. */
794	CHECK_VAL(break_info.count, 0);
795	CHECK_VAL(break_info.failures, 0);
796
797	/* Open for truncate, force a break. */
798	smb2_generic_create(&io, NULL, false, fname,
799	    NTCREATEX_DISP_OVERWRITE_IF, oplock(""), 0, 0);
800	status = smb2_create(tree, mem_ctx, &io);
801	CHECK_STATUS(status, NT_STATUS_OK);
802	h3 = io.out.file.handle;
803	CHECK_CREATED(&io, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
804	CHECK_VAL(io.out.oplock_level, oplock(""));
805	break_info.held_oplock_level = io.out.oplock_level;
806
807	/* Sleep, use a write to clear the recv queue. */
808	msleep(250);
809	ZERO_STRUCT(w);
810	w.in.file.handle = h3;
811	w.in.offset      = 0;
812	w.in.data        = data_blob_talloc(mem_ctx, NULL, 4096);
813	status = smb2_write(tree, &w);
814	CHECK_STATUS(status, NT_STATUS_OK);
815
816	/* Verify one oplock break, one lease break. */
817	CHECK_VAL(break_info.oplock_count, 1);
818	CHECK_VAL(break_info.oplock_failures, 0);
819	CHECK_VAL(break_info.oplock_level, oplock(""));
820	CHECK_BREAK_INFO("R", "", LEASE1);
821
822 done:
823	smb2_util_close(tree, h);
824	smb2_util_close(tree, h2);
825	smb2_util_close(tree, h3);
826
827	smb2_util_unlink(tree, fname);
828
829	talloc_free(mem_ctx);
830
831	return ret;
832}
833
834struct torture_suite *torture_smb2_lease_init(void)
835{
836	struct torture_suite *suite =
837	    torture_suite_create(talloc_autofree_context(), "LEASE");
838
839	torture_suite_add_1smb2_test(suite, "REQUEST", test_lease_request);
840	torture_suite_add_1smb2_test(suite, "UPGRADE", test_lease_upgrade);
841	torture_suite_add_1smb2_test(suite, "BREAK", test_lease_break);
842	torture_suite_add_1smb2_test(suite, "OPLOCK", test_lease_oplock);
843	torture_suite_add_1smb2_test(suite, "MULTIBREAK", test_lease_multibreak);
844
845	suite->description = talloc_strdup(suite, "SMB2-LEASE tests");
846
847	return suite;
848}
849