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