f11c46ea491f38852b8dcd492eb66fd3a7e9ba49
[vlendec/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 3 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, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "libcli/raw/libcliraw.h"
22 #include "libcli/raw/raw_proto.h"
23 #include "libcli/libcli.h"
24 #include "system/filesys.h"
25 #include "torture/util.h"
26 #include "torture/raw/proto.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->transport)) { \
49                 printf("(%d) %s [%s] != %s\n",  __LINE__, #field, field.s, value); \
50                         ret = false; \
51                 goto done; \
52         }} while (0)
53
54 #define CHECK_WSTR2(tctx, field, value, flags) \
55 do { \
56         if (!field.s || strcmp(field.s, value) || \
57             wire_bad_flags(&field, flags, cli->transport)) { \
58                 torture_result(tctx, TORTURE_FAIL, \
59                     "(%d) %s [%s] != %s\n",  __LINE__, #field, field.s, value); \
60         } \
61 } while (0)
62
63 /* 
64    basic testing of change notify on directories
65 */
66 static bool test_notify_dir(struct torture_context *mem_ctx,
67                             struct smbcli_state *cli,
68                             struct smbcli_state *cli2)
69 {
70         bool ret = true;
71         NTSTATUS status;
72         union smb_notify notify;
73         union smb_open io;
74         union smb_close cl;
75         int i, count, fnum, fnum2;
76         struct smbcli_request *req, *req2;
77         extern int torture_numops;
78
79         printf("TESTING CHANGE NOTIFY ON DIRECTORIES\n");
80                 
81         if (!torture_setup_dir(cli, BASEDIR)) {
82                 return false;
83         }
84
85         /*
86           get a handle on the directory
87         */
88         io.generic.level = RAW_OPEN_NTCREATEX;
89         io.ntcreatex.in.root_fid.fnum = 0;
90         io.ntcreatex.in.flags = 0;
91         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
92         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
93         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
94         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
95         io.ntcreatex.in.alloc_size = 0;
96         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
97         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
98         io.ntcreatex.in.security_flags = 0;
99         io.ntcreatex.in.fname = BASEDIR;
100
101         status = smb_raw_open(cli->tree, mem_ctx, &io);
102         CHECK_STATUS(status, NT_STATUS_OK);
103         fnum = io.ntcreatex.out.file.fnum;
104
105         status = smb_raw_open(cli->tree, mem_ctx, &io);
106         CHECK_STATUS(status, NT_STATUS_OK);
107         fnum2 = io.ntcreatex.out.file.fnum;
108
109         /* ask for a change notify,
110            on file or directory name changes */
111         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
112         notify.nttrans.in.buffer_size = 1000;
113         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
114         notify.nttrans.in.file.fnum = fnum;
115         notify.nttrans.in.recursive = true;
116
117         printf("Testing notify cancel\n");
118
119         req = smb_raw_changenotify_send(cli->tree, &notify);
120         smb_raw_ntcancel(req);
121         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
122         CHECK_STATUS(status, NT_STATUS_CANCELLED);
123
124         printf("Testing notify mkdir\n");
125
126         req = smb_raw_changenotify_send(cli->tree, &notify);
127         smbcli_mkdir(cli2->tree, BASEDIR "\\subdir-name");
128
129         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
130         CHECK_STATUS(status, NT_STATUS_OK);
131
132         CHECK_VAL(notify.nttrans.out.num_changes, 1);
133         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_ADDED);
134         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
135
136         printf("Testing notify rmdir\n");
137
138         req = smb_raw_changenotify_send(cli->tree, &notify);
139         smbcli_rmdir(cli2->tree, BASEDIR "\\subdir-name");
140
141         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
142         CHECK_STATUS(status, NT_STATUS_OK);
143         CHECK_VAL(notify.nttrans.out.num_changes, 1);
144         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
145         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
146
147         printf("Testing notify mkdir - rmdir - mkdir - rmdir\n");
148
149         smbcli_mkdir(cli2->tree, BASEDIR "\\subdir-name");
150         smbcli_rmdir(cli2->tree, BASEDIR "\\subdir-name");
151         smbcli_mkdir(cli2->tree, BASEDIR "\\subdir-name");
152         smbcli_rmdir(cli2->tree, BASEDIR "\\subdir-name");
153         smb_msleep(200);
154         req = smb_raw_changenotify_send(cli->tree, &notify);
155         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
156         CHECK_STATUS(status, NT_STATUS_OK);
157         CHECK_VAL(notify.nttrans.out.num_changes, 4);
158         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_ADDED);
159         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
160         CHECK_VAL(notify.nttrans.out.changes[1].action, NOTIFY_ACTION_REMOVED);
161         CHECK_WSTR(notify.nttrans.out.changes[1].name, "subdir-name", STR_UNICODE);
162         CHECK_VAL(notify.nttrans.out.changes[2].action, NOTIFY_ACTION_ADDED);
163         CHECK_WSTR(notify.nttrans.out.changes[2].name, "subdir-name", STR_UNICODE);
164         CHECK_VAL(notify.nttrans.out.changes[3].action, NOTIFY_ACTION_REMOVED);
165         CHECK_WSTR(notify.nttrans.out.changes[3].name, "subdir-name", STR_UNICODE);
166
167         count = torture_numops;
168         printf("Testing buffered notify on create of %d files\n", count);
169         for (i=0;i<count;i++) {
170                 char *fname = talloc_asprintf(cli, BASEDIR "\\test%d.txt", i);
171                 int fnum3 = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
172                 if (fnum3 == -1) {
173                         printf("Failed to create %s - %s\n", 
174                                fname, smbcli_errstr(cli->tree));
175                         ret = false;
176                         goto done;
177                 }
178                 talloc_free(fname);
179                 smbcli_close(cli->tree, fnum3);
180         }
181
182         /* (1st notify) setup a new notify on a different directory handle.
183            This new notify won't see the events above. */
184         notify.nttrans.in.file.fnum = fnum2;
185         req2 = smb_raw_changenotify_send(cli->tree, &notify);
186
187         /* (2nd notify) whereas this notify will see the above buffered events,
188            and it directly returns the buffered events */
189         notify.nttrans.in.file.fnum = fnum;
190         req = smb_raw_changenotify_send(cli->tree, &notify);
191
192         status = smbcli_unlink(cli->tree, BASEDIR "\\nonexistant.txt");
193         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
194
195         /* (1st unlink) as the 2nd notify directly returns,
196            this unlink is only seen by the 1st notify and 
197            the 3rd notify (later) */
198         printf("Testing notify on unlink for the first file\n");
199         status = smbcli_unlink(cli2->tree, BASEDIR "\\test0.txt");
200         CHECK_STATUS(status, NT_STATUS_OK);
201
202         /* receive the reply from the 2nd notify */
203         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
204         CHECK_STATUS(status, NT_STATUS_OK);
205
206         CHECK_VAL(notify.nttrans.out.num_changes, count);
207         for (i=1;i<count;i++) {
208                 CHECK_VAL(notify.nttrans.out.changes[i].action, NOTIFY_ACTION_ADDED);
209         }
210         CHECK_WSTR(notify.nttrans.out.changes[0].name, "test0.txt", STR_UNICODE);
211
212         printf("and now from the 1st notify\n");
213         status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
214         CHECK_STATUS(status, NT_STATUS_OK);
215         CHECK_VAL(notify.nttrans.out.num_changes, 1);
216         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
217         CHECK_WSTR(notify.nttrans.out.changes[0].name, "test0.txt", STR_UNICODE);
218
219         printf("(3rd notify) this notify will only see the 1st unlink\n");
220         req = smb_raw_changenotify_send(cli->tree, &notify);
221
222         status = smbcli_unlink(cli->tree, BASEDIR "\\nonexistant.txt");
223         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
224
225         printf("Testing notify on wildcard unlink for %d files\n", count-1);
226         /* (2nd unlink) do a wildcard unlink */
227         status = smbcli_unlink(cli2->tree, BASEDIR "\\test*.txt");
228         CHECK_STATUS(status, NT_STATUS_OK);
229
230         /* receive the 3rd notify */
231         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
232         CHECK_STATUS(status, NT_STATUS_OK);
233         CHECK_VAL(notify.nttrans.out.num_changes, 1);
234         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
235         CHECK_WSTR(notify.nttrans.out.changes[0].name, "test0.txt", STR_UNICODE);
236
237         /* and we now see the rest of the unlink calls on both directory handles */
238         notify.nttrans.in.file.fnum = fnum;
239         sleep(3);
240         req = smb_raw_changenotify_send(cli->tree, &notify);
241         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
242         CHECK_STATUS(status, NT_STATUS_OK);
243         CHECK_VAL(notify.nttrans.out.num_changes, count-1);
244         for (i=0;i<notify.nttrans.out.num_changes;i++) {
245                 CHECK_VAL(notify.nttrans.out.changes[i].action, NOTIFY_ACTION_REMOVED);
246         }
247         notify.nttrans.in.file.fnum = fnum2;
248         req = smb_raw_changenotify_send(cli->tree, &notify);
249         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
250         CHECK_STATUS(status, NT_STATUS_OK);
251         CHECK_VAL(notify.nttrans.out.num_changes, count-1);
252         for (i=0;i<notify.nttrans.out.num_changes;i++) {
253                 CHECK_VAL(notify.nttrans.out.changes[i].action, NOTIFY_ACTION_REMOVED);
254         }
255
256         printf("Testing if a close() on the dir handle triggers the notify reply\n");
257
258         notify.nttrans.in.file.fnum = fnum;
259         req = smb_raw_changenotify_send(cli->tree, &notify);
260
261         cl.close.level = RAW_CLOSE_CLOSE;
262         cl.close.in.file.fnum = fnum;
263         cl.close.in.write_time = 0;
264         status = smb_raw_close(cli->tree, &cl);
265         CHECK_STATUS(status, NT_STATUS_OK);
266
267         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
268         CHECK_STATUS(status, NT_STATUS_OK);
269         CHECK_VAL(notify.nttrans.out.num_changes, 0);
270
271 done:
272         smb_raw_exit(cli->session);
273         smbcli_deltree(cli->tree, BASEDIR);
274         return ret;
275 }
276
277 /*
278  * Check notify reply for a rename action. Not sure if this is a valid thing
279  * to do, but depending on timing between inotify and messaging we get the
280  * add/remove/modify in any order. This routines tries to find the action/name
281  * pair in any of the three following notify_changes.
282  */
283
284 static bool check_rename_reply(struct smbcli_state *cli,
285                                int line,
286                                struct notify_changes *actions,
287                                uint32_t action, const char *name)
288 {
289         int i;
290
291         for (i=0; i<3; i++) {
292                 if (actions[i].action == action) {
293                         if ((actions[i].name.s == NULL)
294                             || (strcmp(actions[i].name.s, name) != 0)
295                             || (wire_bad_flags(&actions[i].name, STR_UNICODE,
296                                                cli->transport))) {
297                                 printf("(%d) name [%s] != %s\n", line,
298                                        actions[i].name.s, name);
299                                 return false;
300                         }
301                         return true;
302                 }
303         }
304
305         printf("(%d) expected action %d, not found\n", line, action);
306         return false;
307 }
308
309 /* 
310    testing of recursive change notify
311 */
312 static bool test_notify_recursive(struct torture_context *mem_ctx,
313                                   struct smbcli_state *cli)
314 {
315         bool ret = true;
316         NTSTATUS status;
317         union smb_notify notify;
318         union smb_open io;
319         int fnum;
320         struct smbcli_request *req1, *req2;
321
322         printf("TESTING CHANGE NOTIFY WITH RECURSION\n");
323                 
324         if (!torture_setup_dir(cli, BASEDIR)) {
325                 return false;
326         }
327
328         /*
329           get a handle on the directory
330         */
331         io.generic.level = RAW_OPEN_NTCREATEX;
332         io.ntcreatex.in.root_fid.fnum = 0;
333         io.ntcreatex.in.flags = 0;
334         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
335         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
336         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
337         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
338         io.ntcreatex.in.alloc_size = 0;
339         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
340         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
341         io.ntcreatex.in.security_flags = 0;
342         io.ntcreatex.in.fname = BASEDIR;
343
344         status = smb_raw_open(cli->tree, mem_ctx, &io);
345         CHECK_STATUS(status, NT_STATUS_OK);
346         fnum = io.ntcreatex.out.file.fnum;
347
348         /* ask for a change notify, on file or directory name
349            changes. Setup both with and without recursion */
350         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
351         notify.nttrans.in.buffer_size = 1000;
352         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_CREATION;
353         notify.nttrans.in.file.fnum = fnum;
354
355         notify.nttrans.in.recursive = true;
356         req1 = smb_raw_changenotify_send(cli->tree, &notify);
357
358         notify.nttrans.in.recursive = false;
359         req2 = smb_raw_changenotify_send(cli->tree, &notify);
360
361         /* cancel initial requests so the buffer is setup */
362         smb_raw_ntcancel(req1);
363         status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
364         CHECK_STATUS(status, NT_STATUS_CANCELLED);
365
366         smb_raw_ntcancel(req2);
367         status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
368         CHECK_STATUS(status, NT_STATUS_CANCELLED);
369
370         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
371         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name\\subname1");
372         smbcli_close(cli->tree, 
373                      smbcli_open(cli->tree, BASEDIR "\\subdir-name\\subname2", O_CREAT, 0));
374         smbcli_rename(cli->tree, BASEDIR "\\subdir-name\\subname1", BASEDIR "\\subdir-name\\subname1-r");
375         smbcli_rename(cli->tree, BASEDIR "\\subdir-name\\subname2", BASEDIR "\\subname2-r");
376         smbcli_rename(cli->tree, BASEDIR "\\subname2-r", BASEDIR "\\subname3-r");
377
378         notify.nttrans.in.completion_filter = 0;
379         notify.nttrans.in.recursive = true;
380         smb_msleep(200);
381         req1 = smb_raw_changenotify_send(cli->tree, &notify);
382
383         smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name\\subname1-r");
384         smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
385         smbcli_unlink(cli->tree, BASEDIR "\\subname3-r");
386
387         notify.nttrans.in.recursive = false;
388         req2 = smb_raw_changenotify_send(cli->tree, &notify);
389
390         status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
391         CHECK_STATUS(status, NT_STATUS_OK);
392
393         CHECK_VAL(notify.nttrans.out.num_changes, 11);
394         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_ADDED);
395         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
396         CHECK_VAL(notify.nttrans.out.changes[1].action, NOTIFY_ACTION_ADDED);
397         CHECK_WSTR(notify.nttrans.out.changes[1].name, "subdir-name\\subname1", STR_UNICODE);
398         CHECK_VAL(notify.nttrans.out.changes[2].action, NOTIFY_ACTION_ADDED);
399         CHECK_WSTR(notify.nttrans.out.changes[2].name, "subdir-name\\subname2", STR_UNICODE);
400         CHECK_VAL(notify.nttrans.out.changes[3].action, NOTIFY_ACTION_OLD_NAME);
401         CHECK_WSTR(notify.nttrans.out.changes[3].name, "subdir-name\\subname1", STR_UNICODE);
402         CHECK_VAL(notify.nttrans.out.changes[4].action, NOTIFY_ACTION_NEW_NAME);
403         CHECK_WSTR(notify.nttrans.out.changes[4].name, "subdir-name\\subname1-r", STR_UNICODE);
404
405         ret &= check_rename_reply(
406                 cli, __LINE__, &notify.nttrans.out.changes[5],
407                 NOTIFY_ACTION_ADDED, "subname2-r");
408         ret &= check_rename_reply(
409                 cli, __LINE__, &notify.nttrans.out.changes[5],
410                 NOTIFY_ACTION_REMOVED, "subdir-name\\subname2");
411         ret &= check_rename_reply(
412                 cli, __LINE__, &notify.nttrans.out.changes[5],
413                 NOTIFY_ACTION_MODIFIED, "subname2-r");
414                 
415         ret &= check_rename_reply(
416                 cli, __LINE__, &notify.nttrans.out.changes[8],
417                 NOTIFY_ACTION_OLD_NAME, "subname2-r");
418         ret &= check_rename_reply(
419                 cli, __LINE__, &notify.nttrans.out.changes[8],
420                 NOTIFY_ACTION_NEW_NAME, "subname3-r");
421         ret &= check_rename_reply(
422                 cli, __LINE__, &notify.nttrans.out.changes[8],
423                 NOTIFY_ACTION_MODIFIED, "subname3-r");
424
425         if (!ret) {
426                 goto done;
427         }
428
429         status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
430         CHECK_STATUS(status, NT_STATUS_OK);
431
432         CHECK_VAL(notify.nttrans.out.num_changes, 3);
433         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
434         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name\\subname1-r", STR_UNICODE);
435         CHECK_VAL(notify.nttrans.out.changes[1].action, NOTIFY_ACTION_REMOVED);
436         CHECK_WSTR(notify.nttrans.out.changes[1].name, "subdir-name", STR_UNICODE);
437         CHECK_VAL(notify.nttrans.out.changes[2].action, NOTIFY_ACTION_REMOVED);
438         CHECK_WSTR(notify.nttrans.out.changes[2].name, "subname3-r", STR_UNICODE);
439
440 done:
441         smb_raw_exit(cli->session);
442         smbcli_deltree(cli->tree, BASEDIR);
443         return ret;
444 }
445
446 /* 
447    testing of change notify mask change
448 */
449 static bool test_notify_mask_change(struct torture_context *mem_ctx,
450                                     struct smbcli_state *cli)
451 {
452         bool ret = true;
453         NTSTATUS status;
454         union smb_notify notify;
455         union smb_open io;
456         int fnum;
457         struct smbcli_request *req1, *req2;
458
459         printf("TESTING CHANGE NOTIFY WITH MASK CHANGE\n");
460
461         if (!torture_setup_dir(cli, BASEDIR)) {
462                 return false;
463         }
464
465         /*
466           get a handle on the directory
467         */
468         io.generic.level = RAW_OPEN_NTCREATEX;
469         io.ntcreatex.in.root_fid.fnum = 0;
470         io.ntcreatex.in.flags = 0;
471         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
472         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
473         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
474         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
475         io.ntcreatex.in.alloc_size = 0;
476         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
477         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
478         io.ntcreatex.in.security_flags = 0;
479         io.ntcreatex.in.fname = BASEDIR;
480
481         status = smb_raw_open(cli->tree, mem_ctx, &io);
482         CHECK_STATUS(status, NT_STATUS_OK);
483         fnum = io.ntcreatex.out.file.fnum;
484
485         /* ask for a change notify, on file or directory name
486            changes. Setup both with and without recursion */
487         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
488         notify.nttrans.in.buffer_size = 1000;
489         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_ATTRIBUTES;
490         notify.nttrans.in.file.fnum = fnum;
491
492         notify.nttrans.in.recursive = true;
493         req1 = smb_raw_changenotify_send(cli->tree, &notify);
494
495         notify.nttrans.in.recursive = false;
496         req2 = smb_raw_changenotify_send(cli->tree, &notify);
497
498         /* cancel initial requests so the buffer is setup */
499         smb_raw_ntcancel(req1);
500         status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
501         CHECK_STATUS(status, NT_STATUS_CANCELLED);
502
503         smb_raw_ntcancel(req2);
504         status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
505         CHECK_STATUS(status, NT_STATUS_CANCELLED);
506
507         notify.nttrans.in.recursive = true;
508         req1 = smb_raw_changenotify_send(cli->tree, &notify);
509
510         /* Set to hidden then back again. */
511         smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));
512         smbcli_setatr(cli->tree, BASEDIR "\\tname1", FILE_ATTRIBUTE_HIDDEN, 0);
513         smbcli_unlink(cli->tree, BASEDIR "\\tname1");
514
515         status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
516         CHECK_STATUS(status, NT_STATUS_OK);
517
518         CHECK_VAL(notify.nttrans.out.num_changes, 1);
519         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_MODIFIED);
520         CHECK_WSTR(notify.nttrans.out.changes[0].name, "tname1", STR_UNICODE);
521
522         /* Now try and change the mask to include other events.
523          * This should not work - once the mask is set on a directory
524          * fnum it seems to be fixed until the fnum is closed. */
525
526         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_CREATION;
527         notify.nttrans.in.recursive = true;
528         req1 = smb_raw_changenotify_send(cli->tree, &notify);
529
530         notify.nttrans.in.recursive = false;
531         req2 = smb_raw_changenotify_send(cli->tree, &notify);
532
533         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
534         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name\\subname1");
535         smbcli_close(cli->tree, 
536                      smbcli_open(cli->tree, BASEDIR "\\subdir-name\\subname2", O_CREAT, 0));
537         smbcli_rename(cli->tree, BASEDIR "\\subdir-name\\subname1", BASEDIR "\\subdir-name\\subname1-r");
538         smbcli_rename(cli->tree, BASEDIR "\\subdir-name\\subname2", BASEDIR "\\subname2-r");
539         smbcli_rename(cli->tree, BASEDIR "\\subname2-r", BASEDIR "\\subname3-r");
540
541         smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name\\subname1-r");
542         smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
543         smbcli_unlink(cli->tree, BASEDIR "\\subname3-r");
544
545         status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
546         CHECK_STATUS(status, NT_STATUS_OK);
547
548         CHECK_VAL(notify.nttrans.out.num_changes, 1);
549         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_MODIFIED);
550         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subname2-r", STR_UNICODE);
551
552         status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
553         CHECK_STATUS(status, NT_STATUS_OK);
554
555         CHECK_VAL(notify.nttrans.out.num_changes, 1);
556         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_MODIFIED);
557         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subname3-r", STR_UNICODE);
558
559         if (!ret) {
560                 goto done;
561         }
562
563 done:
564         smb_raw_exit(cli->session);
565         smbcli_deltree(cli->tree, BASEDIR);
566         return ret;
567 }
568
569
570 /* 
571    testing of mask bits for change notify
572 */
573 static bool test_notify_mask(struct torture_context *tctx,
574                              struct smbcli_state *cli)
575 {
576         bool ret = true;
577         NTSTATUS status;
578         union smb_notify notify;
579         union smb_open io;
580         int fnum, fnum2;
581         uint32_t mask;
582         int i;
583         char c = 1;
584         struct timeval tv;
585         NTTIME t;
586
587         printf("TESTING CHANGE NOTIFY COMPLETION FILTERS\n");
588
589         if (!torture_setup_dir(cli, BASEDIR)) {
590                 return false;
591         }
592
593         tv = timeval_current_ofs(1000, 0);
594         t = timeval_to_nttime(&tv);
595
596         /*
597           get a handle on the directory
598         */
599         io.generic.level = RAW_OPEN_NTCREATEX;
600         io.ntcreatex.in.root_fid.fnum = 0;
601         io.ntcreatex.in.flags = 0;
602         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
603         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
604         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
605         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
606         io.ntcreatex.in.alloc_size = 0;
607         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
608         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
609         io.ntcreatex.in.security_flags = 0;
610         io.ntcreatex.in.fname = BASEDIR;
611
612         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
613         notify.nttrans.in.buffer_size = 1000;
614         notify.nttrans.in.recursive = true;
615
616 #define NOTIFY_MASK_TEST(test_name, setup, op, cleanup, Action, expected, nchanges) \
617         do { \
618         smbcli_getatr(cli->tree, test_name, NULL, NULL, NULL); \
619         do { for (mask=i=0;i<32;i++) { \
620                 struct smbcli_request *req; \
621                 status = smb_raw_open(cli->tree, tctx, &io); \
622                 CHECK_STATUS(status, NT_STATUS_OK); \
623                 fnum = io.ntcreatex.out.file.fnum; \
624                 setup \
625                 notify.nttrans.in.file.fnum = fnum;     \
626                 notify.nttrans.in.completion_filter = (1<<i); \
627                 req = smb_raw_changenotify_send(cli->tree, &notify); \
628                 op \
629                 smb_msleep(200); smb_raw_ntcancel(req); \
630                 status = smb_raw_changenotify_recv(req, tctx, &notify); \
631                 cleanup \
632                 smbcli_close(cli->tree, fnum); \
633                 if (NT_STATUS_EQUAL(status, NT_STATUS_CANCELLED)) continue; \
634                 CHECK_STATUS(status, NT_STATUS_OK); \
635                 /* special case to cope with file rename behaviour */ \
636                 if (nchanges == 2 && notify.nttrans.out.num_changes == 1 && \
637                     notify.nttrans.out.changes[0].action == NOTIFY_ACTION_MODIFIED && \
638                     ((expected) & FILE_NOTIFY_CHANGE_ATTRIBUTES) && \
639                     Action == NOTIFY_ACTION_OLD_NAME) { \
640                         printf("(rename file special handling OK)\n"); \
641                 } else if (nchanges != notify.nttrans.out.num_changes) { \
642                         printf("ERROR: nchanges=%d expected=%d action=%d filter=0x%08x\n", \
643                                notify.nttrans.out.num_changes, \
644                                nchanges, \
645                                notify.nttrans.out.changes[0].action, \
646                                notify.nttrans.in.completion_filter); \
647                         ret = false; \
648                 } else if (notify.nttrans.out.changes[0].action != Action) { \
649                         printf("ERROR: nchanges=%d action=%d expectedAction=%d filter=0x%08x\n", \
650                                notify.nttrans.out.num_changes, \
651                                notify.nttrans.out.changes[0].action, \
652                                Action, \
653                                notify.nttrans.in.completion_filter); \
654                         ret = false; \
655                 } else if (strcmp(notify.nttrans.out.changes[0].name.s, "tname1") != 0) { \
656                         printf("ERROR: nchanges=%d action=%d filter=0x%08x name=%s\n", \
657                                notify.nttrans.out.num_changes, \
658                                notify.nttrans.out.changes[0].action, \
659                                notify.nttrans.in.completion_filter, \
660                                notify.nttrans.out.changes[0].name.s);   \
661                         ret = false; \
662                 } \
663                 mask |= (1<<i); \
664         } \
665         if ((expected) != mask) { \
666                 if (((expected) & ~mask) != 0) { \
667                         printf("ERROR: trigger on too few bits. mask=0x%08x expected=0x%08x\n", \
668                                mask, expected); \
669                         ret = false; \
670                 } else { \
671                         printf("WARNING: trigger on too many bits. mask=0x%08x expected=0x%08x\n", \
672                                mask, expected); \
673                 } \
674         } \
675         } while (0); \
676         } while (0);
677
678         printf("Testing mkdir\n");
679         NOTIFY_MASK_TEST("Testing mkdir",;,
680                          smbcli_mkdir(cli->tree, BASEDIR "\\tname1");,
681                          smbcli_rmdir(cli->tree, BASEDIR "\\tname1");,
682                          NOTIFY_ACTION_ADDED,
683                          FILE_NOTIFY_CHANGE_DIR_NAME, 1);
684
685         printf("Testing create file\n");
686         NOTIFY_MASK_TEST("Testing create file",;,
687                          smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
688                          smbcli_unlink(cli->tree, BASEDIR "\\tname1");,
689                          NOTIFY_ACTION_ADDED,
690                          FILE_NOTIFY_CHANGE_FILE_NAME, 1);
691
692         printf("Testing unlink\n");
693         NOTIFY_MASK_TEST("Testing unlink",
694                          smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
695                          smbcli_unlink(cli->tree, BASEDIR "\\tname1");,
696                          ;,
697                          NOTIFY_ACTION_REMOVED,
698                          FILE_NOTIFY_CHANGE_FILE_NAME, 1);
699
700         printf("Testing rmdir\n");
701         NOTIFY_MASK_TEST("Testing rmdir",
702                          smbcli_mkdir(cli->tree, BASEDIR "\\tname1");,
703                          smbcli_rmdir(cli->tree, BASEDIR "\\tname1");,
704                          ;,
705                          NOTIFY_ACTION_REMOVED,
706                          FILE_NOTIFY_CHANGE_DIR_NAME, 1);
707
708         printf("Testing rename file\n");
709         NOTIFY_MASK_TEST("Testing rename file",
710                          smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
711                          smbcli_rename(cli->tree, BASEDIR "\\tname1", BASEDIR "\\tname2");,
712                          smbcli_unlink(cli->tree, BASEDIR "\\tname2");,
713                          NOTIFY_ACTION_OLD_NAME,
714                          FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_CREATION, 2);
715
716         printf("Testing rename dir\n");
717         NOTIFY_MASK_TEST("Testing rename dir",
718                 smbcli_mkdir(cli->tree, BASEDIR "\\tname1");,
719                 smbcli_rename(cli->tree, BASEDIR "\\tname1", BASEDIR "\\tname2");,
720                 smbcli_rmdir(cli->tree, BASEDIR "\\tname2");,
721                 NOTIFY_ACTION_OLD_NAME,
722                 FILE_NOTIFY_CHANGE_DIR_NAME, 2);
723
724         printf("Testing set path attribute\n");
725         NOTIFY_MASK_TEST("Testing set path attribute",
726                 smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
727                 smbcli_setatr(cli->tree, BASEDIR "\\tname1", FILE_ATTRIBUTE_HIDDEN, 0);,
728                 smbcli_unlink(cli->tree, BASEDIR "\\tname1");,
729                 NOTIFY_ACTION_MODIFIED,
730                 FILE_NOTIFY_CHANGE_ATTRIBUTES, 1);
731
732         printf("Testing set path write time\n");
733         NOTIFY_MASK_TEST("Testing set path write time",
734                 smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
735                 smbcli_setatr(cli->tree, BASEDIR "\\tname1", FILE_ATTRIBUTE_NORMAL, 1000);,
736                 smbcli_unlink(cli->tree, BASEDIR "\\tname1");,
737                 NOTIFY_ACTION_MODIFIED,
738                 FILE_NOTIFY_CHANGE_LAST_WRITE, 1);
739
740         printf("Testing set file attribute\n");
741         NOTIFY_MASK_TEST("Testing set file attribute",
742                 fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
743                 smbcli_fsetatr(cli->tree, fnum2, FILE_ATTRIBUTE_HIDDEN, 0, 0, 0, 0);,
744                 (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
745                 NOTIFY_ACTION_MODIFIED,
746                 FILE_NOTIFY_CHANGE_ATTRIBUTES, 1);
747
748         if (torture_setting_bool(tctx, "samba3", false)) {
749                 printf("Samba3 does not yet support create times "
750                        "everywhere\n");
751         }
752         else {
753                 printf("Testing set file create time\n");
754                 NOTIFY_MASK_TEST("Testing set file create time",
755                         fnum2 = create_complex_file(cli, tctx,
756                                                     BASEDIR "\\tname1");,
757                         smbcli_fsetatr(cli->tree, fnum2, 0, t, 0, 0, 0);,
758                         (smbcli_close(cli->tree, fnum2),
759                          smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
760                         NOTIFY_ACTION_MODIFIED,
761                         FILE_NOTIFY_CHANGE_CREATION, 1);
762         }
763
764         printf("Testing set file access time\n");
765         NOTIFY_MASK_TEST("Testing set file access time",
766                 fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
767                 smbcli_fsetatr(cli->tree, fnum2, 0, 0, t, 0, 0);,
768                 (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
769                 NOTIFY_ACTION_MODIFIED,
770                 FILE_NOTIFY_CHANGE_LAST_ACCESS, 1);
771
772         printf("Testing set file write time\n");
773         NOTIFY_MASK_TEST("Testing set file write time",
774                 fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
775                 smbcli_fsetatr(cli->tree, fnum2, 0, 0, 0, t, 0);,
776                 (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
777                 NOTIFY_ACTION_MODIFIED,
778                 FILE_NOTIFY_CHANGE_LAST_WRITE, 1);
779
780         printf("Testing set file change time\n");
781         NOTIFY_MASK_TEST("Testing set file change time",
782                 fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
783                 smbcli_fsetatr(cli->tree, fnum2, 0, 0, 0, 0, t);,
784                 (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
785                 NOTIFY_ACTION_MODIFIED,
786                 0, 1);
787
788
789         printf("Testing write\n");
790         NOTIFY_MASK_TEST("Testing write",
791                 fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
792                 smbcli_write(cli->tree, fnum2, 1, &c, 10000, 1);,
793                 (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
794                 NOTIFY_ACTION_MODIFIED,
795                 0, 1);
796
797         printf("Testing truncate\n");
798         NOTIFY_MASK_TEST("Testing truncate",
799                 fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
800                 smbcli_ftruncate(cli->tree, fnum2, 10000);,
801                 (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
802                 NOTIFY_ACTION_MODIFIED,
803                 FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES, 1);
804
805 done:
806         smb_raw_exit(cli->session);
807         smbcli_deltree(cli->tree, BASEDIR);
808         return ret;
809 }
810
811 /*
812   basic testing of change notify on files
813 */
814 static bool test_notify_file(struct torture_context *mem_ctx,
815                              struct smbcli_state *cli)
816 {
817         NTSTATUS status;
818         bool ret = true;
819         union smb_open io;
820         union smb_close cl;
821         union smb_notify notify;
822         struct smbcli_request *req;
823         int fnum;
824         const char *fname = BASEDIR "\\file.txt";
825
826         printf("TESTING CHANGE NOTIFY ON FILES\n");
827
828         if (!torture_setup_dir(cli, BASEDIR)) {
829                 return false;
830         }
831
832         io.generic.level = RAW_OPEN_NTCREATEX;
833         io.ntcreatex.in.root_fid.fnum = 0;
834         io.ntcreatex.in.flags = 0;
835         io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
836         io.ntcreatex.in.create_options = 0;
837         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
838         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
839         io.ntcreatex.in.alloc_size = 0;
840         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
841         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
842         io.ntcreatex.in.security_flags = 0;
843         io.ntcreatex.in.fname = fname;
844         status = smb_raw_open(cli->tree, mem_ctx, &io);
845         CHECK_STATUS(status, NT_STATUS_OK);
846         fnum = io.ntcreatex.out.file.fnum;
847
848         /* ask for a change notify,
849            on file or directory name changes */
850         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
851         notify.nttrans.in.file.fnum = fnum;
852         notify.nttrans.in.buffer_size = 1000;
853         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_STREAM_NAME;
854         notify.nttrans.in.recursive = false;
855
856         printf("Testing if notifies on file handles are invalid (should be)\n");
857
858         req = smb_raw_changenotify_send(cli->tree, &notify);
859         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
860         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
861
862         cl.close.level = RAW_CLOSE_CLOSE;
863         cl.close.in.file.fnum = fnum;
864         cl.close.in.write_time = 0;
865         status = smb_raw_close(cli->tree, &cl);
866         CHECK_STATUS(status, NT_STATUS_OK);
867
868         status = smbcli_unlink(cli->tree, fname);
869         CHECK_STATUS(status, NT_STATUS_OK);
870
871 done:
872         smb_raw_exit(cli->session);
873         smbcli_deltree(cli->tree, BASEDIR);
874         return ret;
875 }
876
877 /*
878   basic testing of change notifies followed by a tdis
879 */
880 static bool test_notify_tdis(struct torture_context *tctx,
881                              struct smbcli_state *cli1)
882 {
883         bool ret = true;
884         NTSTATUS status;
885         union smb_notify notify;
886         union smb_open io;
887         int fnum;
888         struct smbcli_request *req;
889         struct smbcli_state *cli = NULL;
890
891         printf("TESTING CHANGE NOTIFY FOLLOWED BY TDIS\n");
892
893         if (!torture_setup_dir(cli1, BASEDIR)) {
894                 return false;
895         }
896
897         if (!torture_open_connection(&cli, tctx, 0)) {
898                 return false;
899         }
900
901         /*
902           get a handle on the directory
903         */
904         io.generic.level = RAW_OPEN_NTCREATEX;
905         io.ntcreatex.in.root_fid.fnum = 0;
906         io.ntcreatex.in.flags = 0;
907         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
908         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
909         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
910         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
911         io.ntcreatex.in.alloc_size = 0;
912         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
913         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
914         io.ntcreatex.in.security_flags = 0;
915         io.ntcreatex.in.fname = BASEDIR;
916
917         status = smb_raw_open(cli->tree, tctx, &io);
918         CHECK_STATUS(status, NT_STATUS_OK);
919         fnum = io.ntcreatex.out.file.fnum;
920
921         /* ask for a change notify,
922            on file or directory name changes */
923         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
924         notify.nttrans.in.buffer_size = 1000;
925         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
926         notify.nttrans.in.file.fnum = fnum;
927         notify.nttrans.in.recursive = true;
928
929         req = smb_raw_changenotify_send(cli->tree, &notify);
930
931         status = smbcli_tdis(cli);
932         CHECK_STATUS(status, NT_STATUS_OK);
933         cli->tree = NULL;
934
935         status = smb_raw_changenotify_recv(req, tctx, &notify);
936         CHECK_STATUS(status, NT_STATUS_OK);
937         CHECK_VAL(notify.nttrans.out.num_changes, 0);
938
939 done:
940         torture_close_connection(cli);
941         smbcli_deltree(cli1->tree, BASEDIR);
942         return ret;
943 }
944
945 /*
946   basic testing of change notifies followed by a exit
947 */
948 static bool test_notify_exit(struct torture_context *tctx,
949                              struct smbcli_state *cli1)
950 {
951         bool ret = true;
952         NTSTATUS status;
953         union smb_notify notify;
954         union smb_open io;
955         int fnum;
956         struct smbcli_request *req;
957         struct smbcli_state *cli = NULL;
958
959         printf("TESTING CHANGE NOTIFY FOLLOWED BY EXIT\n");
960
961         if (!torture_setup_dir(cli1, BASEDIR)) {
962                 return false;
963         }
964
965         if (!torture_open_connection(&cli, tctx, 0)) {
966                 return false;
967         }
968
969         /*
970           get a handle on the directory
971         */
972         io.generic.level = RAW_OPEN_NTCREATEX;
973         io.ntcreatex.in.root_fid.fnum = 0;
974         io.ntcreatex.in.flags = 0;
975         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
976         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
977         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
978         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
979         io.ntcreatex.in.alloc_size = 0;
980         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
981         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
982         io.ntcreatex.in.security_flags = 0;
983         io.ntcreatex.in.fname = BASEDIR;
984
985         status = smb_raw_open(cli->tree, tctx, &io);
986         CHECK_STATUS(status, NT_STATUS_OK);
987         fnum = io.ntcreatex.out.file.fnum;
988
989         /* ask for a change notify,
990            on file or directory name changes */
991         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
992         notify.nttrans.in.buffer_size = 1000;
993         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
994         notify.nttrans.in.file.fnum = fnum;
995         notify.nttrans.in.recursive = true;
996
997         req = smb_raw_changenotify_send(cli->tree, &notify);
998
999         status = smb_raw_exit(cli->session);
1000         CHECK_STATUS(status, NT_STATUS_OK);
1001
1002         status = smb_raw_changenotify_recv(req, tctx, &notify);
1003         CHECK_STATUS(status, NT_STATUS_OK);
1004         CHECK_VAL(notify.nttrans.out.num_changes, 0);
1005
1006 done:
1007         torture_close_connection(cli);
1008         smbcli_deltree(cli1->tree, BASEDIR);
1009         return ret;
1010 }
1011
1012 /*
1013   basic testing of change notifies followed by a ulogoff
1014 */
1015 static bool test_notify_ulogoff(struct torture_context *tctx)
1016 {
1017         bool ret = true;
1018         NTSTATUS status;
1019         union smb_notify notify;
1020         union smb_open io;
1021         int fnum;
1022         struct smbcli_request *req;
1023         struct smbcli_state *cli = NULL;
1024
1025         printf("TESTING CHANGE NOTIFY FOLLOWED BY ULOGOFF\n");
1026
1027         if (!torture_open_connection(&cli, tctx, 0)) {
1028                 return false;
1029         }
1030
1031         /*
1032           get a handle on the directory
1033         */
1034         io.generic.level = RAW_OPEN_NTCREATEX;
1035         io.ntcreatex.in.root_fid.fnum = 0;
1036         io.ntcreatex.in.flags = 0;
1037         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1038         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1039         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1040         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
1041         io.ntcreatex.in.alloc_size = 0;
1042         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1043         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1044         io.ntcreatex.in.security_flags = 0;
1045         io.ntcreatex.in.fname = BASEDIR;
1046
1047         status = smb_raw_open(cli->tree, tctx, &io);
1048         CHECK_STATUS(status, NT_STATUS_OK);
1049         fnum = io.ntcreatex.out.file.fnum;
1050
1051         /* ask for a change notify,
1052            on file or directory name changes */
1053         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1054         notify.nttrans.in.buffer_size = 1000;
1055         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1056         notify.nttrans.in.file.fnum = fnum;
1057         notify.nttrans.in.recursive = true;
1058
1059         req = smb_raw_changenotify_send(cli->tree, &notify);
1060
1061         status = smb_raw_ulogoff(cli->session);
1062         CHECK_STATUS(status, NT_STATUS_OK);
1063
1064         status = smb_raw_changenotify_recv(req, tctx, &notify);
1065         CHECK_STATUS(status, NT_STATUS_OK);
1066         CHECK_VAL(notify.nttrans.out.num_changes, 0);
1067
1068 done:
1069         torture_close_connection(cli);
1070         return ret;
1071 }
1072
1073 static void tcp_dis_handler(struct smbcli_transport *t, void *p)
1074 {
1075         struct smbcli_state *cli = (struct smbcli_state *)p;
1076         smbcli_transport_dead(cli->transport, NT_STATUS_LOCAL_DISCONNECT);
1077         cli->transport = NULL;
1078         cli->tree = NULL;
1079 }
1080 /*
1081   basic testing of change notifies followed by tcp disconnect
1082 */
1083 static bool test_notify_tcp_dis(struct torture_context *tctx)
1084 {
1085         bool ret = true;
1086         NTSTATUS status;
1087         union smb_notify notify;
1088         union smb_open io;
1089         int fnum;
1090         struct smbcli_request *req;
1091         struct smbcli_state *cli = NULL;
1092
1093         printf("TESTING CHANGE NOTIFY FOLLOWED BY TCP DISCONNECT\n");
1094
1095         if (!torture_open_connection(&cli, tctx, 0)) {
1096                 return false;
1097         }
1098
1099         /*
1100           get a handle on the directory
1101         */
1102         io.generic.level = RAW_OPEN_NTCREATEX;
1103         io.ntcreatex.in.root_fid.fnum = 0;
1104         io.ntcreatex.in.flags = 0;
1105         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1106         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1107         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1108         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
1109         io.ntcreatex.in.alloc_size = 0;
1110         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1111         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1112         io.ntcreatex.in.security_flags = 0;
1113         io.ntcreatex.in.fname = BASEDIR;
1114
1115         status = smb_raw_open(cli->tree, tctx, &io);
1116         CHECK_STATUS(status, NT_STATUS_OK);
1117         fnum = io.ntcreatex.out.file.fnum;
1118
1119         /* ask for a change notify,
1120            on file or directory name changes */
1121         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1122         notify.nttrans.in.buffer_size = 1000;
1123         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1124         notify.nttrans.in.file.fnum = fnum;
1125         notify.nttrans.in.recursive = true;
1126
1127         req = smb_raw_changenotify_send(cli->tree, &notify);
1128
1129         smbcli_transport_idle_handler(cli->transport, tcp_dis_handler, 250, cli);
1130
1131         status = smb_raw_changenotify_recv(req, tctx, &notify);
1132         CHECK_STATUS(status, NT_STATUS_LOCAL_DISCONNECT);
1133
1134 done:
1135         torture_close_connection(cli);
1136         return ret;
1137 }
1138
1139 /* 
1140    test setting up two change notify requests on one handle
1141 */
1142 static bool test_notify_double(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
1143 {
1144         bool ret = true;
1145         NTSTATUS status;
1146         union smb_notify notify;
1147         union smb_open io;
1148         int fnum;
1149         struct smbcli_request *req1, *req2;
1150
1151         printf("TESTING CHANGE NOTIFY TWICE ON ONE DIRECTORY\n");
1152                 
1153         /*
1154           get a handle on the directory
1155         */
1156         io.generic.level = RAW_OPEN_NTCREATEX;
1157         io.ntcreatex.in.root_fid.fnum = 0;
1158         io.ntcreatex.in.flags = 0;
1159         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1160         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1161         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1162         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
1163         io.ntcreatex.in.alloc_size = 0;
1164         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1165         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1166         io.ntcreatex.in.security_flags = 0;
1167         io.ntcreatex.in.fname = BASEDIR;
1168
1169         status = smb_raw_open(cli->tree, mem_ctx, &io);
1170         CHECK_STATUS(status, NT_STATUS_OK);
1171         fnum = io.ntcreatex.out.file.fnum;
1172
1173         /* ask for a change notify,
1174            on file or directory name changes */
1175         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1176         notify.nttrans.in.buffer_size = 1000;
1177         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1178         notify.nttrans.in.file.fnum = fnum;
1179         notify.nttrans.in.recursive = true;
1180
1181         req1 = smb_raw_changenotify_send(cli->tree, &notify);
1182         req2 = smb_raw_changenotify_send(cli->tree, &notify);
1183
1184         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
1185
1186         status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
1187         CHECK_STATUS(status, NT_STATUS_OK);
1188         CHECK_VAL(notify.nttrans.out.num_changes, 1);
1189         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
1190
1191         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name2");
1192
1193         status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
1194         CHECK_STATUS(status, NT_STATUS_OK);
1195         CHECK_VAL(notify.nttrans.out.num_changes, 1);
1196         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name2", STR_UNICODE);
1197
1198 done:
1199         smb_raw_exit(cli->session);
1200         return ret;
1201 }
1202
1203
1204 /* 
1205    test multiple change notifies at different depths and with/without recursion
1206 */
1207 static bool test_notify_tree(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
1208 {
1209         bool ret = true;
1210         union smb_notify notify;
1211         union smb_open io;
1212         struct smbcli_request *req;
1213         struct timeval tv;
1214         struct {
1215                 const char *path;
1216                 bool recursive;
1217                 uint32_t filter;
1218                 int expected;
1219                 int fnum;
1220                 int counted;
1221         } dirs[] = {
1222                 {BASEDIR "\\abc",               true, FILE_NOTIFY_CHANGE_NAME, 30 },
1223                 {BASEDIR "\\zqy",               true, FILE_NOTIFY_CHANGE_NAME, 8 },
1224                 {BASEDIR "\\atsy",              true, FILE_NOTIFY_CHANGE_NAME, 4 },
1225                 {BASEDIR "\\abc\\foo",          true,  FILE_NOTIFY_CHANGE_NAME, 2 },
1226                 {BASEDIR "\\abc\\blah",         true,  FILE_NOTIFY_CHANGE_NAME, 13 },
1227                 {BASEDIR "\\abc\\blah",         false, FILE_NOTIFY_CHANGE_NAME, 7 },
1228                 {BASEDIR "\\abc\\blah\\a",      true, FILE_NOTIFY_CHANGE_NAME, 2 },
1229                 {BASEDIR "\\abc\\blah\\b",      true, FILE_NOTIFY_CHANGE_NAME, 2 },
1230                 {BASEDIR "\\abc\\blah\\c",      true, FILE_NOTIFY_CHANGE_NAME, 2 },
1231                 {BASEDIR "\\abc\\fooblah",      true, FILE_NOTIFY_CHANGE_NAME, 2 },
1232                 {BASEDIR "\\zqy\\xx",           true, FILE_NOTIFY_CHANGE_NAME, 2 },
1233                 {BASEDIR "\\zqy\\yyy",          true, FILE_NOTIFY_CHANGE_NAME, 2 },
1234                 {BASEDIR "\\zqy\\..",           true, FILE_NOTIFY_CHANGE_NAME, 40 },
1235                 {BASEDIR,                       true, FILE_NOTIFY_CHANGE_NAME, 40 },
1236                 {BASEDIR,                       false,FILE_NOTIFY_CHANGE_NAME, 6 },
1237                 {BASEDIR "\\atsy",              false,FILE_NOTIFY_CHANGE_NAME, 4 },
1238                 {BASEDIR "\\abc",               true, FILE_NOTIFY_CHANGE_NAME, 24 },
1239                 {BASEDIR "\\abc",               false,FILE_NOTIFY_CHANGE_FILE_NAME, 0 },
1240                 {BASEDIR "\\abc",               true, FILE_NOTIFY_CHANGE_FILE_NAME, 0 },
1241                 {BASEDIR "\\abc",               true, FILE_NOTIFY_CHANGE_NAME, 24 },
1242         };
1243         int i;
1244         NTSTATUS status;
1245         bool all_done = false;
1246
1247         printf("TESTING CHANGE NOTIFY FOR DIFFERENT DEPTHS\n");
1248
1249         io.generic.level = RAW_OPEN_NTCREATEX;
1250         io.ntcreatex.in.root_fid.fnum = 0;
1251         io.ntcreatex.in.flags = 0;
1252         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1253         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1254         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1255         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
1256         io.ntcreatex.in.alloc_size = 0;
1257         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
1258         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1259         io.ntcreatex.in.security_flags = 0;
1260
1261         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1262         notify.nttrans.in.buffer_size = 20000;
1263
1264         /*
1265           setup the directory tree, and the notify buffer on each directory
1266         */
1267         for (i=0;i<ARRAY_SIZE(dirs);i++) {
1268                 io.ntcreatex.in.fname = dirs[i].path;
1269                 status = smb_raw_open(cli->tree, mem_ctx, &io);
1270                 CHECK_STATUS(status, NT_STATUS_OK);
1271                 dirs[i].fnum = io.ntcreatex.out.file.fnum;
1272
1273                 notify.nttrans.in.completion_filter = dirs[i].filter;
1274                 notify.nttrans.in.file.fnum = dirs[i].fnum;
1275                 notify.nttrans.in.recursive = dirs[i].recursive;
1276                 req = smb_raw_changenotify_send(cli->tree, &notify);
1277                 smb_raw_ntcancel(req);
1278                 status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
1279                 CHECK_STATUS(status, NT_STATUS_CANCELLED);
1280         }
1281
1282         /* trigger 2 events in each dir */
1283         for (i=0;i<ARRAY_SIZE(dirs);i++) {
1284                 char *path = talloc_asprintf(mem_ctx, "%s\\test.dir", dirs[i].path);
1285                 smbcli_mkdir(cli->tree, path);
1286                 smbcli_rmdir(cli->tree, path);
1287                 talloc_free(path);
1288         }
1289
1290         /* give a bit of time for the events to propogate */
1291         tv = timeval_current();
1292
1293         do {
1294                 /* count events that have happened in each dir */
1295                 for (i=0;i<ARRAY_SIZE(dirs);i++) {
1296                         notify.nttrans.in.file.fnum = dirs[i].fnum;
1297                         req = smb_raw_changenotify_send(cli->tree, &notify);
1298                         smb_raw_ntcancel(req);
1299                         notify.nttrans.out.num_changes = 0;
1300                         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
1301                         dirs[i].counted += notify.nttrans.out.num_changes;
1302                 }
1303                 
1304                 all_done = true;
1305
1306                 for (i=0;i<ARRAY_SIZE(dirs);i++) {
1307                         if (dirs[i].counted != dirs[i].expected) {
1308                                 all_done = false;
1309                         }
1310                 }
1311         } while (!all_done && timeval_elapsed(&tv) < 20);
1312
1313         printf("took %.4f seconds to propogate all events\n", timeval_elapsed(&tv));
1314
1315         for (i=0;i<ARRAY_SIZE(dirs);i++) {
1316                 if (dirs[i].counted != dirs[i].expected) {
1317                         printf("ERROR: i=%d expected %d got %d for '%s'\n",
1318                                i, dirs[i].expected, dirs[i].counted, dirs[i].path);
1319                         ret = false;
1320                 }
1321         }
1322
1323         /*
1324           run from the back, closing and deleting
1325         */
1326         for (i=ARRAY_SIZE(dirs)-1;i>=0;i--) {
1327                 smbcli_close(cli->tree, dirs[i].fnum);
1328                 smbcli_rmdir(cli->tree, dirs[i].path);
1329         }
1330
1331 done:
1332         smb_raw_exit(cli->session);
1333         return ret;
1334 }
1335
1336 /*
1337    Test response when cached server events exceed single NT NOTFIY response
1338    packet size.
1339 */
1340 static bool test_notify_overflow(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
1341 {
1342         bool ret = true;
1343         NTSTATUS status;
1344         union smb_notify notify;
1345         union smb_open io;
1346         int fnum;
1347         int count = 100;
1348         struct smbcli_request *req1;
1349         int i;
1350
1351         printf("TESTING CHANGE NOTIFY EVENT OVERFLOW\n");
1352
1353         /* get a handle on the directory */
1354         io.generic.level = RAW_OPEN_NTCREATEX;
1355         io.ntcreatex.in.root_fid.fnum = 0;
1356         io.ntcreatex.in.flags = 0;
1357         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1358         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1359         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1360         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1361             NTCREATEX_SHARE_ACCESS_WRITE;
1362         io.ntcreatex.in.alloc_size = 0;
1363         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1364         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1365         io.ntcreatex.in.security_flags = 0;
1366         io.ntcreatex.in.fname = BASEDIR;
1367
1368         status = smb_raw_open(cli->tree, mem_ctx, &io);
1369         CHECK_STATUS(status, NT_STATUS_OK);
1370         fnum = io.ntcreatex.out.file.fnum;
1371
1372         /* ask for a change notify, on name changes. */
1373         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1374         notify.nttrans.in.buffer_size = 1000;
1375         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1376         notify.nttrans.in.file.fnum = fnum;
1377
1378         notify.nttrans.in.recursive = true;
1379         req1 = smb_raw_changenotify_send(cli->tree, &notify);
1380
1381         /* cancel initial requests so the buffer is setup */
1382         smb_raw_ntcancel(req1);
1383         status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
1384         CHECK_STATUS(status, NT_STATUS_CANCELLED);
1385
1386         /* open a lot of files, filling up the server side notify buffer */
1387         printf("Testing overflowed buffer notify on create of %d files\n",
1388                count);
1389         for (i=0;i<count;i++) {
1390                 char *fname = talloc_asprintf(cli, BASEDIR "\\test%d.txt", i);
1391                 int fnum2 = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR,
1392                                         DENY_NONE);
1393                 if (fnum2 == -1) {
1394                         printf("Failed to create %s - %s\n",
1395                                fname, smbcli_errstr(cli->tree));
1396                         ret = false;
1397                         goto done;
1398                 }
1399                 talloc_free(fname);
1400                 smbcli_close(cli->tree, fnum2);
1401         }
1402
1403         /* expect that 0 events will be returned with NT_STATUS_OK */
1404         req1 = smb_raw_changenotify_send(cli->tree, &notify);
1405         status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
1406         CHECK_STATUS(status, NT_STATUS_OK);
1407         CHECK_VAL(notify.nttrans.out.num_changes, 0);
1408
1409 done:
1410         smb_raw_exit(cli->session);
1411         return ret;
1412 }
1413
1414 /*
1415    Test if notifications are returned for changes to the base directory.
1416    They shouldn't be.
1417 */
1418 static bool test_notify_basedir(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
1419 {
1420         bool ret = true;
1421         NTSTATUS status;
1422         union smb_notify notify;
1423         union smb_open io;
1424         int fnum;
1425         struct smbcli_request *req1;
1426
1427         printf("TESTING CHANGE NOTIFY BASEDIR EVENTS\n");
1428
1429         /* get a handle on the directory */
1430         io.generic.level = RAW_OPEN_NTCREATEX;
1431         io.ntcreatex.in.root_fid.fnum = 0;
1432         io.ntcreatex.in.flags = 0;
1433         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1434         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1435         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1436         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1437             NTCREATEX_SHARE_ACCESS_WRITE;
1438         io.ntcreatex.in.alloc_size = 0;
1439         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1440         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1441         io.ntcreatex.in.security_flags = 0;
1442         io.ntcreatex.in.fname = BASEDIR;
1443
1444         status = smb_raw_open(cli->tree, mem_ctx, &io);
1445         CHECK_STATUS(status, NT_STATUS_OK);
1446         fnum = io.ntcreatex.out.file.fnum;
1447
1448         /* create a test file that will also be modified */
1449         smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1",
1450                                             O_CREAT, 0));
1451
1452         /* ask for a change notify, on attribute changes. */
1453         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1454         notify.nttrans.in.buffer_size = 1000;
1455         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_ATTRIBUTES;
1456         notify.nttrans.in.file.fnum = fnum;
1457         notify.nttrans.in.recursive = true;
1458
1459         req1 = smb_raw_changenotify_send(cli->tree, &notify);
1460
1461         /* set attribute on the base dir */
1462         smbcli_setatr(cli->tree, BASEDIR, FILE_ATTRIBUTE_HIDDEN, 0);
1463
1464         /* set attribute on a file to assure we receive a notification */
1465         smbcli_setatr(cli->tree, BASEDIR "\\tname1", FILE_ATTRIBUTE_HIDDEN, 0);
1466         smb_msleep(200);
1467
1468         /* check how many responses were given, expect only 1 for the file */
1469         status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
1470         CHECK_STATUS(status, NT_STATUS_OK);
1471         CHECK_VAL(notify.nttrans.out.num_changes, 1);
1472         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_MODIFIED);
1473         CHECK_WSTR(notify.nttrans.out.changes[0].name, "tname1", STR_UNICODE);
1474
1475 done:
1476         smb_raw_exit(cli->session);
1477         return ret;
1478 }
1479
1480
1481 /*
1482   create a secondary tree connect - used to test for a bug in Samba3 messaging
1483   with change notify
1484 */
1485 static struct smbcli_tree *secondary_tcon(struct smbcli_state *cli, 
1486                                           struct torture_context *tctx)
1487 {
1488         NTSTATUS status;
1489         const char *share, *host;
1490         struct smbcli_tree *tree;
1491         union smb_tcon tcon;
1492
1493         share = torture_setting_string(tctx, "share", NULL);
1494         host  = torture_setting_string(tctx, "host", NULL);
1495         
1496         printf("create a second tree context on the same session\n");
1497         tree = smbcli_tree_init(cli->session, tctx, false);
1498
1499         tcon.generic.level = RAW_TCON_TCONX;
1500         tcon.tconx.in.flags = 0;
1501         tcon.tconx.in.password = data_blob(NULL, 0);
1502         tcon.tconx.in.path = talloc_asprintf(tctx, "\\\\%s\\%s", host, share);
1503         tcon.tconx.in.device = "A:";    
1504         status = smb_raw_tcon(tree, tctx, &tcon);
1505         if (!NT_STATUS_IS_OK(status)) {
1506                 talloc_free(tree);
1507                 printf("Failed to create secondary tree\n");
1508                 return NULL;
1509         }
1510
1511         tree->tid = tcon.tconx.out.tid;
1512         printf("tid1=%d tid2=%d\n", cli->tree->tid, tree->tid);
1513
1514         return tree;
1515 }
1516
1517
1518 /* 
1519    very simple change notify test
1520 */
1521 static bool test_notify_tcon(struct torture_context *torture,
1522                              struct smbcli_state *cli)
1523 {
1524         bool ret = true;
1525         NTSTATUS status;
1526         union smb_notify notify;
1527         union smb_open io;
1528         int fnum;
1529         struct smbcli_request *req;
1530         extern int torture_numops;
1531         struct smbcli_tree *tree = NULL;
1532                 
1533         printf("TESTING SIMPLE CHANGE NOTIFY\n");
1534                 
1535         if (!torture_setup_dir(cli, BASEDIR)) {
1536                 return false;
1537         }
1538
1539         /*
1540           get a handle on the directory
1541         */
1542         io.generic.level = RAW_OPEN_NTCREATEX;
1543         io.ntcreatex.in.root_fid.fnum = 0;
1544         io.ntcreatex.in.flags = 0;
1545         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1546         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1547         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1548         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
1549         io.ntcreatex.in.alloc_size = 0;
1550         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1551         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1552         io.ntcreatex.in.security_flags = 0;
1553         io.ntcreatex.in.fname = BASEDIR;
1554
1555         status = smb_raw_open(cli->tree, torture, &io);
1556         CHECK_STATUS(status, NT_STATUS_OK);
1557         fnum = io.ntcreatex.out.file.fnum;
1558
1559         status = smb_raw_open(cli->tree, torture, &io);
1560         CHECK_STATUS(status, NT_STATUS_OK);
1561
1562         /* ask for a change notify,
1563            on file or directory name changes */
1564         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1565         notify.nttrans.in.buffer_size = 1000;
1566         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1567         notify.nttrans.in.file.fnum = fnum;
1568         notify.nttrans.in.recursive = true;
1569
1570         printf("Testing notify mkdir\n");
1571         req = smb_raw_changenotify_send(cli->tree, &notify);
1572         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
1573
1574         status = smb_raw_changenotify_recv(req, torture, &notify);
1575         CHECK_STATUS(status, NT_STATUS_OK);
1576
1577         CHECK_VAL(notify.nttrans.out.num_changes, 1);
1578         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_ADDED);
1579         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
1580
1581         printf("Testing notify rmdir\n");
1582         req = smb_raw_changenotify_send(cli->tree, &notify);
1583         smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
1584
1585         status = smb_raw_changenotify_recv(req, torture, &notify);
1586         CHECK_STATUS(status, NT_STATUS_OK);
1587         CHECK_VAL(notify.nttrans.out.num_changes, 1);
1588         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
1589         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
1590
1591         printf("SIMPLE CHANGE NOTIFY OK\n");
1592
1593         printf("TESTING WITH SECONDARY TCON\n");
1594         tree = secondary_tcon(cli, torture);
1595
1596         printf("Testing notify mkdir\n");
1597         req = smb_raw_changenotify_send(cli->tree, &notify);
1598         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
1599
1600         status = smb_raw_changenotify_recv(req, torture, &notify);
1601         CHECK_STATUS(status, NT_STATUS_OK);
1602
1603         CHECK_VAL(notify.nttrans.out.num_changes, 1);
1604         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_ADDED);
1605         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
1606
1607         printf("Testing notify rmdir\n");
1608         req = smb_raw_changenotify_send(cli->tree, &notify);
1609         smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
1610
1611         status = smb_raw_changenotify_recv(req, torture, &notify);
1612         CHECK_STATUS(status, NT_STATUS_OK);
1613         CHECK_VAL(notify.nttrans.out.num_changes, 1);
1614         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
1615         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
1616
1617         printf("CHANGE NOTIFY WITH TCON OK\n");
1618
1619         printf("Disconnecting secondary tree\n");
1620         status = smb_tree_disconnect(tree);
1621         CHECK_STATUS(status, NT_STATUS_OK);
1622         talloc_free(tree);
1623
1624         printf("Testing notify mkdir\n");
1625         req = smb_raw_changenotify_send(cli->tree, &notify);
1626         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
1627
1628         status = smb_raw_changenotify_recv(req, torture, &notify);
1629         CHECK_STATUS(status, NT_STATUS_OK);
1630
1631         CHECK_VAL(notify.nttrans.out.num_changes, 1);
1632         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_ADDED);
1633         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
1634
1635         printf("Testing notify rmdir\n");
1636         req = smb_raw_changenotify_send(cli->tree, &notify);
1637         smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
1638
1639         status = smb_raw_changenotify_recv(req, torture, &notify);
1640         CHECK_STATUS(status, NT_STATUS_OK);
1641         CHECK_VAL(notify.nttrans.out.num_changes, 1);
1642         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
1643         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
1644
1645         printf("CHANGE NOTIFY WITH TDIS OK\n");
1646 done:
1647         smb_raw_exit(cli->session);
1648         smbcli_deltree(cli->tree, BASEDIR);
1649         return ret;
1650 }
1651
1652
1653 /*
1654    testing alignment of multiple change notify infos
1655 */
1656 static bool test_notify_alignment(struct smbcli_state *cli,
1657     struct torture_context *tctx)
1658 {
1659         NTSTATUS status;
1660         union smb_notify notify;
1661         union smb_open io;
1662         int i, fnum, fnum2;
1663         struct smbcli_request *req;
1664         const char *fname = BASEDIR "\\starter";
1665         const char *fnames[] = { "a",
1666                                  "ab",
1667                                  "abc",
1668                                  "abcd" };
1669         int num_names = ARRAY_SIZE(fnames);
1670         char *fpath = NULL;
1671
1672         torture_comment(tctx, "TESTING CHANGE NOTIFY REPLY ALIGNMENT\n");
1673
1674         /* get a handle on the directory */
1675         io.generic.level = RAW_OPEN_NTCREATEX;
1676         io.ntcreatex.in.root_fid.fnum = 0;
1677         io.ntcreatex.in.flags = 0;
1678         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1679         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1680         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1681         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1682                                        NTCREATEX_SHARE_ACCESS_WRITE;
1683         io.ntcreatex.in.alloc_size = 0;
1684         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1685         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1686         io.ntcreatex.in.security_flags = 0;
1687         io.ntcreatex.in.fname = BASEDIR;
1688
1689         status = smb_raw_open(cli->tree, tctx, &io);
1690         torture_assert_ntstatus_ok(tctx, status, "");
1691         fnum = io.ntcreatex.out.file.fnum;
1692
1693         /* ask for a change notify, on file creation */
1694         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1695         notify.nttrans.in.buffer_size = 1000;
1696         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_FILE_NAME;
1697         notify.nttrans.in.file.fnum = fnum;
1698         notify.nttrans.in.recursive = false;
1699
1700         /* start change tracking */
1701         req = smb_raw_changenotify_send(cli->tree, &notify);
1702
1703         fnum2 = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
1704         torture_assert(tctx, fnum2 != -1, smbcli_errstr(cli->tree));
1705         smbcli_close(cli->tree, fnum2);
1706
1707         status = smb_raw_changenotify_recv(req, tctx, &notify);
1708         torture_assert_ntstatus_ok(tctx, status, "");
1709
1710         /* create 4 files that will cause CHANGE_NOTIFY_INFO structures
1711          * to be returned in the same packet with all possible 4-byte padding
1712          * permutations.  As per MS-CIFS 2.2.7.4.2 these structures should be
1713          * 4-byte aligned. */
1714
1715         for (i = 0; i < num_names; i++) {
1716                 fpath = talloc_asprintf(tctx, "%s\\%s", BASEDIR, fnames[i]);
1717                 fnum2 = smbcli_open(cli->tree, fpath,
1718                     O_CREAT|O_RDWR, DENY_NONE);
1719                 torture_assert(tctx, fnum2 != -1, smbcli_errstr(cli->tree));
1720                 smbcli_close(cli->tree, fnum2);
1721                 talloc_free(fpath);
1722         }
1723
1724         /* We send a notify packet, and let smb_raw_changenotify_recv() do
1725          * the alignment checking for us. */
1726         req = smb_raw_changenotify_send(cli->tree, &notify);
1727         status = smb_raw_changenotify_recv(req, tctx, &notify);
1728         torture_assert_ntstatus_ok(tctx, status, "");
1729
1730         /* Do basic checking for correctness. */
1731         torture_assert(tctx, notify.nttrans.out.num_changes == num_names, "");
1732         for (i = 0; i < num_names; i++) {
1733                 torture_assert(tctx, notify.nttrans.out.changes[i].action ==
1734                     NOTIFY_ACTION_ADDED, "");
1735                 CHECK_WSTR2(tctx, notify.nttrans.out.changes[i].name, fnames[i],
1736                     STR_UNICODE);
1737         }
1738
1739         return true;
1740 }
1741
1742 /*
1743    basic testing of change notify
1744 */
1745 static bool test_raw_notify_all(struct torture_context *torture,
1746                                 struct smbcli_state *cli,
1747                                 struct smbcli_state *cli2)
1748 {
1749         bool ret = true;
1750
1751         if (!torture_setup_dir(cli, BASEDIR)) {
1752                 return false;
1753         }
1754
1755         ret &= test_notify_ulogoff(torture);
1756         ret &= test_notify_tcp_dis(torture);
1757         ret &= test_notify_double(cli, torture);
1758         ret &= test_notify_tree(cli, torture);
1759         ret &= test_notify_overflow(cli, torture);
1760         ret &= test_notify_basedir(cli, torture);
1761         ret &= test_notify_alignment(cli, torture);
1762
1763         smb_raw_exit(cli->session);
1764         smbcli_deltree(cli->tree, BASEDIR);
1765         return ret;
1766 }
1767
1768 struct torture_suite *torture_raw_notify(TALLOC_CTX *mem_ctx)
1769 {
1770         struct torture_suite *suite = torture_suite_create(mem_ctx, "notify");
1771
1772         torture_suite_add_1smb_test(suite, "tcon", test_notify_tcon);
1773         torture_suite_add_2smb_test(suite, "dir", test_notify_dir);
1774         torture_suite_add_1smb_test(suite, "mask", test_notify_mask);
1775         torture_suite_add_1smb_test(suite, "recursive", test_notify_recursive);
1776         torture_suite_add_1smb_test(suite, "mask_change",
1777                                     test_notify_mask_change);
1778         torture_suite_add_1smb_test(suite, "file", test_notify_file);
1779         torture_suite_add_1smb_test(suite, "tdis", test_notify_tdis);
1780         torture_suite_add_1smb_test(suite, "exit", test_notify_exit);
1781         torture_suite_add_2smb_test(suite, "all", test_raw_notify_all);
1782
1783         return suite;
1784 }