b03285aa13f31936ddc21e2e0258d546be4417b2
[samba.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 {
882         bool ret = true;
883         NTSTATUS status;
884         union smb_notify notify;
885         union smb_open io;
886         int fnum;
887         struct smbcli_request *req;
888         struct smbcli_state *cli = NULL;
889
890         printf("TESTING CHANGE NOTIFY FOLLOWED BY TDIS\n");
891
892         if (!torture_open_connection(&cli, tctx, 0)) {
893                 return false;
894         }
895
896         /*
897           get a handle on the directory
898         */
899         io.generic.level = RAW_OPEN_NTCREATEX;
900         io.ntcreatex.in.root_fid.fnum = 0;
901         io.ntcreatex.in.flags = 0;
902         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
903         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
904         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
905         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
906         io.ntcreatex.in.alloc_size = 0;
907         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
908         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
909         io.ntcreatex.in.security_flags = 0;
910         io.ntcreatex.in.fname = BASEDIR;
911
912         status = smb_raw_open(cli->tree, tctx, &io);
913         CHECK_STATUS(status, NT_STATUS_OK);
914         fnum = io.ntcreatex.out.file.fnum;
915
916         /* ask for a change notify,
917            on file or directory name changes */
918         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
919         notify.nttrans.in.buffer_size = 1000;
920         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
921         notify.nttrans.in.file.fnum = fnum;
922         notify.nttrans.in.recursive = true;
923
924         req = smb_raw_changenotify_send(cli->tree, &notify);
925
926         status = smbcli_tdis(cli);
927         CHECK_STATUS(status, NT_STATUS_OK);
928         cli->tree = NULL;
929
930         status = smb_raw_changenotify_recv(req, tctx, &notify);
931         CHECK_STATUS(status, NT_STATUS_OK);
932         CHECK_VAL(notify.nttrans.out.num_changes, 0);
933
934 done:
935         torture_close_connection(cli);
936         return ret;
937 }
938
939 /*
940   basic testing of change notifies followed by a exit
941 */
942 static bool test_notify_exit(struct torture_context *tctx)
943 {
944         bool ret = true;
945         NTSTATUS status;
946         union smb_notify notify;
947         union smb_open io;
948         int fnum;
949         struct smbcli_request *req;
950         struct smbcli_state *cli = NULL;
951
952         printf("TESTING CHANGE NOTIFY FOLLOWED BY EXIT\n");
953
954         if (!torture_open_connection(&cli, tctx, 0)) {
955                 return false;
956         }
957
958         /*
959           get a handle on the directory
960         */
961         io.generic.level = RAW_OPEN_NTCREATEX;
962         io.ntcreatex.in.root_fid.fnum = 0;
963         io.ntcreatex.in.flags = 0;
964         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
965         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
966         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
967         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
968         io.ntcreatex.in.alloc_size = 0;
969         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
970         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
971         io.ntcreatex.in.security_flags = 0;
972         io.ntcreatex.in.fname = BASEDIR;
973
974         status = smb_raw_open(cli->tree, tctx, &io);
975         CHECK_STATUS(status, NT_STATUS_OK);
976         fnum = io.ntcreatex.out.file.fnum;
977
978         /* ask for a change notify,
979            on file or directory name changes */
980         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
981         notify.nttrans.in.buffer_size = 1000;
982         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
983         notify.nttrans.in.file.fnum = fnum;
984         notify.nttrans.in.recursive = true;
985
986         req = smb_raw_changenotify_send(cli->tree, &notify);
987
988         status = smb_raw_exit(cli->session);
989         CHECK_STATUS(status, NT_STATUS_OK);
990
991         status = smb_raw_changenotify_recv(req, tctx, &notify);
992         CHECK_STATUS(status, NT_STATUS_OK);
993         CHECK_VAL(notify.nttrans.out.num_changes, 0);
994
995 done:
996         torture_close_connection(cli);
997         return ret;
998 }
999
1000 /*
1001   basic testing of change notifies followed by a ulogoff
1002 */
1003 static bool test_notify_ulogoff(struct torture_context *tctx)
1004 {
1005         bool ret = true;
1006         NTSTATUS status;
1007         union smb_notify notify;
1008         union smb_open io;
1009         int fnum;
1010         struct smbcli_request *req;
1011         struct smbcli_state *cli = NULL;
1012
1013         printf("TESTING CHANGE NOTIFY FOLLOWED BY ULOGOFF\n");
1014
1015         if (!torture_open_connection(&cli, tctx, 0)) {
1016                 return false;
1017         }
1018
1019         /*
1020           get a handle on the directory
1021         */
1022         io.generic.level = RAW_OPEN_NTCREATEX;
1023         io.ntcreatex.in.root_fid.fnum = 0;
1024         io.ntcreatex.in.flags = 0;
1025         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1026         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1027         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1028         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
1029         io.ntcreatex.in.alloc_size = 0;
1030         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1031         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1032         io.ntcreatex.in.security_flags = 0;
1033         io.ntcreatex.in.fname = BASEDIR;
1034
1035         status = smb_raw_open(cli->tree, tctx, &io);
1036         CHECK_STATUS(status, NT_STATUS_OK);
1037         fnum = io.ntcreatex.out.file.fnum;
1038
1039         /* ask for a change notify,
1040            on file or directory name changes */
1041         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1042         notify.nttrans.in.buffer_size = 1000;
1043         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1044         notify.nttrans.in.file.fnum = fnum;
1045         notify.nttrans.in.recursive = true;
1046
1047         req = smb_raw_changenotify_send(cli->tree, &notify);
1048
1049         status = smb_raw_ulogoff(cli->session);
1050         CHECK_STATUS(status, NT_STATUS_OK);
1051
1052         status = smb_raw_changenotify_recv(req, tctx, &notify);
1053         CHECK_STATUS(status, NT_STATUS_OK);
1054         CHECK_VAL(notify.nttrans.out.num_changes, 0);
1055
1056 done:
1057         torture_close_connection(cli);
1058         return ret;
1059 }
1060
1061 static void tcp_dis_handler(struct smbcli_transport *t, void *p)
1062 {
1063         struct smbcli_state *cli = (struct smbcli_state *)p;
1064         smbcli_transport_dead(cli->transport, NT_STATUS_LOCAL_DISCONNECT);
1065         cli->transport = NULL;
1066         cli->tree = NULL;
1067 }
1068 /*
1069   basic testing of change notifies followed by tcp disconnect
1070 */
1071 static bool test_notify_tcp_dis(struct torture_context *tctx)
1072 {
1073         bool ret = true;
1074         NTSTATUS status;
1075         union smb_notify notify;
1076         union smb_open io;
1077         int fnum;
1078         struct smbcli_request *req;
1079         struct smbcli_state *cli = NULL;
1080
1081         printf("TESTING CHANGE NOTIFY FOLLOWED BY TCP DISCONNECT\n");
1082
1083         if (!torture_open_connection(&cli, tctx, 0)) {
1084                 return false;
1085         }
1086
1087         /*
1088           get a handle on the directory
1089         */
1090         io.generic.level = RAW_OPEN_NTCREATEX;
1091         io.ntcreatex.in.root_fid.fnum = 0;
1092         io.ntcreatex.in.flags = 0;
1093         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1094         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1095         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1096         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
1097         io.ntcreatex.in.alloc_size = 0;
1098         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1099         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1100         io.ntcreatex.in.security_flags = 0;
1101         io.ntcreatex.in.fname = BASEDIR;
1102
1103         status = smb_raw_open(cli->tree, tctx, &io);
1104         CHECK_STATUS(status, NT_STATUS_OK);
1105         fnum = io.ntcreatex.out.file.fnum;
1106
1107         /* ask for a change notify,
1108            on file or directory name changes */
1109         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1110         notify.nttrans.in.buffer_size = 1000;
1111         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1112         notify.nttrans.in.file.fnum = fnum;
1113         notify.nttrans.in.recursive = true;
1114
1115         req = smb_raw_changenotify_send(cli->tree, &notify);
1116
1117         smbcli_transport_idle_handler(cli->transport, tcp_dis_handler, 250, cli);
1118
1119         status = smb_raw_changenotify_recv(req, tctx, &notify);
1120         CHECK_STATUS(status, NT_STATUS_LOCAL_DISCONNECT);
1121
1122 done:
1123         torture_close_connection(cli);
1124         return ret;
1125 }
1126
1127 /* 
1128    test setting up two change notify requests on one handle
1129 */
1130 static bool test_notify_double(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
1131 {
1132         bool ret = true;
1133         NTSTATUS status;
1134         union smb_notify notify;
1135         union smb_open io;
1136         int fnum;
1137         struct smbcli_request *req1, *req2;
1138
1139         printf("TESTING CHANGE NOTIFY TWICE ON ONE DIRECTORY\n");
1140                 
1141         /*
1142           get a handle on the directory
1143         */
1144         io.generic.level = RAW_OPEN_NTCREATEX;
1145         io.ntcreatex.in.root_fid.fnum = 0;
1146         io.ntcreatex.in.flags = 0;
1147         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1148         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1149         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1150         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
1151         io.ntcreatex.in.alloc_size = 0;
1152         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1153         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1154         io.ntcreatex.in.security_flags = 0;
1155         io.ntcreatex.in.fname = BASEDIR;
1156
1157         status = smb_raw_open(cli->tree, mem_ctx, &io);
1158         CHECK_STATUS(status, NT_STATUS_OK);
1159         fnum = io.ntcreatex.out.file.fnum;
1160
1161         /* ask for a change notify,
1162            on file or directory name changes */
1163         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1164         notify.nttrans.in.buffer_size = 1000;
1165         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1166         notify.nttrans.in.file.fnum = fnum;
1167         notify.nttrans.in.recursive = true;
1168
1169         req1 = smb_raw_changenotify_send(cli->tree, &notify);
1170         req2 = smb_raw_changenotify_send(cli->tree, &notify);
1171
1172         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
1173
1174         status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
1175         CHECK_STATUS(status, NT_STATUS_OK);
1176         CHECK_VAL(notify.nttrans.out.num_changes, 1);
1177         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
1178
1179         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name2");
1180
1181         status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
1182         CHECK_STATUS(status, NT_STATUS_OK);
1183         CHECK_VAL(notify.nttrans.out.num_changes, 1);
1184         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name2", STR_UNICODE);
1185
1186 done:
1187         smb_raw_exit(cli->session);
1188         return ret;
1189 }
1190
1191
1192 /* 
1193    test multiple change notifies at different depths and with/without recursion
1194 */
1195 static bool test_notify_tree(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
1196 {
1197         bool ret = true;
1198         union smb_notify notify;
1199         union smb_open io;
1200         struct smbcli_request *req;
1201         struct timeval tv;
1202         struct {
1203                 const char *path;
1204                 bool recursive;
1205                 uint32_t filter;
1206                 int expected;
1207                 int fnum;
1208                 int counted;
1209         } dirs[] = {
1210                 {BASEDIR "\\abc",               true, FILE_NOTIFY_CHANGE_NAME, 30 },
1211                 {BASEDIR "\\zqy",               true, FILE_NOTIFY_CHANGE_NAME, 8 },
1212                 {BASEDIR "\\atsy",              true, FILE_NOTIFY_CHANGE_NAME, 4 },
1213                 {BASEDIR "\\abc\\foo",          true,  FILE_NOTIFY_CHANGE_NAME, 2 },
1214                 {BASEDIR "\\abc\\blah",         true,  FILE_NOTIFY_CHANGE_NAME, 13 },
1215                 {BASEDIR "\\abc\\blah",         false, FILE_NOTIFY_CHANGE_NAME, 7 },
1216                 {BASEDIR "\\abc\\blah\\a",      true, FILE_NOTIFY_CHANGE_NAME, 2 },
1217                 {BASEDIR "\\abc\\blah\\b",      true, FILE_NOTIFY_CHANGE_NAME, 2 },
1218                 {BASEDIR "\\abc\\blah\\c",      true, FILE_NOTIFY_CHANGE_NAME, 2 },
1219                 {BASEDIR "\\abc\\fooblah",      true, FILE_NOTIFY_CHANGE_NAME, 2 },
1220                 {BASEDIR "\\zqy\\xx",           true, FILE_NOTIFY_CHANGE_NAME, 2 },
1221                 {BASEDIR "\\zqy\\yyy",          true, FILE_NOTIFY_CHANGE_NAME, 2 },
1222                 {BASEDIR "\\zqy\\..",           true, FILE_NOTIFY_CHANGE_NAME, 40 },
1223                 {BASEDIR,                       true, FILE_NOTIFY_CHANGE_NAME, 40 },
1224                 {BASEDIR,                       false,FILE_NOTIFY_CHANGE_NAME, 6 },
1225                 {BASEDIR "\\atsy",              false,FILE_NOTIFY_CHANGE_NAME, 4 },
1226                 {BASEDIR "\\abc",               true, FILE_NOTIFY_CHANGE_NAME, 24 },
1227                 {BASEDIR "\\abc",               false,FILE_NOTIFY_CHANGE_FILE_NAME, 0 },
1228                 {BASEDIR "\\abc",               true, FILE_NOTIFY_CHANGE_FILE_NAME, 0 },
1229                 {BASEDIR "\\abc",               true, FILE_NOTIFY_CHANGE_NAME, 24 },
1230         };
1231         int i;
1232         NTSTATUS status;
1233         bool all_done = false;
1234
1235         printf("TESTING CHANGE NOTIFY FOR DIFFERENT DEPTHS\n");
1236
1237         io.generic.level = RAW_OPEN_NTCREATEX;
1238         io.ntcreatex.in.root_fid.fnum = 0;
1239         io.ntcreatex.in.flags = 0;
1240         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1241         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1242         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1243         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
1244         io.ntcreatex.in.alloc_size = 0;
1245         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
1246         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1247         io.ntcreatex.in.security_flags = 0;
1248
1249         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1250         notify.nttrans.in.buffer_size = 20000;
1251
1252         /*
1253           setup the directory tree, and the notify buffer on each directory
1254         */
1255         for (i=0;i<ARRAY_SIZE(dirs);i++) {
1256                 io.ntcreatex.in.fname = dirs[i].path;
1257                 status = smb_raw_open(cli->tree, mem_ctx, &io);
1258                 CHECK_STATUS(status, NT_STATUS_OK);
1259                 dirs[i].fnum = io.ntcreatex.out.file.fnum;
1260
1261                 notify.nttrans.in.completion_filter = dirs[i].filter;
1262                 notify.nttrans.in.file.fnum = dirs[i].fnum;
1263                 notify.nttrans.in.recursive = dirs[i].recursive;
1264                 req = smb_raw_changenotify_send(cli->tree, &notify);
1265                 smb_raw_ntcancel(req);
1266                 status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
1267                 CHECK_STATUS(status, NT_STATUS_CANCELLED);
1268         }
1269
1270         /* trigger 2 events in each dir */
1271         for (i=0;i<ARRAY_SIZE(dirs);i++) {
1272                 char *path = talloc_asprintf(mem_ctx, "%s\\test.dir", dirs[i].path);
1273                 smbcli_mkdir(cli->tree, path);
1274                 smbcli_rmdir(cli->tree, path);
1275                 talloc_free(path);
1276         }
1277
1278         /* give a bit of time for the events to propogate */
1279         tv = timeval_current();
1280
1281         do {
1282                 /* count events that have happened in each dir */
1283                 for (i=0;i<ARRAY_SIZE(dirs);i++) {
1284                         notify.nttrans.in.file.fnum = dirs[i].fnum;
1285                         req = smb_raw_changenotify_send(cli->tree, &notify);
1286                         smb_raw_ntcancel(req);
1287                         notify.nttrans.out.num_changes = 0;
1288                         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
1289                         dirs[i].counted += notify.nttrans.out.num_changes;
1290                 }
1291                 
1292                 all_done = true;
1293
1294                 for (i=0;i<ARRAY_SIZE(dirs);i++) {
1295                         if (dirs[i].counted != dirs[i].expected) {
1296                                 all_done = false;
1297                         }
1298                 }
1299         } while (!all_done && timeval_elapsed(&tv) < 20);
1300
1301         printf("took %.4f seconds to propogate all events\n", timeval_elapsed(&tv));
1302
1303         for (i=0;i<ARRAY_SIZE(dirs);i++) {
1304                 if (dirs[i].counted != dirs[i].expected) {
1305                         printf("ERROR: i=%d expected %d got %d for '%s'\n",
1306                                i, dirs[i].expected, dirs[i].counted, dirs[i].path);
1307                         ret = false;
1308                 }
1309         }
1310
1311         /*
1312           run from the back, closing and deleting
1313         */
1314         for (i=ARRAY_SIZE(dirs)-1;i>=0;i--) {
1315                 smbcli_close(cli->tree, dirs[i].fnum);
1316                 smbcli_rmdir(cli->tree, dirs[i].path);
1317         }
1318
1319 done:
1320         smb_raw_exit(cli->session);
1321         return ret;
1322 }
1323
1324 /*
1325    Test response when cached server events exceed single NT NOTFIY response
1326    packet size.
1327 */
1328 static bool test_notify_overflow(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
1329 {
1330         bool ret = true;
1331         NTSTATUS status;
1332         union smb_notify notify;
1333         union smb_open io;
1334         int fnum;
1335         int count = 100;
1336         struct smbcli_request *req1;
1337         int i;
1338
1339         printf("TESTING CHANGE NOTIFY EVENT OVERFLOW\n");
1340
1341         /* get a handle on the directory */
1342         io.generic.level = RAW_OPEN_NTCREATEX;
1343         io.ntcreatex.in.root_fid.fnum = 0;
1344         io.ntcreatex.in.flags = 0;
1345         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1346         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1347         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1348         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1349             NTCREATEX_SHARE_ACCESS_WRITE;
1350         io.ntcreatex.in.alloc_size = 0;
1351         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1352         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1353         io.ntcreatex.in.security_flags = 0;
1354         io.ntcreatex.in.fname = BASEDIR;
1355
1356         status = smb_raw_open(cli->tree, mem_ctx, &io);
1357         CHECK_STATUS(status, NT_STATUS_OK);
1358         fnum = io.ntcreatex.out.file.fnum;
1359
1360         /* ask for a change notify, on name changes. */
1361         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1362         notify.nttrans.in.buffer_size = 1000;
1363         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1364         notify.nttrans.in.file.fnum = fnum;
1365
1366         notify.nttrans.in.recursive = true;
1367         req1 = smb_raw_changenotify_send(cli->tree, &notify);
1368
1369         /* cancel initial requests so the buffer is setup */
1370         smb_raw_ntcancel(req1);
1371         status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
1372         CHECK_STATUS(status, NT_STATUS_CANCELLED);
1373
1374         /* open a lot of files, filling up the server side notify buffer */
1375         printf("Testing overflowed buffer notify on create of %d files\n",
1376                count);
1377         for (i=0;i<count;i++) {
1378                 char *fname = talloc_asprintf(cli, BASEDIR "\\test%d.txt", i);
1379                 int fnum2 = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR,
1380                                         DENY_NONE);
1381                 if (fnum2 == -1) {
1382                         printf("Failed to create %s - %s\n",
1383                                fname, smbcli_errstr(cli->tree));
1384                         ret = false;
1385                         goto done;
1386                 }
1387                 talloc_free(fname);
1388                 smbcli_close(cli->tree, fnum2);
1389         }
1390
1391         /* expect that 0 events will be returned with NT_STATUS_OK */
1392         req1 = smb_raw_changenotify_send(cli->tree, &notify);
1393         status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
1394         CHECK_STATUS(status, NT_STATUS_OK);
1395         CHECK_VAL(notify.nttrans.out.num_changes, 0);
1396
1397 done:
1398         smb_raw_exit(cli->session);
1399         return ret;
1400 }
1401
1402 /*
1403    Test if notifications are returned for changes to the base directory.
1404    They shouldn't be.
1405 */
1406 static bool test_notify_basedir(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
1407 {
1408         bool ret = true;
1409         NTSTATUS status;
1410         union smb_notify notify;
1411         union smb_open io;
1412         int fnum;
1413         struct smbcli_request *req1;
1414
1415         printf("TESTING CHANGE NOTIFY BASEDIR EVENTS\n");
1416
1417         /* get a handle on the directory */
1418         io.generic.level = RAW_OPEN_NTCREATEX;
1419         io.ntcreatex.in.root_fid.fnum = 0;
1420         io.ntcreatex.in.flags = 0;
1421         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1422         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1423         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1424         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1425             NTCREATEX_SHARE_ACCESS_WRITE;
1426         io.ntcreatex.in.alloc_size = 0;
1427         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1428         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1429         io.ntcreatex.in.security_flags = 0;
1430         io.ntcreatex.in.fname = BASEDIR;
1431
1432         status = smb_raw_open(cli->tree, mem_ctx, &io);
1433         CHECK_STATUS(status, NT_STATUS_OK);
1434         fnum = io.ntcreatex.out.file.fnum;
1435
1436         /* create a test file that will also be modified */
1437         smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1",
1438                                             O_CREAT, 0));
1439
1440         /* ask for a change notify, on attribute changes. */
1441         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1442         notify.nttrans.in.buffer_size = 1000;
1443         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_ATTRIBUTES;
1444         notify.nttrans.in.file.fnum = fnum;
1445         notify.nttrans.in.recursive = true;
1446
1447         req1 = smb_raw_changenotify_send(cli->tree, &notify);
1448
1449         /* set attribute on the base dir */
1450         smbcli_setatr(cli->tree, BASEDIR, FILE_ATTRIBUTE_HIDDEN, 0);
1451
1452         /* set attribute on a file to assure we receive a notification */
1453         smbcli_setatr(cli->tree, BASEDIR "\\tname1", FILE_ATTRIBUTE_HIDDEN, 0);
1454         smb_msleep(200);
1455
1456         /* check how many responses were given, expect only 1 for the file */
1457         status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
1458         CHECK_STATUS(status, NT_STATUS_OK);
1459         CHECK_VAL(notify.nttrans.out.num_changes, 1);
1460         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_MODIFIED);
1461         CHECK_WSTR(notify.nttrans.out.changes[0].name, "tname1", STR_UNICODE);
1462
1463 done:
1464         smb_raw_exit(cli->session);
1465         return ret;
1466 }
1467
1468
1469 /*
1470   create a secondary tree connect - used to test for a bug in Samba3 messaging
1471   with change notify
1472 */
1473 static struct smbcli_tree *secondary_tcon(struct smbcli_state *cli, 
1474                                           struct torture_context *tctx)
1475 {
1476         NTSTATUS status;
1477         const char *share, *host;
1478         struct smbcli_tree *tree;
1479         union smb_tcon tcon;
1480
1481         share = torture_setting_string(tctx, "share", NULL);
1482         host  = torture_setting_string(tctx, "host", NULL);
1483         
1484         printf("create a second tree context on the same session\n");
1485         tree = smbcli_tree_init(cli->session, tctx, false);
1486
1487         tcon.generic.level = RAW_TCON_TCONX;
1488         tcon.tconx.in.flags = 0;
1489         tcon.tconx.in.password = data_blob(NULL, 0);
1490         tcon.tconx.in.path = talloc_asprintf(tctx, "\\\\%s\\%s", host, share);
1491         tcon.tconx.in.device = "A:";    
1492         status = smb_raw_tcon(tree, tctx, &tcon);
1493         if (!NT_STATUS_IS_OK(status)) {
1494                 talloc_free(tree);
1495                 printf("Failed to create secondary tree\n");
1496                 return NULL;
1497         }
1498
1499         tree->tid = tcon.tconx.out.tid;
1500         printf("tid1=%d tid2=%d\n", cli->tree->tid, tree->tid);
1501
1502         return tree;
1503 }
1504
1505
1506 /* 
1507    very simple change notify test
1508 */
1509 static bool test_notify_tcon(struct torture_context *torture,
1510                              struct smbcli_state *cli)
1511 {
1512         bool ret = true;
1513         NTSTATUS status;
1514         union smb_notify notify;
1515         union smb_open io;
1516         int fnum;
1517         struct smbcli_request *req;
1518         extern int torture_numops;
1519         struct smbcli_tree *tree = NULL;
1520                 
1521         printf("TESTING SIMPLE CHANGE NOTIFY\n");
1522                 
1523         if (!torture_setup_dir(cli, BASEDIR)) {
1524                 return false;
1525         }
1526
1527         /*
1528           get a handle on the directory
1529         */
1530         io.generic.level = RAW_OPEN_NTCREATEX;
1531         io.ntcreatex.in.root_fid.fnum = 0;
1532         io.ntcreatex.in.flags = 0;
1533         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1534         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1535         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1536         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
1537         io.ntcreatex.in.alloc_size = 0;
1538         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1539         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1540         io.ntcreatex.in.security_flags = 0;
1541         io.ntcreatex.in.fname = BASEDIR;
1542
1543         status = smb_raw_open(cli->tree, torture, &io);
1544         CHECK_STATUS(status, NT_STATUS_OK);
1545         fnum = io.ntcreatex.out.file.fnum;
1546
1547         status = smb_raw_open(cli->tree, torture, &io);
1548         CHECK_STATUS(status, NT_STATUS_OK);
1549
1550         /* ask for a change notify,
1551            on file or directory name changes */
1552         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1553         notify.nttrans.in.buffer_size = 1000;
1554         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1555         notify.nttrans.in.file.fnum = fnum;
1556         notify.nttrans.in.recursive = true;
1557
1558         printf("Testing notify mkdir\n");
1559         req = smb_raw_changenotify_send(cli->tree, &notify);
1560         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
1561
1562         status = smb_raw_changenotify_recv(req, torture, &notify);
1563         CHECK_STATUS(status, NT_STATUS_OK);
1564
1565         CHECK_VAL(notify.nttrans.out.num_changes, 1);
1566         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_ADDED);
1567         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
1568
1569         printf("Testing notify rmdir\n");
1570         req = smb_raw_changenotify_send(cli->tree, &notify);
1571         smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
1572
1573         status = smb_raw_changenotify_recv(req, torture, &notify);
1574         CHECK_STATUS(status, NT_STATUS_OK);
1575         CHECK_VAL(notify.nttrans.out.num_changes, 1);
1576         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
1577         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
1578
1579         printf("SIMPLE CHANGE NOTIFY OK\n");
1580
1581         printf("TESTING WITH SECONDARY TCON\n");
1582         tree = secondary_tcon(cli, torture);
1583
1584         printf("Testing notify mkdir\n");
1585         req = smb_raw_changenotify_send(cli->tree, &notify);
1586         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
1587
1588         status = smb_raw_changenotify_recv(req, torture, &notify);
1589         CHECK_STATUS(status, NT_STATUS_OK);
1590
1591         CHECK_VAL(notify.nttrans.out.num_changes, 1);
1592         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_ADDED);
1593         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
1594
1595         printf("Testing notify rmdir\n");
1596         req = smb_raw_changenotify_send(cli->tree, &notify);
1597         smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
1598
1599         status = smb_raw_changenotify_recv(req, torture, &notify);
1600         CHECK_STATUS(status, NT_STATUS_OK);
1601         CHECK_VAL(notify.nttrans.out.num_changes, 1);
1602         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
1603         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
1604
1605         printf("CHANGE NOTIFY WITH TCON OK\n");
1606
1607         printf("Disconnecting secondary tree\n");
1608         status = smb_tree_disconnect(tree);
1609         CHECK_STATUS(status, NT_STATUS_OK);
1610         talloc_free(tree);
1611
1612         printf("Testing notify mkdir\n");
1613         req = smb_raw_changenotify_send(cli->tree, &notify);
1614         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
1615
1616         status = smb_raw_changenotify_recv(req, torture, &notify);
1617         CHECK_STATUS(status, NT_STATUS_OK);
1618
1619         CHECK_VAL(notify.nttrans.out.num_changes, 1);
1620         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_ADDED);
1621         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
1622
1623         printf("Testing notify rmdir\n");
1624         req = smb_raw_changenotify_send(cli->tree, &notify);
1625         smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
1626
1627         status = smb_raw_changenotify_recv(req, torture, &notify);
1628         CHECK_STATUS(status, NT_STATUS_OK);
1629         CHECK_VAL(notify.nttrans.out.num_changes, 1);
1630         CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
1631         CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
1632
1633         printf("CHANGE NOTIFY WITH TDIS OK\n");
1634 done:
1635         smb_raw_exit(cli->session);
1636         smbcli_deltree(cli->tree, BASEDIR);
1637         return ret;
1638 }
1639
1640
1641 /*
1642    testing alignment of multiple change notify infos
1643 */
1644 static bool test_notify_alignment(struct smbcli_state *cli,
1645     struct torture_context *tctx)
1646 {
1647         NTSTATUS status;
1648         union smb_notify notify;
1649         union smb_open io;
1650         int i, fnum, fnum2;
1651         struct smbcli_request *req;
1652         const char *fname = BASEDIR "\\starter";
1653         const char *fnames[] = { "a",
1654                                  "ab",
1655                                  "abc",
1656                                  "abcd" };
1657         int num_names = ARRAY_SIZE(fnames);
1658         char *fpath = NULL;
1659
1660         torture_comment(tctx, "TESTING CHANGE NOTIFY REPLY ALIGNMENT\n");
1661
1662         /* get a handle on the directory */
1663         io.generic.level = RAW_OPEN_NTCREATEX;
1664         io.ntcreatex.in.root_fid.fnum = 0;
1665         io.ntcreatex.in.flags = 0;
1666         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1667         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1668         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1669         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1670                                        NTCREATEX_SHARE_ACCESS_WRITE;
1671         io.ntcreatex.in.alloc_size = 0;
1672         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1673         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1674         io.ntcreatex.in.security_flags = 0;
1675         io.ntcreatex.in.fname = BASEDIR;
1676
1677         status = smb_raw_open(cli->tree, tctx, &io);
1678         torture_assert_ntstatus_ok(tctx, status, "");
1679         fnum = io.ntcreatex.out.file.fnum;
1680
1681         /* ask for a change notify, on file creation */
1682         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1683         notify.nttrans.in.buffer_size = 1000;
1684         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_FILE_NAME;
1685         notify.nttrans.in.file.fnum = fnum;
1686         notify.nttrans.in.recursive = false;
1687
1688         /* start change tracking */
1689         req = smb_raw_changenotify_send(cli->tree, &notify);
1690
1691         fnum2 = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
1692         torture_assert(tctx, fnum2 != -1, smbcli_errstr(cli->tree));
1693         smbcli_close(cli->tree, fnum2);
1694
1695         status = smb_raw_changenotify_recv(req, tctx, &notify);
1696         torture_assert_ntstatus_ok(tctx, status, "");
1697
1698         /* create 4 files that will cause CHANGE_NOTIFY_INFO structures
1699          * to be returned in the same packet with all possible 4-byte padding
1700          * permutations.  As per MS-CIFS 2.2.7.4.2 these structures should be
1701          * 4-byte aligned. */
1702
1703         for (i = 0; i < num_names; i++) {
1704                 fpath = talloc_asprintf(tctx, "%s\\%s", BASEDIR, fnames[i]);
1705                 fnum2 = smbcli_open(cli->tree, fpath,
1706                     O_CREAT|O_RDWR, DENY_NONE);
1707                 torture_assert(tctx, fnum2 != -1, smbcli_errstr(cli->tree));
1708                 smbcli_close(cli->tree, fnum2);
1709                 talloc_free(fpath);
1710         }
1711
1712         /* We send a notify packet, and let smb_raw_changenotify_recv() do
1713          * the alignment checking for us. */
1714         req = smb_raw_changenotify_send(cli->tree, &notify);
1715         status = smb_raw_changenotify_recv(req, tctx, &notify);
1716         torture_assert_ntstatus_ok(tctx, status, "");
1717
1718         /* Do basic checking for correctness. */
1719         torture_assert(tctx, notify.nttrans.out.num_changes == num_names, "");
1720         for (i = 0; i < num_names; i++) {
1721                 torture_assert(tctx, notify.nttrans.out.changes[i].action ==
1722                     NOTIFY_ACTION_ADDED, "");
1723                 CHECK_WSTR2(tctx, notify.nttrans.out.changes[i].name, fnames[i],
1724                     STR_UNICODE);
1725         }
1726
1727         return true;
1728 }
1729
1730 /*
1731    basic testing of change notify
1732 */
1733 static bool test_raw_notify_all(struct torture_context *torture,
1734                                 struct smbcli_state *cli,
1735                                 struct smbcli_state *cli2)
1736 {
1737         bool ret = true;
1738
1739         if (!torture_setup_dir(cli, BASEDIR)) {
1740                 return false;
1741         }
1742
1743         ret &= test_notify_tdis(torture);
1744         ret &= test_notify_exit(torture);
1745         ret &= test_notify_ulogoff(torture);
1746         ret &= test_notify_tcp_dis(torture);
1747         ret &= test_notify_double(cli, torture);
1748         ret &= test_notify_tree(cli, torture);
1749         ret &= test_notify_overflow(cli, torture);
1750         ret &= test_notify_basedir(cli, torture);
1751         ret &= test_notify_alignment(cli, torture);
1752
1753         smb_raw_exit(cli->session);
1754         smbcli_deltree(cli->tree, BASEDIR);
1755         return ret;
1756 }
1757
1758 struct torture_suite *torture_raw_notify(TALLOC_CTX *mem_ctx)
1759 {
1760         struct torture_suite *suite = torture_suite_create(mem_ctx, "notify");
1761
1762         torture_suite_add_1smb_test(suite, "tcon", test_notify_tcon);
1763         torture_suite_add_2smb_test(suite, "dir", test_notify_dir);
1764         torture_suite_add_1smb_test(suite, "mask", test_notify_mask);
1765         torture_suite_add_1smb_test(suite, "recursive", test_notify_recursive);
1766         torture_suite_add_1smb_test(suite, "mask_change",
1767                                     test_notify_mask_change);
1768         torture_suite_add_1smb_test(suite, "file", test_notify_file);
1769         torture_suite_add_2smb_test(suite, "all", test_raw_notify_all);
1770
1771         return suite;
1772 }