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