r3539: much nicer async open delay code.
[abartlet/samba.git/.git] / source4 / torture / raw / mux.c
1 /* 
2    Unix SMB/CIFS implementation.
3    basic raw test suite for multiplexing
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 "libcli/raw/libcliraw.h"
23
24 #define BASEDIR "\\test_mux"
25
26 #define CHECK_STATUS(status, correct) do { \
27         if (!NT_STATUS_EQUAL(status, correct)) { \
28                 printf("(%d) Incorrect status %s - should be %s\n", \
29                        __LINE__, nt_errstr(status), nt_errstr(correct)); \
30                 ret = False; \
31                 goto done; \
32         }} while (0)
33
34
35 /*
36   test the delayed reply to a open that leads to a sharing violation
37 */
38 static BOOL test_mux_open(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
39 {
40         union smb_open io;
41         NTSTATUS status;
42         int fnum1, fnum2;
43         BOOL ret = True;
44         struct smbcli_request *req;
45         struct timeval tv;
46         double d;
47
48         printf("testing multiplexed open/open/close\n");
49
50         /*
51           file open with no share access
52         */
53         io.generic.level = RAW_OPEN_NTCREATEX;
54         io.ntcreatex.in.root_fid = 0;
55         io.ntcreatex.in.flags = 0;
56         io.ntcreatex.in.access_mask = SA_RIGHT_FILE_READ_DATA;
57         io.ntcreatex.in.create_options = 0;
58         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
59         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ;
60         io.ntcreatex.in.alloc_size = 0;
61         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
62         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
63         io.ntcreatex.in.security_flags = 0;
64         io.ntcreatex.in.fname = BASEDIR "\\open.dat";
65         status = smb_raw_open(cli->tree, mem_ctx, &io);
66         CHECK_STATUS(status, NT_STATUS_OK);
67         fnum1 = io.ntcreatex.out.fnum;
68
69         /* and a 2nd open, this will not conflict */
70         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
71         status = smb_raw_open(cli->tree, mem_ctx, &io);
72         CHECK_STATUS(status, NT_STATUS_OK);
73         fnum2 = io.ntcreatex.out.fnum;
74
75         tv = timeval_current();
76
77         /* send an open that will conflict */
78         io.ntcreatex.in.share_access = 0;
79         status = smb_raw_open(cli->tree, mem_ctx, &io);
80         CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
81
82         d = timeval_elapsed(&tv);
83         if (d < 0.5 || d > 1.5) {
84                 printf("bad timeout for conflict - %.2f should be 1.0\n", d);
85                 ret = False;
86         } else {
87                 printf("open delay %.2f\n", d);
88         }
89
90         /*
91           same request, but async
92         */
93         tv = timeval_current();
94         req = smb_raw_open_send(cli->tree, &io);
95         
96         /* and close the first file */
97         smbcli_close(cli->tree, fnum1);
98
99         /* then the 2nd file */
100         smbcli_close(cli->tree, fnum2);
101
102         /* see if the async open succeeded */
103         status = smb_raw_open_recv(req, mem_ctx, &io);
104         CHECK_STATUS(status, NT_STATUS_OK);
105
106         d = timeval_elapsed(&tv);
107         if (d > 0.25) {
108                 printf("bad timeout for async conflict - %.2f should be <0.25\n", d);
109                 ret = False;
110         } else {
111                 printf("async open delay %.2f\n", d);
112         }
113
114         smbcli_close(cli->tree, io.ntcreatex.out.fnum);
115
116 done:
117         return ret;
118 }
119
120
121 /*
122   test a write that hits a byte range lock and send the close after the write
123 */
124 static BOOL test_mux_write(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
125 {
126         union smb_write io;
127         NTSTATUS status;
128         int fnum;
129         BOOL ret = True;
130         struct smbcli_request *req;
131
132         printf("testing multiplexed lock/write/close\n");
133
134         fnum = smbcli_open(cli->tree, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE);
135         if (fnum == -1) {
136                 printf("open failed in mux_write - %s\n", smbcli_errstr(cli->tree));
137                 ret = False;
138                 goto done;
139         }
140
141         cli->session->pid = 1;
142
143         /* lock a range */
144         if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 0, 4, 0, WRITE_LOCK))) {
145                 printf("lock failed in mux_write - %s\n", smbcli_errstr(cli->tree));
146                 ret = False;
147                 goto done;
148         }
149
150         cli->session->pid = 2;
151
152         /* send an async write */
153         io.generic.level = RAW_WRITE_WRITEX;
154         io.writex.in.fnum = fnum;
155         io.writex.in.offset = 0;
156         io.writex.in.wmode = 0;
157         io.writex.in.remaining = 0;
158         io.writex.in.count = 4;
159         io.writex.in.data = (void *)&fnum;      
160         req = smb_raw_write_send(cli->tree, &io);
161
162         /* unlock the range */
163         cli->session->pid = 1;
164         smbcli_unlock(cli->tree, fnum, 0, 4);
165
166         /* and recv the async write reply */
167         status = smb_raw_write_recv(req, &io);
168         CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
169
170         smbcli_close(cli->tree, fnum);
171
172 done:
173         return ret;
174 }
175
176
177 /*
178   test a lock that conflicts with an existing lock
179 */
180 static BOOL test_mux_lock(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
181 {
182         union smb_lock io;
183         NTSTATUS status;
184         int fnum;
185         BOOL ret = True;
186         struct smbcli_request *req;
187         struct smb_lock_entry lock[1];
188
189         printf("TESTING MULTIPLEXED LOCK/LOCK/UNLOCK\n");
190
191         fnum = smbcli_open(cli->tree, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE);
192         if (fnum == -1) {
193                 printf("open failed in mux_write - %s\n", smbcli_errstr(cli->tree));
194                 ret = False;
195                 goto done;
196         }
197
198         printf("establishing a lock\n");
199         io.lockx.level = RAW_LOCK_LOCKX;
200         io.lockx.in.fnum = fnum;
201         io.lockx.in.mode = 0;
202         io.lockx.in.timeout = 0;
203         io.lockx.in.lock_cnt = 1;
204         io.lockx.in.ulock_cnt = 0;
205         lock[0].pid = 1;
206         lock[0].offset = 0;
207         lock[0].count = 4;
208         io.lockx.in.locks = &lock[0];
209
210         status = smb_raw_lock(cli->tree, &io);
211         CHECK_STATUS(status, NT_STATUS_OK);
212
213         printf("the second lock will conflict with the first\n");
214         lock[0].pid = 2;
215         io.lockx.in.timeout = 1000;
216         status = smb_raw_lock(cli->tree, &io);
217         CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
218
219         printf("this will too, but we'll unlock while waiting\n");
220         req = smb_raw_lock_send(cli->tree, &io);
221
222         printf("unlock the first range\n");
223         lock[0].pid = 1;
224         io.lockx.in.ulock_cnt = 1;
225         io.lockx.in.lock_cnt = 0;
226         io.lockx.in.timeout = 0;
227         status = smb_raw_lock(cli->tree, &io);
228         CHECK_STATUS(status, NT_STATUS_OK);
229
230         printf("recv the async reply\n");
231         status = smbcli_request_simple_recv(req);
232         CHECK_STATUS(status, NT_STATUS_OK);     
233
234         printf("reopening with an exit\n");
235         smb_raw_exit(cli->session);
236         fnum = smbcli_open(cli->tree, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE);
237
238         printf("Now trying with a cancel\n");
239
240         io.lockx.level = RAW_LOCK_LOCKX;
241         io.lockx.in.fnum = fnum;
242         io.lockx.in.mode = 0;
243         io.lockx.in.timeout = 0;
244         io.lockx.in.lock_cnt = 1;
245         io.lockx.in.ulock_cnt = 0;
246         lock[0].pid = 1;
247         lock[0].offset = 0;
248         lock[0].count = 4;
249         io.lockx.in.locks = &lock[0];
250
251         status = smb_raw_lock(cli->tree, &io);
252         CHECK_STATUS(status, NT_STATUS_OK);
253
254         lock[0].pid = 2;
255         io.lockx.in.timeout = 1000;
256         status = smb_raw_lock(cli->tree, &io);
257         CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
258
259         req = smb_raw_lock_send(cli->tree, &io);
260
261         /* cancel the blocking lock */
262         smb_raw_ntcancel(req);
263
264         /* the 2nd cancel is totally harmless, but tests the server trying to 
265            cancel an already cancelled request */
266         smb_raw_ntcancel(req);
267
268         lock[0].pid = 1;
269         io.lockx.in.ulock_cnt = 1;
270         io.lockx.in.lock_cnt = 0;
271         io.lockx.in.timeout = 0;
272         status = smb_raw_lock(cli->tree, &io);
273         CHECK_STATUS(status, NT_STATUS_OK);
274
275         status = smbcli_request_simple_recv(req);
276         CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);     
277
278         smbcli_close(cli->tree, fnum);
279
280 done:
281         return ret;
282 }
283
284
285
286 /* 
287    basic testing of multiplexing notify
288 */
289 BOOL torture_raw_mux(void)
290 {
291         struct smbcli_state *cli;
292         BOOL ret = True;
293         TALLOC_CTX *mem_ctx;
294                 
295         if (!torture_open_connection(&cli)) {
296                 return False;
297         }
298
299         mem_ctx = talloc_init("torture_raw_mux");
300
301         /* cleanup */
302         if (smbcli_deltree(cli->tree, BASEDIR) == -1) {
303                 printf("Failed to cleanup " BASEDIR "\n");
304                 ret = False;
305                 goto done;
306         }
307
308
309         if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, BASEDIR))) {
310                 printf("Failed to create %s\n", BASEDIR);
311                 ret = False;
312                 goto done;
313         }
314
315         if (!test_mux_open(cli, mem_ctx)) {
316                 ret = False;
317         }
318
319         if (!test_mux_write(cli, mem_ctx)) {
320                 ret = False;
321         }
322
323         if (!test_mux_lock(cli, mem_ctx)) {
324                 ret = False;
325         }
326
327 done:
328         smb_raw_exit(cli->session);
329         smbcli_deltree(cli->tree, BASEDIR);
330         torture_close_connection(cli);
331         talloc_destroy(mem_ctx);
332         return ret;
333 }