8a982a721117a29eeb3b22cd23d0f5df5ebdb3a5
[gd/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 2 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, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22 #include "torture/torture.h"
23 #include "libcli/raw/libcliraw.h"
24 #include "libcli/libcli.h"
25 #include "system/filesys.h"
26 #include "torture/util.h"
27
28 #define BASEDIR "\\test_notify"
29
30 #define CHECK_STATUS(status, correct) do { \
31         if (!NT_STATUS_EQUAL(status, correct)) { \
32                 printf("(%d) Incorrect status %s - should be %s\n", \
33                        __LINE__, nt_errstr(status), nt_errstr(correct)); \
34                 ret = False; \
35                 goto done; \
36         }} while (0)
37
38
39 #define CHECK_VAL(v, correct) do { \
40         if ((v) != (correct)) { \
41                 printf("(%d) wrong value for %s  0x%x should be 0x%x\n", \
42                        __LINE__, #v, (int)v, (int)correct); \
43                 ret = False; \
44                 goto done; \
45         }} while (0)
46
47 #define CHECK_WSTR(field, value, flags) do { \
48         if (!field.s || strcmp(field.s, value) || wire_bad_flags(&field, flags, cli)) { \
49                 printf("(%d) %s [%s] != %s\n",  __LINE__, #field, field.s, value); \
50                         ret = False; \
51                 goto done; \
52         }} while (0)
53
54
55 /* 
56    basic testing of change notify on directories
57 */
58 static BOOL test_notify_dir(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
59 {
60         BOOL ret = True;
61         NTSTATUS status;
62         struct smb_notify notify;
63         union smb_open io;
64         union smb_close cl;
65         int i, count, fnum, fnum2;
66         struct smbcli_request *req, *req2;
67         extern int torture_numops;
68
69         printf("TESTING CHANGE NOTIFY ON DIRECTRIES\n");
70                 
71         /*
72           get a handle on the directory
73         */
74         io.generic.level = RAW_OPEN_NTCREATEX;
75         io.ntcreatex.in.root_fid = 0;
76         io.ntcreatex.in.flags = 0;
77         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
78         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
79         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
80         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
81         io.ntcreatex.in.alloc_size = 0;
82         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
83         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
84         io.ntcreatex.in.security_flags = 0;
85         io.ntcreatex.in.fname = BASEDIR;
86
87         status = smb_raw_open(cli->tree, mem_ctx, &io);
88         CHECK_STATUS(status, NT_STATUS_OK);
89         fnum = io.ntcreatex.out.file.fnum;
90
91         status = smb_raw_open(cli->tree, mem_ctx, &io);
92         CHECK_STATUS(status, NT_STATUS_OK);
93         fnum2 = io.ntcreatex.out.file.fnum;
94
95         /* ask for a change notify,
96            on file or directory name changes */
97         notify.in.buffer_size = 1000;
98         notify.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
99         notify.in.file.fnum = fnum;
100         notify.in.recursive = True;
101
102         printf("testing notify cancel\n");
103
104         req = smb_raw_changenotify_send(cli->tree, &notify);
105         smb_raw_ntcancel(req);
106         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
107         CHECK_STATUS(status, NT_STATUS_CANCELLED);
108
109         printf("testing notify mkdir\n");
110
111         req = smb_raw_changenotify_send(cli->tree, &notify);
112         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
113
114         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
115         CHECK_STATUS(status, NT_STATUS_OK);
116
117         CHECK_VAL(notify.out.num_changes, 1);
118         CHECK_VAL(notify.out.changes[0].action, NOTIFY_ACTION_ADDED);
119         CHECK_WSTR(notify.out.changes[0].name, "subdir-name", STR_UNICODE);
120
121         printf("testing notify rmdir\n");
122
123         req = smb_raw_changenotify_send(cli->tree, &notify);
124         smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
125
126         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
127         CHECK_STATUS(status, NT_STATUS_OK);
128         CHECK_VAL(notify.out.num_changes, 1);
129         CHECK_VAL(notify.out.changes[0].action, NOTIFY_ACTION_REMOVED);
130         CHECK_WSTR(notify.out.changes[0].name, "subdir-name", STR_UNICODE);
131
132         printf("testing notify mkdir - rmdir - mkdir - rmdir\n");
133
134         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
135         smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
136         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
137         smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
138         req = smb_raw_changenotify_send(cli->tree, &notify);
139         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
140         CHECK_STATUS(status, NT_STATUS_OK);
141         CHECK_VAL(notify.out.num_changes, 4);
142         CHECK_VAL(notify.out.changes[0].action, NOTIFY_ACTION_ADDED);
143         CHECK_WSTR(notify.out.changes[0].name, "subdir-name", STR_UNICODE);
144         CHECK_VAL(notify.out.changes[1].action, NOTIFY_ACTION_REMOVED);
145         CHECK_WSTR(notify.out.changes[1].name, "subdir-name", STR_UNICODE);
146         CHECK_VAL(notify.out.changes[2].action, NOTIFY_ACTION_ADDED);
147         CHECK_WSTR(notify.out.changes[2].name, "subdir-name", STR_UNICODE);
148         CHECK_VAL(notify.out.changes[3].action, NOTIFY_ACTION_REMOVED);
149         CHECK_WSTR(notify.out.changes[3].name, "subdir-name", STR_UNICODE);
150
151         count = torture_numops;
152         printf("testing buffered notify on create of %d files\n", count);
153         for (i=0;i<count;i++) {
154                 char *fname = talloc_asprintf(cli, BASEDIR "\\test%d.txt", i);
155                 int fnum3 = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
156                 if (fnum3 == -1) {
157                         printf("Failed to create %s - %s\n", 
158                                fname, smbcli_errstr(cli->tree));
159                         ret = False;
160                         goto done;
161                 }
162                 talloc_free(fname);
163                 smbcli_close(cli->tree, fnum3);
164         }
165
166         /* (1st notify) setup a new notify on a different directory handle.
167            This new notify won't see the events above. */
168         notify.in.file.fnum = fnum2;
169         req2 = smb_raw_changenotify_send(cli->tree, &notify);
170
171         /* (2nd notify) whereas this notify will see the above buffered events,
172            and it directly returns the buffered events */
173         notify.in.file.fnum = fnum;
174         req = smb_raw_changenotify_send(cli->tree, &notify);
175
176         /* (1st unlink) as the 2nd notify directly returns,
177            this unlink is only seen by the 1st notify and 
178            the 3rd notify (later) */
179         printf("testing notify on unlink for the first file\n");
180         status = smbcli_unlink(cli->tree, BASEDIR "\\test0.txt");
181         CHECK_STATUS(status, NT_STATUS_OK);
182
183         /* receive the reply from the 2nd notify */
184         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
185         CHECK_STATUS(status, NT_STATUS_OK);
186
187         CHECK_VAL(notify.out.num_changes, count);
188         for (i=1;i<notify.out.num_changes;i++) {
189                 CHECK_VAL(notify.out.changes[i].action, NOTIFY_ACTION_ADDED);
190         }
191         CHECK_WSTR(notify.out.changes[0].name, "test0.txt", STR_UNICODE);
192
193         /* and now from the 1st notify */
194         status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
195         CHECK_STATUS(status, NT_STATUS_OK);
196         CHECK_VAL(notify.out.num_changes, 1);
197         CHECK_VAL(notify.out.changes[0].action, NOTIFY_ACTION_REMOVED);
198         CHECK_WSTR(notify.out.changes[0].name, "test0.txt", STR_UNICODE);
199
200         /* (3rd notify) this notify will only see the 1st unlink */
201         req = smb_raw_changenotify_send(cli->tree, &notify);
202
203         printf("testing notify on wildcard unlink for %d files\n", count-1);
204         /* (2nd unlink) do a wildcard unlink */
205         status = smbcli_unlink(cli->tree, BASEDIR "\\test*.txt");
206         CHECK_STATUS(status, NT_STATUS_OK);
207
208         /* recev the 3rd notify */
209         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
210         CHECK_STATUS(status, NT_STATUS_OK);
211         CHECK_VAL(notify.out.num_changes, 1);
212         CHECK_VAL(notify.out.changes[0].action, NOTIFY_ACTION_REMOVED);
213         CHECK_WSTR(notify.out.changes[0].name, "test0.txt", STR_UNICODE);
214
215         /* and we now see the rest of the unlink calls on both directory handles */
216         notify.in.file.fnum = fnum;
217         req = smb_raw_changenotify_send(cli->tree, &notify);
218         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
219         CHECK_STATUS(status, NT_STATUS_OK);
220         CHECK_VAL(notify.out.num_changes, count-1);
221         for (i=0;i<notify.out.num_changes;i++) {
222                 CHECK_VAL(notify.out.changes[i].action, NOTIFY_ACTION_REMOVED);
223         }
224         notify.in.file.fnum = fnum2;
225         req = smb_raw_changenotify_send(cli->tree, &notify);
226         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
227         CHECK_STATUS(status, NT_STATUS_OK);
228         CHECK_VAL(notify.out.num_changes, count-1);
229         for (i=0;i<notify.out.num_changes;i++) {
230                 CHECK_VAL(notify.out.changes[i].action, NOTIFY_ACTION_REMOVED);
231         }
232
233         printf("testing if a close() on the dir handle triggers the notify reply\n");
234
235         notify.in.file.fnum = fnum;
236         req = smb_raw_changenotify_send(cli->tree, &notify);
237
238         cl.close.level = RAW_CLOSE_CLOSE;
239         cl.close.in.file.fnum = fnum;
240         cl.close.in.write_time = 0;
241         status = smb_raw_close(cli->tree, &cl);
242         CHECK_STATUS(status, NT_STATUS_OK);
243
244         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
245         CHECK_STATUS(status, NT_STATUS_OK);
246         CHECK_VAL(notify.out.num_changes, 0);
247
248 done:
249         smb_raw_exit(cli->session);
250         return ret;
251 }
252
253
254 /* 
255    testing of recursive change notify
256 */
257 static BOOL test_notify_recursive(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
258 {
259         BOOL ret = True;
260         NTSTATUS status;
261         struct smb_notify notify;
262         union smb_open io;
263         int fnum;
264         struct smbcli_request *req1, *req2;
265
266         printf("TESTING CHANGE NOTIFY WITH RECURSION\n");
267                 
268         /*
269           get a handle on the directory
270         */
271         io.generic.level = RAW_OPEN_NTCREATEX;
272         io.ntcreatex.in.root_fid = 0;
273         io.ntcreatex.in.flags = 0;
274         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
275         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
276         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
277         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
278         io.ntcreatex.in.alloc_size = 0;
279         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
280         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
281         io.ntcreatex.in.security_flags = 0;
282         io.ntcreatex.in.fname = BASEDIR;
283
284         status = smb_raw_open(cli->tree, mem_ctx, &io);
285         CHECK_STATUS(status, NT_STATUS_OK);
286         fnum = io.ntcreatex.out.file.fnum;
287
288         /* ask for a change notify, on file or directory name
289            changes. Setup both with and without recursion */
290         notify.in.buffer_size = 1000;
291         notify.in.completion_filter = FILE_NOTIFY_CHANGE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_CREATION;
292         notify.in.file.fnum = fnum;
293
294         notify.in.recursive = True;
295         req1 = smb_raw_changenotify_send(cli->tree, &notify);
296
297         notify.in.recursive = False;
298         req2 = smb_raw_changenotify_send(cli->tree, &notify);
299
300         /* cancel initial requests so the buffer is setup */
301         smb_raw_ntcancel(req1);
302         status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
303         CHECK_STATUS(status, NT_STATUS_CANCELLED);
304
305         smb_raw_ntcancel(req2);
306         status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
307         CHECK_STATUS(status, NT_STATUS_CANCELLED);
308
309         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
310         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name\\subname1");
311         smbcli_close(cli->tree, 
312                      smbcli_open(cli->tree, BASEDIR "\\subdir-name\\subname2", O_CREAT, 0));
313         smbcli_rename(cli->tree, BASEDIR "\\subdir-name\\subname1", BASEDIR "\\subdir-name\\subname1-r");
314         smbcli_rename(cli->tree, BASEDIR "\\subdir-name\\subname2", BASEDIR "\\subname2-r");
315         smbcli_rename(cli->tree, BASEDIR "\\subname2-r", BASEDIR "\\subname3-r");
316
317         notify.in.completion_filter = 0;
318         notify.in.recursive = True;
319         msleep(10);
320         req1 = smb_raw_changenotify_send(cli->tree, &notify);
321
322         smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name\\subname1-r");
323         smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
324         smbcli_unlink(cli->tree, BASEDIR "\\subname3-r");
325
326         notify.in.recursive = False;
327         req2 = smb_raw_changenotify_send(cli->tree, &notify);
328
329         status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
330         CHECK_STATUS(status, NT_STATUS_OK);
331
332         CHECK_VAL(notify.out.num_changes, 11);
333         CHECK_VAL(notify.out.changes[0].action, NOTIFY_ACTION_ADDED);
334         CHECK_WSTR(notify.out.changes[0].name, "subdir-name", STR_UNICODE);
335         CHECK_VAL(notify.out.changes[1].action, NOTIFY_ACTION_ADDED);
336         CHECK_WSTR(notify.out.changes[1].name, "subdir-name\\subname1", STR_UNICODE);
337         CHECK_VAL(notify.out.changes[2].action, NOTIFY_ACTION_ADDED);
338         CHECK_WSTR(notify.out.changes[2].name, "subdir-name\\subname2", STR_UNICODE);
339         CHECK_VAL(notify.out.changes[3].action, NOTIFY_ACTION_OLD_NAME);
340         CHECK_WSTR(notify.out.changes[3].name, "subdir-name\\subname1", STR_UNICODE);
341         CHECK_VAL(notify.out.changes[4].action, NOTIFY_ACTION_NEW_NAME);
342         CHECK_WSTR(notify.out.changes[4].name, "subdir-name\\subname1-r", STR_UNICODE);
343
344         /* the remove/add between directories is acceptable in either order */
345         if (notify.out.changes[5].action == NOTIFY_ACTION_ADDED) {
346                 CHECK_VAL(notify.out.changes[6].action, NOTIFY_ACTION_REMOVED);
347                 CHECK_WSTR(notify.out.changes[6].name, "subdir-name\\subname2", STR_UNICODE);
348                 CHECK_VAL(notify.out.changes[5].action, NOTIFY_ACTION_ADDED);
349                 CHECK_WSTR(notify.out.changes[5].name, "subname2-r", STR_UNICODE);
350         } else {
351                 CHECK_VAL(notify.out.changes[5].action, NOTIFY_ACTION_REMOVED);
352                 CHECK_WSTR(notify.out.changes[5].name, "subdir-name\\subname2", STR_UNICODE);
353                 CHECK_VAL(notify.out.changes[6].action, NOTIFY_ACTION_ADDED);
354                 CHECK_WSTR(notify.out.changes[6].name, "subname2-r", STR_UNICODE);
355         }
356
357         CHECK_VAL(notify.out.changes[7].action, NOTIFY_ACTION_MODIFIED);
358         CHECK_WSTR(notify.out.changes[7].name, "subname2-r", STR_UNICODE);
359
360         CHECK_VAL(notify.out.changes[8].action, NOTIFY_ACTION_OLD_NAME);
361         CHECK_WSTR(notify.out.changes[8].name, "subname2-r", STR_UNICODE);
362         CHECK_VAL(notify.out.changes[9].action, NOTIFY_ACTION_NEW_NAME);
363         CHECK_WSTR(notify.out.changes[9].name, "subname3-r", STR_UNICODE);
364         CHECK_VAL(notify.out.changes[10].action, NOTIFY_ACTION_MODIFIED);
365         CHECK_WSTR(notify.out.changes[10].name, "subname3-r", STR_UNICODE);
366
367         status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
368         CHECK_STATUS(status, NT_STATUS_OK);
369
370         CHECK_VAL(notify.out.num_changes, 3);
371         CHECK_VAL(notify.out.changes[0].action, NOTIFY_ACTION_REMOVED);
372         CHECK_WSTR(notify.out.changes[0].name, "subdir-name\\subname1-r", STR_UNICODE);
373         CHECK_VAL(notify.out.changes[1].action, NOTIFY_ACTION_REMOVED);
374         CHECK_WSTR(notify.out.changes[1].name, "subdir-name", STR_UNICODE);
375         CHECK_VAL(notify.out.changes[2].action, NOTIFY_ACTION_REMOVED);
376         CHECK_WSTR(notify.out.changes[2].name, "subname3-r", STR_UNICODE);
377
378 done:
379         smb_raw_exit(cli->session);
380         return ret;
381 }
382
383
384 /* 
385    testing of mask bits for change notify
386 */
387 static BOOL test_notify_mask(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
388 {
389         BOOL ret = True;
390         NTSTATUS status;
391         struct smb_notify notify;
392         union smb_open io;
393         int fnum, fnum2;
394         uint32_t mask;
395         int i;
396         char c = 1;
397         struct timeval tv;
398         NTTIME t;
399
400         printf("TESTING CHANGE NOTIFY COMPLETION FILTERS\n");
401
402         tv = timeval_current_ofs(1000, 0);
403         t = timeval_to_nttime(&tv);
404                 
405         /*
406           get a handle on the directory
407         */
408         io.generic.level = RAW_OPEN_NTCREATEX;
409         io.ntcreatex.in.root_fid = 0;
410         io.ntcreatex.in.flags = 0;
411         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
412         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
413         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
414         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
415         io.ntcreatex.in.alloc_size = 0;
416         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
417         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
418         io.ntcreatex.in.security_flags = 0;
419         io.ntcreatex.in.fname = BASEDIR;
420
421         notify.in.buffer_size = 1000;
422         notify.in.recursive = True;
423
424 #define NOTIFY_MASK_TEST(setup, op, cleanup, Action, expected, nchanges) \
425         do { for (mask=i=0;i<32;i++) { \
426                 struct smbcli_request *req; \
427                 status = smb_raw_open(cli->tree, mem_ctx, &io); \
428                 CHECK_STATUS(status, NT_STATUS_OK); \
429                 fnum = io.ntcreatex.out.file.fnum; \
430                 setup \
431                 notify.in.file.fnum = fnum;     \
432                 notify.in.completion_filter = (1<<i); \
433                 req = smb_raw_changenotify_send(cli->tree, &notify); \
434                 op \
435                 msleep(10); smb_raw_ntcancel(req); \
436                 status = smb_raw_changenotify_recv(req, mem_ctx, &notify); \
437                 cleanup \
438                 smbcli_close(cli->tree, fnum); \
439                 if (NT_STATUS_EQUAL(status, NT_STATUS_CANCELLED)) continue; \
440                 CHECK_STATUS(status, NT_STATUS_OK); \
441                 /* special case to cope with file rename behaviour */ \
442                 if (nchanges == 2 && notify.out.num_changes == 1 && \
443                     notify.out.changes[0].action == NOTIFY_ACTION_MODIFIED && \
444                     ((expected) & FILE_NOTIFY_CHANGE_ATTRIBUTES) && \
445                     Action == NOTIFY_ACTION_OLD_NAME) { \
446                         printf("(rename file special handling OK)\n"); \
447                 } else if (nchanges != notify.out.num_changes || \
448                     notify.out.changes[0].action != Action || \
449                     strcmp(notify.out.changes[0].name.s, "tname1") != 0) { \
450                         printf("ERROR: nchanges=%d action=%d filter=0x%08x\n", \
451                                notify.out.num_changes, \
452                                notify.out.changes[0].action, \
453                                notify.in.completion_filter); \
454                         ret = False; \
455                 } \
456                 mask |= (1<<i); \
457         } \
458         if ((expected) != mask) { \
459                 if (((expected) & ~mask) != 0) { \
460                         printf("ERROR: trigger on too few bits. mask=0x%08x expected=0x%08x\n", \
461                                mask, expected); \
462                         ret = False; \
463                 } else { \
464                         printf("WARNING: trigger on too many bits. mask=0x%08x expected=0x%08x\n", \
465                                mask, expected); \
466                 } \
467         } \
468         } while (0)
469
470         printf("testing mkdir\n");
471         NOTIFY_MASK_TEST(;,
472                          smbcli_mkdir(cli->tree, BASEDIR "\\tname1");,
473                          smbcli_rmdir(cli->tree, BASEDIR "\\tname1");,
474                          NOTIFY_ACTION_ADDED,
475                          FILE_NOTIFY_CHANGE_DIR_NAME, 1);
476
477         printf("testing create file\n");
478         NOTIFY_MASK_TEST(;,
479                          smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
480                          smbcli_unlink(cli->tree, BASEDIR "\\tname1");,
481                          NOTIFY_ACTION_ADDED,
482                          FILE_NOTIFY_CHANGE_FILE_NAME, 1);
483
484         printf("testing unlink\n");
485         NOTIFY_MASK_TEST(
486                          smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
487                          smbcli_unlink(cli->tree, BASEDIR "\\tname1");,
488                          ;,
489                          NOTIFY_ACTION_REMOVED,
490                          FILE_NOTIFY_CHANGE_FILE_NAME, 1);
491
492         printf("testing rmdir\n");
493         NOTIFY_MASK_TEST(
494                          smbcli_mkdir(cli->tree, BASEDIR "\\tname1");,
495                          smbcli_rmdir(cli->tree, BASEDIR "\\tname1");,
496                          ;,
497                          NOTIFY_ACTION_REMOVED,
498                          FILE_NOTIFY_CHANGE_DIR_NAME, 1);
499
500         printf("testing rename file\n");
501         NOTIFY_MASK_TEST(
502                          smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
503                          smbcli_rename(cli->tree, BASEDIR "\\tname1", BASEDIR "\\tname2");,
504                          smbcli_unlink(cli->tree, BASEDIR "\\tname2");,
505                          NOTIFY_ACTION_OLD_NAME,
506                          FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_CREATION, 2);
507
508         printf("testing rename dir\n");
509         NOTIFY_MASK_TEST(
510                 smbcli_mkdir(cli->tree, BASEDIR "\\tname1");,
511                 smbcli_rename(cli->tree, BASEDIR "\\tname1", BASEDIR "\\tname2");,
512                 smbcli_rmdir(cli->tree, BASEDIR "\\tname2");,
513                 NOTIFY_ACTION_OLD_NAME,
514                 FILE_NOTIFY_CHANGE_DIR_NAME, 2);
515
516         printf("testing set path attribute\n");
517         NOTIFY_MASK_TEST(
518                 smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
519                 smbcli_setatr(cli->tree, BASEDIR "\\tname1", FILE_ATTRIBUTE_HIDDEN, 0);,
520                 smbcli_unlink(cli->tree, BASEDIR "\\tname1");,
521                 NOTIFY_ACTION_MODIFIED,
522                 FILE_NOTIFY_CHANGE_ATTRIBUTES, 1);
523
524         printf("testing set path write time\n");
525         NOTIFY_MASK_TEST(
526                 smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
527                 smbcli_setatr(cli->tree, BASEDIR "\\tname1", FILE_ATTRIBUTE_NORMAL, 1000);,
528                 smbcli_unlink(cli->tree, BASEDIR "\\tname1");,
529                 NOTIFY_ACTION_MODIFIED,
530                 FILE_NOTIFY_CHANGE_LAST_WRITE, 1);
531
532         printf("testing set file attribute\n");
533         NOTIFY_MASK_TEST(
534                 fnum2 = create_complex_file(cli, mem_ctx, BASEDIR "\\tname1");,
535                 smbcli_fsetatr(cli->tree, fnum2, FILE_ATTRIBUTE_HIDDEN, 0, 0, 0, 0);,
536                 (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
537                 NOTIFY_ACTION_MODIFIED,
538                 FILE_NOTIFY_CHANGE_ATTRIBUTES, 1);
539
540         printf("testing set file create time\n");
541         NOTIFY_MASK_TEST(
542                 fnum2 = create_complex_file(cli, mem_ctx, BASEDIR "\\tname1");,
543                 smbcli_fsetatr(cli->tree, fnum2, 0, t, 0, 0, 0);,
544                 (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
545                 NOTIFY_ACTION_MODIFIED,
546                 FILE_NOTIFY_CHANGE_CREATION, 1);
547
548         printf("testing set file access time\n");
549         NOTIFY_MASK_TEST(
550                 fnum2 = create_complex_file(cli, mem_ctx, BASEDIR "\\tname1");,
551                 smbcli_fsetatr(cli->tree, fnum2, 0, 0, t, 0, 0);,
552                 (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
553                 NOTIFY_ACTION_MODIFIED,
554                 FILE_NOTIFY_CHANGE_LAST_ACCESS, 1);
555
556         printf("testing set file write time\n");
557         NOTIFY_MASK_TEST(
558                 fnum2 = create_complex_file(cli, mem_ctx, BASEDIR "\\tname1");,
559                 smbcli_fsetatr(cli->tree, fnum2, 0, 0, 0, t, 0);,
560                 (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
561                 NOTIFY_ACTION_MODIFIED,
562                 FILE_NOTIFY_CHANGE_LAST_WRITE, 1);
563
564         printf("testing set file change time\n");
565         NOTIFY_MASK_TEST(
566                 fnum2 = create_complex_file(cli, mem_ctx, BASEDIR "\\tname1");,
567                 smbcli_fsetatr(cli->tree, fnum2, 0, 0, 0, 0, t);,
568                 (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
569                 NOTIFY_ACTION_MODIFIED,
570                 0, 1);
571
572
573         printf("testing write\n");
574         NOTIFY_MASK_TEST(
575                 fnum2 = create_complex_file(cli, mem_ctx, BASEDIR "\\tname1");,
576                 smbcli_write(cli->tree, fnum2, 1, &c, 10000, 1);,
577                 (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
578                 NOTIFY_ACTION_MODIFIED,
579                 0, 1);
580
581 done:
582         smb_raw_exit(cli->session);
583         return ret;
584 }
585
586 /*
587   basic testing of change notify on files
588 */
589 static BOOL test_notify_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
590 {
591         NTSTATUS status;
592         BOOL ret = True;
593         union smb_open io;
594         union smb_close cl;
595         struct smb_notify notify;
596         struct smbcli_request *req;
597         int fnum;
598         const char *fname = BASEDIR "\\file.txt";
599
600         printf("TESTING CHANGE NOTIFY ON FILES\n");
601
602         io.generic.level = RAW_OPEN_NTCREATEX;
603         io.ntcreatex.in.root_fid = 0;
604         io.ntcreatex.in.flags = 0;
605         io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
606         io.ntcreatex.in.create_options = 0;
607         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
608         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
609         io.ntcreatex.in.alloc_size = 0;
610         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
611         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
612         io.ntcreatex.in.security_flags = 0;
613         io.ntcreatex.in.fname = fname;
614         status = smb_raw_open(cli->tree, mem_ctx, &io);
615         CHECK_STATUS(status, NT_STATUS_OK);
616         fnum = io.ntcreatex.out.file.fnum;
617
618         /* ask for a change notify,
619            on file or directory name changes */
620         notify.in.file.fnum = fnum;
621         notify.in.buffer_size = 1000;
622         notify.in.completion_filter = FILE_NOTIFY_CHANGE_STREAM_NAME;
623         notify.in.recursive = False;
624
625         printf("testing if notifies on file handles are invalid (should be)\n");
626
627         req = smb_raw_changenotify_send(cli->tree, &notify);
628         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
629         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
630
631         cl.close.level = RAW_CLOSE_CLOSE;
632         cl.close.in.file.fnum = fnum;
633         cl.close.in.write_time = 0;
634         status = smb_raw_close(cli->tree, &cl);
635         CHECK_STATUS(status, NT_STATUS_OK);
636
637         status = smbcli_unlink(cli->tree, fname);
638         CHECK_STATUS(status, NT_STATUS_OK);
639
640 done:
641         smb_raw_exit(cli->session);
642         return ret;
643 }
644
645 /*
646   basic testing of change notifies followed by a tdis
647 */
648 static BOOL test_notify_tdis(TALLOC_CTX *mem_ctx)
649 {
650         BOOL ret = True;
651         NTSTATUS status;
652         struct smb_notify notify;
653         union smb_open io;
654         int fnum;
655         struct smbcli_request *req;
656         struct smbcli_state *cli = NULL;
657
658         printf("TESTING CHANGE NOTIFY FOLLOWED BY TDIS\n");
659
660         if (!torture_open_connection(&cli)) {
661                 return False;
662         }
663
664         /*
665           get a handle on the directory
666         */
667         io.generic.level = RAW_OPEN_NTCREATEX;
668         io.ntcreatex.in.root_fid = 0;
669         io.ntcreatex.in.flags = 0;
670         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
671         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
672         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
673         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
674         io.ntcreatex.in.alloc_size = 0;
675         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
676         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
677         io.ntcreatex.in.security_flags = 0;
678         io.ntcreatex.in.fname = BASEDIR;
679
680         status = smb_raw_open(cli->tree, mem_ctx, &io);
681         CHECK_STATUS(status, NT_STATUS_OK);
682         fnum = io.ntcreatex.out.file.fnum;
683
684         /* ask for a change notify,
685            on file or directory name changes */
686         notify.in.buffer_size = 1000;
687         notify.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
688         notify.in.file.fnum = fnum;
689         notify.in.recursive = True;
690
691         req = smb_raw_changenotify_send(cli->tree, &notify);
692
693         status = smbcli_tdis(cli);
694         CHECK_STATUS(status, NT_STATUS_OK);
695
696         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
697         CHECK_STATUS(status, NT_STATUS_OK);
698         CHECK_VAL(notify.out.num_changes, 0);
699
700 done:
701         torture_close_connection(cli);
702         return ret;
703 }
704
705 /*
706   basic testing of change notifies followed by a exit
707 */
708 static BOOL test_notify_exit(TALLOC_CTX *mem_ctx)
709 {
710         BOOL ret = True;
711         NTSTATUS status;
712         struct smb_notify notify;
713         union smb_open io;
714         int fnum;
715         struct smbcli_request *req;
716         struct smbcli_state *cli = NULL;
717
718         printf("TESTING CHANGE NOTIFY FOLLOWED BY EXIT\n");
719
720         if (!torture_open_connection(&cli)) {
721                 return False;
722         }
723
724         /*
725           get a handle on the directory
726         */
727         io.generic.level = RAW_OPEN_NTCREATEX;
728         io.ntcreatex.in.root_fid = 0;
729         io.ntcreatex.in.flags = 0;
730         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
731         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
732         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
733         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
734         io.ntcreatex.in.alloc_size = 0;
735         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
736         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
737         io.ntcreatex.in.security_flags = 0;
738         io.ntcreatex.in.fname = BASEDIR;
739
740         status = smb_raw_open(cli->tree, mem_ctx, &io);
741         CHECK_STATUS(status, NT_STATUS_OK);
742         fnum = io.ntcreatex.out.file.fnum;
743
744         /* ask for a change notify,
745            on file or directory name changes */
746         notify.in.buffer_size = 1000;
747         notify.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
748         notify.in.file.fnum = fnum;
749         notify.in.recursive = True;
750
751         req = smb_raw_changenotify_send(cli->tree, &notify);
752
753         status = smb_raw_exit(cli->session);
754         CHECK_STATUS(status, NT_STATUS_OK);
755
756         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
757         CHECK_STATUS(status, NT_STATUS_OK);
758         CHECK_VAL(notify.out.num_changes, 0);
759
760 done:
761         torture_close_connection(cli);
762         return ret;
763 }
764
765 /*
766   basic testing of change notifies followed by a ulogoff
767 */
768 static BOOL test_notify_ulogoff(TALLOC_CTX *mem_ctx)
769 {
770         BOOL ret = True;
771         NTSTATUS status;
772         struct smb_notify notify;
773         union smb_open io;
774         int fnum;
775         struct smbcli_request *req;
776         struct smbcli_state *cli = NULL;
777
778         printf("TESTING CHANGE NOTIFY FOLLOWED BY ULOGOFF\n");
779
780         if (!torture_open_connection(&cli)) {
781                 return False;
782         }
783
784         /*
785           get a handle on the directory
786         */
787         io.generic.level = RAW_OPEN_NTCREATEX;
788         io.ntcreatex.in.root_fid = 0;
789         io.ntcreatex.in.flags = 0;
790         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
791         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
792         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
793         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
794         io.ntcreatex.in.alloc_size = 0;
795         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
796         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
797         io.ntcreatex.in.security_flags = 0;
798         io.ntcreatex.in.fname = BASEDIR;
799
800         status = smb_raw_open(cli->tree, mem_ctx, &io);
801         CHECK_STATUS(status, NT_STATUS_OK);
802         fnum = io.ntcreatex.out.file.fnum;
803
804         /* ask for a change notify,
805            on file or directory name changes */
806         notify.in.buffer_size = 1000;
807         notify.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
808         notify.in.file.fnum = fnum;
809         notify.in.recursive = True;
810
811         req = smb_raw_changenotify_send(cli->tree, &notify);
812
813         status = smb_raw_ulogoff(cli->session);
814         CHECK_STATUS(status, NT_STATUS_OK);
815
816         status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
817         CHECK_STATUS(status, NT_STATUS_OK);
818         CHECK_VAL(notify.out.num_changes, 0);
819
820 done:
821         torture_close_connection(cli);
822         return ret;
823 }
824
825
826 /* 
827    test setting up two change notify requests on one handle
828 */
829 static BOOL test_notify_double(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
830 {
831         BOOL ret = True;
832         NTSTATUS status;
833         struct smb_notify notify;
834         union smb_open io;
835         int fnum;
836         struct smbcli_request *req1, *req2;
837
838         printf("TESTING CHANGE NOTIFY TWICE ON ONE DIRECTORY\n");
839                 
840         /*
841           get a handle on the directory
842         */
843         io.generic.level = RAW_OPEN_NTCREATEX;
844         io.ntcreatex.in.root_fid = 0;
845         io.ntcreatex.in.flags = 0;
846         io.ntcreatex.in.access_mask = SEC_FILE_ALL;
847         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
848         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
849         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
850         io.ntcreatex.in.alloc_size = 0;
851         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
852         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
853         io.ntcreatex.in.security_flags = 0;
854         io.ntcreatex.in.fname = BASEDIR;
855
856         status = smb_raw_open(cli->tree, mem_ctx, &io);
857         CHECK_STATUS(status, NT_STATUS_OK);
858         fnum = io.ntcreatex.out.file.fnum;
859
860         /* ask for a change notify,
861            on file or directory name changes */
862         notify.in.buffer_size = 1000;
863         notify.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
864         notify.in.file.fnum = fnum;
865         notify.in.recursive = True;
866
867         req1 = smb_raw_changenotify_send(cli->tree, &notify);
868         req2 = smb_raw_changenotify_send(cli->tree, &notify);
869
870         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
871
872         status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
873         CHECK_STATUS(status, NT_STATUS_OK);
874         CHECK_VAL(notify.out.num_changes, 1);
875         CHECK_WSTR(notify.out.changes[0].name, "subdir-name", STR_UNICODE);
876
877         smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name2");
878
879         status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
880         CHECK_STATUS(status, NT_STATUS_OK);
881         CHECK_VAL(notify.out.num_changes, 1);
882         CHECK_WSTR(notify.out.changes[0].name, "subdir-name2", STR_UNICODE);
883
884 done:
885         smb_raw_exit(cli->session);
886         return ret;
887 }
888
889 /* 
890    basic testing of change notify
891 */
892 BOOL torture_raw_notify(struct torture_context *torture)
893 {
894         struct smbcli_state *cli;
895         BOOL ret = True;
896         TALLOC_CTX *mem_ctx;
897                 
898         if (!torture_open_connection(&cli)) {
899                 return False;
900         }
901
902         mem_ctx = talloc_init("torture_raw_notify");
903
904         if (!torture_setup_dir(cli, BASEDIR)) {
905                 return False;
906         }
907
908         ret &= test_notify_dir(cli, mem_ctx);
909         ret &= test_notify_mask(cli, mem_ctx);
910         ret &= test_notify_recursive(cli, mem_ctx);
911         ret &= test_notify_file(cli, mem_ctx);
912         ret &= test_notify_tdis(mem_ctx);
913         ret &= test_notify_exit(mem_ctx);
914         ret &= test_notify_ulogoff(mem_ctx);
915         ret &= test_notify_double(cli, mem_ctx);
916
917         smb_raw_exit(cli->session);
918         smbcli_deltree(cli->tree, BASEDIR);
919         torture_close_connection(cli);
920         talloc_free(mem_ctx);
921         return ret;
922 }