fixed the handling of level II oplocks in samba4, especially when
[kai/samba.git] / source4 / torture / raw / oplock.c
1 /* 
2    Unix SMB/CIFS implementation.
3    basic raw test suite for oplocks
4    Copyright (C) Andrew Tridgell 2003
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 #define CHECK_VAL(v, correct) do { \
24         if ((v) != (correct)) { \
25                 printf("(%d) wrong value for %s  0x%x - 0x%x\n", \
26                        __LINE__, #v, (int)v, (int)correct); \
27                 ret = False; \
28         }} while (0)
29
30 #define CHECK_STATUS(status, correct) do { \
31         if (!NT_STATUS_EQUAL(status, correct)) { \
32                 printf("(%d) Incorrect status %s - should be %s\n", \
33                        __LINE__, nt_errstr(status), nt_errstr(correct)); \
34                 ret = False; \
35                 goto done; \
36         }} while (0)
37
38
39 static struct {
40         int fnum;
41         unsigned char level;
42         int count;
43 } break_info;
44
45 /*
46   a handler function for oplock break requests
47 */
48 static BOOL oplock_handler_ack(struct cli_transport *transport, uint16 tid, uint16 fnum, uint8 level, void *private)
49 {
50         struct cli_tree *tree = private;
51         break_info.fnum = fnum;
52         break_info.level = level;
53         break_info.count++;
54
55         printf("Acking in oplock handler\n");
56
57         return cli_oplock_ack(tree, fnum, level);
58 }
59
60 /*
61   a handler function for oplock break requests - close the file
62 */
63 static BOOL oplock_handler_close(struct cli_transport *transport, uint16 tid, uint16 fnum, uint8 level, void *private)
64 {
65         union smb_close io;
66         NTSTATUS status;
67         struct cli_tree *tree = private;
68
69         break_info.fnum = fnum;
70         break_info.level = level;
71         break_info.count++;
72
73         io.close.level = RAW_CLOSE_CLOSE;
74         io.close.in.fnum = fnum;
75         io.close.in.write_time = 0;
76         status = smb_raw_close(tree, &io);
77
78         printf("Closing in oplock handler\n");
79
80         if (!NT_STATUS_IS_OK(status)) {
81                 printf("close failed in oplock_handler_close\n");
82                 return False;
83         }
84         return True;
85 }
86
87 /*
88   test oplock ops
89 */
90 static BOOL test_oplock(struct cli_state *cli, TALLOC_CTX *mem_ctx)
91 {
92         const char *fname = "\\test_oplock.dat";
93         NTSTATUS status;
94         BOOL ret = True;
95         union smb_open io;
96         struct smb_unlink unl;
97         union smb_read rd;
98         uint16 fnum, fnum2;
99
100         /* cleanup */
101         cli_unlink(cli->tree, fname);
102
103         cli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
104
105         /*
106           base ntcreatex parms
107         */
108         io.generic.level = RAW_OPEN_NTCREATEX;
109         io.ntcreatex.in.root_fid = 0;
110         io.ntcreatex.in.access_mask = GENERIC_RIGHTS_FILE_ALL_ACCESS;
111         io.ntcreatex.in.alloc_size = 0;
112         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
113         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
114         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
115         io.ntcreatex.in.create_options = 0;
116         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
117         io.ntcreatex.in.security_flags = 0;
118         io.ntcreatex.in.fname = fname;
119
120         printf("open a file with a normal oplock\n");
121         ZERO_STRUCT(break_info);
122         io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK;
123
124         status = smb_raw_open(cli->tree, mem_ctx, &io);
125         CHECK_STATUS(status, NT_STATUS_OK);
126         fnum = io.ntcreatex.out.fnum;
127         CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
128
129         printf("unlink it - should be no break\n");
130         unl.in.pattern = fname;
131         unl.in.attrib = 0;
132         status = smb_raw_unlink(cli->tree, &unl);
133         CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
134         CHECK_VAL(break_info.count, 0);
135
136         cli_close(cli->tree, fnum);
137
138         /*
139           with a batch oplock we get a break
140         */
141         printf("open with batch oplock\n");
142         ZERO_STRUCT(break_info);
143         io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | 
144                 NTCREATEX_FLAGS_REQUEST_OPLOCK | 
145                 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
146         status = smb_raw_open(cli->tree, mem_ctx, &io);
147         CHECK_STATUS(status, NT_STATUS_OK);
148         fnum = io.ntcreatex.out.fnum;
149         CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
150
151         printf("unlink should generate a break\n");
152         unl.in.pattern = fname;
153         unl.in.attrib = 0;
154         status = smb_raw_unlink(cli->tree, &unl);
155         CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
156
157         CHECK_VAL(break_info.fnum, fnum);
158         CHECK_VAL(break_info.level, 2);
159         CHECK_VAL(break_info.count, 1);
160
161
162         cli_close(cli->tree, fnum);
163
164         printf("if we close on break then the unlink can succeed\n");
165         ZERO_STRUCT(break_info);
166         cli_oplock_handler(cli->transport, oplock_handler_close, cli->tree);
167         io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | 
168                 NTCREATEX_FLAGS_REQUEST_OPLOCK | 
169                 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
170         status = smb_raw_open(cli->tree, mem_ctx, &io);
171         CHECK_STATUS(status, NT_STATUS_OK);
172         fnum = io.ntcreatex.out.fnum;
173         CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
174
175         unl.in.pattern = fname;
176         unl.in.attrib = 0;
177         ZERO_STRUCT(break_info);
178         status = smb_raw_unlink(cli->tree, &unl);
179         CHECK_STATUS(status, NT_STATUS_OK);
180
181         CHECK_VAL(break_info.fnum, fnum);
182         CHECK_VAL(break_info.level, 2);
183         CHECK_VAL(break_info.count, 1);
184
185         printf("a self read should not cause a break\n");
186         ZERO_STRUCT(break_info);
187         cli_close(cli->tree, fnum);
188         cli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
189
190         io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | 
191                 NTCREATEX_FLAGS_REQUEST_OPLOCK | 
192                 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
193         status = smb_raw_open(cli->tree, mem_ctx, &io);
194         CHECK_STATUS(status, NT_STATUS_OK);
195         fnum = io.ntcreatex.out.fnum;
196         CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
197
198         rd.read.level = RAW_READ_READ;
199         rd.read.in.fnum = fnum;
200         rd.read.in.count = 1;
201         rd.read.in.offset = 0;
202         rd.read.in.remaining = 0;
203         status = smb_raw_read(cli->tree, &rd);
204         CHECK_STATUS(status, NT_STATUS_OK);
205         CHECK_VAL(break_info.count, 0);
206
207
208         printf("a 2nd open should give a break\n");
209         ZERO_STRUCT(break_info);
210         cli_close(cli->tree, fnum);
211         cli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
212
213         io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | 
214                 NTCREATEX_FLAGS_REQUEST_OPLOCK | 
215                 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
216         status = smb_raw_open(cli->tree, mem_ctx, &io);
217         CHECK_STATUS(status, NT_STATUS_OK);
218         fnum = io.ntcreatex.out.fnum;
219         CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
220
221         ZERO_STRUCT(break_info);
222
223         io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
224         status = smb_raw_open(cli->tree, mem_ctx, &io);
225         CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
226
227         CHECK_VAL(break_info.count, 1);
228         CHECK_VAL(break_info.fnum, fnum);
229         CHECK_VAL(break_info.level, 2);
230
231         printf("a 2nd open should get an oplock when we close instead of ack\n");
232         ZERO_STRUCT(break_info);
233         cli_close(cli->tree, fnum);
234         cli_oplock_handler(cli->transport, oplock_handler_close, cli->tree);
235
236         io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | 
237                 NTCREATEX_FLAGS_REQUEST_OPLOCK | 
238                 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
239         status = smb_raw_open(cli->tree, mem_ctx, &io);
240         CHECK_STATUS(status, NT_STATUS_OK);
241         fnum2 = io.ntcreatex.out.fnum;
242         CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
243
244         ZERO_STRUCT(break_info);
245
246         io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | 
247                 NTCREATEX_FLAGS_REQUEST_OPLOCK | 
248                 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
249         status = smb_raw_open(cli->tree, mem_ctx, &io);
250         CHECK_STATUS(status, NT_STATUS_OK);
251         fnum = io.ntcreatex.out.fnum;
252         CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
253
254         CHECK_VAL(break_info.count, 1);
255         CHECK_VAL(break_info.fnum, fnum2);
256         CHECK_VAL(break_info.level, 2);
257         
258
259 done:
260         cli_close(cli->tree, fnum);
261         cli_unlink(cli->tree, fname);
262         return ret;
263 }
264
265
266 /* 
267    basic testing of oplocks
268 */
269 BOOL torture_raw_oplock(int dummy)
270 {
271         struct cli_state *cli1;
272         BOOL ret = True;
273         TALLOC_CTX *mem_ctx;
274
275         if (!torture_open_connection(&cli1)) {
276                 return False;
277         }
278
279         mem_ctx = talloc_init("torture_raw_oplock");
280
281         if (!test_oplock(cli1, mem_ctx)) {
282                 ret = False;
283         }
284
285         torture_close_connection(cli1);
286         talloc_destroy(mem_ctx);
287         return ret;
288 }