pyldb: avoid segfault when adding an element with no name
[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, 250000, 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                 {
1469                         .path      = BASEDIR_CN1_TNT "\\abc",
1470                         .recursive = true,
1471                         .filter    = FILE_NOTIFY_CHANGE_NAME,
1472                         .expected  = 30,
1473                 },
1474                 {
1475                         .path      = BASEDIR_CN1_TNT "\\zqy",
1476                         .recursive = true,
1477                         .filter    = FILE_NOTIFY_CHANGE_NAME,
1478                         .expected  = 8,
1479                 },
1480                 {
1481                         .path      = BASEDIR_CN1_TNT "\\atsy",
1482                         .recursive = true,
1483                         .filter    = FILE_NOTIFY_CHANGE_NAME,
1484                         .expected  = 4,
1485                 },
1486                 {
1487                         .path      = BASEDIR_CN1_TNT "\\abc\\foo",
1488                         .recursive = true,
1489                         .filter    = FILE_NOTIFY_CHANGE_NAME,
1490                         .expected  = 2,
1491                 },
1492                 {
1493                         .path      = BASEDIR_CN1_TNT "\\abc\\blah",
1494                         .recursive = true,
1495                         .filter    = FILE_NOTIFY_CHANGE_NAME,
1496                         .expected  = 13,
1497                 },
1498                 {
1499                         .path      = BASEDIR_CN1_TNT "\\abc\\blah",
1500                         .recursive = false,
1501                         .filter    = FILE_NOTIFY_CHANGE_NAME,
1502                         .expected  = 7,
1503                 },
1504                 {
1505                         .path      = BASEDIR_CN1_TNT "\\abc\\blah\\a",
1506                         .recursive = true,
1507                         .filter    = FILE_NOTIFY_CHANGE_NAME,
1508                         .expected  = 2,
1509                 },
1510                 {
1511                         .path      = BASEDIR_CN1_TNT "\\abc\\blah\\b",
1512                         .recursive = true,
1513                         .filter    = FILE_NOTIFY_CHANGE_NAME,
1514                         .expected  = 2,
1515                 },
1516                 {
1517                         .path      = BASEDIR_CN1_TNT "\\abc\\blah\\c",
1518                         .recursive = true,
1519                         .filter    = FILE_NOTIFY_CHANGE_NAME,
1520                         .expected  = 2,
1521                 },
1522                 {
1523                         .path      = BASEDIR_CN1_TNT "\\abc\\fooblah",
1524                         .recursive = true,
1525                         .filter    = FILE_NOTIFY_CHANGE_NAME,
1526                         .expected  = 2,
1527                 },
1528                 {
1529                         .path      = BASEDIR_CN1_TNT "\\zqy\\xx",
1530                         .recursive = true,
1531                         .filter    = FILE_NOTIFY_CHANGE_NAME,
1532                         .expected  = 2,
1533                 },
1534                 {
1535                         .path      = BASEDIR_CN1_TNT "\\zqy\\yyy",
1536                         .recursive = true,
1537                         .filter    = FILE_NOTIFY_CHANGE_NAME,
1538                         .expected  = 2,
1539                 },
1540                 {
1541                         .path      = BASEDIR_CN1_TNT "\\zqy\\..",
1542                         .recursive = true,
1543                         .filter    = FILE_NOTIFY_CHANGE_NAME,
1544                         .expected  = 40,
1545                 },
1546                 {
1547                         .path      = BASEDIR_CN1_TNT,
1548                         .recursive = true,
1549                         .filter    = FILE_NOTIFY_CHANGE_NAME,
1550                         .expected  = 40,
1551                 },
1552                 {
1553                         .path      = BASEDIR_CN1_TNT,
1554                         .recursive = false,
1555                         .filter    = FILE_NOTIFY_CHANGE_NAME,
1556                         .expected  = 6,
1557                 },
1558                 {
1559                         .path      = BASEDIR_CN1_TNT "\\atsy",
1560                         .recursive = false,
1561                         .filter    = FILE_NOTIFY_CHANGE_NAME,
1562                         .expected  = 4,
1563                 },
1564                 {
1565                         .path      = BASEDIR_CN1_TNT "\\abc",
1566                         .recursive = true,
1567                         .filter    = FILE_NOTIFY_CHANGE_NAME,
1568                         .expected  = 24,
1569                 },
1570                 {
1571                         .path      = BASEDIR_CN1_TNT "\\abc",
1572                         .recursive = false,
1573                         .filter    = FILE_NOTIFY_CHANGE_FILE_NAME,
1574                         .expected  = 0,
1575                 },
1576                 {
1577                         .path      = BASEDIR_CN1_TNT "\\abc",
1578                         .recursive = true,
1579                         .filter    = FILE_NOTIFY_CHANGE_FILE_NAME,
1580                         .expected  = 0,
1581                 },
1582                 {
1583                         .path      = BASEDIR_CN1_TNT "\\abc",
1584                         .recursive = true,
1585                         .filter    = FILE_NOTIFY_CHANGE_NAME,
1586                         .expected  = 24,
1587                 },
1588         };
1589         int i;
1590         NTSTATUS status;
1591         bool all_done = false;
1592
1593         torture_comment(tctx, "TESTING CHANGE NOTIFY FOR DIFFERENT DEPTHS\n");
1594
1595         torture_assert(tctx, torture_setup_dir(cli, BASEDIR_CN1_TNT),
1596                        "Failed to setup up test directory: " BASEDIR_CN1_TNT);
1597
1598         io.generic.level = RAW_OPEN_NTCREATEX;
1599         io.ntcreatex.in.root_fid.fnum = 0;
1600         io.ntcreatex.in.flags = 0;
1601         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1602         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1603         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1604         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
1605         io.ntcreatex.in.alloc_size = 0;
1606         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
1607         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1608         io.ntcreatex.in.security_flags = 0;
1609
1610         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1611         notify.nttrans.in.buffer_size = 20000;
1612
1613         /*
1614           setup the directory tree, and the notify buffer on each directory
1615         */
1616         for (i=0;i<ARRAY_SIZE(dirs);i++) {
1617                 io.ntcreatex.in.fname = dirs[i].path;
1618                 status = smb_raw_open(cli->tree, tctx, &io);
1619                 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1620                                                 "smb_raw_open");
1621                 dirs[i].fnum = io.ntcreatex.out.file.fnum;
1622
1623                 notify.nttrans.in.completion_filter = dirs[i].filter;
1624                 notify.nttrans.in.file.fnum = dirs[i].fnum;
1625                 notify.nttrans.in.recursive = dirs[i].recursive;
1626                 req = smb_raw_changenotify_send(cli->tree, &notify);
1627                 smb_raw_ntcancel(req);
1628                 status = smb_raw_changenotify_recv(req, tctx, &notify);
1629                 torture_assert_ntstatus_equal_goto(tctx, status,
1630                                                    NT_STATUS_CANCELLED,
1631                                                    ret, done,
1632                                                    "smb_raw_changenotify_recv");
1633         }
1634
1635         /* trigger 2 events in each dir */
1636         for (i=0;i<ARRAY_SIZE(dirs);i++) {
1637                 char *path = talloc_asprintf(tctx, "%s\\test.dir", dirs[i].path);
1638                 /*
1639                  * Make notifies a bit more interesting in a cluster
1640                  * by doing the changes against different nodes with
1641                  * --unclist
1642                  */
1643                 smbcli_mkdir(cli->tree, path);
1644                 smbcli_rmdir(cli2->tree, path);
1645                 talloc_free(path);
1646         }
1647
1648         /* give a bit of time for the events to propagate */
1649         tv = timeval_current();
1650
1651         do {
1652                 /* count events that have happened in each dir */
1653                 for (i=0;i<ARRAY_SIZE(dirs);i++) {
1654                         notify.nttrans.in.file.fnum = dirs[i].fnum;
1655                         req = smb_raw_changenotify_send(cli->tree, &notify);
1656                         smb_raw_ntcancel(req);
1657                         notify.nttrans.out.num_changes = 0;
1658                         status = smb_raw_changenotify_recv(req, tctx, &notify);
1659                         dirs[i].counted += notify.nttrans.out.num_changes;
1660                 }
1661                 
1662                 all_done = true;
1663
1664                 for (i=0;i<ARRAY_SIZE(dirs);i++) {
1665                         if (dirs[i].counted != dirs[i].expected) {
1666                                 all_done = false;
1667                         }
1668                 }
1669         } while (!all_done && timeval_elapsed(&tv) < 20);
1670
1671         torture_comment(tctx, "took %.4f seconds to propagate all events\n", timeval_elapsed(&tv));
1672
1673         for (i=0;i<ARRAY_SIZE(dirs);i++) {
1674                 torture_assert_int_equal_goto(tctx,
1675                         dirs[i].counted, dirs[i].expected, ret, done,
1676                         talloc_asprintf(tctx,
1677                                         "unexpected number of events for '%s'",
1678                                         dirs[i].path));
1679         }
1680
1681         /*
1682           run from the back, closing and deleting
1683         */
1684         for (i=ARRAY_SIZE(dirs)-1;i>=0;i--) {
1685                 smbcli_close(cli->tree, dirs[i].fnum);
1686                 smbcli_rmdir(cli->tree, dirs[i].path);
1687         }
1688
1689 done:
1690         smb_raw_exit(cli->session);
1691         smbcli_deltree(cli->tree, BASEDIR_CN1_TNT);
1692         return ret;
1693 }
1694
1695 /*
1696    Test response when cached server events exceed single NT NOTFIY response
1697    packet size.
1698 */
1699
1700 #define BASEDIR_CN1_NO BASEDIR "_CN1_NO"
1701
1702 static bool test_notify_overflow(struct torture_context *tctx,
1703                                  struct smbcli_state *cli)
1704 {
1705         bool ret = true;
1706         NTSTATUS status;
1707         union smb_notify notify;
1708         union smb_open io;
1709         int fnum;
1710         int count = 100;
1711         struct smbcli_request *req1;
1712         int i;
1713
1714         torture_comment(tctx, "TESTING CHANGE NOTIFY EVENT OVERFLOW\n");
1715
1716         torture_assert(tctx, torture_setup_dir(cli, BASEDIR_CN1_NO),
1717                        "Failed to setup up test directory: " BASEDIR_CN1_NO);
1718
1719         /* get a handle on the directory */
1720         io.generic.level = RAW_OPEN_NTCREATEX;
1721         io.ntcreatex.in.root_fid.fnum = 0;
1722         io.ntcreatex.in.flags = 0;
1723         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1724         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1725         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1726         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1727             NTCREATEX_SHARE_ACCESS_WRITE;
1728         io.ntcreatex.in.alloc_size = 0;
1729         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1730         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1731         io.ntcreatex.in.security_flags = 0;
1732         io.ntcreatex.in.fname = BASEDIR_CN1_NO;
1733
1734         status = smb_raw_open(cli->tree, tctx, &io);
1735         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1736                                         "smb_raw_open");
1737         fnum = io.ntcreatex.out.file.fnum;
1738
1739         /* ask for a change notify, on name changes. */
1740         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1741         notify.nttrans.in.buffer_size = 1000;
1742         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1743         notify.nttrans.in.file.fnum = fnum;
1744
1745         notify.nttrans.in.recursive = true;
1746         req1 = smb_raw_changenotify_send(cli->tree, &notify);
1747
1748         /* cancel initial requests so the buffer is setup */
1749         smb_raw_ntcancel(req1);
1750         status = smb_raw_changenotify_recv(req1, tctx, &notify);
1751         torture_assert_ntstatus_equal_goto(tctx, status,
1752                                            NT_STATUS_CANCELLED,
1753                                            ret, done,
1754                                            "smb_raw_changenotify_recv");
1755
1756         /* open a lot of files, filling up the server side notify buffer */
1757         torture_comment(tctx, "Testing overflowed buffer notify on create of %d files\n",
1758                count);
1759         for (i=0;i<count;i++) {
1760                 char *fname = talloc_asprintf(cli,
1761                                 BASEDIR_CN1_NO "\\test%d.txt", i);
1762                 int fnum2 = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR,
1763                                         DENY_NONE);
1764                 torture_assert_int_not_equal_goto(tctx, fnum2, -1, ret, done,
1765                         talloc_asprintf(tctx, "Failed to create %s - %s",
1766                                         fname, smbcli_errstr(cli->tree)));
1767                 talloc_free(fname);
1768                 smbcli_close(cli->tree, fnum2);
1769         }
1770
1771         /* expect that 0 events will be returned with NT_STATUS_OK */
1772         req1 = smb_raw_changenotify_send(cli->tree, &notify);
1773         status = smb_raw_changenotify_recv(req1, tctx, &notify);
1774         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1775                                         "smb_raw_changenotify_recv");
1776         torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes,
1777                                       0, ret, done, "no changes expected");
1778
1779 done:
1780         smb_raw_exit(cli->session);
1781         smbcli_deltree(cli->tree, BASEDIR_CN1_NO);
1782         return ret;
1783 }
1784
1785 /*
1786    Test if notifications are returned for changes to the base directory.
1787    They shouldn't be.
1788 */
1789
1790 #define BASEDIR_CN1_NBASE BASEDIR "_CN1_NBASE"
1791
1792 static bool test_notify_basedir(struct torture_context *tctx,
1793                                 struct smbcli_state *cli)
1794 {
1795         bool ret = true;
1796         NTSTATUS status;
1797         union smb_notify notify;
1798         union smb_open io;
1799         int fnum;
1800         struct smbcli_request *req1;
1801
1802         torture_comment(tctx, "TESTING CHANGE NOTIFY BASEDIR EVENTS\n");
1803
1804         torture_assert(tctx, torture_setup_dir(cli, BASEDIR_CN1_NBASE),
1805                        "Failed to setup up test directory: " BASEDIR_CN1_NBASE);
1806
1807         /* get a handle on the directory */
1808         io.generic.level = RAW_OPEN_NTCREATEX;
1809         io.ntcreatex.in.root_fid.fnum = 0;
1810         io.ntcreatex.in.flags = 0;
1811         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1812         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1813         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1814         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1815             NTCREATEX_SHARE_ACCESS_WRITE;
1816         io.ntcreatex.in.alloc_size = 0;
1817         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1818         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1819         io.ntcreatex.in.security_flags = 0;
1820         io.ntcreatex.in.fname = BASEDIR_CN1_NBASE;
1821
1822         status = smb_raw_open(cli->tree, tctx, &io);
1823         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1824                                         "smb_raw_open");
1825         fnum = io.ntcreatex.out.file.fnum;
1826
1827         /* create a test file that will also be modified */
1828         smbcli_close(cli->tree, smbcli_open(cli->tree,
1829                                 BASEDIR_CN1_NBASE "\\tname1",
1830                                             O_CREAT, 0));
1831
1832         /* ask for a change notify, on attribute changes. */
1833         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1834         notify.nttrans.in.buffer_size = 1000;
1835         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_ATTRIBUTES;
1836         notify.nttrans.in.file.fnum = fnum;
1837         notify.nttrans.in.recursive = true;
1838
1839         req1 = smb_raw_changenotify_send(cli->tree, &notify);
1840
1841         /* set attribute on the base dir */
1842         smbcli_setatr(cli->tree, BASEDIR_CN1_NBASE, FILE_ATTRIBUTE_HIDDEN, 0);
1843
1844         /* set attribute on a file to assure we receive a notification */
1845         smbcli_setatr(cli->tree, BASEDIR_CN1_NBASE "\\tname1",
1846                         FILE_ATTRIBUTE_HIDDEN, 0);
1847         smb_msleep(200);
1848
1849         /* check how many responses were given, expect only 1 for the file */
1850         status = smb_raw_changenotify_recv(req1, tctx, &notify);
1851         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1852                                         "smb_raw_changenotify_recv");
1853         torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes,
1854                                       1, ret, done, "wrong number of  changes");
1855         torture_assert_int_equal_goto(tctx,
1856                                       notify.nttrans.out.changes[0].action,
1857                                       NOTIFY_ACTION_MODIFIED, ret, done,
1858                                       "wrong action (exp: MODIFIED)");
1859         CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "tname1",
1860                    STR_UNICODE);
1861
1862 done:
1863         smb_raw_exit(cli->session);
1864         smbcli_deltree(cli->tree, BASEDIR_CN1_NBASE);
1865         return ret;
1866 }
1867
1868
1869 /*
1870   create a secondary tree connect - used to test for a bug in Samba3 messaging
1871   with change notify
1872 */
1873
1874 static struct smbcli_tree *secondary_tcon(struct smbcli_state *cli, 
1875                                           struct torture_context *tctx)
1876 {
1877         NTSTATUS status;
1878         const char *share, *host;
1879         struct smbcli_tree *tree;
1880         union smb_tcon tcon;
1881
1882         share = torture_setting_string(tctx, "share", NULL);
1883         host  = torture_setting_string(tctx, "host", NULL);
1884         
1885         torture_comment(tctx, "create a second tree context on the same session\n");
1886         tree = smbcli_tree_init(cli->session, tctx, false);
1887
1888         tcon.generic.level = RAW_TCON_TCONX;
1889         tcon.tconx.in.flags = TCONX_FLAG_EXTENDED_RESPONSE;
1890         tcon.tconx.in.password = data_blob(NULL, 0);
1891         tcon.tconx.in.path = talloc_asprintf(tctx, "\\\\%s\\%s", host, share);
1892         tcon.tconx.in.device = "A:";    
1893         status = smb_raw_tcon(tree, tctx, &tcon);
1894         if (!NT_STATUS_IS_OK(status)) {
1895                 talloc_free(tree);
1896                 torture_comment(tctx, "Failed to create secondary tree\n");
1897                 return NULL;
1898         }
1899
1900         tree->tid = tcon.tconx.out.tid;
1901         torture_comment(tctx, "tid1=%d tid2=%d\n", cli->tree->tid, tree->tid);
1902
1903         return tree;
1904 }
1905
1906
1907 /* 
1908    very simple change notify test
1909 */
1910
1911 #define BASEDIR_CN1_NTCON BASEDIR "_CN1_NTCON"
1912
1913 static bool test_notify_tcon(struct torture_context *tctx,
1914                              struct smbcli_state *cli)
1915 {
1916         bool ret = true;
1917         NTSTATUS status;
1918         union smb_notify notify;
1919         union smb_open io;
1920         int fnum;
1921         struct smbcli_request *req;
1922         extern int torture_numops;
1923         struct smbcli_tree *tree = NULL;
1924                 
1925         torture_comment(tctx, "TESTING SIMPLE CHANGE NOTIFY\n");
1926
1927         torture_assert(tctx, torture_setup_dir(cli, BASEDIR_CN1_NTCON),
1928                        "Failed to setup up test directory: " BASEDIR_CN1_NTCON);
1929
1930         /*
1931           get a handle on the directory
1932         */
1933         io.generic.level = RAW_OPEN_NTCREATEX;
1934         io.ntcreatex.in.root_fid.fnum = 0;
1935         io.ntcreatex.in.flags = 0;
1936         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1937         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1938         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1939         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
1940         io.ntcreatex.in.alloc_size = 0;
1941         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1942         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1943         io.ntcreatex.in.security_flags = 0;
1944         io.ntcreatex.in.fname = BASEDIR_CN1_NTCON;
1945
1946         status = smb_raw_open(cli->tree, tctx, &io);
1947         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1948                                         "smb_raw_open");
1949         fnum = io.ntcreatex.out.file.fnum;
1950
1951         status = smb_raw_open(cli->tree, tctx, &io);
1952         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1953                                         "smb_raw_open");
1954
1955         /* ask for a change notify,
1956            on file or directory name changes */
1957         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1958         notify.nttrans.in.buffer_size = 1000;
1959         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1960         notify.nttrans.in.file.fnum = fnum;
1961         notify.nttrans.in.recursive = true;
1962
1963         torture_comment(tctx, "Testing notify mkdir\n");
1964         req = smb_raw_changenotify_send(cli->tree, &notify);
1965         smbcli_mkdir(cli->tree, BASEDIR_CN1_NTCON "\\subdir-name");
1966
1967         status = smb_raw_changenotify_recv(req, tctx, &notify);
1968         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1969                                         "smb_raw_changenotify_recv");
1970
1971         torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes,
1972                                       1, ret, done, "wrong number of changes");
1973         torture_assert_int_equal_goto(tctx,
1974                                       notify.nttrans.out.changes[0].action,
1975                                       NOTIFY_ACTION_ADDED, ret, done,
1976                                       "wrong action (exp: ADDED)");
1977         CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subdir-name",
1978                    STR_UNICODE);
1979
1980         torture_comment(tctx, "Testing notify rmdir\n");
1981         req = smb_raw_changenotify_send(cli->tree, &notify);
1982         smbcli_rmdir(cli->tree, BASEDIR_CN1_NTCON "\\subdir-name");
1983
1984         status = smb_raw_changenotify_recv(req, tctx, &notify);
1985         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1986                                         "smb_raw_changenotify_recv");
1987         torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes,
1988                                       1, ret, done, "wrong number of changes");
1989         torture_assert_int_equal_goto(tctx,
1990                                       notify.nttrans.out.changes[0].action,
1991                                       NOTIFY_ACTION_REMOVED, ret, done,
1992                                       "wrong action (exp: REMOVED)");
1993         CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subdir-name",
1994                    STR_UNICODE);
1995
1996         torture_comment(tctx, "SIMPLE CHANGE NOTIFY OK\n");
1997
1998         torture_comment(tctx, "TESTING WITH SECONDARY TCON\n");
1999         tree = secondary_tcon(cli, tctx);
2000         torture_assert_not_null_goto(tctx, tree, ret, done,
2001                                      "failed to create secondary tcon");
2002
2003         torture_comment(tctx, "Testing notify mkdir\n");
2004         req = smb_raw_changenotify_send(cli->tree, &notify);
2005         smbcli_mkdir(cli->tree, BASEDIR_CN1_NTCON "\\subdir-name");
2006
2007         status = smb_raw_changenotify_recv(req, tctx, &notify);
2008         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2009                                         "smb_raw_changenotify_recv");
2010
2011         torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes,
2012                                       1, ret, done, "wrong number of changes");
2013         torture_assert_int_equal_goto(tctx,
2014                                       notify.nttrans.out.changes[0].action,
2015                                       NOTIFY_ACTION_ADDED, ret, done,
2016                                       "wrong action (exp: ADDED)");
2017         CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subdir-name",
2018                    STR_UNICODE);
2019
2020         torture_comment(tctx, "Testing notify rmdir\n");
2021         req = smb_raw_changenotify_send(cli->tree, &notify);
2022         smbcli_rmdir(cli->tree, BASEDIR_CN1_NTCON "\\subdir-name");
2023
2024         status = smb_raw_changenotify_recv(req, tctx, &notify);
2025         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2026                                         "smb_raw_changenotify_recv");
2027         torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes,
2028                                       1, ret, done, "wrong number of changes");
2029         torture_assert_int_equal_goto(tctx,
2030                                       notify.nttrans.out.changes[0].action,
2031                                       NOTIFY_ACTION_REMOVED, ret, done,
2032                                       "wrong action (exp: REMOVED)");
2033         CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subdir-name",
2034                    STR_UNICODE);
2035
2036         torture_comment(tctx, "CHANGE NOTIFY WITH TCON OK\n");
2037
2038         torture_comment(tctx, "Disconnecting secondary tree\n");
2039         status = smb_tree_disconnect(tree);
2040         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2041                                         "smb_tree_disconnect");
2042         talloc_free(tree);
2043
2044         torture_comment(tctx, "Testing notify mkdir\n");
2045         req = smb_raw_changenotify_send(cli->tree, &notify);
2046         smbcli_mkdir(cli->tree, BASEDIR_CN1_NTCON "\\subdir-name");
2047
2048         status = smb_raw_changenotify_recv(req, tctx, &notify);
2049         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2050                                         "smb_raw_changenotify_recv");
2051
2052         torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes,
2053                                       1, ret, done, "wrong number of changes");
2054         torture_assert_int_equal_goto(tctx,
2055                                       notify.nttrans.out.changes[0].action,
2056                                       NOTIFY_ACTION_ADDED, ret, done,
2057                                       "wrong action (exp: ADDED)");
2058         CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subdir-name",
2059                    STR_UNICODE);
2060
2061         torture_comment(tctx, "Testing notify rmdir\n");
2062         req = smb_raw_changenotify_send(cli->tree, &notify);
2063         smbcli_rmdir(cli->tree, BASEDIR_CN1_NTCON "\\subdir-name");
2064
2065         status = smb_raw_changenotify_recv(req, tctx, &notify);
2066         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2067                                         "smb_raw_changenotify_recv");
2068         torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes,
2069                                       1, ret, done, "wrong number of changes");
2070         torture_assert_int_equal_goto(tctx,
2071                                       notify.nttrans.out.changes[0].action,
2072                                       NOTIFY_ACTION_REMOVED, ret, done,
2073                                       "wrong action (exp: REMOVED)");
2074         CHECK_WSTR(tctx, notify.nttrans.out.changes[0].name, "subdir-name",
2075                    STR_UNICODE);
2076
2077         torture_comment(tctx, "CHANGE NOTIFY WITH TDIS OK\n");
2078 done:
2079         smb_raw_exit(cli->session);
2080         smbcli_deltree(cli->tree, BASEDIR_CN1_NTCON);
2081         return ret;
2082 }
2083
2084 struct cb_data {
2085         struct smbcli_request *req;
2086         bool timed_out;
2087 };
2088
2089 static void timeout_cb(struct tevent_context *ev,
2090                         struct tevent_timer *te,
2091                         struct timeval current_time,
2092                         void *private_data)
2093 {
2094         struct cb_data *cbp = (struct cb_data *)private_data;
2095         cbp->req->state = SMBCLI_REQUEST_ERROR;
2096         cbp->timed_out = true;
2097 }
2098
2099 /*
2100    testing alignment of multiple change notify infos
2101 */
2102
2103 #define BASEDIR_CN1_NALIGN BASEDIR "_CN1_NALIGN"
2104
2105 static bool test_notify_alignment(struct torture_context *tctx,
2106                                   struct smbcli_state *cli)
2107 {
2108         NTSTATUS status;
2109         union smb_notify notify;
2110         union smb_open io;
2111         int fnum, fnum2;
2112         struct smbcli_request *req;
2113         const char *fname = BASEDIR_CN1_NALIGN "\\starter";
2114         const char *fnames[] = { "a",
2115                                  "ab",
2116                                  "abc",
2117                                  "abcd" };
2118         bool fnames_received[] = {false,
2119                                   false,
2120                                   false,
2121                                   false};
2122         size_t total_names_received = 0;
2123         size_t num_names = ARRAY_SIZE(fnames);
2124         size_t i;
2125         char *fpath = NULL;
2126
2127         torture_comment(tctx, "TESTING CHANGE NOTIFY REPLY ALIGNMENT\n");
2128
2129         torture_assert(tctx, torture_setup_dir(cli, BASEDIR_CN1_NALIGN),
2130                 "Failed to setup up test directory: " BASEDIR_CN1_NALIGN);
2131
2132         /* get a handle on the directory */
2133         io.generic.level = RAW_OPEN_NTCREATEX;
2134         io.ntcreatex.in.root_fid.fnum = 0;
2135         io.ntcreatex.in.flags = 0;
2136         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
2137         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
2138         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
2139         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
2140                                        NTCREATEX_SHARE_ACCESS_WRITE;
2141         io.ntcreatex.in.alloc_size = 0;
2142         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
2143         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
2144         io.ntcreatex.in.security_flags = 0;
2145         io.ntcreatex.in.fname = BASEDIR_CN1_NALIGN;
2146
2147         status = smb_raw_open(cli->tree, tctx, &io);
2148         torture_assert_ntstatus_ok(tctx, status, "smb_raw_open");
2149         fnum = io.ntcreatex.out.file.fnum;
2150
2151         /* ask for a change notify, on file creation */
2152         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
2153         notify.nttrans.in.buffer_size = 1000;
2154         notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_FILE_NAME;
2155         notify.nttrans.in.file.fnum = fnum;
2156         notify.nttrans.in.recursive = false;
2157
2158         /* start change tracking */
2159         req = smb_raw_changenotify_send(cli->tree, &notify);
2160
2161         fnum2 = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
2162         torture_assert(tctx, fnum2 != -1, smbcli_errstr(cli->tree));
2163         smbcli_close(cli->tree, fnum2);
2164
2165         status = smb_raw_changenotify_recv(req, tctx, &notify);
2166         torture_assert_ntstatus_ok(tctx, status, "smb_raw_changenotify_recv");
2167
2168         /* create 4 files that will cause CHANGE_NOTIFY_INFO structures
2169          * to be returned in the same packet with all possible 4-byte padding
2170          * permutations.  As per MS-CIFS 2.2.7.4.2 these structures should be
2171          * 4-byte aligned. */
2172
2173         for (i = 0; i < num_names; i++) {
2174                 fpath = talloc_asprintf(tctx, "%s\\%s",
2175                                 BASEDIR_CN1_NALIGN, fnames[i]);
2176                 fnum2 = smbcli_open(cli->tree, fpath,
2177                     O_CREAT|O_RDWR, DENY_NONE);
2178                 torture_assert(tctx, fnum2 != -1, smbcli_errstr(cli->tree));
2179                 smbcli_close(cli->tree, fnum2);
2180                 talloc_free(fpath);
2181         }
2182
2183         /*
2184          * Slow cloud filesystems mean we might
2185          * not get everything in one go. Keep going
2186          * until we get them all.
2187          */
2188         while (total_names_received < num_names) {
2189                 struct tevent_timer *te = NULL;
2190                 struct cb_data to_data = {0};
2191
2192                 /*
2193                  * We send a notify packet, and let
2194                  * smb_raw_changenotify_recv() do
2195                  * the alignment checking for us.
2196                  */
2197                 req = smb_raw_changenotify_send(cli->tree, &notify);
2198                 torture_assert(tctx,
2199                         req != NULL,
2200                         "smb_raw_changenotify_send failed\n");
2201
2202                 /* Ensure we don't wait more than 30 seconds. */
2203                 to_data.req = req;
2204                 to_data.timed_out = false;
2205
2206                 te = tevent_add_timer(tctx->ev,
2207                                 req,
2208                                 tevent_timeval_current_ofs(30, 0),
2209                                 timeout_cb,
2210                                 &to_data);
2211                 if (te == NULL) {
2212                         torture_fail(tctx, "tevent_add_timer fail\n");
2213                 }
2214
2215                 status = smb_raw_changenotify_recv(req, tctx, &notify);
2216                 if (!NT_STATUS_IS_OK(status)) {
2217                         if (to_data.timed_out == true) {
2218                                 torture_fail(tctx, "smb_raw_changenotify_recv "
2219                                         "timed out\n");
2220                         }
2221                 }
2222
2223                 torture_assert_ntstatus_ok(tctx, status,
2224                         "smb_raw_changenotify_recv");
2225
2226                 for (i = 0; i < notify.nttrans.out.num_changes; i++) {
2227                         size_t j;
2228
2229                         /* Ensure it was an 'add'. */
2230                         torture_assert(tctx,
2231                                 notify.nttrans.out.changes[i].action ==
2232                                         NOTIFY_ACTION_ADDED,
2233                                 "");
2234
2235                         for (j = 0; j < num_names; j++) {
2236                                 if (strcmp(notify.nttrans.out.changes[i].name.s,
2237                                                 fnames[j]) == 0) {
2238                                         if (fnames_received[j] == true) {
2239                                                 const char *err =
2240                                                         talloc_asprintf(tctx,
2241                                                                 "Duplicate "
2242                                                                 "name %s\n",
2243                                                                 fnames[j]);
2244                                                 if (err == NULL) {
2245                                                         torture_fail(tctx,
2246                                                                 "talloc "
2247                                                                 "fail\n");
2248                                                 }
2249                                                 /* already got this. */
2250                                                 torture_fail(tctx, err);
2251                                         }
2252                                         fnames_received[j] = true;
2253                                         break;
2254                                 }
2255                         }
2256                         if (j == num_names) {
2257                                 /* No name match. */
2258                                 const char *err = talloc_asprintf(tctx,
2259                                         "Unexpected name %s\n",
2260                                         notify.nttrans.out.changes[i].name.s);
2261                                 if (err == NULL) {
2262                                         torture_fail(tctx, "talloc fail\n");
2263                                 }
2264                                 torture_fail(tctx, err);
2265                         }
2266                         total_names_received++;
2267                 }
2268         }
2269
2270         smb_raw_exit(cli->session);
2271         smbcli_deltree(cli->tree, BASEDIR_CN1_NALIGN);
2272         return true;
2273 }
2274
2275 struct torture_suite *torture_raw_notify(TALLOC_CTX *mem_ctx)
2276 {
2277         struct torture_suite *suite = torture_suite_create(mem_ctx, "notify");
2278
2279         torture_suite_add_1smb_test(suite, "tcon", test_notify_tcon);
2280         torture_suite_add_2smb_test(suite, "dir", test_notify_dir);
2281         torture_suite_add_2smb_test(suite, "mask", test_notify_mask);
2282         torture_suite_add_2smb_test(suite, "recursive", test_notify_recursive);
2283         torture_suite_add_1smb_test(suite, "mask_change",
2284                                     test_notify_mask_change);
2285         torture_suite_add_1smb_test(suite, "file", test_notify_file);
2286         torture_suite_add_1smb_test(suite, "tdis", test_notify_tdis);
2287         torture_suite_add_1smb_test(suite, "exit", test_notify_exit);
2288         torture_suite_add_1smb_test(suite, "ulogoff", test_notify_ulogoff);
2289         torture_suite_add_1smb_test(suite, "tcp_dis", test_notify_tcp_dis);
2290         torture_suite_add_1smb_test(suite, "double", test_notify_double);
2291         torture_suite_add_2smb_test(suite, "tree", test_notify_tree);
2292         torture_suite_add_1smb_test(suite, "overflow", test_notify_overflow);
2293         torture_suite_add_1smb_test(suite, "basedir", test_notify_basedir);
2294         torture_suite_add_1smb_test(suite, "alignment", test_notify_alignment);
2295
2296         return suite;
2297 }