2 Unix SMB/CIFS implementation.
3 basic raw test suite for change notify
4 Copyright (C) Andrew Tridgell 2003
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.
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.
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/>.
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"
28 #define BASEDIR "\\test_notify"
30 #define CHECK_WSTR(tctx, field, value, flags) \
32 torture_assert_str_equal(tctx, field.s, value, "values don't match"); \
33 torture_assert(tctx, \
34 !wire_bad_flags(&field, STR_UNICODE, cli->transport), \
39 basic testing of change notify on directories
41 static bool test_notify_dir(struct torture_context *mem_ctx,
42 struct smbcli_state *cli,
43 struct smbcli_state *cli2)
47 union smb_notify notify;
50 int i, count, fnum, fnum2;
51 struct smbcli_request *req, *req2;
52 extern int torture_numops;
54 printf("TESTING CHANGE NOTIFY ON DIRECTORIES\n");
56 if (!torture_setup_dir(cli, BASEDIR)) {
61 get a handle on the directory
63 io.generic.level = RAW_OPEN_NTCREATEX;
64 io.ntcreatex.in.root_fid.fnum = 0;
65 io.ntcreatex.in.flags = 0;
66 io.ntcreatex.in.access_mask = SEC_FILE_ALL;
67 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
68 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
69 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
70 io.ntcreatex.in.alloc_size = 0;
71 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
72 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
73 io.ntcreatex.in.security_flags = 0;
74 io.ntcreatex.in.fname = BASEDIR;
76 status = smb_raw_open(cli->tree, mem_ctx, &io);
77 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
79 fnum = io.ntcreatex.out.file.fnum;
81 status = smb_raw_open(cli->tree, mem_ctx, &io);
82 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
84 fnum2 = io.ntcreatex.out.file.fnum;
86 /* ask for a change notify,
87 on file or directory name changes */
88 notify.nttrans.level = RAW_NOTIFY_NTTRANS;
89 notify.nttrans.in.buffer_size = 1000;
90 notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
91 notify.nttrans.in.file.fnum = fnum;
92 notify.nttrans.in.recursive = true;
94 printf("Testing notify cancel\n");
96 req = smb_raw_changenotify_send(cli->tree, ¬ify);
97 smb_raw_ntcancel(req);
98 status = smb_raw_changenotify_recv(req, mem_ctx, ¬ify);
99 torture_assert_ntstatus_equal_goto(mem_ctx, status, NT_STATUS_CANCELLED,
101 "smb_raw_changenotify_recv");
103 printf("Testing notify mkdir\n");
105 req = smb_raw_changenotify_send(cli->tree, ¬ify);
106 smbcli_mkdir(cli2->tree, BASEDIR "\\subdir-name");
108 status = smb_raw_changenotify_recv(req, mem_ctx, ¬ify);
109 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
110 "smb_raw_changenotify_recv");
112 torture_assert_int_equal_goto(mem_ctx, notify.nttrans.out.num_changes,
113 1, ret, done, "more than one change");
114 torture_assert_int_equal_goto(mem_ctx,
115 notify.nttrans.out.changes[0].action,
116 NOTIFY_ACTION_ADDED, ret, done,
117 "wrong action (exp: ADDED)");
118 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[0].name, "subdir-name",
121 printf("Testing notify rmdir\n");
123 req = smb_raw_changenotify_send(cli->tree, ¬ify);
124 smbcli_rmdir(cli2->tree, BASEDIR "\\subdir-name");
126 status = smb_raw_changenotify_recv(req, mem_ctx, ¬ify);
127 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
128 "smb_raw_changenotify_recv");
129 torture_assert_int_equal_goto(mem_ctx, notify.nttrans.out.num_changes,
130 1, ret, done, "more than one change");
131 torture_assert_int_equal_goto(mem_ctx,
132 notify.nttrans.out.changes[0].action,
133 NOTIFY_ACTION_REMOVED, ret, done,
134 "wrong action (exp: REMOVED)");
135 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[0].name, "subdir-name",
138 printf("Testing notify mkdir - rmdir - mkdir - rmdir\n");
140 smbcli_mkdir(cli2->tree, BASEDIR "\\subdir-name");
141 smbcli_rmdir(cli2->tree, BASEDIR "\\subdir-name");
142 smbcli_mkdir(cli2->tree, BASEDIR "\\subdir-name");
143 smbcli_rmdir(cli2->tree, BASEDIR "\\subdir-name");
145 req = smb_raw_changenotify_send(cli->tree, ¬ify);
146 status = smb_raw_changenotify_recv(req, mem_ctx, ¬ify);
147 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
148 "smb_raw_changenotify_recv");
149 torture_assert_int_equal_goto(mem_ctx, notify.nttrans.out.num_changes,
150 4, ret, done, "wrong number of changes");
151 torture_assert_int_equal_goto(mem_ctx,
152 notify.nttrans.out.changes[0].action,
153 NOTIFY_ACTION_ADDED, ret, done,
154 "wrong action (exp: ADDED)");
155 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[0].name, "subdir-name",
157 torture_assert_int_equal_goto(mem_ctx,
158 notify.nttrans.out.changes[1].action,
159 NOTIFY_ACTION_REMOVED, ret, done,
160 "wrong action (exp: REMOVED)");
161 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[1].name, "subdir-name",
163 torture_assert_int_equal_goto(mem_ctx,
164 notify.nttrans.out.changes[2].action,
165 NOTIFY_ACTION_ADDED, ret, done,
166 "wrong action (exp: ADDED)");
167 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[2].name, "subdir-name",
169 torture_assert_int_equal_goto(mem_ctx,
170 notify.nttrans.out.changes[3].action,
171 NOTIFY_ACTION_REMOVED, ret, done,
172 "wrong action (exp: REMOVED)");
173 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[3].name, "subdir-name",
176 count = torture_numops;
177 printf("Testing buffered notify on create of %d files\n", count);
178 for (i=0;i<count;i++) {
179 char *fname = talloc_asprintf(cli, BASEDIR "\\test%d.txt", i);
180 int fnum3 = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
182 printf("Failed to create %s - %s\n",
183 fname, smbcli_errstr(cli->tree));
188 smbcli_close(cli->tree, fnum3);
191 /* (1st notify) setup a new notify on a different directory handle.
192 This new notify won't see the events above. */
193 notify.nttrans.in.file.fnum = fnum2;
194 req2 = smb_raw_changenotify_send(cli->tree, ¬ify);
196 /* (2nd notify) whereas this notify will see the above buffered events,
197 and it directly returns the buffered events */
198 notify.nttrans.in.file.fnum = fnum;
199 req = smb_raw_changenotify_send(cli->tree, ¬ify);
201 status = smbcli_unlink(cli->tree, BASEDIR "\\nonexistent.txt");
202 torture_assert_ntstatus_equal_goto(mem_ctx, status,
203 NT_STATUS_OBJECT_NAME_NOT_FOUND,
207 /* (1st unlink) as the 2nd notify directly returns,
208 this unlink is only seen by the 1st notify and
209 the 3rd notify (later) */
210 printf("Testing notify on unlink for the first file\n");
211 status = smbcli_unlink(cli2->tree, BASEDIR "\\test0.txt");
212 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
215 /* receive the reply from the 2nd notify */
216 status = smb_raw_changenotify_recv(req, mem_ctx, ¬ify);
217 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
218 "smb_raw_changenotify_recv");
220 torture_assert_int_equal_goto(mem_ctx, notify.nttrans.out.num_changes,
222 "wrong number of changes");
223 for (i=1;i<count;i++) {
224 torture_assert_int_equal_goto(mem_ctx,
225 notify.nttrans.out.changes[i].action,
226 NOTIFY_ACTION_ADDED, ret, done,
227 "wrong action (exp: ADDED)");
229 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[0].name, "test0.txt",
232 printf("and now from the 1st notify\n");
233 status = smb_raw_changenotify_recv(req2, mem_ctx, ¬ify);
234 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
235 "smb_raw_changenotify_recv");
236 torture_assert_int_equal_goto(mem_ctx, notify.nttrans.out.num_changes,
237 1, ret, done, "wrong number of changes");
238 torture_assert_int_equal_goto(mem_ctx,
239 notify.nttrans.out.changes[0].action,
240 NOTIFY_ACTION_REMOVED, ret, done,
241 "wrong action (exp: REMOVED)");
242 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[0].name, "test0.txt",
245 printf("(3rd notify) this notify will only see the 1st unlink\n");
246 req = smb_raw_changenotify_send(cli->tree, ¬ify);
248 status = smbcli_unlink(cli->tree, BASEDIR "\\nonexistent.txt");
249 torture_assert_ntstatus_equal_goto(mem_ctx, status,
250 NT_STATUS_OBJECT_NAME_NOT_FOUND,
254 printf("Testing notify on wildcard unlink for %d files\n", count-1);
255 /* (2nd unlink) do a wildcard unlink */
256 status = smbcli_unlink(cli2->tree, BASEDIR "\\test*.txt");
257 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
258 "smb_raw_changenotify_recv");
260 /* receive the 3rd notify */
261 status = smb_raw_changenotify_recv(req, mem_ctx, ¬ify);
262 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
263 "smb_raw_changenotify_recv");
264 torture_assert_int_equal_goto(mem_ctx, notify.nttrans.out.num_changes,
265 1, ret, done, "wrong number of changes");
266 torture_assert_int_equal_goto(mem_ctx,
267 notify.nttrans.out.changes[0].action,
268 NOTIFY_ACTION_REMOVED, ret, done,
269 "wrong action (exp: REMOVED)");
270 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[0].name, "test0.txt",
273 /* and we now see the rest of the unlink calls on both directory handles */
274 notify.nttrans.in.file.fnum = fnum;
276 req = smb_raw_changenotify_send(cli->tree, ¬ify);
277 status = smb_raw_changenotify_recv(req, mem_ctx, ¬ify);
278 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
279 "smb_raw_changenotify_recv");
280 torture_assert_int_equal_goto(mem_ctx, notify.nttrans.out.num_changes,
281 count - 1, ret, done,
282 "wrong number of changes");
283 for (i=0;i<notify.nttrans.out.num_changes;i++) {
284 torture_assert_int_equal_goto(mem_ctx,
285 notify.nttrans.out.changes[i].action,
286 NOTIFY_ACTION_REMOVED, ret, done,
287 "wrong action (exp: REMOVED)");
289 notify.nttrans.in.file.fnum = fnum2;
290 req = smb_raw_changenotify_send(cli->tree, ¬ify);
291 status = smb_raw_changenotify_recv(req, mem_ctx, ¬ify);
292 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
293 "smb_raw_changenotify_recv");
294 torture_assert_int_equal_goto(mem_ctx, notify.nttrans.out.num_changes,
295 count - 1, ret, done,
296 "wrong number of changes");
297 for (i=0;i<notify.nttrans.out.num_changes;i++) {
298 torture_assert_int_equal_goto(mem_ctx,
299 notify.nttrans.out.changes[i].action,
300 NOTIFY_ACTION_REMOVED, ret, done,
301 "wrong action (exp: REMOVED)");
304 printf("Testing if a close() on the dir handle triggers the notify reply\n");
306 notify.nttrans.in.file.fnum = fnum;
307 req = smb_raw_changenotify_send(cli->tree, ¬ify);
309 cl.close.level = RAW_CLOSE_CLOSE;
310 cl.close.in.file.fnum = fnum;
311 cl.close.in.write_time = 0;
312 status = smb_raw_close(cli->tree, &cl);
313 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
316 status = smb_raw_changenotify_recv(req, mem_ctx, ¬ify);
317 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
318 "smb_raw_changenotify_recv");
319 torture_assert_int_equal_goto(mem_ctx, notify.nttrans.out.num_changes,
320 0, ret, done, "no changes expected");
323 smb_raw_exit(cli->session);
324 smbcli_deltree(cli->tree, BASEDIR);
329 * Check notify reply for a rename action. Not sure if this is a valid thing
330 * to do, but depending on timing between inotify and messaging we get the
331 * add/remove/modify in any order. This routines tries to find the action/name
332 * pair in any of the three following notify_changes.
335 static bool check_rename_reply(struct torture_context *tctx,
336 struct smbcli_state *cli,
338 struct notify_changes *actions,
339 uint32_t action, const char *name)
343 for (i=0; i<3; i++) {
344 if (actions[i].action == action) {
345 CHECK_WSTR(tctx, actions[i].name, name, STR_UNICODE);
350 torture_result(tctx, TORTURE_FAIL,
351 __location__": (%d) expected action %d, not found\n",
357 testing of recursive change notify
359 static bool test_notify_recursive(struct torture_context *mem_ctx,
360 struct smbcli_state *cli,
361 struct smbcli_state *cli2)
365 union smb_notify notify;
368 struct smbcli_request *req1, *req2;
370 printf("TESTING CHANGE NOTIFY WITH RECURSION\n");
372 if (!torture_setup_dir(cli, BASEDIR)) {
377 get a handle on the directory
379 io.generic.level = RAW_OPEN_NTCREATEX;
380 io.ntcreatex.in.root_fid.fnum = 0;
381 io.ntcreatex.in.flags = 0;
382 io.ntcreatex.in.access_mask = SEC_FILE_ALL;
383 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
384 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
385 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
386 io.ntcreatex.in.alloc_size = 0;
387 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
388 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
389 io.ntcreatex.in.security_flags = 0;
390 io.ntcreatex.in.fname = BASEDIR;
392 status = smb_raw_open(cli->tree, mem_ctx, &io);
393 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
395 fnum = io.ntcreatex.out.file.fnum;
397 /* ask for a change notify, on file or directory name
398 changes. Setup both with and without recursion */
399 notify.nttrans.level = RAW_NOTIFY_NTTRANS;
400 notify.nttrans.in.buffer_size = 1000;
401 notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_CREATION;
402 notify.nttrans.in.file.fnum = fnum;
404 notify.nttrans.in.recursive = true;
405 req1 = smb_raw_changenotify_send(cli->tree, ¬ify);
407 notify.nttrans.in.recursive = false;
408 req2 = smb_raw_changenotify_send(cli->tree, ¬ify);
410 /* cancel initial requests so the buffer is setup */
411 smb_raw_ntcancel(req1);
412 status = smb_raw_changenotify_recv(req1, mem_ctx, ¬ify);
413 torture_assert_ntstatus_equal_goto(mem_ctx, status,
416 "smb_raw_changenotify_recv");
418 smb_raw_ntcancel(req2);
419 status = smb_raw_changenotify_recv(req2, mem_ctx, ¬ify);
420 torture_assert_ntstatus_equal_goto(mem_ctx, status,
423 "smb_raw_changenotify_recv");
426 * Make notifies a bit more interesting in a cluster by doing
427 * the changes against different nodes with --unclist
429 smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
430 smbcli_mkdir(cli2->tree, BASEDIR "\\subdir-name\\subname1");
431 smbcli_close(cli->tree,
432 smbcli_open(cli->tree, BASEDIR "\\subdir-name\\subname2", O_CREAT, 0));
433 smbcli_rename(cli2->tree, BASEDIR "\\subdir-name\\subname1",
434 BASEDIR "\\subdir-name\\subname1-r");
435 smbcli_rename(cli->tree, BASEDIR "\\subdir-name\\subname2", BASEDIR "\\subname2-r");
436 smbcli_rename(cli2->tree, BASEDIR "\\subname2-r",
437 BASEDIR "\\subname3-r");
439 notify.nttrans.in.completion_filter = 0;
440 notify.nttrans.in.recursive = true;
442 req1 = smb_raw_changenotify_send(cli->tree, ¬ify);
444 smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name\\subname1-r");
445 smbcli_rmdir(cli2->tree, BASEDIR "\\subdir-name");
446 smbcli_unlink(cli->tree, BASEDIR "\\subname3-r");
448 notify.nttrans.in.recursive = false;
449 req2 = smb_raw_changenotify_send(cli->tree, ¬ify);
451 status = smb_raw_changenotify_recv(req1, mem_ctx, ¬ify);
452 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
453 "smb_raw_changenotify_recv");
455 torture_assert_int_equal_goto(mem_ctx, notify.nttrans.out.num_changes,
456 11, ret, done, "wrong number of changes");
457 torture_assert_int_equal_goto(mem_ctx,
458 notify.nttrans.out.changes[0].action,
459 NOTIFY_ACTION_ADDED, ret, done,
460 "wrong action (exp: ADDED)");
461 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[0].name, "subdir-name",
463 torture_assert_int_equal_goto(mem_ctx,
464 notify.nttrans.out.changes[1].action,
465 NOTIFY_ACTION_ADDED, ret, done,
466 "wrong action (exp: ADDED)");
467 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[1].name,
468 "subdir-name\\subname1", STR_UNICODE);
469 torture_assert_int_equal_goto(mem_ctx,
470 notify.nttrans.out.changes[2].action,
471 NOTIFY_ACTION_ADDED, ret, done,
472 "wrong action (exp: ADDED)");
473 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[2].name,
474 "subdir-name\\subname2", STR_UNICODE);
475 torture_assert_int_equal_goto(mem_ctx,
476 notify.nttrans.out.changes[3].action,
477 NOTIFY_ACTION_OLD_NAME, ret, done,
478 "wrong action (exp: OLD_NAME)");
479 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[3].name,
480 "subdir-name\\subname1", STR_UNICODE);
481 torture_assert_int_equal_goto(mem_ctx,
482 notify.nttrans.out.changes[4].action,
483 NOTIFY_ACTION_NEW_NAME, ret, done,
484 "wrong action (exp: NEW_NAME)");
485 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[4].name,
486 "subdir-name\\subname1-r", STR_UNICODE);
488 ret &= check_rename_reply(mem_ctx,
489 cli, __LINE__, ¬ify.nttrans.out.changes[5],
490 NOTIFY_ACTION_ADDED, "subname2-r");
491 ret &= check_rename_reply(mem_ctx,
492 cli, __LINE__, ¬ify.nttrans.out.changes[5],
493 NOTIFY_ACTION_REMOVED, "subdir-name\\subname2");
494 ret &= check_rename_reply(mem_ctx,
495 cli, __LINE__, ¬ify.nttrans.out.changes[5],
496 NOTIFY_ACTION_MODIFIED, "subname2-r");
498 ret &= check_rename_reply(mem_ctx,
499 cli, __LINE__, ¬ify.nttrans.out.changes[8],
500 NOTIFY_ACTION_OLD_NAME, "subname2-r");
501 ret &= check_rename_reply(mem_ctx,
502 cli, __LINE__, ¬ify.nttrans.out.changes[8],
503 NOTIFY_ACTION_NEW_NAME, "subname3-r");
504 ret &= check_rename_reply(mem_ctx,
505 cli, __LINE__, ¬ify.nttrans.out.changes[8],
506 NOTIFY_ACTION_MODIFIED, "subname3-r");
512 status = smb_raw_changenotify_recv(req2, mem_ctx, ¬ify);
513 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
514 "smb_raw_changenotify_recv");
516 torture_assert_int_equal_goto(mem_ctx, notify.nttrans.out.num_changes,
517 3, ret, done, "wrong number of changes");
518 torture_assert_int_equal_goto(mem_ctx,
519 notify.nttrans.out.changes[0].action,
520 NOTIFY_ACTION_REMOVED, ret, done,
521 "wrong action (exp: REMOVED)");
522 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[0].name,
523 "subdir-name\\subname1-r", STR_UNICODE);
524 torture_assert_int_equal_goto(mem_ctx,
525 notify.nttrans.out.changes[1].action,
526 NOTIFY_ACTION_REMOVED, ret, done,
527 "wrong action (exp: REMOVED)");
528 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[1].name, "subdir-name",
530 torture_assert_int_equal_goto(mem_ctx,
531 notify.nttrans.out.changes[2].action,
532 NOTIFY_ACTION_REMOVED, ret, done,
533 "wrong action (exp: REMOVED)");
534 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[2].name, "subname3-r",
538 smb_raw_exit(cli->session);
539 smbcli_deltree(cli->tree, BASEDIR);
544 testing of change notify mask change
546 static bool test_notify_mask_change(struct torture_context *mem_ctx,
547 struct smbcli_state *cli)
551 union smb_notify notify;
554 struct smbcli_request *req1, *req2;
556 printf("TESTING CHANGE NOTIFY WITH MASK CHANGE\n");
558 if (!torture_setup_dir(cli, BASEDIR)) {
563 get a handle on the directory
565 io.generic.level = RAW_OPEN_NTCREATEX;
566 io.ntcreatex.in.root_fid.fnum = 0;
567 io.ntcreatex.in.flags = 0;
568 io.ntcreatex.in.access_mask = SEC_FILE_ALL;
569 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
570 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
571 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
572 io.ntcreatex.in.alloc_size = 0;
573 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
574 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
575 io.ntcreatex.in.security_flags = 0;
576 io.ntcreatex.in.fname = BASEDIR;
578 status = smb_raw_open(cli->tree, mem_ctx, &io);
579 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
581 fnum = io.ntcreatex.out.file.fnum;
583 /* ask for a change notify, on file or directory name
584 changes. Setup both with and without recursion */
585 notify.nttrans.level = RAW_NOTIFY_NTTRANS;
586 notify.nttrans.in.buffer_size = 1000;
587 notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_ATTRIBUTES;
588 notify.nttrans.in.file.fnum = fnum;
590 notify.nttrans.in.recursive = true;
591 req1 = smb_raw_changenotify_send(cli->tree, ¬ify);
593 notify.nttrans.in.recursive = false;
594 req2 = smb_raw_changenotify_send(cli->tree, ¬ify);
596 /* cancel initial requests so the buffer is setup */
597 smb_raw_ntcancel(req1);
598 status = smb_raw_changenotify_recv(req1, mem_ctx, ¬ify);
599 torture_assert_ntstatus_equal_goto(mem_ctx, status,
602 "smb_raw_changenotify_recv");
604 smb_raw_ntcancel(req2);
605 status = smb_raw_changenotify_recv(req2, mem_ctx, ¬ify);
606 torture_assert_ntstatus_equal_goto(mem_ctx, status,
609 "smb_raw_changenotify_recv");
611 notify.nttrans.in.recursive = true;
612 req1 = smb_raw_changenotify_send(cli->tree, ¬ify);
614 /* Set to hidden then back again. */
615 smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));
616 smbcli_setatr(cli->tree, BASEDIR "\\tname1", FILE_ATTRIBUTE_HIDDEN, 0);
617 smbcli_unlink(cli->tree, BASEDIR "\\tname1");
619 status = smb_raw_changenotify_recv(req1, mem_ctx, ¬ify);
620 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
621 "smb_raw_changenotify_recv");
623 torture_assert_int_equal_goto(mem_ctx, notify.nttrans.out.num_changes,
624 1, ret, done, "wrong number of changes");
625 torture_assert_int_equal_goto(mem_ctx,
626 notify.nttrans.out.changes[0].action,
627 NOTIFY_ACTION_MODIFIED, ret, done,
628 "wrong action (exp: MODIFIED)");
629 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[0].name, "tname1",
632 /* Now try and change the mask to include other events.
633 * This should not work - once the mask is set on a directory
634 * fnum it seems to be fixed until the fnum is closed. */
636 notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_CREATION;
637 notify.nttrans.in.recursive = true;
638 req1 = smb_raw_changenotify_send(cli->tree, ¬ify);
640 notify.nttrans.in.recursive = false;
641 req2 = smb_raw_changenotify_send(cli->tree, ¬ify);
643 smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
644 smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name\\subname1");
645 smbcli_close(cli->tree,
646 smbcli_open(cli->tree, BASEDIR "\\subdir-name\\subname2", O_CREAT, 0));
647 smbcli_rename(cli->tree, BASEDIR "\\subdir-name\\subname1", BASEDIR "\\subdir-name\\subname1-r");
648 smbcli_rename(cli->tree, BASEDIR "\\subdir-name\\subname2", BASEDIR "\\subname2-r");
649 smbcli_rename(cli->tree, BASEDIR "\\subname2-r", BASEDIR "\\subname3-r");
651 smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name\\subname1-r");
652 smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
653 smbcli_unlink(cli->tree, BASEDIR "\\subname3-r");
655 status = smb_raw_changenotify_recv(req1, mem_ctx, ¬ify);
656 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
657 "smb_raw_changenotify_recv");
659 torture_assert_int_equal_goto(mem_ctx, notify.nttrans.out.num_changes,
660 1, ret, done, "wrong number of changes");
661 torture_assert_int_equal_goto(mem_ctx,
662 notify.nttrans.out.changes[0].action,
663 NOTIFY_ACTION_MODIFIED, ret, done,
664 "wrong action (exp: MODIFIED)");
665 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[0].name, "subname2-r",
668 status = smb_raw_changenotify_recv(req2, mem_ctx, ¬ify);
669 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
670 "smb_raw_changenotify_recv");
672 torture_assert_int_equal_goto(mem_ctx, notify.nttrans.out.num_changes,
673 1, ret, done, "wrong number of changes");
674 torture_assert_int_equal_goto(mem_ctx,
675 notify.nttrans.out.changes[0].action,
676 NOTIFY_ACTION_MODIFIED, ret, done,
677 "wrong action (exp: MODIFIED)");
678 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[0].name, "subname3-r",
686 smb_raw_exit(cli->session);
687 smbcli_deltree(cli->tree, BASEDIR);
693 testing of mask bits for change notify
695 static bool test_notify_mask(struct torture_context *tctx,
696 struct smbcli_state *cli,
697 struct smbcli_state *cli2)
701 union smb_notify notify;
703 union smb_chkpath chkpath;
711 printf("TESTING CHANGE NOTIFY COMPLETION FILTERS\n");
713 if (!torture_setup_dir(cli, BASEDIR)) {
717 tv = timeval_current_ofs(1000, 0);
718 t = timeval_to_nttime(&tv);
721 get a handle on the directory
723 io.generic.level = RAW_OPEN_NTCREATEX;
724 io.ntcreatex.in.root_fid.fnum = 0;
725 io.ntcreatex.in.flags = 0;
726 io.ntcreatex.in.access_mask = SEC_FILE_ALL;
727 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
728 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
729 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
730 io.ntcreatex.in.alloc_size = 0;
731 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
732 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
733 io.ntcreatex.in.security_flags = 0;
734 io.ntcreatex.in.fname = BASEDIR;
736 notify.nttrans.level = RAW_NOTIFY_NTTRANS;
737 notify.nttrans.in.buffer_size = 1000;
738 notify.nttrans.in.recursive = true;
740 chkpath.chkpath.in.path = "\\";
742 #define NOTIFY_MASK_TEST(test_name, setup, op, cleanup, Action, expected, nchanges) \
744 smbcli_getatr(cli->tree, test_name, NULL, NULL, NULL); \
745 do { for (mask=i=0;i<32;i++) { \
746 struct smbcli_request *req; \
747 status = smb_raw_open(cli->tree, tctx, &io); \
748 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, \
750 fnum = io.ntcreatex.out.file.fnum; \
752 notify.nttrans.in.file.fnum = fnum; \
753 notify.nttrans.in.completion_filter = (1<<i); \
754 req = smb_raw_changenotify_send(cli->tree, ¬ify); \
755 smb_raw_chkpath(cli->tree, &chkpath); \
757 smb_msleep(200); smb_raw_ntcancel(req); \
758 status = smb_raw_changenotify_recv(req, tctx, ¬ify); \
760 smbcli_close(cli->tree, fnum); \
761 if (NT_STATUS_EQUAL(status, NT_STATUS_CANCELLED)) continue; \
762 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, \
764 /* special case to cope with file rename behaviour */ \
765 if (nchanges == 2 && notify.nttrans.out.num_changes == 1 && \
766 notify.nttrans.out.changes[0].action == NOTIFY_ACTION_MODIFIED && \
767 ((expected) & FILE_NOTIFY_CHANGE_ATTRIBUTES) && \
768 Action == NOTIFY_ACTION_OLD_NAME) { \
769 printf("(rename file special handling OK)\n"); \
770 } else if (nchanges != notify.nttrans.out.num_changes) { \
771 printf("ERROR: nchanges=%d expected=%d action=%d filter=0x%08x\n", \
772 notify.nttrans.out.num_changes, \
774 notify.nttrans.out.changes[0].action, \
775 notify.nttrans.in.completion_filter); \
777 } else if (notify.nttrans.out.changes[0].action != Action) { \
778 printf("ERROR: nchanges=%d action=%d expectedAction=%d filter=0x%08x\n", \
779 notify.nttrans.out.num_changes, \
780 notify.nttrans.out.changes[0].action, \
782 notify.nttrans.in.completion_filter); \
784 } else if (strcmp(notify.nttrans.out.changes[0].name.s, "tname1") != 0) { \
785 printf("ERROR: nchanges=%d action=%d filter=0x%08x name=%s\n", \
786 notify.nttrans.out.num_changes, \
787 notify.nttrans.out.changes[0].action, \
788 notify.nttrans.in.completion_filter, \
789 notify.nttrans.out.changes[0].name.s); \
794 if ((expected) != mask) { \
795 if (((expected) & ~mask) != 0) { \
796 printf("ERROR: trigger on too few bits. mask=0x%08x expected=0x%08x\n", \
800 printf("WARNING: trigger on too many bits. mask=0x%08x expected=0x%08x\n", \
807 printf("Testing mkdir\n");
808 NOTIFY_MASK_TEST("Testing mkdir",;,
809 smbcli_mkdir(cli->tree, BASEDIR "\\tname1");,
810 smbcli_rmdir(cli2->tree, BASEDIR "\\tname1");,
812 FILE_NOTIFY_CHANGE_DIR_NAME, 1);
814 printf("Testing create file\n");
815 NOTIFY_MASK_TEST("Testing create file",;,
816 smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
817 smbcli_unlink(cli2->tree, BASEDIR "\\tname1");,
819 FILE_NOTIFY_CHANGE_FILE_NAME, 1);
821 printf("Testing unlink\n");
822 NOTIFY_MASK_TEST("Testing unlink",
823 smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
824 smbcli_unlink(cli2->tree, BASEDIR "\\tname1");,
826 NOTIFY_ACTION_REMOVED,
827 FILE_NOTIFY_CHANGE_FILE_NAME, 1);
829 printf("Testing rmdir\n");
830 NOTIFY_MASK_TEST("Testing rmdir",
831 smbcli_mkdir(cli->tree, BASEDIR "\\tname1");,
832 smbcli_rmdir(cli2->tree, BASEDIR "\\tname1");,
834 NOTIFY_ACTION_REMOVED,
835 FILE_NOTIFY_CHANGE_DIR_NAME, 1);
837 printf("Testing rename file\n");
838 NOTIFY_MASK_TEST("Testing rename file",
839 smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
840 smbcli_rename(cli2->tree, BASEDIR "\\tname1", BASEDIR "\\tname2");,
841 smbcli_unlink(cli->tree, BASEDIR "\\tname2");,
842 NOTIFY_ACTION_OLD_NAME,
843 FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_CREATION, 2);
845 printf("Testing rename dir\n");
846 NOTIFY_MASK_TEST("Testing rename dir",
847 smbcli_mkdir(cli->tree, BASEDIR "\\tname1");,
848 smbcli_rename(cli2->tree, BASEDIR "\\tname1", BASEDIR "\\tname2");,
849 smbcli_rmdir(cli->tree, BASEDIR "\\tname2");,
850 NOTIFY_ACTION_OLD_NAME,
851 FILE_NOTIFY_CHANGE_DIR_NAME, 2);
853 printf("Testing set path attribute\n");
854 NOTIFY_MASK_TEST("Testing set path attribute",
855 smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
856 smbcli_setatr(cli2->tree, BASEDIR "\\tname1", FILE_ATTRIBUTE_HIDDEN, 0);,
857 smbcli_unlink(cli->tree, BASEDIR "\\tname1");,
858 NOTIFY_ACTION_MODIFIED,
859 FILE_NOTIFY_CHANGE_ATTRIBUTES, 1);
861 printf("Testing set path write time\n");
862 NOTIFY_MASK_TEST("Testing set path write time",
863 smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
864 smbcli_setatr(cli2->tree, BASEDIR "\\tname1", FILE_ATTRIBUTE_NORMAL, 1000);,
865 smbcli_unlink(cli->tree, BASEDIR "\\tname1");,
866 NOTIFY_ACTION_MODIFIED,
867 FILE_NOTIFY_CHANGE_LAST_WRITE, 1);
869 printf("Testing set file attribute\n");
870 NOTIFY_MASK_TEST("Testing set file attribute",
871 fnum2 = create_complex_file(cli2, tctx, BASEDIR "\\tname1");,
872 smbcli_fsetatr(cli2->tree, fnum2, FILE_ATTRIBUTE_HIDDEN, 0, 0, 0, 0);,
873 (smbcli_close(cli2->tree, fnum2), smbcli_unlink(cli2->tree, BASEDIR "\\tname1"));,
874 NOTIFY_ACTION_MODIFIED,
875 FILE_NOTIFY_CHANGE_ATTRIBUTES, 1);
877 if (torture_setting_bool(tctx, "samba3", false)) {
878 printf("Samba3 does not yet support create times "
882 printf("Testing set file create time\n");
883 NOTIFY_MASK_TEST("Testing set file create time",
884 fnum2 = create_complex_file(cli, tctx,
885 BASEDIR "\\tname1");,
886 smbcli_fsetatr(cli->tree, fnum2, 0, t, 0, 0, 0);,
887 (smbcli_close(cli->tree, fnum2),
888 smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
889 NOTIFY_ACTION_MODIFIED,
890 FILE_NOTIFY_CHANGE_CREATION, 1);
893 printf("Testing set file access time\n");
894 NOTIFY_MASK_TEST("Testing set file access time",
895 fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
896 smbcli_fsetatr(cli->tree, fnum2, 0, 0, t, 0, 0);,
897 (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
898 NOTIFY_ACTION_MODIFIED,
899 FILE_NOTIFY_CHANGE_LAST_ACCESS, 1);
901 printf("Testing set file write time\n");
902 NOTIFY_MASK_TEST("Testing set file write time",
903 fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
904 smbcli_fsetatr(cli->tree, fnum2, 0, 0, 0, t, 0);,
905 (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
906 NOTIFY_ACTION_MODIFIED,
907 FILE_NOTIFY_CHANGE_LAST_WRITE, 1);
909 printf("Testing set file change time\n");
910 NOTIFY_MASK_TEST("Testing set file change time",
911 fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
912 smbcli_fsetatr(cli->tree, fnum2, 0, 0, 0, 0, t);,
913 (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
914 NOTIFY_ACTION_MODIFIED,
918 printf("Testing write\n");
919 NOTIFY_MASK_TEST("Testing write",
920 fnum2 = create_complex_file(cli2, tctx, BASEDIR "\\tname1");,
921 smbcli_write(cli2->tree, fnum2, 1, &c, 10000, 1);,
922 (smbcli_close(cli2->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
923 NOTIFY_ACTION_MODIFIED,
926 printf("Testing truncate\n");
927 NOTIFY_MASK_TEST("Testing truncate",
928 fnum2 = create_complex_file(cli2, tctx, BASEDIR "\\tname1");,
929 smbcli_ftruncate(cli2->tree, fnum2, 10000);,
930 (smbcli_close(cli2->tree, fnum2), smbcli_unlink(cli2->tree, BASEDIR "\\tname1"));,
931 NOTIFY_ACTION_MODIFIED,
932 FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES, 1);
935 smb_raw_exit(cli->session);
936 smbcli_deltree(cli->tree, BASEDIR);
941 basic testing of change notify on files
943 static bool test_notify_file(struct torture_context *mem_ctx,
944 struct smbcli_state *cli)
950 union smb_notify notify;
951 struct smbcli_request *req;
953 const char *fname = BASEDIR "\\file.txt";
955 printf("TESTING CHANGE NOTIFY ON FILES\n");
957 if (!torture_setup_dir(cli, BASEDIR)) {
961 io.generic.level = RAW_OPEN_NTCREATEX;
962 io.ntcreatex.in.root_fid.fnum = 0;
963 io.ntcreatex.in.flags = 0;
964 io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
965 io.ntcreatex.in.create_options = 0;
966 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
967 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
968 io.ntcreatex.in.alloc_size = 0;
969 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
970 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
971 io.ntcreatex.in.security_flags = 0;
972 io.ntcreatex.in.fname = fname;
973 status = smb_raw_open(cli->tree, mem_ctx, &io);
974 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
976 fnum = io.ntcreatex.out.file.fnum;
978 /* ask for a change notify,
979 on file or directory name changes */
980 notify.nttrans.level = RAW_NOTIFY_NTTRANS;
981 notify.nttrans.in.file.fnum = fnum;
982 notify.nttrans.in.buffer_size = 1000;
983 notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_STREAM_NAME;
984 notify.nttrans.in.recursive = false;
986 printf("Testing if notifies on file handles are invalid (should be)\n");
988 req = smb_raw_changenotify_send(cli->tree, ¬ify);
989 status = smb_raw_changenotify_recv(req, mem_ctx, ¬ify);
990 torture_assert_ntstatus_equal_goto(mem_ctx, status,
991 NT_STATUS_INVALID_PARAMETER,
993 "smb_raw_changenotify_recv");
995 cl.close.level = RAW_CLOSE_CLOSE;
996 cl.close.in.file.fnum = fnum;
997 cl.close.in.write_time = 0;
998 status = smb_raw_close(cli->tree, &cl);
999 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
1002 status = smbcli_unlink(cli->tree, fname);
1003 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
1007 smb_raw_exit(cli->session);
1008 smbcli_deltree(cli->tree, BASEDIR);
1013 basic testing of change notifies followed by a tdis
1015 static bool test_notify_tdis(struct torture_context *tctx,
1016 struct smbcli_state *cli1)
1020 union smb_notify notify;
1023 struct smbcli_request *req;
1024 struct smbcli_state *cli = NULL;
1026 printf("TESTING CHANGE NOTIFY FOLLOWED BY TDIS\n");
1028 if (!torture_setup_dir(cli1, BASEDIR)) {
1032 if (!torture_open_connection(&cli, tctx, 0)) {
1037 get a handle on the directory
1039 io.generic.level = RAW_OPEN_NTCREATEX;
1040 io.ntcreatex.in.root_fid.fnum = 0;
1041 io.ntcreatex.in.flags = 0;
1042 io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1043 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1044 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1045 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
1046 io.ntcreatex.in.alloc_size = 0;
1047 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1048 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1049 io.ntcreatex.in.security_flags = 0;
1050 io.ntcreatex.in.fname = BASEDIR;
1052 status = smb_raw_open(cli->tree, tctx, &io);
1053 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1055 fnum = io.ntcreatex.out.file.fnum;
1057 /* ask for a change notify,
1058 on file or directory name changes */
1059 notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1060 notify.nttrans.in.buffer_size = 1000;
1061 notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1062 notify.nttrans.in.file.fnum = fnum;
1063 notify.nttrans.in.recursive = true;
1065 req = smb_raw_changenotify_send(cli->tree, ¬ify);
1067 status = smbcli_tdis(cli);
1068 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1072 status = smb_raw_changenotify_recv(req, tctx, ¬ify);
1073 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1074 "smb_raw_changenotify_recv");
1075 torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes,
1076 0, ret, done, "no changes expected");
1079 torture_close_connection(cli);
1080 smbcli_deltree(cli1->tree, BASEDIR);
1085 basic testing of change notifies followed by a exit
1087 static bool test_notify_exit(struct torture_context *tctx,
1088 struct smbcli_state *cli1)
1092 union smb_notify notify;
1095 struct smbcli_request *req;
1096 struct smbcli_state *cli = NULL;
1098 printf("TESTING CHANGE NOTIFY FOLLOWED BY EXIT\n");
1100 if (!torture_setup_dir(cli1, BASEDIR)) {
1104 if (!torture_open_connection(&cli, tctx, 0)) {
1109 get a handle on the directory
1111 io.generic.level = RAW_OPEN_NTCREATEX;
1112 io.ntcreatex.in.root_fid.fnum = 0;
1113 io.ntcreatex.in.flags = 0;
1114 io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1115 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1116 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1117 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
1118 io.ntcreatex.in.alloc_size = 0;
1119 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1120 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1121 io.ntcreatex.in.security_flags = 0;
1122 io.ntcreatex.in.fname = BASEDIR;
1124 status = smb_raw_open(cli->tree, tctx, &io);
1125 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1127 fnum = io.ntcreatex.out.file.fnum;
1129 /* ask for a change notify,
1130 on file or directory name changes */
1131 notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1132 notify.nttrans.in.buffer_size = 1000;
1133 notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1134 notify.nttrans.in.file.fnum = fnum;
1135 notify.nttrans.in.recursive = true;
1137 req = smb_raw_changenotify_send(cli->tree, ¬ify);
1139 status = smb_raw_exit(cli->session);
1140 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1143 status = smb_raw_changenotify_recv(req, tctx, ¬ify);
1144 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1145 "smb_raw_changenotify_recv");
1146 torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes,
1147 0, ret, done, "no changes expected");
1150 torture_close_connection(cli);
1151 smbcli_deltree(cli1->tree, BASEDIR);
1156 basic testing of change notifies followed by a ulogoff
1158 static bool test_notify_ulogoff(struct torture_context *tctx,
1159 struct smbcli_state *cli1)
1163 union smb_notify notify;
1166 struct smbcli_request *req;
1167 struct smbcli_state *cli = NULL;
1169 printf("TESTING CHANGE NOTIFY FOLLOWED BY ULOGOFF\n");
1171 if (!torture_setup_dir(cli1, BASEDIR)) {
1175 if (!torture_open_connection(&cli, tctx, 0)) {
1180 get a handle on the directory
1182 io.generic.level = RAW_OPEN_NTCREATEX;
1183 io.ntcreatex.in.root_fid.fnum = 0;
1184 io.ntcreatex.in.flags = 0;
1185 io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1186 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1187 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1188 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
1189 io.ntcreatex.in.alloc_size = 0;
1190 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1191 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1192 io.ntcreatex.in.security_flags = 0;
1193 io.ntcreatex.in.fname = BASEDIR;
1195 status = smb_raw_open(cli->tree, tctx, &io);
1196 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1198 fnum = io.ntcreatex.out.file.fnum;
1200 /* ask for a change notify,
1201 on file or directory name changes */
1202 notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1203 notify.nttrans.in.buffer_size = 1000;
1204 notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1205 notify.nttrans.in.file.fnum = fnum;
1206 notify.nttrans.in.recursive = true;
1208 req = smb_raw_changenotify_send(cli->tree, ¬ify);
1210 status = smb_raw_ulogoff(cli->session);
1211 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1214 status = smb_raw_changenotify_recv(req, tctx, ¬ify);
1215 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1216 "smb_raw_changenotify_recv");
1217 torture_assert_int_equal_goto(tctx, notify.nttrans.out.num_changes,
1218 0, ret, done, "no changes expected");
1221 torture_close_connection(cli);
1222 smbcli_deltree(cli1->tree, BASEDIR);
1226 static void tcp_dis_handler(struct smbcli_transport *t, void *p)
1228 struct smbcli_state *cli = (struct smbcli_state *)p;
1229 smbcli_transport_dead(cli->transport, NT_STATUS_LOCAL_DISCONNECT);
1230 cli->transport = NULL;
1234 basic testing of change notifies followed by tcp disconnect
1236 static bool test_notify_tcp_dis(struct torture_context *tctx,
1237 struct smbcli_state *cli1)
1241 union smb_notify notify;
1244 struct smbcli_request *req;
1245 struct smbcli_state *cli = NULL;
1247 printf("TESTING CHANGE NOTIFY FOLLOWED BY TCP DISCONNECT\n");
1249 if (!torture_setup_dir(cli1, BASEDIR)) {
1253 if (!torture_open_connection(&cli, tctx, 0)) {
1258 get a handle on the directory
1260 io.generic.level = RAW_OPEN_NTCREATEX;
1261 io.ntcreatex.in.root_fid.fnum = 0;
1262 io.ntcreatex.in.flags = 0;
1263 io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1264 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1265 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1266 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
1267 io.ntcreatex.in.alloc_size = 0;
1268 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1269 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1270 io.ntcreatex.in.security_flags = 0;
1271 io.ntcreatex.in.fname = BASEDIR;
1273 status = smb_raw_open(cli->tree, tctx, &io);
1274 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1276 fnum = io.ntcreatex.out.file.fnum;
1278 /* ask for a change notify,
1279 on file or directory name changes */
1280 notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1281 notify.nttrans.in.buffer_size = 1000;
1282 notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1283 notify.nttrans.in.file.fnum = fnum;
1284 notify.nttrans.in.recursive = true;
1286 req = smb_raw_changenotify_send(cli->tree, ¬ify);
1288 smbcli_transport_idle_handler(cli->transport, tcp_dis_handler, 250, cli);
1290 status = smb_raw_changenotify_recv(req, tctx, ¬ify);
1291 torture_assert_ntstatus_equal_goto(tctx, status,
1292 NT_STATUS_LOCAL_DISCONNECT,
1294 "smb_raw_changenotify_recv");
1297 torture_close_connection(cli);
1298 smbcli_deltree(cli1->tree, BASEDIR);
1303 test setting up two change notify requests on one handle
1305 static bool test_notify_double(struct torture_context *mem_ctx,
1306 struct smbcli_state *cli)
1310 union smb_notify notify;
1313 struct smbcli_request *req1, *req2;
1315 printf("TESTING CHANGE NOTIFY TWICE ON ONE DIRECTORY\n");
1317 if (!torture_setup_dir(cli, BASEDIR)) {
1321 get a handle on the directory
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;
1336 status = smb_raw_open(cli->tree, mem_ctx, &io);
1337 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
1339 fnum = io.ntcreatex.out.file.fnum;
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;
1349 req1 = smb_raw_changenotify_send(cli->tree, ¬ify);
1350 req2 = smb_raw_changenotify_send(cli->tree, ¬ify);
1352 smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
1354 status = smb_raw_changenotify_recv(req1, mem_ctx, ¬ify);
1355 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
1356 "smb_raw_changenotify_recv");
1357 torture_assert_int_equal_goto(mem_ctx, notify.nttrans.out.num_changes,
1358 1, ret, done, "wrong number of changes");
1359 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[0].name, "subdir-name",
1362 smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name2");
1364 status = smb_raw_changenotify_recv(req2, mem_ctx, ¬ify);
1365 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
1366 "smb_raw_changenotify_recv");
1367 torture_assert_int_equal_goto(mem_ctx, notify.nttrans.out.num_changes,
1368 1, ret, done, "wrong number of changes");
1369 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[0].name, "subdir-name2",
1373 smb_raw_exit(cli->session);
1374 smbcli_deltree(cli->tree, BASEDIR);
1380 test multiple change notifies at different depths and with/without recursion
1382 static bool test_notify_tree(struct torture_context *mem_ctx,
1383 struct smbcli_state *cli,
1384 struct smbcli_state *cli2)
1387 union smb_notify notify;
1389 struct smbcli_request *req;
1399 {BASEDIR "\\abc", true, FILE_NOTIFY_CHANGE_NAME, 30 },
1400 {BASEDIR "\\zqy", true, FILE_NOTIFY_CHANGE_NAME, 8 },
1401 {BASEDIR "\\atsy", true, FILE_NOTIFY_CHANGE_NAME, 4 },
1402 {BASEDIR "\\abc\\foo", true, FILE_NOTIFY_CHANGE_NAME, 2 },
1403 {BASEDIR "\\abc\\blah", true, FILE_NOTIFY_CHANGE_NAME, 13 },
1404 {BASEDIR "\\abc\\blah", false, FILE_NOTIFY_CHANGE_NAME, 7 },
1405 {BASEDIR "\\abc\\blah\\a", true, FILE_NOTIFY_CHANGE_NAME, 2 },
1406 {BASEDIR "\\abc\\blah\\b", true, FILE_NOTIFY_CHANGE_NAME, 2 },
1407 {BASEDIR "\\abc\\blah\\c", true, FILE_NOTIFY_CHANGE_NAME, 2 },
1408 {BASEDIR "\\abc\\fooblah", true, FILE_NOTIFY_CHANGE_NAME, 2 },
1409 {BASEDIR "\\zqy\\xx", true, FILE_NOTIFY_CHANGE_NAME, 2 },
1410 {BASEDIR "\\zqy\\yyy", true, FILE_NOTIFY_CHANGE_NAME, 2 },
1411 {BASEDIR "\\zqy\\..", true, FILE_NOTIFY_CHANGE_NAME, 40 },
1412 {BASEDIR, true, FILE_NOTIFY_CHANGE_NAME, 40 },
1413 {BASEDIR, false,FILE_NOTIFY_CHANGE_NAME, 6 },
1414 {BASEDIR "\\atsy", false,FILE_NOTIFY_CHANGE_NAME, 4 },
1415 {BASEDIR "\\abc", true, FILE_NOTIFY_CHANGE_NAME, 24 },
1416 {BASEDIR "\\abc", false,FILE_NOTIFY_CHANGE_FILE_NAME, 0 },
1417 {BASEDIR "\\abc", true, FILE_NOTIFY_CHANGE_FILE_NAME, 0 },
1418 {BASEDIR "\\abc", true, FILE_NOTIFY_CHANGE_NAME, 24 },
1422 bool all_done = false;
1424 printf("TESTING CHANGE NOTIFY FOR DIFFERENT DEPTHS\n");
1426 if (!torture_setup_dir(cli, BASEDIR)) {
1430 io.generic.level = RAW_OPEN_NTCREATEX;
1431 io.ntcreatex.in.root_fid.fnum = 0;
1432 io.ntcreatex.in.flags = 0;
1433 io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1434 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1435 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1436 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
1437 io.ntcreatex.in.alloc_size = 0;
1438 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
1439 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1440 io.ntcreatex.in.security_flags = 0;
1442 notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1443 notify.nttrans.in.buffer_size = 20000;
1446 setup the directory tree, and the notify buffer on each directory
1448 for (i=0;i<ARRAY_SIZE(dirs);i++) {
1449 io.ntcreatex.in.fname = dirs[i].path;
1450 status = smb_raw_open(cli->tree, mem_ctx, &io);
1451 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
1453 dirs[i].fnum = io.ntcreatex.out.file.fnum;
1455 notify.nttrans.in.completion_filter = dirs[i].filter;
1456 notify.nttrans.in.file.fnum = dirs[i].fnum;
1457 notify.nttrans.in.recursive = dirs[i].recursive;
1458 req = smb_raw_changenotify_send(cli->tree, ¬ify);
1459 smb_raw_ntcancel(req);
1460 status = smb_raw_changenotify_recv(req, mem_ctx, ¬ify);
1461 torture_assert_ntstatus_equal_goto(mem_ctx, status,
1462 NT_STATUS_CANCELLED,
1464 "smb_raw_changenotify_recv");
1467 /* trigger 2 events in each dir */
1468 for (i=0;i<ARRAY_SIZE(dirs);i++) {
1469 char *path = talloc_asprintf(mem_ctx, "%s\\test.dir", dirs[i].path);
1471 * Make notifies a bit more interesting in a cluster
1472 * by doing the changes against different nodes with
1475 smbcli_mkdir(cli->tree, path);
1476 smbcli_rmdir(cli2->tree, path);
1480 /* give a bit of time for the events to propogate */
1481 tv = timeval_current();
1484 /* count events that have happened in each dir */
1485 for (i=0;i<ARRAY_SIZE(dirs);i++) {
1486 notify.nttrans.in.file.fnum = dirs[i].fnum;
1487 req = smb_raw_changenotify_send(cli->tree, ¬ify);
1488 smb_raw_ntcancel(req);
1489 notify.nttrans.out.num_changes = 0;
1490 status = smb_raw_changenotify_recv(req, mem_ctx, ¬ify);
1491 dirs[i].counted += notify.nttrans.out.num_changes;
1496 for (i=0;i<ARRAY_SIZE(dirs);i++) {
1497 if (dirs[i].counted != dirs[i].expected) {
1501 } while (!all_done && timeval_elapsed(&tv) < 20);
1503 printf("took %.4f seconds to propogate all events\n", timeval_elapsed(&tv));
1505 for (i=0;i<ARRAY_SIZE(dirs);i++) {
1506 if (dirs[i].counted != dirs[i].expected) {
1507 printf("ERROR: i=%d expected %d got %d for '%s'\n",
1508 i, dirs[i].expected, dirs[i].counted, dirs[i].path);
1514 run from the back, closing and deleting
1516 for (i=ARRAY_SIZE(dirs)-1;i>=0;i--) {
1517 smbcli_close(cli->tree, dirs[i].fnum);
1518 smbcli_rmdir(cli->tree, dirs[i].path);
1522 smb_raw_exit(cli->session);
1523 smbcli_deltree(cli->tree, BASEDIR);
1528 Test response when cached server events exceed single NT NOTFIY response
1531 static bool test_notify_overflow(struct torture_context *mem_ctx,
1532 struct smbcli_state *cli)
1536 union smb_notify notify;
1540 struct smbcli_request *req1;
1543 printf("TESTING CHANGE NOTIFY EVENT OVERFLOW\n");
1545 if (!torture_setup_dir(cli, BASEDIR)) {
1549 /* get a handle on the directory */
1550 io.generic.level = RAW_OPEN_NTCREATEX;
1551 io.ntcreatex.in.root_fid.fnum = 0;
1552 io.ntcreatex.in.flags = 0;
1553 io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1554 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1555 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1556 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1557 NTCREATEX_SHARE_ACCESS_WRITE;
1558 io.ntcreatex.in.alloc_size = 0;
1559 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1560 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1561 io.ntcreatex.in.security_flags = 0;
1562 io.ntcreatex.in.fname = BASEDIR;
1564 status = smb_raw_open(cli->tree, mem_ctx, &io);
1565 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
1567 fnum = io.ntcreatex.out.file.fnum;
1569 /* ask for a change notify, on name changes. */
1570 notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1571 notify.nttrans.in.buffer_size = 1000;
1572 notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1573 notify.nttrans.in.file.fnum = fnum;
1575 notify.nttrans.in.recursive = true;
1576 req1 = smb_raw_changenotify_send(cli->tree, ¬ify);
1578 /* cancel initial requests so the buffer is setup */
1579 smb_raw_ntcancel(req1);
1580 status = smb_raw_changenotify_recv(req1, mem_ctx, ¬ify);
1581 torture_assert_ntstatus_equal_goto(mem_ctx, status,
1582 NT_STATUS_CANCELLED,
1584 "smb_raw_changenotify_recv");
1586 /* open a lot of files, filling up the server side notify buffer */
1587 printf("Testing overflowed buffer notify on create of %d files\n",
1589 for (i=0;i<count;i++) {
1590 char *fname = talloc_asprintf(cli, BASEDIR "\\test%d.txt", i);
1591 int fnum2 = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR,
1594 printf("Failed to create %s - %s\n",
1595 fname, smbcli_errstr(cli->tree));
1600 smbcli_close(cli->tree, fnum2);
1603 /* expect that 0 events will be returned with NT_STATUS_OK */
1604 req1 = smb_raw_changenotify_send(cli->tree, ¬ify);
1605 status = smb_raw_changenotify_recv(req1, mem_ctx, ¬ify);
1606 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
1607 "smb_raw_changenotify_recv");
1608 torture_assert_int_equal_goto(mem_ctx, notify.nttrans.out.num_changes,
1609 0, ret, done, "no changes expected");
1612 smb_raw_exit(cli->session);
1613 smbcli_deltree(cli->tree, BASEDIR);
1618 Test if notifications are returned for changes to the base directory.
1621 static bool test_notify_basedir(struct torture_context *mem_ctx,
1622 struct smbcli_state *cli)
1626 union smb_notify notify;
1629 struct smbcli_request *req1;
1631 printf("TESTING CHANGE NOTIFY BASEDIR EVENTS\n");
1633 if (!torture_setup_dir(cli, BASEDIR)) {
1637 /* get a handle on the directory */
1638 io.generic.level = RAW_OPEN_NTCREATEX;
1639 io.ntcreatex.in.root_fid.fnum = 0;
1640 io.ntcreatex.in.flags = 0;
1641 io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1642 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1643 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1644 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1645 NTCREATEX_SHARE_ACCESS_WRITE;
1646 io.ntcreatex.in.alloc_size = 0;
1647 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1648 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1649 io.ntcreatex.in.security_flags = 0;
1650 io.ntcreatex.in.fname = BASEDIR;
1652 status = smb_raw_open(cli->tree, mem_ctx, &io);
1653 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
1655 fnum = io.ntcreatex.out.file.fnum;
1657 /* create a test file that will also be modified */
1658 smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1",
1661 /* ask for a change notify, on attribute changes. */
1662 notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1663 notify.nttrans.in.buffer_size = 1000;
1664 notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_ATTRIBUTES;
1665 notify.nttrans.in.file.fnum = fnum;
1666 notify.nttrans.in.recursive = true;
1668 req1 = smb_raw_changenotify_send(cli->tree, ¬ify);
1670 /* set attribute on the base dir */
1671 smbcli_setatr(cli->tree, BASEDIR, FILE_ATTRIBUTE_HIDDEN, 0);
1673 /* set attribute on a file to assure we receive a notification */
1674 smbcli_setatr(cli->tree, BASEDIR "\\tname1", FILE_ATTRIBUTE_HIDDEN, 0);
1677 /* check how many responses were given, expect only 1 for the file */
1678 status = smb_raw_changenotify_recv(req1, mem_ctx, ¬ify);
1679 torture_assert_ntstatus_ok_goto(mem_ctx, status, ret, done,
1680 "smb_raw_changenotify_recv");
1681 torture_assert_int_equal_goto(mem_ctx, notify.nttrans.out.num_changes,
1682 1, ret, done, "wrong number of changes");
1683 torture_assert_int_equal_goto(mem_ctx,
1684 notify.nttrans.out.changes[0].action,
1685 NOTIFY_ACTION_MODIFIED, ret, done,
1686 "wrong action (exp: MODIFIED)");
1687 CHECK_WSTR(mem_ctx, notify.nttrans.out.changes[0].name, "tname1",
1691 smb_raw_exit(cli->session);
1692 smbcli_deltree(cli->tree, BASEDIR);
1698 create a secondary tree connect - used to test for a bug in Samba3 messaging
1701 static struct smbcli_tree *secondary_tcon(struct smbcli_state *cli,
1702 struct torture_context *tctx)
1705 const char *share, *host;
1706 struct smbcli_tree *tree;
1707 union smb_tcon tcon;
1709 share = torture_setting_string(tctx, "share", NULL);
1710 host = torture_setting_string(tctx, "host", NULL);
1712 printf("create a second tree context on the same session\n");
1713 tree = smbcli_tree_init(cli->session, tctx, false);
1715 tcon.generic.level = RAW_TCON_TCONX;
1716 tcon.tconx.in.flags = TCONX_FLAG_EXTENDED_RESPONSE;
1717 tcon.tconx.in.password = data_blob(NULL, 0);
1718 tcon.tconx.in.path = talloc_asprintf(tctx, "\\\\%s\\%s", host, share);
1719 tcon.tconx.in.device = "A:";
1720 status = smb_raw_tcon(tree, tctx, &tcon);
1721 if (!NT_STATUS_IS_OK(status)) {
1723 printf("Failed to create secondary tree\n");
1727 tree->tid = tcon.tconx.out.tid;
1728 printf("tid1=%d tid2=%d\n", cli->tree->tid, tree->tid);
1735 very simple change notify test
1737 static bool test_notify_tcon(struct torture_context *torture,
1738 struct smbcli_state *cli)
1742 union smb_notify notify;
1745 struct smbcli_request *req;
1746 extern int torture_numops;
1747 struct smbcli_tree *tree = NULL;
1749 printf("TESTING SIMPLE CHANGE NOTIFY\n");
1751 if (!torture_setup_dir(cli, BASEDIR)) {
1756 get a handle on the directory
1758 io.generic.level = RAW_OPEN_NTCREATEX;
1759 io.ntcreatex.in.root_fid.fnum = 0;
1760 io.ntcreatex.in.flags = 0;
1761 io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1762 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1763 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1764 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
1765 io.ntcreatex.in.alloc_size = 0;
1766 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1767 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1768 io.ntcreatex.in.security_flags = 0;
1769 io.ntcreatex.in.fname = BASEDIR;
1771 status = smb_raw_open(cli->tree, torture, &io);
1772 torture_assert_ntstatus_ok_goto(torture, status, ret, done,
1774 fnum = io.ntcreatex.out.file.fnum;
1776 status = smb_raw_open(cli->tree, torture, &io);
1777 torture_assert_ntstatus_ok_goto(torture, status, ret, done,
1780 /* ask for a change notify,
1781 on file or directory name changes */
1782 notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1783 notify.nttrans.in.buffer_size = 1000;
1784 notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1785 notify.nttrans.in.file.fnum = fnum;
1786 notify.nttrans.in.recursive = true;
1788 printf("Testing notify mkdir\n");
1789 req = smb_raw_changenotify_send(cli->tree, ¬ify);
1790 smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
1792 status = smb_raw_changenotify_recv(req, torture, ¬ify);
1793 torture_assert_ntstatus_ok_goto(torture, status, ret, done,
1794 "smb_raw_changenotify_recv");
1796 torture_assert_int_equal_goto(torture, notify.nttrans.out.num_changes,
1797 1, ret, done, "wrong number of changes");
1798 torture_assert_int_equal_goto(torture,
1799 notify.nttrans.out.changes[0].action,
1800 NOTIFY_ACTION_ADDED, ret, done,
1801 "wrong action (exp: ADDED)");
1802 CHECK_WSTR(torture, notify.nttrans.out.changes[0].name, "subdir-name",
1805 printf("Testing notify rmdir\n");
1806 req = smb_raw_changenotify_send(cli->tree, ¬ify);
1807 smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
1809 status = smb_raw_changenotify_recv(req, torture, ¬ify);
1810 torture_assert_ntstatus_ok_goto(torture, status, ret, done,
1811 "smb_raw_changenotify_recv");
1812 torture_assert_int_equal_goto(torture, notify.nttrans.out.num_changes,
1813 1, ret, done, "wrong number of changes");
1814 torture_assert_int_equal_goto(torture,
1815 notify.nttrans.out.changes[0].action,
1816 NOTIFY_ACTION_REMOVED, ret, done,
1817 "wrong action (exp: REMOVED)");
1818 CHECK_WSTR(torture, notify.nttrans.out.changes[0].name, "subdir-name",
1821 printf("SIMPLE CHANGE NOTIFY OK\n");
1823 printf("TESTING WITH SECONDARY TCON\n");
1824 tree = secondary_tcon(cli, torture);
1826 printf("Testing notify mkdir\n");
1827 req = smb_raw_changenotify_send(cli->tree, ¬ify);
1828 smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
1830 status = smb_raw_changenotify_recv(req, torture, ¬ify);
1831 torture_assert_ntstatus_ok_goto(torture, status, ret, done,
1832 "smb_raw_changenotify_recv");
1834 torture_assert_int_equal_goto(torture, notify.nttrans.out.num_changes,
1835 1, ret, done, "wrong number of changes");
1836 torture_assert_int_equal_goto(torture,
1837 notify.nttrans.out.changes[0].action,
1838 NOTIFY_ACTION_ADDED, ret, done,
1839 "wrong action (exp: ADDED)");
1840 CHECK_WSTR(torture, notify.nttrans.out.changes[0].name, "subdir-name",
1843 printf("Testing notify rmdir\n");
1844 req = smb_raw_changenotify_send(cli->tree, ¬ify);
1845 smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
1847 status = smb_raw_changenotify_recv(req, torture, ¬ify);
1848 torture_assert_ntstatus_ok_goto(torture, status, ret, done,
1849 "smb_raw_changenotify_recv");
1850 torture_assert_int_equal_goto(torture, notify.nttrans.out.num_changes,
1851 1, ret, done, "wrong number of changes");
1852 torture_assert_int_equal_goto(torture,
1853 notify.nttrans.out.changes[0].action,
1854 NOTIFY_ACTION_REMOVED, ret, done,
1855 "wrong action (exp: REMOVED)");
1856 CHECK_WSTR(torture, notify.nttrans.out.changes[0].name, "subdir-name",
1859 printf("CHANGE NOTIFY WITH TCON OK\n");
1861 printf("Disconnecting secondary tree\n");
1862 status = smb_tree_disconnect(tree);
1863 torture_assert_ntstatus_ok_goto(torture, status, ret, done,
1864 "smb_tree_disconnect");
1867 printf("Testing notify mkdir\n");
1868 req = smb_raw_changenotify_send(cli->tree, ¬ify);
1869 smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
1871 status = smb_raw_changenotify_recv(req, torture, ¬ify);
1872 torture_assert_ntstatus_ok_goto(torture, status, ret, done,
1873 "smb_raw_changenotify_recv");
1875 torture_assert_int_equal_goto(torture, notify.nttrans.out.num_changes,
1876 1, ret, done, "wrong number of changes");
1877 torture_assert_int_equal_goto(torture,
1878 notify.nttrans.out.changes[0].action,
1879 NOTIFY_ACTION_ADDED, ret, done,
1880 "wrong action (exp: ADDED)");
1881 CHECK_WSTR(torture, notify.nttrans.out.changes[0].name, "subdir-name",
1884 printf("Testing notify rmdir\n");
1885 req = smb_raw_changenotify_send(cli->tree, ¬ify);
1886 smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
1888 status = smb_raw_changenotify_recv(req, torture, ¬ify);
1889 torture_assert_ntstatus_ok_goto(torture, status, ret, done,
1890 "smb_raw_changenotify_recv");
1891 torture_assert_int_equal_goto(torture, notify.nttrans.out.num_changes,
1892 1, ret, done, "wrong number of changes");
1893 torture_assert_int_equal_goto(torture,
1894 notify.nttrans.out.changes[0].action,
1895 NOTIFY_ACTION_REMOVED, ret, done,
1896 "wrong action (exp: REMOVED)");
1897 CHECK_WSTR(torture, notify.nttrans.out.changes[0].name, "subdir-name",
1900 printf("CHANGE NOTIFY WITH TDIS OK\n");
1902 smb_raw_exit(cli->session);
1903 smbcli_deltree(cli->tree, BASEDIR);
1909 testing alignment of multiple change notify infos
1911 static bool test_notify_alignment(struct torture_context *tctx,
1912 struct smbcli_state *cli)
1915 union smb_notify notify;
1918 struct smbcli_request *req;
1919 const char *fname = BASEDIR "\\starter";
1920 const char *fnames[] = { "a",
1924 int num_names = ARRAY_SIZE(fnames);
1927 torture_comment(tctx, "TESTING CHANGE NOTIFY REPLY ALIGNMENT\n");
1929 if (!torture_setup_dir(cli, BASEDIR)) {
1933 /* get a handle on the directory */
1934 io.generic.level = RAW_OPEN_NTCREATEX;
1935 io.ntcreatex.in.root_fid.fnum = 0;
1936 io.ntcreatex.in.flags = 0;
1937 io.ntcreatex.in.access_mask = SEC_FILE_ALL;
1938 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1939 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1940 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1941 NTCREATEX_SHARE_ACCESS_WRITE;
1942 io.ntcreatex.in.alloc_size = 0;
1943 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1944 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1945 io.ntcreatex.in.security_flags = 0;
1946 io.ntcreatex.in.fname = BASEDIR;
1948 status = smb_raw_open(cli->tree, tctx, &io);
1949 torture_assert_ntstatus_ok(tctx, status, "");
1950 fnum = io.ntcreatex.out.file.fnum;
1952 /* ask for a change notify, on file creation */
1953 notify.nttrans.level = RAW_NOTIFY_NTTRANS;
1954 notify.nttrans.in.buffer_size = 1000;
1955 notify.nttrans.in.completion_filter = FILE_NOTIFY_CHANGE_FILE_NAME;
1956 notify.nttrans.in.file.fnum = fnum;
1957 notify.nttrans.in.recursive = false;
1959 /* start change tracking */
1960 req = smb_raw_changenotify_send(cli->tree, ¬ify);
1962 fnum2 = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
1963 torture_assert(tctx, fnum2 != -1, smbcli_errstr(cli->tree));
1964 smbcli_close(cli->tree, fnum2);
1966 status = smb_raw_changenotify_recv(req, tctx, ¬ify);
1967 torture_assert_ntstatus_ok(tctx, status, "");
1969 /* create 4 files that will cause CHANGE_NOTIFY_INFO structures
1970 * to be returned in the same packet with all possible 4-byte padding
1971 * permutations. As per MS-CIFS 2.2.7.4.2 these structures should be
1972 * 4-byte aligned. */
1974 for (i = 0; i < num_names; i++) {
1975 fpath = talloc_asprintf(tctx, "%s\\%s", BASEDIR, fnames[i]);
1976 fnum2 = smbcli_open(cli->tree, fpath,
1977 O_CREAT|O_RDWR, DENY_NONE);
1978 torture_assert(tctx, fnum2 != -1, smbcli_errstr(cli->tree));
1979 smbcli_close(cli->tree, fnum2);
1983 /* We send a notify packet, and let smb_raw_changenotify_recv() do
1984 * the alignment checking for us. */
1985 req = smb_raw_changenotify_send(cli->tree, ¬ify);
1986 status = smb_raw_changenotify_recv(req, tctx, ¬ify);
1987 torture_assert_ntstatus_ok(tctx, status, "");
1989 /* Do basic checking for correctness. */
1990 torture_assert(tctx, notify.nttrans.out.num_changes == num_names, "");
1991 for (i = 0; i < num_names; i++) {
1992 torture_assert(tctx, notify.nttrans.out.changes[i].action ==
1993 NOTIFY_ACTION_ADDED, "");
1994 CHECK_WSTR(tctx, notify.nttrans.out.changes[i].name, fnames[i],
1998 smb_raw_exit(cli->session);
1999 smbcli_deltree(cli->tree, BASEDIR);
2003 struct torture_suite *torture_raw_notify(TALLOC_CTX *mem_ctx)
2005 struct torture_suite *suite = torture_suite_create(mem_ctx, "notify");
2007 torture_suite_add_1smb_test(suite, "tcon", test_notify_tcon);
2008 torture_suite_add_2smb_test(suite, "dir", test_notify_dir);
2009 torture_suite_add_2smb_test(suite, "mask", test_notify_mask);
2010 torture_suite_add_2smb_test(suite, "recursive", test_notify_recursive);
2011 torture_suite_add_1smb_test(suite, "mask_change",
2012 test_notify_mask_change);
2013 torture_suite_add_1smb_test(suite, "file", test_notify_file);
2014 torture_suite_add_1smb_test(suite, "tdis", test_notify_tdis);
2015 torture_suite_add_1smb_test(suite, "exit", test_notify_exit);
2016 torture_suite_add_1smb_test(suite, "ulogoff", test_notify_ulogoff);
2017 torture_suite_add_1smb_test(suite, "tcp_dis", test_notify_tcp_dis);
2018 torture_suite_add_1smb_test(suite, "double", test_notify_double);
2019 torture_suite_add_2smb_test(suite, "tree", test_notify_tree);
2020 torture_suite_add_1smb_test(suite, "overflow", test_notify_overflow);
2021 torture_suite_add_1smb_test(suite, "basedir", test_notify_basedir);
2022 torture_suite_add_1smb_test(suite, "alignment", test_notify_alignment);