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