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/ndr_security.h"
24 #include "libcli/raw/libcliraw.h"
26 #define CHECK_VAL(v, correct) do { \
27 if ((v) != (correct)) { \
28 printf("(%d) wrong value for %s got 0x%x - should be 0x%x\n", \
29 __LINE__, #v, (int)v, (int)correct); \
33 #define CHECK_STATUS(status, correct) do { \
34 if (!NT_STATUS_EQUAL(status, correct)) { \
35 printf("(%d) Incorrect status %s - should be %s\n", \
36 __LINE__, nt_errstr(status), nt_errstr(correct)); \
50 a handler function for oplock break requests
52 static BOOL oplock_handler_ack(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private)
54 struct smbcli_tree *tree = private;
55 break_info.fnum = fnum;
56 break_info.level = level;
59 printf("Acking in oplock handler\n");
61 return smbcli_oplock_ack(tree, fnum, level);
64 static void oplock_handler_close_recv(struct smbcli_request *req)
67 status = smbcli_request_simple_recv(req);
68 if (!NT_STATUS_IS_OK(status)) {
69 printf("close failed in oplock_handler_close\n");
70 break_info.failures++;
75 a handler function for oplock break requests - close the file
77 static BOOL oplock_handler_close(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private)
80 struct smbcli_tree *tree = private;
81 struct smbcli_request *req;
83 break_info.fnum = fnum;
84 break_info.level = level;
87 printf("Closing in oplock handler\n");
89 io.close.level = RAW_CLOSE_CLOSE;
90 io.close.in.fnum = fnum;
91 io.close.in.write_time = 0;
92 req = smb_raw_close_send(tree, &io);
94 printf("failed to send close in oplock_handler_close\n");
98 req->async.fn = oplock_handler_close_recv;
99 req->async.private = NULL;
107 static BOOL test_oplock(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
109 const char *fname = "\\test_oplock.dat";
113 struct smb_unlink unl;
115 uint16_t fnum=0, fnum2=0;
118 smbcli_unlink(cli->tree, fname);
120 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
125 io.generic.level = RAW_OPEN_NTCREATEX;
126 io.ntcreatex.in.root_fid = 0;
127 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
128 io.ntcreatex.in.alloc_size = 0;
129 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
130 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
131 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
132 io.ntcreatex.in.create_options = 0;
133 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
134 io.ntcreatex.in.security_flags = 0;
135 io.ntcreatex.in.fname = fname;
137 printf("open a file with a normal oplock\n");
138 ZERO_STRUCT(break_info);
139 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK;
141 status = smb_raw_open(cli->tree, mem_ctx, &io);
142 CHECK_STATUS(status, NT_STATUS_OK);
143 fnum = io.ntcreatex.out.fnum;
144 CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
146 printf("unlink it - should be no break\n");
147 unl.in.pattern = fname;
149 status = smb_raw_unlink(cli->tree, &unl);
150 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
151 CHECK_VAL(break_info.count, 0);
152 CHECK_VAL(break_info.failures, 0);
154 smbcli_close(cli->tree, fnum);
157 with a batch oplock we get a break
159 printf("open with batch oplock\n");
160 ZERO_STRUCT(break_info);
161 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
162 NTCREATEX_FLAGS_REQUEST_OPLOCK |
163 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
164 status = smb_raw_open(cli->tree, mem_ctx, &io);
165 CHECK_STATUS(status, NT_STATUS_OK);
166 fnum = io.ntcreatex.out.fnum;
167 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
169 printf("unlink should generate a break\n");
170 unl.in.pattern = fname;
172 status = smb_raw_unlink(cli->tree, &unl);
173 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
175 CHECK_VAL(break_info.fnum, fnum);
176 CHECK_VAL(break_info.level, 1);
177 CHECK_VAL(break_info.count, 1);
178 CHECK_VAL(break_info.failures, 0);
180 smbcli_close(cli->tree, fnum);
182 printf("if we close on break then the unlink can succeed\n");
183 ZERO_STRUCT(break_info);
184 smbcli_oplock_handler(cli->transport, oplock_handler_close, cli->tree);
185 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
186 NTCREATEX_FLAGS_REQUEST_OPLOCK |
187 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
188 status = smb_raw_open(cli->tree, mem_ctx, &io);
189 CHECK_STATUS(status, NT_STATUS_OK);
190 fnum = io.ntcreatex.out.fnum;
191 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
193 unl.in.pattern = fname;
195 ZERO_STRUCT(break_info);
196 status = smb_raw_unlink(cli->tree, &unl);
197 CHECK_STATUS(status, NT_STATUS_OK);
199 CHECK_VAL(break_info.fnum, fnum);
200 CHECK_VAL(break_info.level, 1);
201 CHECK_VAL(break_info.count, 1);
202 CHECK_VAL(break_info.failures, 0);
204 printf("a self read should not cause a break\n");
205 ZERO_STRUCT(break_info);
206 smbcli_close(cli->tree, fnum);
207 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
209 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
210 NTCREATEX_FLAGS_REQUEST_OPLOCK |
211 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
212 status = smb_raw_open(cli->tree, mem_ctx, &io);
213 CHECK_STATUS(status, NT_STATUS_OK);
214 fnum = io.ntcreatex.out.fnum;
215 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
217 rd.read.level = RAW_READ_READ;
218 rd.read.in.fnum = fnum;
219 rd.read.in.count = 1;
220 rd.read.in.offset = 0;
221 rd.read.in.remaining = 0;
222 status = smb_raw_read(cli->tree, &rd);
223 CHECK_STATUS(status, NT_STATUS_OK);
224 CHECK_VAL(break_info.count, 0);
225 CHECK_VAL(break_info.failures, 0);
227 printf("a 2nd open should give a break\n");
228 ZERO_STRUCT(break_info);
229 smbcli_close(cli->tree, fnum);
230 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
232 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
233 NTCREATEX_FLAGS_REQUEST_OPLOCK |
234 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
235 status = smb_raw_open(cli->tree, mem_ctx, &io);
236 CHECK_STATUS(status, NT_STATUS_OK);
237 fnum = io.ntcreatex.out.fnum;
238 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
240 ZERO_STRUCT(break_info);
242 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
243 status = smb_raw_open(cli->tree, mem_ctx, &io);
244 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
246 CHECK_VAL(break_info.count, 1);
247 CHECK_VAL(break_info.fnum, fnum);
248 CHECK_VAL(break_info.level, 1);
249 CHECK_VAL(break_info.failures, 0);
251 printf("a 2nd open should get an oplock when we close instead of ack\n");
252 ZERO_STRUCT(break_info);
253 smbcli_close(cli->tree, fnum);
254 smbcli_oplock_handler(cli->transport, oplock_handler_close, cli->tree);
256 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
257 NTCREATEX_FLAGS_REQUEST_OPLOCK |
258 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
259 status = smb_raw_open(cli->tree, mem_ctx, &io);
260 CHECK_STATUS(status, NT_STATUS_OK);
261 fnum2 = io.ntcreatex.out.fnum;
262 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
264 ZERO_STRUCT(break_info);
266 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
267 NTCREATEX_FLAGS_REQUEST_OPLOCK |
268 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
269 status = smb_raw_open(cli->tree, mem_ctx, &io);
270 CHECK_STATUS(status, NT_STATUS_OK);
271 fnum = io.ntcreatex.out.fnum;
272 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
274 CHECK_VAL(break_info.count, 1);
275 CHECK_VAL(break_info.fnum, fnum2);
276 CHECK_VAL(break_info.level, 1);
277 CHECK_VAL(break_info.failures, 0);
279 smbcli_close(cli->tree, fnum);
281 printf("open with batch oplock\n");
282 ZERO_STRUCT(break_info);
283 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
285 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
286 NTCREATEX_FLAGS_REQUEST_OPLOCK |
287 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
288 status = smb_raw_open(cli->tree, mem_ctx, &io);
289 CHECK_STATUS(status, NT_STATUS_OK);
290 fnum = io.ntcreatex.out.fnum;
291 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
293 ZERO_STRUCT(break_info);
294 printf("second open with attributes only shouldn't cause oplock break\n");
296 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
297 NTCREATEX_FLAGS_REQUEST_OPLOCK |
298 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
299 io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE;
300 status = smb_raw_open(cli->tree, mem_ctx, &io);
301 CHECK_STATUS(status, NT_STATUS_OK);
302 fnum2 = io.ntcreatex.out.fnum;
303 CHECK_VAL(io.ntcreatex.out.oplock_level, NO_OPLOCK_RETURN);
304 CHECK_VAL(break_info.count, 0);
305 CHECK_VAL(break_info.fnum, 0);
306 CHECK_VAL(break_info.level, 0);
307 CHECK_VAL(break_info.failures, 0);
309 smbcli_close(cli->tree, fnum);
310 smbcli_close(cli->tree, fnum2);
311 smbcli_unlink(cli->tree, fname);
313 printf("open with attributes only can create file\n");
314 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
315 NTCREATEX_FLAGS_REQUEST_OPLOCK |
316 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
317 io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE;
318 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
319 status = smb_raw_open(cli->tree, mem_ctx, &io);
320 CHECK_STATUS(status, NT_STATUS_OK);
321 fnum = io.ntcreatex.out.fnum;
322 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
324 printf("Subsequent normal open should break oplock on attribute only open to level II\n");
326 ZERO_STRUCT(break_info);
327 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
329 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
330 NTCREATEX_FLAGS_REQUEST_OPLOCK |
331 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
332 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
333 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
334 status = smb_raw_open(cli->tree, mem_ctx, &io);
335 CHECK_STATUS(status, NT_STATUS_OK);
336 fnum2 = io.ntcreatex.out.fnum;
337 CHECK_VAL(break_info.count, 1);
338 CHECK_VAL(break_info.fnum, fnum);
339 CHECK_VAL(break_info.failures, 0);
340 CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
341 smbcli_close(cli->tree, fnum2);
343 printf("third oplocked open should grant level2 without break\n");
344 ZERO_STRUCT(break_info);
345 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
346 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
347 NTCREATEX_FLAGS_REQUEST_OPLOCK |
348 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
349 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
350 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
351 status = smb_raw_open(cli->tree, mem_ctx, &io);
352 CHECK_STATUS(status, NT_STATUS_OK);
353 fnum2 = io.ntcreatex.out.fnum;
354 CHECK_VAL(break_info.count, 0);
355 CHECK_VAL(break_info.failures, 0);
356 CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
358 ZERO_STRUCT(break_info);
360 printf("write should trigger a break to none on both\n");
363 wr.write.level = RAW_WRITE_WRITE;
364 wr.write.in.fnum = fnum2;
365 wr.write.in.count = 1;
366 wr.write.in.offset = 0;
367 wr.write.in.remaining = 0;
368 wr.write.in.data = "x";
369 status = smb_raw_write(cli->tree, &wr);
370 CHECK_STATUS(status, NT_STATUS_OK);
373 /* Now the oplock break request comes in. But right now we can't
374 * answer it. Do another write */
380 wr.write.level = RAW_WRITE_WRITE;
381 wr.write.in.fnum = fnum2;
382 wr.write.in.count = 1;
383 wr.write.in.offset = 0;
384 wr.write.in.remaining = 0;
385 wr.write.in.data = "x";
386 status = smb_raw_write(cli->tree, &wr);
387 CHECK_STATUS(status, NT_STATUS_OK);
390 CHECK_VAL(break_info.count, 2);
391 CHECK_VAL(break_info.level, 0);
392 CHECK_VAL(break_info.failures, 0);
394 smbcli_close(cli->tree, fnum);
395 smbcli_close(cli->tree, fnum2);
397 ZERO_STRUCT(break_info);
398 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
400 printf("Open with oplock after a on-oplock open should grant level2\n");
401 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
402 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
403 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
404 NTCREATEX_SHARE_ACCESS_WRITE|
405 NTCREATEX_SHARE_ACCESS_DELETE;
406 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
407 status = smb_raw_open(cli->tree, mem_ctx, &io);
408 CHECK_STATUS(status, NT_STATUS_OK);
409 fnum = io.ntcreatex.out.fnum;
410 CHECK_VAL(break_info.count, 0);
411 CHECK_VAL(break_info.fnum, 0);
412 CHECK_VAL(break_info.failures, 0);
413 CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
415 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
416 NTCREATEX_FLAGS_REQUEST_OPLOCK |
417 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
418 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
419 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
420 NTCREATEX_SHARE_ACCESS_WRITE|
421 NTCREATEX_SHARE_ACCESS_DELETE;
422 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
423 status = smb_raw_open(cli->tree, mem_ctx, &io);
424 CHECK_STATUS(status, NT_STATUS_OK);
425 fnum2 = io.ntcreatex.out.fnum;
426 CHECK_VAL(break_info.count, 0);
427 CHECK_VAL(break_info.fnum, 0);
428 CHECK_VAL(break_info.failures, 0);
429 CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
431 printf("write should trigger a break to none\n");
434 wr.write.level = RAW_WRITE_WRITE;
435 wr.write.in.fnum = fnum;
436 wr.write.in.count = 1;
437 wr.write.in.offset = 0;
438 wr.write.in.remaining = 0;
439 wr.write.in.data = "x";
440 status = smb_raw_write(cli->tree, &wr);
441 CHECK_STATUS(status, NT_STATUS_OK);
444 /* Now the oplock break request comes in. But right now we can't
445 * answer it. Do another write */
451 wr.write.level = RAW_WRITE_WRITE;
452 wr.write.in.fnum = fnum;
453 wr.write.in.count = 1;
454 wr.write.in.offset = 0;
455 wr.write.in.remaining = 0;
456 wr.write.in.data = "x";
457 status = smb_raw_write(cli->tree, &wr);
458 CHECK_STATUS(status, NT_STATUS_OK);
461 CHECK_VAL(break_info.count, 1);
462 CHECK_VAL(break_info.fnum, fnum2);
463 CHECK_VAL(break_info.level, 0);
464 CHECK_VAL(break_info.failures, 0);
467 smbcli_close(cli->tree, fnum);
468 smbcli_close(cli->tree, fnum2);
469 smbcli_unlink(cli->tree, fname);
475 basic testing of oplocks
477 BOOL torture_raw_oplock(void)
479 struct smbcli_state *cli1;
483 if (!torture_open_connection(&cli1)) {
487 mem_ctx = talloc_init("torture_raw_oplock");
489 if (!test_oplock(cli1, mem_ctx)) {
493 torture_close_connection(cli1);
494 talloc_free(mem_ctx);