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