2 Unix SMB/CIFS implementation.
3 basic raw test suite for oplocks
4 Copyright (C) Andrew Tridgell 2003
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.
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.
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.
22 #include "librpc/gen_ndr/ndr_security.h"
24 #define CHECK_VAL(v, correct) do { \
25 if ((v) != (correct)) { \
26 printf("(%d) wrong value for %s got 0x%x - should be 0x%x\n", \
27 __LINE__, #v, (int)v, (int)correct); \
31 #define CHECK_STATUS(status, correct) do { \
32 if (!NT_STATUS_EQUAL(status, correct)) { \
33 printf("(%d) Incorrect status %s - should be %s\n", \
34 __LINE__, nt_errstr(status), nt_errstr(correct)); \
47 a handler function for oplock break requests
49 static BOOL oplock_handler_ack(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private)
51 struct smbcli_tree *tree = private;
52 break_info.fnum = fnum;
53 break_info.level = level;
56 printf("Acking in oplock handler\n");
58 return smbcli_oplock_ack(tree, fnum, level);
62 a handler function for oplock break requests - close the file
64 static BOOL oplock_handler_close(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private)
68 struct smbcli_tree *tree = private;
70 break_info.fnum = fnum;
71 break_info.level = level;
74 io.close.level = RAW_CLOSE_CLOSE;
75 io.close.in.fnum = fnum;
76 io.close.in.write_time = 0;
77 status = smb_raw_close(tree, &io);
79 printf("Closing in oplock handler\n");
81 if (!NT_STATUS_IS_OK(status)) {
82 printf("close failed in oplock_handler_close\n");
91 static BOOL test_oplock(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
93 const char *fname = "\\test_oplock.dat";
97 struct smb_unlink unl;
99 uint16_t fnum=0, fnum2=0;
102 smbcli_unlink(cli->tree, fname);
104 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
109 io.generic.level = RAW_OPEN_NTCREATEX;
110 io.ntcreatex.in.root_fid = 0;
111 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
112 io.ntcreatex.in.alloc_size = 0;
113 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
114 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
115 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
116 io.ntcreatex.in.create_options = 0;
117 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
118 io.ntcreatex.in.security_flags = 0;
119 io.ntcreatex.in.fname = fname;
121 printf("open a file with a normal oplock\n");
122 ZERO_STRUCT(break_info);
123 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK;
125 status = smb_raw_open(cli->tree, mem_ctx, &io);
126 CHECK_STATUS(status, NT_STATUS_OK);
127 fnum = io.ntcreatex.out.fnum;
128 CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
130 printf("unlink it - should be no break\n");
131 unl.in.pattern = fname;
133 status = smb_raw_unlink(cli->tree, &unl);
134 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
135 CHECK_VAL(break_info.count, 0);
137 smbcli_close(cli->tree, fnum);
140 with a batch oplock we get a break
142 printf("open with batch oplock\n");
143 ZERO_STRUCT(break_info);
144 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
145 NTCREATEX_FLAGS_REQUEST_OPLOCK |
146 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
147 status = smb_raw_open(cli->tree, mem_ctx, &io);
148 CHECK_STATUS(status, NT_STATUS_OK);
149 fnum = io.ntcreatex.out.fnum;
150 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
152 printf("unlink should generate a break\n");
153 unl.in.pattern = fname;
155 status = smb_raw_unlink(cli->tree, &unl);
156 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
158 CHECK_VAL(break_info.fnum, fnum);
159 CHECK_VAL(break_info.level, 1);
160 CHECK_VAL(break_info.count, 1);
163 smbcli_close(cli->tree, fnum);
165 printf("if we close on break then the unlink can succeed\n");
166 ZERO_STRUCT(break_info);
167 smbcli_oplock_handler(cli->transport, oplock_handler_close, cli->tree);
168 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
169 NTCREATEX_FLAGS_REQUEST_OPLOCK |
170 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
171 status = smb_raw_open(cli->tree, mem_ctx, &io);
172 CHECK_STATUS(status, NT_STATUS_OK);
173 fnum = io.ntcreatex.out.fnum;
174 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
176 unl.in.pattern = fname;
178 ZERO_STRUCT(break_info);
179 status = smb_raw_unlink(cli->tree, &unl);
180 CHECK_STATUS(status, NT_STATUS_OK);
182 CHECK_VAL(break_info.fnum, fnum);
183 CHECK_VAL(break_info.level, 1);
184 CHECK_VAL(break_info.count, 1);
186 printf("a self read should not cause a break\n");
187 ZERO_STRUCT(break_info);
188 smbcli_close(cli->tree, fnum);
189 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
191 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
192 NTCREATEX_FLAGS_REQUEST_OPLOCK |
193 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
194 status = smb_raw_open(cli->tree, mem_ctx, &io);
195 CHECK_STATUS(status, NT_STATUS_OK);
196 fnum = io.ntcreatex.out.fnum;
197 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
199 rd.read.level = RAW_READ_READ;
200 rd.read.in.fnum = fnum;
201 rd.read.in.count = 1;
202 rd.read.in.offset = 0;
203 rd.read.in.remaining = 0;
204 status = smb_raw_read(cli->tree, &rd);
205 CHECK_STATUS(status, NT_STATUS_OK);
206 CHECK_VAL(break_info.count, 0);
209 printf("a 2nd open should give a break\n");
210 ZERO_STRUCT(break_info);
211 smbcli_close(cli->tree, fnum);
212 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
214 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
215 NTCREATEX_FLAGS_REQUEST_OPLOCK |
216 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
217 status = smb_raw_open(cli->tree, mem_ctx, &io);
218 CHECK_STATUS(status, NT_STATUS_OK);
219 fnum = io.ntcreatex.out.fnum;
220 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
222 ZERO_STRUCT(break_info);
224 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
225 status = smb_raw_open(cli->tree, mem_ctx, &io);
226 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
228 CHECK_VAL(break_info.count, 1);
229 CHECK_VAL(break_info.fnum, fnum);
230 CHECK_VAL(break_info.level, 1);
232 printf("a 2nd open should get an oplock when we close instead of ack\n");
233 ZERO_STRUCT(break_info);
234 smbcli_close(cli->tree, fnum);
235 smbcli_oplock_handler(cli->transport, oplock_handler_close, cli->tree);
237 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
238 NTCREATEX_FLAGS_REQUEST_OPLOCK |
239 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
240 status = smb_raw_open(cli->tree, mem_ctx, &io);
241 CHECK_STATUS(status, NT_STATUS_OK);
242 fnum2 = io.ntcreatex.out.fnum;
243 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
245 ZERO_STRUCT(break_info);
247 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
248 NTCREATEX_FLAGS_REQUEST_OPLOCK |
249 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
250 status = smb_raw_open(cli->tree, mem_ctx, &io);
251 CHECK_STATUS(status, NT_STATUS_OK);
252 fnum = io.ntcreatex.out.fnum;
253 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
255 CHECK_VAL(break_info.count, 1);
256 CHECK_VAL(break_info.fnum, fnum2);
257 CHECK_VAL(break_info.level, 1);
259 smbcli_close(cli->tree, fnum);
261 printf("open with batch oplock\n");
262 ZERO_STRUCT(break_info);
263 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
265 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
266 NTCREATEX_FLAGS_REQUEST_OPLOCK |
267 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
268 status = smb_raw_open(cli->tree, mem_ctx, &io);
269 CHECK_STATUS(status, NT_STATUS_OK);
270 fnum = io.ntcreatex.out.fnum;
271 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
273 ZERO_STRUCT(break_info);
274 printf("second open with attributes only shouldn't cause oplock break\n");
276 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
277 NTCREATEX_FLAGS_REQUEST_OPLOCK |
278 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
279 io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE;
280 status = smb_raw_open(cli->tree, mem_ctx, &io);
281 CHECK_STATUS(status, NT_STATUS_OK);
282 fnum2 = io.ntcreatex.out.fnum;
283 CHECK_VAL(io.ntcreatex.out.oplock_level, NO_OPLOCK_RETURN);
284 CHECK_VAL(break_info.count, 0);
285 CHECK_VAL(break_info.fnum, 0);
286 CHECK_VAL(break_info.level, 0);
288 smbcli_close(cli->tree, fnum);
289 smbcli_close(cli->tree, fnum2);
290 smbcli_unlink(cli->tree, fname);
292 printf("open with attributes only can create file\n");
293 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
294 NTCREATEX_FLAGS_REQUEST_OPLOCK |
295 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
296 io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE;
297 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
298 status = smb_raw_open(cli->tree, mem_ctx, &io);
299 CHECK_STATUS(status, NT_STATUS_OK);
300 fnum = io.ntcreatex.out.fnum;
301 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
303 printf("Subsequent normal open should break oplock on attribute only open to level II\n");
305 ZERO_STRUCT(break_info);
306 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
308 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
309 NTCREATEX_FLAGS_REQUEST_OPLOCK |
310 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
311 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
312 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
313 status = smb_raw_open(cli->tree, mem_ctx, &io);
314 CHECK_STATUS(status, NT_STATUS_OK);
315 fnum2 = io.ntcreatex.out.fnum;
316 CHECK_VAL(break_info.count, 1);
317 CHECK_VAL(break_info.fnum, fnum);
318 CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
319 smbcli_close(cli->tree, fnum2);
321 printf("third oplocked open should grant level2 without break\n");
322 ZERO_STRUCT(break_info);
323 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
324 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
325 NTCREATEX_FLAGS_REQUEST_OPLOCK |
326 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
327 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
328 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
329 status = smb_raw_open(cli->tree, mem_ctx, &io);
330 CHECK_STATUS(status, NT_STATUS_OK);
331 fnum2 = io.ntcreatex.out.fnum;
332 CHECK_VAL(break_info.count, 0);
333 CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
335 ZERO_STRUCT(break_info);
337 printf("write should trigger a break to none on both\n");
340 wr.write.level = RAW_WRITE_WRITE;
341 wr.write.in.fnum = fnum2;
342 wr.write.in.count = 1;
343 wr.write.in.offset = 0;
344 wr.write.in.remaining = 0;
345 wr.write.in.data = "x";
346 status = smb_raw_write(cli->tree, &wr);
347 CHECK_STATUS(status, NT_STATUS_OK);
350 /* Now the oplock break request comes in. But right now we can't
351 * answer it. Do another write */
357 wr.write.level = RAW_WRITE_WRITE;
358 wr.write.in.fnum = fnum2;
359 wr.write.in.count = 1;
360 wr.write.in.offset = 0;
361 wr.write.in.remaining = 0;
362 wr.write.in.data = "x";
363 status = smb_raw_write(cli->tree, &wr);
364 CHECK_STATUS(status, NT_STATUS_OK);
367 CHECK_VAL(break_info.count, 2);
368 CHECK_VAL(break_info.level, 0);
370 smbcli_close(cli->tree, fnum);
371 smbcli_close(cli->tree, fnum2);
373 ZERO_STRUCT(break_info);
374 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
376 printf("Open with oplock after a on-oplock open should grant level2\n");
377 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
378 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
379 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
380 NTCREATEX_SHARE_ACCESS_WRITE|
381 NTCREATEX_SHARE_ACCESS_DELETE;
382 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
383 status = smb_raw_open(cli->tree, mem_ctx, &io);
384 CHECK_STATUS(status, NT_STATUS_OK);
385 fnum = io.ntcreatex.out.fnum;
386 CHECK_VAL(break_info.count, 0);
387 CHECK_VAL(break_info.fnum, 0);
388 CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
390 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
391 NTCREATEX_FLAGS_REQUEST_OPLOCK |
392 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
393 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
394 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
395 NTCREATEX_SHARE_ACCESS_WRITE|
396 NTCREATEX_SHARE_ACCESS_DELETE;
397 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
398 status = smb_raw_open(cli->tree, mem_ctx, &io);
399 CHECK_STATUS(status, NT_STATUS_OK);
400 fnum2 = io.ntcreatex.out.fnum;
401 CHECK_VAL(break_info.count, 0);
402 CHECK_VAL(break_info.fnum, 0);
403 CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
405 printf("write should trigger a break to none\n");
408 wr.write.level = RAW_WRITE_WRITE;
409 wr.write.in.fnum = fnum;
410 wr.write.in.count = 1;
411 wr.write.in.offset = 0;
412 wr.write.in.remaining = 0;
413 wr.write.in.data = "x";
414 status = smb_raw_write(cli->tree, &wr);
415 CHECK_STATUS(status, NT_STATUS_OK);
418 /* Now the oplock break request comes in. But right now we can't
419 * answer it. Do another write */
425 wr.write.level = RAW_WRITE_WRITE;
426 wr.write.in.fnum = fnum;
427 wr.write.in.count = 1;
428 wr.write.in.offset = 0;
429 wr.write.in.remaining = 0;
430 wr.write.in.data = "x";
431 status = smb_raw_write(cli->tree, &wr);
432 CHECK_STATUS(status, NT_STATUS_OK);
435 CHECK_VAL(break_info.count, 1);
436 CHECK_VAL(break_info.fnum, fnum2);
437 CHECK_VAL(break_info.level, 0);
440 smbcli_close(cli->tree, fnum);
441 smbcli_close(cli->tree, fnum2);
442 smbcli_unlink(cli->tree, fname);
448 basic testing of oplocks
450 BOOL torture_raw_oplock(void)
452 struct smbcli_state *cli1;
456 if (!torture_open_connection(&cli1)) {
460 mem_ctx = talloc_init("torture_raw_oplock");
462 if (!test_oplock(cli1, mem_ctx)) {
466 torture_close_connection(cli1);
467 talloc_free(mem_ctx);