r14527: Fix build problems.
[sfrench/samba-autobuild/.git] / source4 / torture / raw / notify.c
1 /* 
2    Unix SMB/CIFS implementation.
3    basic raw test suite for change notify
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 "libcli/raw/libcliraw.h"
24 #include "libcli/libcli.h"
25 #include "system/filesys.h"
26 #include "torture/util.h"
27
28 #define BASEDIR "\\test_notify"
29
30 #define CHECK_STATUS(status, correct) do { \
31         if (!NT_STATUS_EQUAL(status, correct)) { \
32                 printf("(%d) Incorrect status %s - should be %s\n", \
33                        __LINE__, nt_errstr(status), nt_errstr(correct)); \
34                 ret = False; \
35                 goto done; \
36         }} while (0)
37
38
39 #define CHECK_VAL(v, correct) do { \
40         if ((v) != (correct)) { \
41                 printf("(%d) wrong value for %s  0x%x should be 0x%x\n", \
42                        __LINE__, #v, (int)v, (int)correct); \
43                 ret = False; \
44                 goto done; \
45         }} while (0)
46
47 #define CHECK_WSTR(field, value, flags) do { \
48         if (!field.s || strcmp(field.s, value) || wire_bad_flags(&field, flags, cli)) { \
49                 printf("(%d) %s [%s] != %s\n",  __LINE__, #field, field.s, value); \
50                         ret = False; \
51                 goto done; \
52         }} while (0)
53
54
55 /* 
56    basic testing of change notify on directories
57 */
58 static BOOL test_notify_dir(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
59 {
60         BOOL ret = True;
61         NTSTATUS status;
62         union smb_notify notify;
63         union smb_open io;
64         int i, count, fnum, fnum2;
65         struct smbcli_request *req, *req2;
66         extern int torture_numops;
67
68         printf("TESTING CHANGE NOTIFY ON DIRECTRIES\n");
69                 
70         /*
71           get a handle on the directory
72         */
73         io.generic.level = RAW_OPEN_NTCREATEX;
74         io.ntcreatex.in.root_fid = 0;
75         io.ntcreatex.in.flags = 0;
76         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
77         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
78         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
79         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
80         io.ntcreatex.in.alloc_size = 0;
81         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
82         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
83         io.ntcreatex.in.security_flags = 0;
84         io.ntcreatex.in.fname = BASEDIR;
85
86         status = smb_raw_open(cli->tree, mem_ctx, &io);
87         CHECK_STATUS(status, NT_STATUS_OK);
88         fnum = io.ntcreatex.out.file.fnum;
89
90         status = smb_raw_open(cli->tree, mem_ctx, &io);
91         CHECK_STATUS(status, NT_STATUS_OK);
92         fnum2 = io.ntcreatex.out.file.fnum;
93
94         /* ask for a change notify,
95            on file or directory name changes */
96         notify.notify.in.buffer_size = 1000;
97         notify.notify.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
98         notify.notify.in.file.fnum = fnum;
99         notify.notify.in.recursive = True;
100
101         printf("testing notify cancel\n");
102
103         req = smb_raw_changenotify_send(cli->tree, &notify);
104         smb_raw_ntcancel(req);
105         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
106         CHECK_STATUS(status, NT_STATUS_CANCELLED);
107
108         printf("testing notify mkdir\n");
109
110         req = smb_raw_changenotify_send(cli->tree, &notify);
111         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
112
113         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
114         CHECK_STATUS(status, NT_STATUS_OK);
115
116         CHECK_VAL(notify.notify.out.num_changes, 1);
117         CHECK_VAL(notify.notify.out.changes[0].action, NOTIFY_ACTION_ADDED);
118         CHECK_WSTR(notify.notify.out.changes[0].name, "subdir-name", STR_UNICODE);
119
120         printf("testing notify rmdir\n");
121
122         req = smb_raw_changenotify_send(cli->tree, &notify);
123         smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
124
125         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
126         CHECK_STATUS(status, NT_STATUS_OK);
127         CHECK_VAL(notify.notify.out.num_changes, 1);
128         CHECK_VAL(notify.notify.out.changes[0].action, NOTIFY_ACTION_REMOVED);
129         CHECK_WSTR(notify.notify.out.changes[0].name, "subdir-name", STR_UNICODE);
130
131         printf("testing notify mkdir - rmdir - mkdir - rmdir\n");
132
133         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
134         smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
135         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
136         smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
137         req = smb_raw_changenotify_send(cli->tree, &notify);
138         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
139         CHECK_STATUS(status, NT_STATUS_OK);
140         CHECK_VAL(notify.notify.out.num_changes, 4);
141         CHECK_VAL(notify.notify.out.changes[0].action, NOTIFY_ACTION_ADDED);
142         CHECK_WSTR(notify.notify.out.changes[0].name, "subdir-name", STR_UNICODE);
143         CHECK_VAL(notify.notify.out.changes[1].action, NOTIFY_ACTION_REMOVED);
144         CHECK_WSTR(notify.notify.out.changes[1].name, "subdir-name", STR_UNICODE);
145         CHECK_VAL(notify.notify.out.changes[2].action, NOTIFY_ACTION_ADDED);
146         CHECK_WSTR(notify.notify.out.changes[2].name, "subdir-name", STR_UNICODE);
147         CHECK_VAL(notify.notify.out.changes[3].action, NOTIFY_ACTION_REMOVED);
148         CHECK_WSTR(notify.notify.out.changes[3].name, "subdir-name", STR_UNICODE);
149
150         count = torture_numops;
151         printf("testing buffered notify on create of %d files\n", count);
152         for (i=0;i<count;i++) {
153                 char *fname = talloc_asprintf(cli, BASEDIR "\\test%d.txt", i);
154                 int fnum3 = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
155                 if (fnum3 == -1) {
156                         printf("Failed to create %s - %s\n", 
157                                fname, smbcli_errstr(cli->tree));
158                         ret = False;
159                         goto done;
160                 }
161                 talloc_free(fname);
162                 smbcli_close(cli->tree, fnum3);
163         }
164
165         /* (1st notify) setup a new notify on a different directory handle.
166            This new notify won't see the events above. */
167         notify.notify.in.file.fnum = fnum2;
168         req2 = smb_raw_changenotify_send(cli->tree, &notify);
169
170         /* (2nd notify) whereas this notify will see the above buffered events,
171            and it directly returns the buffered events */
172         notify.notify.in.file.fnum = fnum;
173         req = smb_raw_changenotify_send(cli->tree, &notify);
174
175         /* (1st unlink) as the 2nd notify directly returns,
176            this unlink is only seen by the 1st notify and 
177            the 3rd notify (later) */
178         printf("testing notify on unlink for the first file\n");
179         status = smbcli_unlink(cli->tree, BASEDIR "\\test0.txt");
180         CHECK_STATUS(status, NT_STATUS_OK);
181
182         /* receive the reply from the 2nd notify */
183         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
184         CHECK_STATUS(status, NT_STATUS_OK);
185
186         CHECK_VAL(notify.notify.out.num_changes, count);
187         for (i=1;i<notify.notify.out.num_changes;i++) {
188                 CHECK_VAL(notify.notify.out.changes[i].action, NOTIFY_ACTION_ADDED);
189         }
190         CHECK_WSTR(notify.notify.out.changes[0].name, "test0.txt", STR_UNICODE);
191
192         /* and now from the 1st notify */
193         status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
194         CHECK_STATUS(status, NT_STATUS_OK);
195         CHECK_VAL(notify.notify.out.num_changes, 1);
196         CHECK_VAL(notify.notify.out.changes[0].action, NOTIFY_ACTION_REMOVED);
197         CHECK_WSTR(notify.notify.out.changes[0].name, "test0.txt", STR_UNICODE);
198
199         /* (3rd notify) this notify will only see the 1st unlink */
200         req = smb_raw_changenotify_send(cli->tree, &notify);
201
202         printf("testing notify on wildcard unlink for %d files\n", count-1);
203         /* (2nd unlink) do a wildcard unlink */
204         status = smbcli_unlink(cli->tree, BASEDIR "\\test*.txt");
205         CHECK_STATUS(status, NT_STATUS_OK);
206
207         /* recev the 3rd notify */
208         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
209         CHECK_STATUS(status, NT_STATUS_OK);
210         CHECK_VAL(notify.notify.out.num_changes, 1);
211         CHECK_VAL(notify.notify.out.changes[0].action, NOTIFY_ACTION_REMOVED);
212         CHECK_WSTR(notify.notify.out.changes[0].name, "test0.txt", STR_UNICODE);
213
214         /* and we now see the rest of the unlink calls on both directory handles */
215         notify.notify.in.file.fnum = fnum;
216         req = smb_raw_changenotify_send(cli->tree, &notify);
217         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
218         CHECK_STATUS(status, NT_STATUS_OK);
219         CHECK_VAL(notify.notify.out.num_changes, count-1);
220         for (i=0;i<notify.notify.out.num_changes;i++) {
221                 CHECK_VAL(notify.notify.out.changes[i].action, NOTIFY_ACTION_REMOVED);
222         }
223         notify.notify.in.file.fnum = fnum2;
224         req = smb_raw_changenotify_send(cli->tree, &notify);
225         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
226         CHECK_STATUS(status, NT_STATUS_OK);
227         CHECK_VAL(notify.notify.out.num_changes, count-1);
228         for (i=0;i<notify.notify.out.num_changes;i++) {
229                 CHECK_VAL(notify.notify.out.changes[i].action, NOTIFY_ACTION_REMOVED);
230         }
231
232 done:
233         smb_raw_exit(cli->session);
234         return ret;
235 }
236
237 /*
238   basic testing of change notify on files
239 */
240 static BOOL test_notify_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
241 {
242         NTSTATUS status;
243         BOOL ret = True;
244         union smb_open io;
245         union smb_close cl;
246         union smb_notify notify;
247         struct smbcli_request *req;
248         int fnum;
249         const char *fname = BASEDIR "\\file.txt";
250
251         printf("TESTING CHANGE NOTIFY ON FILES\n");
252
253         io.generic.level = RAW_OPEN_NTCREATEX;
254         io.ntcreatex.in.root_fid = 0;
255         io.ntcreatex.in.flags = 0;
256         io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
257         io.ntcreatex.in.create_options = 0;
258         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
259         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
260         io.ntcreatex.in.alloc_size = 0;
261         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
262         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
263         io.ntcreatex.in.security_flags = 0;
264         io.ntcreatex.in.fname = fname;
265         status = smb_raw_open(cli->tree, mem_ctx, &io);
266         CHECK_STATUS(status, NT_STATUS_OK);
267         fnum = io.ntcreatex.out.file.fnum;
268
269         /* ask for a change notify,
270            on file or directory name changes */
271         notify.notify.in.file.fnum = fnum;
272         notify.notify.in.buffer_size = 1000;
273         notify.notify.in.completion_filter = FILE_NOTIFY_CHANGE_STREAM_NAME;
274         notify.notify.in.recursive = False;
275
276         printf("testing if notifies on file handles are invalid (should be)\n");
277
278         req = smb_raw_changenotify_send(cli->tree, &notify);
279         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
280         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
281
282         cl.close.level = RAW_CLOSE_CLOSE;
283         cl.close.in.file.fnum = fnum;
284         cl.close.in.write_time = 0;
285         status = smb_raw_close(cli->tree, &cl);
286         CHECK_STATUS(status, NT_STATUS_OK);
287
288         status = smbcli_unlink(cli->tree, fname);
289         CHECK_STATUS(status, NT_STATUS_OK);
290
291 done:
292         smb_raw_exit(cli->session);
293         return ret;
294 }
295
296 /* 
297    basic testing of change notify
298 */
299 BOOL torture_raw_notify(void)
300 {
301         struct smbcli_state *cli;
302         BOOL ret = True;
303         TALLOC_CTX *mem_ctx;
304                 
305         if (!torture_open_connection(&cli)) {
306                 return False;
307         }
308
309         mem_ctx = talloc_init("torture_raw_notify");
310
311         if (!torture_setup_dir(cli, BASEDIR)) {
312                 return False;
313         }
314
315         ret &= test_notify_dir(cli, mem_ctx);
316         ret &= test_notify_file(cli, mem_ctx);
317
318         smb_raw_exit(cli->session);
319         smbcli_deltree(cli->tree, BASEDIR);
320         torture_close_connection(cli);
321         talloc_free(mem_ctx);
322         return ret;
323 }