6fa4c514b393c30769ed1aa8501ef4e557127384
[jelmer/samba4-debian.git] / source / 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 #include "torture/torture.h"
23 #include "librpc/gen_ndr/ndr_security.h"
24 #include "libcli/raw/libcliraw.h"
25 #include "libcli/libcli.h"
26
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); \
31                 ret = False; \
32         }} while (0)
33
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)); \
38                 ret = False; \
39                 goto done; \
40         }} while (0)
41
42
43 static struct {
44         int fnum;
45         uint8_t level;
46         int count;
47         int failures;
48 } break_info;
49
50 /*
51   a handler function for oplock break requests
52 */
53 static BOOL oplock_handler_ack(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private)
54 {
55         struct smbcli_tree *tree = private;
56         break_info.fnum = fnum;
57         break_info.level = level;
58         break_info.count++;
59
60         printf("Acking in oplock handler\n");
61
62         return smbcli_oplock_ack(tree, fnum, level);
63 }
64
65 static void oplock_handler_close_recv(struct smbcli_request *req)
66 {
67         NTSTATUS status;
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++;
72         }
73 }
74
75 /*
76   a handler function for oplock break requests - close the file
77 */
78 static BOOL oplock_handler_close(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private)
79 {
80         union smb_close io;
81         struct smbcli_tree *tree = private;
82         struct smbcli_request *req;
83
84         break_info.fnum = fnum;
85         break_info.level = level;
86         break_info.count++;
87
88         printf("Closing in oplock handler\n");
89
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);
94         if (req == NULL) {
95                 printf("failed to send close in oplock_handler_close\n");
96                 return False;
97         }
98
99         req->async.fn = oplock_handler_close_recv;
100         req->async.private = NULL;
101
102         return True;
103 }
104
105 /*
106   test oplock ops
107 */
108 static BOOL test_oplock(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
109 {
110         const char *fname = "\\test_oplock.dat";
111         NTSTATUS status;
112         BOOL ret = True;
113         union smb_open io;
114         struct smb_unlink unl;
115         union smb_read rd;
116         uint16_t fnum=0, fnum2=0;
117
118         /* cleanup */
119         smbcli_unlink(cli->tree, fname);
120
121         smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
122
123         /*
124           base ntcreatex parms
125         */
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;
137
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;
141
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);
146
147         printf("unlink it - should be no break\n");
148         unl.in.pattern = fname;
149         unl.in.attrib = 0;
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);
154
155         smbcli_close(cli->tree, fnum);
156
157         /*
158           with a batch oplock we get a break
159         */
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);
169
170         printf("unlink should generate a break\n");
171         unl.in.pattern = fname;
172         unl.in.attrib = 0;
173         status = smb_raw_unlink(cli->tree, &unl);
174         CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
175
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);
180
181         smbcli_close(cli->tree, fnum);
182
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);
193
194         unl.in.pattern = fname;
195         unl.in.attrib = 0;
196         ZERO_STRUCT(break_info);
197         status = smb_raw_unlink(cli->tree, &unl);
198         CHECK_STATUS(status, NT_STATUS_OK);
199
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);
204
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);
209
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);
217
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);
227
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);
232
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);
240
241         ZERO_STRUCT(break_info);
242
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);
246
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);
251
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);
256
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);
264
265         ZERO_STRUCT(break_info);
266
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);
274
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);
279         
280         smbcli_close(cli->tree, fnum);
281
282         printf("open with batch oplock\n");
283         ZERO_STRUCT(break_info);
284         smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
285
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);
293
294         ZERO_STRUCT(break_info);
295         printf("second open with attributes only shouldn't cause oplock break\n");
296
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);
309
310         smbcli_close(cli->tree, fnum);
311         smbcli_close(cli->tree, fnum2);
312         smbcli_unlink(cli->tree, fname);
313
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);
324
325         printf("Subsequent normal open should break oplock on attribute only open to level II\n");
326
327         ZERO_STRUCT(break_info);
328         smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
329
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);
343
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);
358
359         ZERO_STRUCT(break_info);
360
361         printf("write should trigger a break to none on both\n");
362         {
363                 union smb_write wr;
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);
372         }
373
374         /* Now the oplock break request comes in. But right now we can't
375          * answer it. Do another write */
376
377         msleep(100);
378         
379         {
380                 union smb_write wr;
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);
389         }
390
391         CHECK_VAL(break_info.count, 2);
392         CHECK_VAL(break_info.level, 0);
393         CHECK_VAL(break_info.failures, 0);
394
395         smbcli_close(cli->tree, fnum);
396         smbcli_close(cli->tree, fnum2);
397
398         ZERO_STRUCT(break_info);
399         smbcli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
400
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);
415
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);
431
432         printf("write should trigger a break to none\n");
433         {
434                 union smb_write wr;
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);
443         }
444
445         /* Now the oplock break request comes in. But right now we can't
446          * answer it. Do another write */
447
448         msleep(100);
449         
450         {
451                 union smb_write wr;
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);
460         }
461
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);
466
467 done:
468         smbcli_close(cli->tree, fnum);
469         smbcli_close(cli->tree, fnum2);
470         smbcli_unlink(cli->tree, fname);
471         return ret;
472 }
473
474
475 /* 
476    basic testing of oplocks
477 */
478 BOOL torture_raw_oplock(void)
479 {
480         struct smbcli_state *cli1;
481         BOOL ret = True;
482         TALLOC_CTX *mem_ctx;
483
484         if (!torture_open_connection(&cli1)) {
485                 return False;
486         }
487
488         mem_ctx = talloc_init("torture_raw_oplock");
489
490         if (!test_oplock(cli1, mem_ctx)) {
491                 ret = False;
492         }
493
494         torture_close_connection(cli1);
495         talloc_free(mem_ctx);
496         return ret;
497 }