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