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 "torture/torture.h"
23 #include "librpc/gen_ndr/security.h"
24 #include "libcli/raw/libcliraw.h"
25 #include "libcli/libcli.h"
26 #include "torture/util.h"
27 #include "lib/events/events.h"
29 #define CHECK_VAL(v, correct) do { \
30 if ((v) != (correct)) { \
31 printf("(%d) wrong value for %s got 0x%x - should be 0x%x\n", \
32 __LINE__, #v, (int)v, (int)correct); \
36 #define CHECK_STATUS(status, correct) do { \
37 if (!NT_STATUS_EQUAL(status, correct)) { \
38 printf("(%d) Incorrect status %s - should be %s\n", \
39 __LINE__, nt_errstr(status), nt_errstr(correct)); \
52 #define BASEDIR "\\test_notify"
55 a handler function for oplock break requests
57 static BOOL oplock_handler_ack(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private)
59 struct smbcli_tree *tree = private;
60 break_info.fnum = fnum;
61 break_info.level = level;
64 printf("Acking in oplock handler\n");
66 return smbcli_oplock_ack(tree, fnum, level);
69 static void oplock_handler_close_recv(struct smbcli_request *req)
72 status = smbcli_request_simple_recv(req);
73 if (!NT_STATUS_IS_OK(status)) {
74 printf("close failed in oplock_handler_close\n");
75 break_info.failures++;
80 a handler function for oplock break requests - close the file
82 static BOOL oplock_handler_close(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private)
85 struct smbcli_tree *tree = private;
86 struct smbcli_request *req;
88 break_info.fnum = fnum;
89 break_info.level = level;
92 io.close.level = RAW_CLOSE_CLOSE;
93 io.close.in.file.fnum = fnum;
94 io.close.in.write_time = 0;
95 req = smb_raw_close_send(tree, &io);
97 printf("failed to send close in oplock_handler_close\n");
101 req->async.fn = oplock_handler_close_recv;
102 req->async.private = NULL;
110 static BOOL test_oplock(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
112 const char *fname = BASEDIR "\\test_oplock.dat";
116 union smb_unlink unl;
118 uint16_t fnum=0, fnum2=0;
121 smbcli_unlink(cli->tree, fname);
123 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
128 io.generic.level = RAW_OPEN_NTCREATEX;
129 io.ntcreatex.in.root_fid = 0;
130 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
131 io.ntcreatex.in.alloc_size = 0;
132 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
133 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
134 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
135 io.ntcreatex.in.create_options = 0;
136 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
137 io.ntcreatex.in.security_flags = 0;
138 io.ntcreatex.in.fname = fname;
140 printf("open a file with a normal oplock\n");
141 ZERO_STRUCT(break_info);
142 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK;
144 status = smb_raw_open(cli->tree, mem_ctx, &io);
145 CHECK_STATUS(status, NT_STATUS_OK);
146 fnum = io.ntcreatex.out.file.fnum;
147 CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
149 printf("unlink it - should be no break\n");
150 unl.unlink.in.pattern = fname;
151 unl.unlink.in.attrib = 0;
152 status = smb_raw_unlink(cli->tree, &unl);
153 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
154 CHECK_VAL(break_info.count, 0);
155 CHECK_VAL(break_info.failures, 0);
157 smbcli_close(cli->tree, fnum);
160 with a batch oplock we get a break
162 printf("open with batch oplock\n");
163 ZERO_STRUCT(break_info);
164 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
165 NTCREATEX_FLAGS_REQUEST_OPLOCK |
166 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
167 status = smb_raw_open(cli->tree, mem_ctx, &io);
168 CHECK_STATUS(status, NT_STATUS_OK);
169 fnum = io.ntcreatex.out.file.fnum;
170 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
172 printf("unlink should generate a break\n");
173 unl.unlink.in.pattern = fname;
174 unl.unlink.in.attrib = 0;
175 status = smb_raw_unlink(cli->tree, &unl);
176 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
178 CHECK_VAL(break_info.fnum, fnum);
179 CHECK_VAL(break_info.level, 1);
180 CHECK_VAL(break_info.count, 1);
181 CHECK_VAL(break_info.failures, 0);
183 smbcli_close(cli->tree, fnum);
185 printf("if we close on break then the unlink can succeed\n");
186 ZERO_STRUCT(break_info);
187 smbcli_oplock_handler(cli->transport, oplock_handler_close, cli->tree);
188 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
189 NTCREATEX_FLAGS_REQUEST_OPLOCK |
190 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
191 status = smb_raw_open(cli->tree, mem_ctx, &io);
192 CHECK_STATUS(status, NT_STATUS_OK);
193 fnum = io.ntcreatex.out.file.fnum;
194 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
196 unl.unlink.in.pattern = fname;
197 unl.unlink.in.attrib = 0;
198 ZERO_STRUCT(break_info);
199 status = smb_raw_unlink(cli->tree, &unl);
200 CHECK_STATUS(status, NT_STATUS_OK);
202 CHECK_VAL(break_info.fnum, fnum);
203 CHECK_VAL(break_info.level, 1);
204 CHECK_VAL(break_info.count, 1);
205 CHECK_VAL(break_info.failures, 0);
207 printf("a self read should not cause a break\n");
208 ZERO_STRUCT(break_info);
209 smbcli_close(cli->tree, fnum);
210 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
212 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
213 NTCREATEX_FLAGS_REQUEST_OPLOCK |
214 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
215 status = smb_raw_open(cli->tree, mem_ctx, &io);
216 CHECK_STATUS(status, NT_STATUS_OK);
217 fnum = io.ntcreatex.out.file.fnum;
218 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
220 rd.read.level = RAW_READ_READ;
221 rd.read.in.file.fnum = fnum;
222 rd.read.in.count = 1;
223 rd.read.in.offset = 0;
224 rd.read.in.remaining = 0;
225 status = smb_raw_read(cli->tree, &rd);
226 CHECK_STATUS(status, NT_STATUS_OK);
227 CHECK_VAL(break_info.count, 0);
228 CHECK_VAL(break_info.failures, 0);
230 printf("a 2nd open should give a break\n");
231 ZERO_STRUCT(break_info);
232 smbcli_close(cli->tree, fnum);
233 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
235 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
236 NTCREATEX_FLAGS_REQUEST_OPLOCK |
237 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
238 status = smb_raw_open(cli->tree, mem_ctx, &io);
239 CHECK_STATUS(status, NT_STATUS_OK);
240 fnum = io.ntcreatex.out.file.fnum;
241 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
243 ZERO_STRUCT(break_info);
245 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
246 status = smb_raw_open(cli->tree, mem_ctx, &io);
247 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
249 CHECK_VAL(break_info.count, 1);
250 CHECK_VAL(break_info.fnum, fnum);
251 CHECK_VAL(break_info.level, 1);
252 CHECK_VAL(break_info.failures, 0);
254 printf("a 2nd open should get an oplock when we close instead of ack\n");
255 ZERO_STRUCT(break_info);
256 smbcli_close(cli->tree, fnum);
257 smbcli_oplock_handler(cli->transport, oplock_handler_close, cli->tree);
259 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
260 NTCREATEX_FLAGS_REQUEST_OPLOCK |
261 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
262 status = smb_raw_open(cli->tree, mem_ctx, &io);
263 CHECK_STATUS(status, NT_STATUS_OK);
264 fnum2 = io.ntcreatex.out.file.fnum;
265 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
267 ZERO_STRUCT(break_info);
269 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
270 NTCREATEX_FLAGS_REQUEST_OPLOCK |
271 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
272 status = smb_raw_open(cli->tree, mem_ctx, &io);
273 CHECK_STATUS(status, NT_STATUS_OK);
274 fnum = io.ntcreatex.out.file.fnum;
275 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
277 CHECK_VAL(break_info.count, 1);
278 CHECK_VAL(break_info.fnum, fnum2);
279 CHECK_VAL(break_info.level, 1);
280 CHECK_VAL(break_info.failures, 0);
282 smbcli_close(cli->tree, fnum);
284 printf("open with batch oplock\n");
285 ZERO_STRUCT(break_info);
286 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
288 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
289 NTCREATEX_FLAGS_REQUEST_OPLOCK |
290 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
291 status = smb_raw_open(cli->tree, mem_ctx, &io);
292 CHECK_STATUS(status, NT_STATUS_OK);
293 fnum = io.ntcreatex.out.file.fnum;
294 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
296 ZERO_STRUCT(break_info);
297 printf("second open with attributes only shouldn't cause oplock break\n");
299 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
300 NTCREATEX_FLAGS_REQUEST_OPLOCK |
301 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
302 io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE;
303 status = smb_raw_open(cli->tree, mem_ctx, &io);
304 CHECK_STATUS(status, NT_STATUS_OK);
305 fnum2 = io.ntcreatex.out.file.fnum;
306 CHECK_VAL(io.ntcreatex.out.oplock_level, NO_OPLOCK_RETURN);
307 CHECK_VAL(break_info.count, 0);
308 CHECK_VAL(break_info.fnum, 0);
309 CHECK_VAL(break_info.level, 0);
310 CHECK_VAL(break_info.failures, 0);
312 smbcli_close(cli->tree, fnum);
313 smbcli_close(cli->tree, fnum2);
314 smbcli_unlink(cli->tree, fname);
316 printf("open with attributes only can create file\n");
317 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
318 NTCREATEX_FLAGS_REQUEST_OPLOCK |
319 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
320 io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE;
321 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
322 status = smb_raw_open(cli->tree, mem_ctx, &io);
323 CHECK_STATUS(status, NT_STATUS_OK);
324 fnum = io.ntcreatex.out.file.fnum;
325 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
327 printf("Subsequent normal open should break oplock on attribute only open to level II\n");
329 ZERO_STRUCT(break_info);
330 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
332 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
333 NTCREATEX_FLAGS_REQUEST_OPLOCK |
334 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
335 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
336 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
337 status = smb_raw_open(cli->tree, mem_ctx, &io);
338 CHECK_STATUS(status, NT_STATUS_OK);
339 fnum2 = io.ntcreatex.out.file.fnum;
340 CHECK_VAL(break_info.count, 1);
341 CHECK_VAL(break_info.fnum, fnum);
342 CHECK_VAL(break_info.failures, 0);
343 CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
344 smbcli_close(cli->tree, fnum2);
346 printf("third oplocked open should grant level2 without break\n");
347 ZERO_STRUCT(break_info);
348 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
349 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
350 NTCREATEX_FLAGS_REQUEST_OPLOCK |
351 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
352 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
353 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
354 status = smb_raw_open(cli->tree, mem_ctx, &io);
355 CHECK_STATUS(status, NT_STATUS_OK);
356 fnum2 = io.ntcreatex.out.file.fnum;
357 CHECK_VAL(break_info.count, 0);
358 CHECK_VAL(break_info.failures, 0);
359 CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
361 ZERO_STRUCT(break_info);
363 printf("write should trigger a break to none on both\n");
366 wr.write.level = RAW_WRITE_WRITE;
367 wr.write.in.file.fnum = fnum2;
368 wr.write.in.count = 1;
369 wr.write.in.offset = 0;
370 wr.write.in.remaining = 0;
371 wr.write.in.data = (const uint8_t *)"x";
372 status = smb_raw_write(cli->tree, &wr);
373 CHECK_STATUS(status, NT_STATUS_OK);
376 /* Now the oplock break request comes in. But right now we can't
377 * answer it. Do another write */
383 wr.write.level = RAW_WRITE_WRITE;
384 wr.write.in.file.fnum = fnum2;
385 wr.write.in.count = 1;
386 wr.write.in.offset = 0;
387 wr.write.in.remaining = 0;
388 wr.write.in.data = (const uint8_t *)"x";
389 status = smb_raw_write(cli->tree, &wr);
390 CHECK_STATUS(status, NT_STATUS_OK);
393 CHECK_VAL(break_info.count, 2);
394 CHECK_VAL(break_info.level, 0);
395 CHECK_VAL(break_info.failures, 0);
397 smbcli_close(cli->tree, fnum);
398 smbcli_close(cli->tree, fnum2);
400 ZERO_STRUCT(break_info);
401 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
403 printf("Open with oplock after a on-oplock open should grant level2\n");
404 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
405 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
406 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
407 NTCREATEX_SHARE_ACCESS_WRITE|
408 NTCREATEX_SHARE_ACCESS_DELETE;
409 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
410 status = smb_raw_open(cli->tree, mem_ctx, &io);
411 CHECK_STATUS(status, NT_STATUS_OK);
412 fnum = io.ntcreatex.out.file.fnum;
413 CHECK_VAL(break_info.count, 0);
414 CHECK_VAL(break_info.fnum, 0);
415 CHECK_VAL(break_info.failures, 0);
416 CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
418 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
419 NTCREATEX_FLAGS_REQUEST_OPLOCK |
420 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
421 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
422 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
423 NTCREATEX_SHARE_ACCESS_WRITE|
424 NTCREATEX_SHARE_ACCESS_DELETE;
425 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
426 status = smb_raw_open(cli->tree, mem_ctx, &io);
427 CHECK_STATUS(status, NT_STATUS_OK);
428 fnum2 = io.ntcreatex.out.file.fnum;
429 CHECK_VAL(break_info.count, 0);
430 CHECK_VAL(break_info.fnum, 0);
431 CHECK_VAL(break_info.failures, 0);
432 CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
434 printf("write should trigger a break to none\n");
437 wr.write.level = RAW_WRITE_WRITE;
438 wr.write.in.file.fnum = fnum;
439 wr.write.in.count = 1;
440 wr.write.in.offset = 0;
441 wr.write.in.remaining = 0;
442 wr.write.in.data = (const uint8_t *)"x";
443 status = smb_raw_write(cli->tree, &wr);
444 CHECK_STATUS(status, NT_STATUS_OK);
447 /* Now the oplock break request comes in. But right now we can't
448 * answer it. Do another write */
454 wr.write.level = RAW_WRITE_WRITE;
455 wr.write.in.file.fnum = fnum;
456 wr.write.in.count = 1;
457 wr.write.in.offset = 0;
458 wr.write.in.remaining = 0;
459 wr.write.in.data = (const uint8_t *)"x";
460 status = smb_raw_write(cli->tree, &wr);
461 CHECK_STATUS(status, NT_STATUS_OK);
464 CHECK_VAL(break_info.count, 1);
465 CHECK_VAL(break_info.fnum, fnum2);
466 CHECK_VAL(break_info.level, 0);
467 CHECK_VAL(break_info.failures, 0);
470 smbcli_close(cli->tree, fnum);
471 smbcli_close(cli->tree, fnum2);
472 smbcli_unlink(cli->tree, fname);
478 basic testing of oplocks
480 BOOL torture_raw_oplock(struct torture_context *torture)
482 struct smbcli_state *cli;
486 if (!torture_open_connection(&cli)) {
490 if (!torture_setup_dir(cli, BASEDIR)) {
494 mem_ctx = talloc_init("torture_raw_oplock");
496 if (!test_oplock(cli, mem_ctx)) {
500 smb_raw_exit(cli->session);
501 smbcli_deltree(cli->tree, BASEDIR);
502 torture_close_connection(cli);
503 talloc_free(mem_ctx);
509 stress testing of oplocks
511 BOOL torture_bench_oplock(struct torture_context *torture)
513 struct smbcli_state **cli;
515 TALLOC_CTX *mem_ctx = talloc_new(torture);
516 extern int torture_nprocs;
518 int timelimit = lp_parm_int(-1, "torture", "timelimit", 10);
521 struct event_context *ev = event_context_find(mem_ctx);
523 cli = talloc_array(mem_ctx, struct smbcli_state *, torture_nprocs);
525 printf("Opening %d connections\n", torture_nprocs);
526 for (i=0;i<torture_nprocs;i++) {
527 if (!torture_open_connection_ev(&cli[i], ev)) {
530 talloc_steal(mem_ctx, cli[i]);
531 smbcli_oplock_handler(cli[i]->transport, oplock_handler_close,
535 if (!torture_setup_dir(cli[0], BASEDIR)) {
540 io.ntcreatex.level = RAW_OPEN_NTCREATEX;
541 io.ntcreatex.in.root_fid = 0;
542 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
543 io.ntcreatex.in.alloc_size = 0;
544 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
545 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
546 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
547 io.ntcreatex.in.create_options = 0;
548 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
549 io.ntcreatex.in.security_flags = 0;
550 io.ntcreatex.in.fname = BASEDIR "\\test.dat";
551 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
552 NTCREATEX_FLAGS_REQUEST_OPLOCK |
553 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
555 tv = timeval_current();
557 printf("Running for %d seconds\n", timelimit);
558 while (timeval_elapsed(&tv) < timelimit) {
559 for (i=0;i<torture_nprocs;i++) {
562 status = smb_raw_open(cli[i]->tree, mem_ctx, &io);
563 CHECK_STATUS(status, NT_STATUS_OK);
566 printf("%.2f ops/second\r", count/timeval_elapsed(&tv));
569 printf("%.2f ops/second\n", count/timeval_elapsed(&tv));
571 smb_raw_exit(cli[torture_nprocs-1]->session);
574 smb_raw_exit(cli[0]->session);
575 smbcli_deltree(cli[0]->tree, BASEDIR);
576 talloc_free(mem_ctx);