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"
25 #include "libcli/libcli.h"
27 #define CHECK_VAL(v, correct) do { \
28 if ((v) != (correct)) { \
29 printf("(%d) wrong value for %s got 0x%x - should be 0x%x\n", \
30 __LINE__, #v, (int)v, (int)correct); \
34 #define CHECK_STATUS(status, correct) do { \
35 if (!NT_STATUS_EQUAL(status, correct)) { \
36 printf("(%d) Incorrect status %s - should be %s\n", \
37 __LINE__, nt_errstr(status), nt_errstr(correct)); \
51 a handler function for oplock break requests
53 static BOOL oplock_handler_ack(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private)
55 struct smbcli_tree *tree = private;
56 break_info.fnum = fnum;
57 break_info.level = level;
60 printf("Acking in oplock handler\n");
62 return smbcli_oplock_ack(tree, fnum, level);
65 static void oplock_handler_close_recv(struct smbcli_request *req)
68 status = smbcli_request_simple_recv(req);
69 if (!NT_STATUS_IS_OK(status)) {
70 printf("close failed in oplock_handler_close\n");
71 break_info.failures++;
76 a handler function for oplock break requests - close the file
78 static BOOL oplock_handler_close(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private)
81 struct smbcli_tree *tree = private;
82 struct smbcli_request *req;
84 break_info.fnum = fnum;
85 break_info.level = level;
88 printf("Closing in oplock handler\n");
90 io.close.level = RAW_CLOSE_CLOSE;
91 io.close.in.fnum = fnum;
92 io.close.in.write_time = 0;
93 req = smb_raw_close_send(tree, &io);
95 printf("failed to send close in oplock_handler_close\n");
99 req->async.fn = oplock_handler_close_recv;
100 req->async.private = NULL;
108 static BOOL test_oplock(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
110 const char *fname = "\\test_oplock.dat";
114 struct smb_unlink unl;
116 uint16_t fnum=0, fnum2=0;
119 smbcli_unlink(cli->tree, fname);
121 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
126 io.generic.level = RAW_OPEN_NTCREATEX;
127 io.ntcreatex.in.root_fid = 0;
128 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
129 io.ntcreatex.in.alloc_size = 0;
130 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
131 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
132 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
133 io.ntcreatex.in.create_options = 0;
134 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
135 io.ntcreatex.in.security_flags = 0;
136 io.ntcreatex.in.fname = fname;
138 printf("open a file with a normal oplock\n");
139 ZERO_STRUCT(break_info);
140 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK;
142 status = smb_raw_open(cli->tree, mem_ctx, &io);
143 CHECK_STATUS(status, NT_STATUS_OK);
144 fnum = io.ntcreatex.out.fnum;
145 CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
147 printf("unlink it - should be no break\n");
148 unl.in.pattern = fname;
150 status = smb_raw_unlink(cli->tree, &unl);
151 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
152 CHECK_VAL(break_info.count, 0);
153 CHECK_VAL(break_info.failures, 0);
155 smbcli_close(cli->tree, fnum);
158 with a batch oplock we get a break
160 printf("open with batch oplock\n");
161 ZERO_STRUCT(break_info);
162 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
163 NTCREATEX_FLAGS_REQUEST_OPLOCK |
164 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
165 status = smb_raw_open(cli->tree, mem_ctx, &io);
166 CHECK_STATUS(status, NT_STATUS_OK);
167 fnum = io.ntcreatex.out.fnum;
168 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
170 printf("unlink should generate a break\n");
171 unl.in.pattern = fname;
173 status = smb_raw_unlink(cli->tree, &unl);
174 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
176 CHECK_VAL(break_info.fnum, fnum);
177 CHECK_VAL(break_info.level, 1);
178 CHECK_VAL(break_info.count, 1);
179 CHECK_VAL(break_info.failures, 0);
181 smbcli_close(cli->tree, fnum);
183 printf("if we close on break then the unlink can succeed\n");
184 ZERO_STRUCT(break_info);
185 smbcli_oplock_handler(cli->transport, oplock_handler_close, cli->tree);
186 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
187 NTCREATEX_FLAGS_REQUEST_OPLOCK |
188 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
189 status = smb_raw_open(cli->tree, mem_ctx, &io);
190 CHECK_STATUS(status, NT_STATUS_OK);
191 fnum = io.ntcreatex.out.fnum;
192 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
194 unl.in.pattern = fname;
196 ZERO_STRUCT(break_info);
197 status = smb_raw_unlink(cli->tree, &unl);
198 CHECK_STATUS(status, NT_STATUS_OK);
200 CHECK_VAL(break_info.fnum, fnum);
201 CHECK_VAL(break_info.level, 1);
202 CHECK_VAL(break_info.count, 1);
203 CHECK_VAL(break_info.failures, 0);
205 printf("a self read should not cause a break\n");
206 ZERO_STRUCT(break_info);
207 smbcli_close(cli->tree, fnum);
208 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
210 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
211 NTCREATEX_FLAGS_REQUEST_OPLOCK |
212 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
213 status = smb_raw_open(cli->tree, mem_ctx, &io);
214 CHECK_STATUS(status, NT_STATUS_OK);
215 fnum = io.ntcreatex.out.fnum;
216 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
218 rd.read.level = RAW_READ_READ;
219 rd.read.in.fnum = fnum;
220 rd.read.in.count = 1;
221 rd.read.in.offset = 0;
222 rd.read.in.remaining = 0;
223 status = smb_raw_read(cli->tree, &rd);
224 CHECK_STATUS(status, NT_STATUS_OK);
225 CHECK_VAL(break_info.count, 0);
226 CHECK_VAL(break_info.failures, 0);
228 printf("a 2nd open should give a break\n");
229 ZERO_STRUCT(break_info);
230 smbcli_close(cli->tree, fnum);
231 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
233 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
234 NTCREATEX_FLAGS_REQUEST_OPLOCK |
235 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
236 status = smb_raw_open(cli->tree, mem_ctx, &io);
237 CHECK_STATUS(status, NT_STATUS_OK);
238 fnum = io.ntcreatex.out.fnum;
239 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
241 ZERO_STRUCT(break_info);
243 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
244 status = smb_raw_open(cli->tree, mem_ctx, &io);
245 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
247 CHECK_VAL(break_info.count, 1);
248 CHECK_VAL(break_info.fnum, fnum);
249 CHECK_VAL(break_info.level, 1);
250 CHECK_VAL(break_info.failures, 0);
252 printf("a 2nd open should get an oplock when we close instead of ack\n");
253 ZERO_STRUCT(break_info);
254 smbcli_close(cli->tree, fnum);
255 smbcli_oplock_handler(cli->transport, oplock_handler_close, cli->tree);
257 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
258 NTCREATEX_FLAGS_REQUEST_OPLOCK |
259 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
260 status = smb_raw_open(cli->tree, mem_ctx, &io);
261 CHECK_STATUS(status, NT_STATUS_OK);
262 fnum2 = io.ntcreatex.out.fnum;
263 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
265 ZERO_STRUCT(break_info);
267 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
268 NTCREATEX_FLAGS_REQUEST_OPLOCK |
269 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
270 status = smb_raw_open(cli->tree, mem_ctx, &io);
271 CHECK_STATUS(status, NT_STATUS_OK);
272 fnum = io.ntcreatex.out.fnum;
273 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
275 CHECK_VAL(break_info.count, 1);
276 CHECK_VAL(break_info.fnum, fnum2);
277 CHECK_VAL(break_info.level, 1);
278 CHECK_VAL(break_info.failures, 0);
280 smbcli_close(cli->tree, fnum);
282 printf("open with batch oplock\n");
283 ZERO_STRUCT(break_info);
284 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
286 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
287 NTCREATEX_FLAGS_REQUEST_OPLOCK |
288 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
289 status = smb_raw_open(cli->tree, mem_ctx, &io);
290 CHECK_STATUS(status, NT_STATUS_OK);
291 fnum = io.ntcreatex.out.fnum;
292 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
294 ZERO_STRUCT(break_info);
295 printf("second open with attributes only shouldn't cause oplock break\n");
297 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
298 NTCREATEX_FLAGS_REQUEST_OPLOCK |
299 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
300 io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE;
301 status = smb_raw_open(cli->tree, mem_ctx, &io);
302 CHECK_STATUS(status, NT_STATUS_OK);
303 fnum2 = io.ntcreatex.out.fnum;
304 CHECK_VAL(io.ntcreatex.out.oplock_level, NO_OPLOCK_RETURN);
305 CHECK_VAL(break_info.count, 0);
306 CHECK_VAL(break_info.fnum, 0);
307 CHECK_VAL(break_info.level, 0);
308 CHECK_VAL(break_info.failures, 0);
310 smbcli_close(cli->tree, fnum);
311 smbcli_close(cli->tree, fnum2);
312 smbcli_unlink(cli->tree, fname);
314 printf("open with attributes only can create file\n");
315 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
316 NTCREATEX_FLAGS_REQUEST_OPLOCK |
317 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
318 io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE;
319 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
320 status = smb_raw_open(cli->tree, mem_ctx, &io);
321 CHECK_STATUS(status, NT_STATUS_OK);
322 fnum = io.ntcreatex.out.fnum;
323 CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
325 printf("Subsequent normal open should break oplock on attribute only open to level II\n");
327 ZERO_STRUCT(break_info);
328 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
330 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
331 NTCREATEX_FLAGS_REQUEST_OPLOCK |
332 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
333 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
334 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
335 status = smb_raw_open(cli->tree, mem_ctx, &io);
336 CHECK_STATUS(status, NT_STATUS_OK);
337 fnum2 = io.ntcreatex.out.fnum;
338 CHECK_VAL(break_info.count, 1);
339 CHECK_VAL(break_info.fnum, fnum);
340 CHECK_VAL(break_info.failures, 0);
341 CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
342 smbcli_close(cli->tree, fnum2);
344 printf("third oplocked open should grant level2 without break\n");
345 ZERO_STRUCT(break_info);
346 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
347 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
348 NTCREATEX_FLAGS_REQUEST_OPLOCK |
349 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
350 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
351 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
352 status = smb_raw_open(cli->tree, mem_ctx, &io);
353 CHECK_STATUS(status, NT_STATUS_OK);
354 fnum2 = io.ntcreatex.out.fnum;
355 CHECK_VAL(break_info.count, 0);
356 CHECK_VAL(break_info.failures, 0);
357 CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
359 ZERO_STRUCT(break_info);
361 printf("write should trigger a break to none on both\n");
364 wr.write.level = RAW_WRITE_WRITE;
365 wr.write.in.fnum = fnum2;
366 wr.write.in.count = 1;
367 wr.write.in.offset = 0;
368 wr.write.in.remaining = 0;
369 wr.write.in.data = (const uint8_t *)"x";
370 status = smb_raw_write(cli->tree, &wr);
371 CHECK_STATUS(status, NT_STATUS_OK);
374 /* Now the oplock break request comes in. But right now we can't
375 * answer it. Do another write */
381 wr.write.level = RAW_WRITE_WRITE;
382 wr.write.in.fnum = fnum2;
383 wr.write.in.count = 1;
384 wr.write.in.offset = 0;
385 wr.write.in.remaining = 0;
386 wr.write.in.data = (const uint8_t *)"x";
387 status = smb_raw_write(cli->tree, &wr);
388 CHECK_STATUS(status, NT_STATUS_OK);
391 CHECK_VAL(break_info.count, 2);
392 CHECK_VAL(break_info.level, 0);
393 CHECK_VAL(break_info.failures, 0);
395 smbcli_close(cli->tree, fnum);
396 smbcli_close(cli->tree, fnum2);
398 ZERO_STRUCT(break_info);
399 smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
401 printf("Open with oplock after a on-oplock open should grant level2\n");
402 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
403 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
404 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
405 NTCREATEX_SHARE_ACCESS_WRITE|
406 NTCREATEX_SHARE_ACCESS_DELETE;
407 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
408 status = smb_raw_open(cli->tree, mem_ctx, &io);
409 CHECK_STATUS(status, NT_STATUS_OK);
410 fnum = io.ntcreatex.out.fnum;
411 CHECK_VAL(break_info.count, 0);
412 CHECK_VAL(break_info.fnum, 0);
413 CHECK_VAL(break_info.failures, 0);
414 CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
416 io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
417 NTCREATEX_FLAGS_REQUEST_OPLOCK |
418 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
419 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
420 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
421 NTCREATEX_SHARE_ACCESS_WRITE|
422 NTCREATEX_SHARE_ACCESS_DELETE;
423 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
424 status = smb_raw_open(cli->tree, mem_ctx, &io);
425 CHECK_STATUS(status, NT_STATUS_OK);
426 fnum2 = io.ntcreatex.out.fnum;
427 CHECK_VAL(break_info.count, 0);
428 CHECK_VAL(break_info.fnum, 0);
429 CHECK_VAL(break_info.failures, 0);
430 CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
432 printf("write should trigger a break to none\n");
435 wr.write.level = RAW_WRITE_WRITE;
436 wr.write.in.fnum = fnum;
437 wr.write.in.count = 1;
438 wr.write.in.offset = 0;
439 wr.write.in.remaining = 0;
440 wr.write.in.data = (const uint8_t *)"x";
441 status = smb_raw_write(cli->tree, &wr);
442 CHECK_STATUS(status, NT_STATUS_OK);
445 /* Now the oplock break request comes in. But right now we can't
446 * answer it. Do another write */
452 wr.write.level = RAW_WRITE_WRITE;
453 wr.write.in.fnum = fnum;
454 wr.write.in.count = 1;
455 wr.write.in.offset = 0;
456 wr.write.in.remaining = 0;
457 wr.write.in.data = (const uint8_t *)"x";
458 status = smb_raw_write(cli->tree, &wr);
459 CHECK_STATUS(status, NT_STATUS_OK);
462 CHECK_VAL(break_info.count, 1);
463 CHECK_VAL(break_info.fnum, fnum2);
464 CHECK_VAL(break_info.level, 0);
465 CHECK_VAL(break_info.failures, 0);
468 smbcli_close(cli->tree, fnum);
469 smbcli_close(cli->tree, fnum2);
470 smbcli_unlink(cli->tree, fname);
476 basic testing of oplocks
478 BOOL torture_raw_oplock(void)
480 struct smbcli_state *cli1;
484 if (!torture_open_connection(&cli1)) {
488 mem_ctx = talloc_init("torture_raw_oplock");
490 if (!test_oplock(cli1, mem_ctx)) {
494 torture_close_connection(cli1);
495 talloc_free(mem_ctx);