Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : SMB2 notify test suite
5 :
6 : Copyright (C) Stefan Metzmacher 2006
7 : Copyright (C) Andrew Tridgell 2009
8 :
9 : This program is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "includes.h"
24 : #include "libcli/smb2/smb2.h"
25 : #include "libcli/smb2/smb2_calls.h"
26 : #include "../libcli/smb/smbXcli_base.h"
27 :
28 : #include "torture/torture.h"
29 : #include "torture/smb2/proto.h"
30 : #include "librpc/gen_ndr/ndr_security.h"
31 : #include "libcli/security/security.h"
32 : #include "torture/util.h"
33 :
34 : #include "system/filesys.h"
35 : #include "auth/credentials/credentials.h"
36 : #include "lib/cmdline/cmdline.h"
37 : #include "librpc/gen_ndr/security.h"
38 :
39 : #include "lib/events/events.h"
40 :
41 : #include "libcli/raw/libcliraw.h"
42 : #include "libcli/raw/raw_proto.h"
43 : #include "libcli/libcli.h"
44 :
45 : #define CHECK_STATUS(status, correct) do { \
46 : if (!NT_STATUS_EQUAL(status, correct)) { \
47 : torture_result(torture, TORTURE_FAIL, \
48 : "(%s) Incorrect status %s - should be %s\n", \
49 : __location__, nt_errstr(status), nt_errstr(correct)); \
50 : ret = false; \
51 : goto done; \
52 : }} while (0)
53 :
54 : #define CHECK_VAL(v, correct) do { \
55 : if ((v) != (correct)) { \
56 : torture_result(torture, TORTURE_FAIL, \
57 : "(%s) wrong value for %s 0x%x should be 0x%x\n", \
58 : __location__, #v, (int)v, (int)correct); \
59 : ret = false; \
60 : goto done; \
61 : }} while (0)
62 :
63 : #define CHECK_WIRE_STR(field, value) do { \
64 : if (!field.s || strcmp(field.s, value)) { \
65 : torture_result(torture, TORTURE_FAIL, \
66 : "(%s) %s [%s] != %s\n", __location__, #field, \
67 : field.s, value); \
68 : ret = false; \
69 : goto done; \
70 : }} while (0)
71 :
72 : #define WAIT_FOR_ASYNC_RESPONSE(req) \
73 : while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { \
74 : if (tevent_loop_once(torture->ev) != 0) { \
75 : break; \
76 : } \
77 : }
78 :
79 : #define BASEDIR "test_notify"
80 : #define FNAME "smb2-notify01.dat"
81 :
82 2 : static bool test_valid_request(struct torture_context *torture,
83 : struct smb2_tree *tree)
84 : {
85 2 : bool ret = true;
86 : NTSTATUS status;
87 : struct smb2_handle dh;
88 : struct smb2_notify n;
89 : struct smb2_request *req;
90 : uint32_t max_buffer_size;
91 :
92 2 : torture_comment(torture, "TESTING VALIDITY OF CHANGE NOTIFY REQUEST\n");
93 :
94 2 : smb2_transport_credits_ask_num(tree->session->transport, 256);
95 :
96 2 : smb2_util_unlink(tree, FNAME);
97 :
98 2 : status = smb2_util_roothandle(tree, &dh);
99 2 : CHECK_STATUS(status, NT_STATUS_OK);
100 :
101 2 : max_buffer_size =
102 2 : smb2cli_conn_max_trans_size(tree->session->transport->conn);
103 :
104 2 : n.in.recursive = 0x0000;
105 2 : n.in.buffer_size = max_buffer_size;
106 2 : n.in.file.handle = dh;
107 2 : n.in.completion_filter = FILE_NOTIFY_CHANGE_ALL;
108 2 : n.in.unknown = 0x00000000;
109 2 : req = smb2_notify_send(tree, &n);
110 :
111 2 : while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) {
112 8 : if (tevent_loop_once(torture->ev) != 0) {
113 0 : break;
114 : }
115 : }
116 :
117 2 : status = torture_setup_simple_file(torture, tree, FNAME);
118 2 : CHECK_STATUS(status, NT_STATUS_OK);
119 :
120 2 : status = smb2_notify_recv(req, torture, &n);
121 2 : CHECK_STATUS(status, NT_STATUS_OK);
122 2 : CHECK_VAL(n.out.num_changes, 1);
123 2 : CHECK_VAL(n.out.changes[0].action, NOTIFY_ACTION_ADDED);
124 2 : CHECK_WIRE_STR(n.out.changes[0].name, FNAME);
125 :
126 : /*
127 : * if the change response doesn't fit in the buffer
128 : * NOTIFY_ENUM_DIR is returned.
129 : */
130 2 : n.in.buffer_size = 0x00000000;
131 2 : req = smb2_notify_send(tree, &n);
132 :
133 2 : while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) {
134 8 : if (tevent_loop_once(torture->ev) != 0) {
135 0 : break;
136 : }
137 : }
138 :
139 2 : status = torture_setup_simple_file(torture, tree, FNAME);
140 2 : CHECK_STATUS(status, NT_STATUS_OK);
141 :
142 2 : status = smb2_notify_recv(req, torture, &n);
143 2 : CHECK_STATUS(status, NT_STATUS_NOTIFY_ENUM_DIR);
144 :
145 : /*
146 : * if the change response fits in the buffer we get
147 : * NT_STATUS_OK again
148 : */
149 2 : n.in.buffer_size = max_buffer_size;
150 2 : req = smb2_notify_send(tree, &n);
151 :
152 2 : while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) {
153 8 : if (tevent_loop_once(torture->ev) != 0) {
154 0 : break;
155 : }
156 : }
157 :
158 2 : status = torture_setup_simple_file(torture, tree, FNAME);
159 2 : CHECK_STATUS(status, NT_STATUS_OK);
160 :
161 2 : status = smb2_notify_recv(req, torture, &n);
162 2 : CHECK_STATUS(status, NT_STATUS_OK);
163 2 : CHECK_VAL(n.out.num_changes, 3);
164 0 : CHECK_VAL(n.out.changes[0].action, NOTIFY_ACTION_REMOVED);
165 0 : CHECK_WIRE_STR(n.out.changes[0].name, FNAME);
166 0 : CHECK_VAL(n.out.changes[1].action, NOTIFY_ACTION_ADDED);
167 0 : CHECK_WIRE_STR(n.out.changes[1].name, FNAME);
168 0 : CHECK_VAL(n.out.changes[2].action, NOTIFY_ACTION_MODIFIED);
169 0 : CHECK_WIRE_STR(n.out.changes[2].name, FNAME);
170 :
171 : /* if the first notify returns NOTIFY_ENUM_DIR, all do */
172 0 : status = smb2_util_close(tree, dh);
173 0 : CHECK_STATUS(status, NT_STATUS_OK);
174 0 : status = smb2_util_roothandle(tree, &dh);
175 0 : CHECK_STATUS(status, NT_STATUS_OK);
176 :
177 0 : n.in.recursive = 0x0000;
178 0 : n.in.buffer_size = 0x00000001;
179 0 : n.in.file.handle = dh;
180 0 : n.in.completion_filter = FILE_NOTIFY_CHANGE_ALL;
181 0 : n.in.unknown = 0x00000000;
182 0 : req = smb2_notify_send(tree, &n);
183 :
184 0 : while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) {
185 0 : if (tevent_loop_once(torture->ev) != 0) {
186 0 : break;
187 : }
188 : }
189 :
190 0 : status = torture_setup_simple_file(torture, tree, FNAME);
191 0 : CHECK_STATUS(status, NT_STATUS_OK);
192 :
193 0 : status = smb2_notify_recv(req, torture, &n);
194 0 : CHECK_STATUS(status, NT_STATUS_NOTIFY_ENUM_DIR);
195 :
196 0 : n.in.buffer_size = max_buffer_size;
197 0 : req = smb2_notify_send(tree, &n);
198 0 : while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) {
199 0 : if (tevent_loop_once(torture->ev) != 0) {
200 0 : break;
201 : }
202 : }
203 :
204 0 : status = torture_setup_simple_file(torture, tree, FNAME);
205 0 : CHECK_STATUS(status, NT_STATUS_OK);
206 :
207 0 : status = smb2_notify_recv(req, torture, &n);
208 0 : CHECK_STATUS(status, NT_STATUS_NOTIFY_ENUM_DIR);
209 :
210 : /* if the buffer size is too large, we get invalid parameter */
211 0 : n.in.recursive = 0x0000;
212 0 : n.in.buffer_size = max_buffer_size + 1;
213 0 : n.in.file.handle = dh;
214 0 : n.in.completion_filter = FILE_NOTIFY_CHANGE_ALL;
215 0 : n.in.unknown = 0x00000000;
216 0 : req = smb2_notify_send(tree, &n);
217 0 : status = smb2_notify_recv(req, torture, &n);
218 0 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
219 :
220 2 : done:
221 2 : return ret;
222 : }
223 :
224 : /*
225 : basic testing of change notify on directories
226 : */
227 :
228 : #define BASEDIR_DIR BASEDIR "_DIR"
229 :
230 2 : static bool torture_smb2_notify_dir(struct torture_context *torture,
231 : struct smb2_tree *tree1,
232 : struct smb2_tree *tree2)
233 : {
234 2 : bool ret = true;
235 : NTSTATUS status;
236 : union smb_notify notify;
237 : union smb_open io;
238 : union smb_close cl;
239 : int i, count;
240 2 : struct smb2_handle h1 = {{0}};
241 2 : struct smb2_handle h2 = {{0}};
242 : struct smb2_request *req, *req2;
243 2 : const char *fname = BASEDIR_DIR "\\subdir-name";
244 : extern int torture_numops;
245 :
246 2 : torture_comment(torture, "TESTING CHANGE NOTIFY ON DIRECTORIES\n");
247 :
248 2 : smb2_deltree(tree1, BASEDIR_DIR);
249 2 : smb2_util_rmdir(tree1, BASEDIR_DIR);
250 : /*
251 : get a handle on the directory
252 : */
253 2 : ZERO_STRUCT(io.smb2);
254 2 : io.generic.level = RAW_OPEN_SMB2;
255 2 : io.smb2.in.create_flags = 0;
256 2 : io.smb2.in.desired_access = SEC_FILE_ALL;
257 2 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
258 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
259 2 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
260 : NTCREATEX_SHARE_ACCESS_WRITE;
261 2 : io.smb2.in.alloc_size = 0;
262 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
263 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
264 2 : io.smb2.in.security_flags = 0;
265 2 : io.smb2.in.fname = BASEDIR_DIR;
266 :
267 2 : status = smb2_create(tree1, torture, &(io.smb2));
268 2 : CHECK_STATUS(status, NT_STATUS_OK);
269 2 : h1 = io.smb2.out.file.handle;
270 :
271 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
272 2 : io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ;
273 2 : status = smb2_create(tree1, torture, &(io.smb2));
274 2 : CHECK_STATUS(status, NT_STATUS_OK);
275 2 : h2 = io.smb2.out.file.handle;
276 :
277 : /* ask for a change notify,
278 : on file or directory name changes */
279 2 : ZERO_STRUCT(notify.smb2);
280 2 : notify.smb2.level = RAW_NOTIFY_SMB2;
281 2 : notify.smb2.in.buffer_size = 1000;
282 2 : notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
283 2 : notify.smb2.in.file.handle = h1;
284 2 : notify.smb2.in.recursive = true;
285 :
286 2 : torture_comment(torture, "Testing notify cancel\n");
287 :
288 2 : req = smb2_notify_send(tree1, &(notify.smb2));
289 2 : smb2_cancel(req);
290 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
291 2 : CHECK_STATUS(status, NT_STATUS_CANCELLED);
292 :
293 2 : torture_comment(torture, "Testing notify mkdir\n");
294 :
295 2 : req = smb2_notify_send(tree1, &(notify.smb2));
296 2 : smb2_util_mkdir(tree2, fname);
297 :
298 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
299 2 : CHECK_STATUS(status, NT_STATUS_OK);
300 :
301 2 : CHECK_VAL(notify.smb2.out.num_changes, 1);
302 2 : CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_ADDED);
303 2 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name");
304 :
305 2 : torture_comment(torture, "Testing notify rmdir\n");
306 :
307 2 : req = smb2_notify_send(tree1, &(notify.smb2));
308 2 : smb2_util_rmdir(tree2, fname);
309 :
310 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
311 2 : CHECK_STATUS(status, NT_STATUS_OK);
312 2 : CHECK_VAL(notify.smb2.out.num_changes, 1);
313 2 : CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_REMOVED);
314 2 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name");
315 :
316 2 : torture_comment(torture,
317 : "Testing notify mkdir - rmdir - mkdir - rmdir\n");
318 :
319 2 : smb2_util_mkdir(tree2, fname);
320 2 : smb2_util_rmdir(tree2, fname);
321 2 : smb2_util_mkdir(tree2, fname);
322 2 : smb2_util_rmdir(tree2, fname);
323 2 : smb_msleep(200);
324 2 : req = smb2_notify_send(tree1, &(notify.smb2));
325 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
326 2 : CHECK_STATUS(status, NT_STATUS_OK);
327 2 : CHECK_VAL(notify.smb2.out.num_changes, 4);
328 2 : CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_ADDED);
329 2 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name");
330 2 : CHECK_VAL(notify.smb2.out.changes[1].action, NOTIFY_ACTION_REMOVED);
331 2 : CHECK_WIRE_STR(notify.smb2.out.changes[1].name, "subdir-name");
332 2 : CHECK_VAL(notify.smb2.out.changes[2].action, NOTIFY_ACTION_ADDED);
333 2 : CHECK_WIRE_STR(notify.smb2.out.changes[2].name, "subdir-name");
334 2 : CHECK_VAL(notify.smb2.out.changes[3].action, NOTIFY_ACTION_REMOVED);
335 2 : CHECK_WIRE_STR(notify.smb2.out.changes[3].name, "subdir-name");
336 :
337 2 : count = torture_numops;
338 2 : torture_comment(torture,
339 : "Testing buffered notify on create of %d files\n", count);
340 22 : for (i=0;i<count;i++) {
341 : struct smb2_handle h12;
342 20 : char *fname2 = talloc_asprintf(torture,
343 : BASEDIR_DIR "\\test%d.txt",
344 : i);
345 :
346 20 : ZERO_STRUCT(io.smb2);
347 20 : io.generic.level = RAW_OPEN_SMB2;
348 20 : io.smb2.in.create_flags = 0;
349 20 : io.smb2.in.desired_access = SEC_FILE_ALL;
350 20 : io.smb2.in.create_options =
351 : NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
352 20 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
353 20 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
354 : NTCREATEX_SHARE_ACCESS_WRITE;
355 20 : io.smb2.in.alloc_size = 0;
356 20 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
357 20 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
358 20 : io.smb2.in.security_flags = 0;
359 20 : io.smb2.in.fname = fname2;
360 :
361 20 : status = smb2_create(tree1, torture, &(io.smb2));
362 20 : if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
363 0 : torture_comment(torture, "Failed to create %s \n",
364 : fname);
365 0 : ret = false;
366 0 : goto done;
367 : }
368 20 : h12 = io.smb2.out.file.handle;
369 20 : talloc_free(fname2);
370 20 : smb2_util_close(tree1, h12);
371 : }
372 :
373 : /* (1st notify) setup a new notify on a different directory handle.
374 : This new notify won't see the events above. */
375 2 : notify.smb2.in.file.handle = h2;
376 2 : req2 = smb2_notify_send(tree1, &(notify.smb2));
377 :
378 : /* (2nd notify) whereas this notify will see the above buffered events,
379 : and it directly returns the buffered events */
380 2 : notify.smb2.in.file.handle = h1;
381 2 : req = smb2_notify_send(tree1, &(notify.smb2));
382 :
383 2 : status = smb2_util_unlink(tree1, BASEDIR_DIR "\\nonexistent.txt");
384 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
385 :
386 : /* (1st unlink) as the 2nd notify directly returns,
387 : this unlink is only seen by the 1st notify and
388 : the 3rd notify (later) */
389 2 : torture_comment(torture,
390 : "Testing notify on unlink for the first file\n");
391 2 : status = smb2_util_unlink(tree2, BASEDIR_DIR "\\test0.txt");
392 2 : CHECK_STATUS(status, NT_STATUS_OK);
393 :
394 : /* receive the reply from the 2nd notify */
395 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
396 2 : CHECK_STATUS(status, NT_STATUS_OK);
397 :
398 2 : CHECK_VAL(notify.smb2.out.num_changes, count);
399 20 : for (i=1;i<count;i++) {
400 18 : CHECK_VAL(notify.smb2.out.changes[i].action,
401 : NOTIFY_ACTION_ADDED);
402 : }
403 2 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "test0.txt");
404 :
405 2 : torture_comment(torture, "and now from the 1st notify\n");
406 2 : status = smb2_notify_recv(req2, torture, &(notify.smb2));
407 2 : CHECK_STATUS(status, NT_STATUS_OK);
408 2 : CHECK_VAL(notify.smb2.out.num_changes, 1);
409 2 : CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_REMOVED);
410 2 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "test0.txt");
411 :
412 2 : torture_comment(torture,
413 : "(3rd notify) this notify will only see the 1st unlink\n");
414 2 : req = smb2_notify_send(tree1, &(notify.smb2));
415 :
416 2 : status = smb2_util_unlink(tree1, BASEDIR_DIR "\\nonexistent.txt");
417 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
418 :
419 20 : for (i=1;i<count;i++) {
420 18 : char *fname2 = talloc_asprintf(torture,
421 : BASEDIR_DIR "\\test%d.txt", i);
422 18 : status = smb2_util_unlink(tree2, fname2);
423 18 : CHECK_STATUS(status, NT_STATUS_OK);
424 18 : talloc_free(fname2);
425 : }
426 :
427 : /* receive the 3rd notify */
428 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
429 2 : CHECK_STATUS(status, NT_STATUS_OK);
430 2 : CHECK_VAL(notify.smb2.out.num_changes, 1);
431 2 : CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_REMOVED);
432 2 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "test0.txt");
433 :
434 : /* and we now see the rest of the unlink calls on both
435 : * directory handles */
436 2 : notify.smb2.in.file.handle = h1;
437 2 : sleep(3);
438 2 : req = smb2_notify_send(tree1, &(notify.smb2));
439 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
440 2 : CHECK_STATUS(status, NT_STATUS_OK);
441 2 : CHECK_VAL(notify.smb2.out.num_changes, count-1);
442 20 : for (i=0;i<notify.smb2.out.num_changes;i++) {
443 18 : CHECK_VAL(notify.smb2.out.changes[i].action,
444 : NOTIFY_ACTION_REMOVED);
445 : }
446 2 : notify.smb2.in.file.handle = h2;
447 2 : req = smb2_notify_send(tree1, &(notify.smb2));
448 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
449 2 : CHECK_STATUS(status, NT_STATUS_OK);
450 2 : CHECK_VAL(notify.smb2.out.num_changes, count-1);
451 20 : for (i=0;i<notify.smb2.out.num_changes;i++) {
452 18 : CHECK_VAL(notify.smb2.out.changes[i].action,
453 : NOTIFY_ACTION_REMOVED);
454 : }
455 :
456 2 : torture_comment(torture,
457 : "Testing if a close() on the dir handle triggers the notify reply\n");
458 :
459 2 : notify.smb2.in.file.handle = h1;
460 2 : req = smb2_notify_send(tree1, &(notify.smb2));
461 :
462 2 : ZERO_STRUCT(cl.smb2);
463 2 : cl.smb2.level = RAW_CLOSE_SMB2;
464 2 : cl.smb2.in.file.handle = h1;
465 2 : status = smb2_close(tree1, &(cl.smb2));
466 2 : CHECK_STATUS(status, NT_STATUS_OK);
467 :
468 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
469 2 : CHECK_STATUS(status, NT_STATUS_NOTIFY_CLEANUP);
470 2 : CHECK_VAL(notify.smb2.out.num_changes, 9);
471 :
472 4 : done:
473 2 : smb2_util_close(tree1, h1);
474 2 : smb2_util_close(tree1, h2);
475 2 : smb2_deltree(tree1, BASEDIR_DIR);
476 2 : return ret;
477 : }
478 :
479 522 : static struct smb2_handle custom_smb2_create(struct smb2_tree *tree,
480 : struct torture_context *torture,
481 : struct smb2_create *smb2)
482 : {
483 : struct smb2_handle h1;
484 522 : bool ret = true;
485 : NTSTATUS status;
486 522 : smb2_deltree(tree, smb2->in.fname);
487 522 : status = smb2_create(tree, torture, smb2);
488 522 : CHECK_STATUS(status, NT_STATUS_OK);
489 522 : h1 = smb2->out.file.handle;
490 522 : done:
491 522 : if (!ret) {
492 0 : h1 = (struct smb2_handle) {
493 : .data = { 0 , 0},
494 : };
495 : }
496 522 : return h1;
497 : }
498 :
499 : /*
500 : testing of recursive change notify
501 : */
502 :
503 : #define BASEDIR_REC BASEDIR "_REC"
504 :
505 2 : static bool torture_smb2_notify_recursive(struct torture_context *torture,
506 : struct smb2_tree *tree1,
507 : struct smb2_tree *tree2)
508 : {
509 2 : bool ret = true;
510 : NTSTATUS status;
511 : union smb_notify notify;
512 : union smb_open io, io1;
513 : union smb_setfileinfo sinfo;
514 : struct smb2_handle h1;
515 : struct smb2_request *req1, *req2;
516 :
517 2 : smb2_deltree(tree1, BASEDIR_REC);
518 2 : smb2_util_rmdir(tree1, BASEDIR_REC);
519 :
520 2 : torture_comment(torture, "TESTING CHANGE NOTIFY WITH RECURSION\n");
521 :
522 : /*
523 : get a handle on the directory
524 : */
525 2 : ZERO_STRUCT(io.smb2);
526 2 : io.generic.level = RAW_OPEN_SMB2;
527 2 : io.smb2.in.create_flags = 0;
528 2 : io.smb2.in.desired_access = SEC_FILE_ALL;
529 2 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
530 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
531 2 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
532 : NTCREATEX_SHARE_ACCESS_WRITE;
533 2 : io.smb2.in.alloc_size = 0;
534 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
535 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
536 2 : io.smb2.in.security_flags = 0;
537 2 : io.smb2.in.fname = BASEDIR_REC;
538 :
539 2 : status = smb2_create(tree1, torture, &(io.smb2));
540 2 : CHECK_STATUS(status, NT_STATUS_OK);
541 2 : h1 = io.smb2.out.file.handle;
542 :
543 : /* ask for a change notify, on file or directory name
544 : changes. Setup both with and without recursion */
545 2 : ZERO_STRUCT(notify.smb2);
546 2 : notify.smb2.level = RAW_NOTIFY_SMB2;
547 2 : notify.smb2.in.buffer_size = 1000;
548 2 : notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME |
549 : FILE_NOTIFY_CHANGE_ATTRIBUTES |
550 : FILE_NOTIFY_CHANGE_CREATION;
551 2 : notify.smb2.in.file.handle = h1;
552 :
553 2 : notify.smb2.in.recursive = true;
554 2 : req1 = smb2_notify_send(tree1, &(notify.smb2));
555 2 : smb2_cancel(req1);
556 2 : status = smb2_notify_recv(req1, torture, &(notify.smb2));
557 2 : CHECK_STATUS(status, NT_STATUS_CANCELLED);
558 :
559 2 : notify.smb2.in.recursive = false;
560 2 : req2 = smb2_notify_send(tree1, &(notify.smb2));
561 2 : smb2_cancel(req2);
562 2 : status = smb2_notify_recv(req2, torture, &(notify.smb2));
563 2 : CHECK_STATUS(status, NT_STATUS_CANCELLED);
564 :
565 2 : ZERO_STRUCT(io1.smb2);
566 2 : io1.generic.level = RAW_OPEN_SMB2;
567 2 : io1.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED;
568 2 : io1.smb2.in.desired_access = SEC_RIGHTS_FILE_READ |
569 : SEC_RIGHTS_FILE_WRITE|
570 : SEC_RIGHTS_FILE_ALL;
571 2 : io1.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
572 2 : io1.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
573 2 : io1.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
574 : NTCREATEX_SHARE_ACCESS_WRITE |
575 : NTCREATEX_SHARE_ACCESS_DELETE;
576 2 : io1.smb2.in.alloc_size = 0;
577 2 : io1.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
578 2 : io1.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
579 2 : io1.smb2.in.security_flags = 0;
580 2 : io1.smb2.in.fname = BASEDIR_REC "\\subdir-name";
581 2 : status = smb2_create(tree2, torture, &(io1.smb2));
582 2 : CHECK_STATUS(status, NT_STATUS_OK);
583 2 : smb2_util_close(tree2, io1.smb2.out.file.handle);
584 :
585 2 : io1.smb2.in.fname = BASEDIR_REC "\\subdir-name\\subname1";
586 2 : status = smb2_create(tree2, torture, &(io1.smb2));
587 2 : CHECK_STATUS(status, NT_STATUS_OK);
588 2 : ZERO_STRUCT(sinfo);
589 2 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
590 2 : sinfo.rename_information.in.file.handle = io1.smb2.out.file.handle;
591 2 : sinfo.rename_information.in.overwrite = 0;
592 2 : sinfo.rename_information.in.root_fid = 0;
593 2 : sinfo.rename_information.in.new_name =
594 : BASEDIR_REC "\\subdir-name\\subname1-r";
595 2 : status = smb2_setinfo_file(tree2, &sinfo);
596 2 : CHECK_STATUS(status, NT_STATUS_OK);
597 :
598 2 : io1.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
599 2 : io1.smb2.in.fname = BASEDIR_REC "\\subdir-name\\subname2";
600 2 : status = smb2_create(tree2, torture, &(io1.smb2));
601 2 : CHECK_STATUS(status, NT_STATUS_OK);
602 2 : ZERO_STRUCT(sinfo);
603 2 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
604 2 : sinfo.rename_information.in.file.handle = io1.smb2.out.file.handle;
605 2 : sinfo.rename_information.in.overwrite = true;
606 2 : sinfo.rename_information.in.root_fid = 0;
607 2 : sinfo.rename_information.in.new_name = BASEDIR_REC "\\subname2-r";
608 2 : status = smb2_setinfo_file(tree2, &sinfo);
609 2 : CHECK_STATUS(status, NT_STATUS_OK);
610 :
611 2 : io1.smb2.in.fname = BASEDIR_REC "\\subname2-r";
612 2 : io1.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
613 2 : status = smb2_create(tree2, torture, &(io1.smb2));
614 2 : CHECK_STATUS(status, NT_STATUS_OK);
615 2 : ZERO_STRUCT(sinfo);
616 2 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
617 2 : sinfo.rename_information.in.file.handle = io1.smb2.out.file.handle;
618 2 : sinfo.rename_information.in.overwrite = true;
619 2 : sinfo.rename_information.in.root_fid = 0;
620 2 : sinfo.rename_information.in.new_name = BASEDIR_REC "\\subname3-r";
621 2 : status = smb2_setinfo_file(tree2, &sinfo);
622 2 : CHECK_STATUS(status, NT_STATUS_OK);
623 :
624 2 : notify.smb2.in.completion_filter = 0;
625 2 : notify.smb2.in.recursive = true;
626 2 : smb_msleep(200);
627 2 : req1 = smb2_notify_send(tree1, &(notify.smb2));
628 :
629 2 : status = smb2_util_rmdir(tree2,
630 : BASEDIR_REC "\\subdir-name\\subname1-r");
631 2 : CHECK_STATUS(status, NT_STATUS_OK);
632 2 : status = smb2_util_rmdir(tree2,
633 : BASEDIR_REC "\\subdir-name");
634 2 : CHECK_STATUS(status, NT_STATUS_OK);
635 2 : status = smb2_util_unlink(tree2, BASEDIR_REC "\\subname3-r");
636 2 : CHECK_STATUS(status, NT_STATUS_OK);
637 :
638 2 : notify.smb2.in.recursive = false;
639 2 : req2 = smb2_notify_send(tree1, &(notify.smb2));
640 :
641 2 : status = smb2_notify_recv(req1, torture, &(notify.smb2));
642 2 : CHECK_STATUS(status, NT_STATUS_OK);
643 :
644 2 : CHECK_VAL(notify.smb2.out.num_changes, 9);
645 0 : CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_ADDED);
646 0 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name");
647 0 : CHECK_VAL(notify.smb2.out.changes[1].action, NOTIFY_ACTION_ADDED);
648 0 : CHECK_WIRE_STR(notify.smb2.out.changes[1].name, "subdir-name\\subname1");
649 0 : CHECK_VAL(notify.smb2.out.changes[2].action, NOTIFY_ACTION_OLD_NAME);
650 0 : CHECK_WIRE_STR(notify.smb2.out.changes[2].name, "subdir-name\\subname1");
651 0 : CHECK_VAL(notify.smb2.out.changes[3].action, NOTIFY_ACTION_NEW_NAME);
652 0 : CHECK_WIRE_STR(notify.smb2.out.changes[3].name, "subdir-name\\subname1-r");
653 0 : CHECK_VAL(notify.smb2.out.changes[4].action, NOTIFY_ACTION_ADDED);
654 0 : CHECK_WIRE_STR(notify.smb2.out.changes[4].name, "subdir-name\\subname2");
655 0 : CHECK_VAL(notify.smb2.out.changes[5].action, NOTIFY_ACTION_REMOVED);
656 0 : CHECK_WIRE_STR(notify.smb2.out.changes[5].name, "subdir-name\\subname2");
657 0 : CHECK_VAL(notify.smb2.out.changes[6].action, NOTIFY_ACTION_ADDED);
658 0 : CHECK_WIRE_STR(notify.smb2.out.changes[6].name, "subname2-r");
659 0 : CHECK_VAL(notify.smb2.out.changes[7].action, NOTIFY_ACTION_OLD_NAME);
660 0 : CHECK_WIRE_STR(notify.smb2.out.changes[7].name, "subname2-r");
661 0 : CHECK_VAL(notify.smb2.out.changes[8].action, NOTIFY_ACTION_NEW_NAME);
662 0 : CHECK_WIRE_STR(notify.smb2.out.changes[8].name, "subname3-r");
663 :
664 2 : done:
665 2 : smb2_deltree(tree1, BASEDIR_REC);
666 2 : return ret;
667 : }
668 :
669 : /*
670 : testing of change notify mask change
671 : */
672 :
673 : #define BASEDIR_MC BASEDIR "_MC"
674 :
675 2 : static bool torture_smb2_notify_mask_change(struct torture_context *torture,
676 : struct smb2_tree *tree1,
677 : struct smb2_tree *tree2)
678 : {
679 2 : bool ret = true;
680 : NTSTATUS status;
681 : union smb_notify notify;
682 : union smb_open io, io1;
683 : struct smb2_handle h1;
684 : struct smb2_request *req1, *req2;
685 : union smb_setfileinfo sinfo;
686 :
687 2 : smb2_deltree(tree1, BASEDIR_MC);
688 2 : smb2_util_rmdir(tree1, BASEDIR_MC);
689 :
690 2 : torture_comment(torture, "TESTING CHANGE NOTIFY WITH MASK CHANGE\n");
691 :
692 : /*
693 : get a handle on the directory
694 : */
695 2 : ZERO_STRUCT(io.smb2);
696 2 : io.generic.level = RAW_OPEN_SMB2;
697 2 : io.smb2.in.create_flags = 0;
698 2 : io.smb2.in.desired_access = SEC_FILE_ALL;
699 2 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
700 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
701 2 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
702 : NTCREATEX_SHARE_ACCESS_WRITE;
703 2 : io.smb2.in.alloc_size = 0;
704 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
705 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
706 2 : io.smb2.in.security_flags = 0;
707 2 : io.smb2.in.fname = BASEDIR_MC;
708 :
709 2 : status = smb2_create(tree1, torture, &(io.smb2));
710 2 : CHECK_STATUS(status, NT_STATUS_OK);
711 2 : h1 = io.smb2.out.file.handle;
712 :
713 : /* ask for a change notify, on file or directory name
714 : changes. Setup both with and without recursion */
715 2 : ZERO_STRUCT(notify.smb2);
716 2 : notify.smb2.level = RAW_NOTIFY_SMB2;
717 2 : notify.smb2.in.buffer_size = 1000;
718 2 : notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_ATTRIBUTES;
719 2 : notify.smb2.in.file.handle = h1;
720 :
721 2 : notify.smb2.in.recursive = true;
722 2 : req1 = smb2_notify_send(tree1, &(notify.smb2));
723 :
724 2 : smb2_cancel(req1);
725 2 : status = smb2_notify_recv(req1, torture, &(notify.smb2));
726 2 : CHECK_STATUS(status, NT_STATUS_CANCELLED);
727 :
728 :
729 2 : notify.smb2.in.recursive = false;
730 2 : req2 = smb2_notify_send(tree1, &(notify.smb2));
731 :
732 2 : smb2_cancel(req2);
733 2 : status = smb2_notify_recv(req2, torture, &(notify.smb2));
734 2 : CHECK_STATUS(status, NT_STATUS_CANCELLED);
735 :
736 2 : notify.smb2.in.recursive = true;
737 2 : req1 = smb2_notify_send(tree1, &(notify.smb2));
738 :
739 : /* Set to hidden then back again. */
740 2 : ZERO_STRUCT(io1.smb2);
741 2 : io1.generic.level = RAW_OPEN_SMB2;
742 2 : io1.smb2.in.create_flags = 0;
743 2 : io1.smb2.in.desired_access = SEC_RIGHTS_FILE_READ |
744 : SEC_RIGHTS_FILE_WRITE|
745 : SEC_RIGHTS_FILE_ALL;
746 2 : io1.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
747 2 : io1.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
748 : NTCREATEX_SHARE_ACCESS_WRITE |
749 : NTCREATEX_SHARE_ACCESS_DELETE;
750 2 : io1.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
751 2 : io1.smb2.in.security_flags = 0;
752 2 : io1.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
753 2 : io1.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
754 2 : io1.smb2.in.fname = BASEDIR_MC "\\tname1";
755 :
756 2 : smb2_util_close(tree1,
757 : custom_smb2_create(tree1, torture, &(io1.smb2)));
758 2 : status = smb2_util_setatr(tree1, BASEDIR_MC "\\tname1",
759 : FILE_ATTRIBUTE_HIDDEN);
760 2 : CHECK_STATUS(status, NT_STATUS_OK);
761 2 : smb2_util_unlink(tree1, BASEDIR_MC "\\tname1");
762 :
763 2 : status = smb2_notify_recv(req1, torture, &(notify.smb2));
764 2 : CHECK_STATUS(status, NT_STATUS_OK);
765 :
766 2 : CHECK_VAL(notify.smb2.out.num_changes, 1);
767 2 : CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_MODIFIED);
768 2 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "tname1");
769 :
770 : /* Now try and change the mask to include other events.
771 : * This should not work - once the mask is set on a directory
772 : * h1 it seems to be fixed until the fnum is closed. */
773 :
774 2 : notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME |
775 : FILE_NOTIFY_CHANGE_ATTRIBUTES |
776 : FILE_NOTIFY_CHANGE_CREATION;
777 2 : notify.smb2.in.recursive = true;
778 2 : req1 = smb2_notify_send(tree1, &(notify.smb2));
779 :
780 2 : notify.smb2.in.recursive = false;
781 2 : req2 = smb2_notify_send(tree1, &(notify.smb2));
782 :
783 2 : io1.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
784 2 : io1.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
785 2 : io1.smb2.in.fname = BASEDIR_MC "\\subdir-name";
786 2 : status = smb2_create(tree2, torture, &(io1.smb2));
787 2 : CHECK_STATUS(status, NT_STATUS_OK);
788 2 : smb2_util_close(tree2, io1.smb2.out.file.handle);
789 :
790 2 : ZERO_STRUCT(sinfo);
791 2 : io1.smb2.in.fname = BASEDIR_MC "\\subdir-name\\subname1";
792 2 : io1.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
793 2 : io1.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
794 2 : status = smb2_create(tree2, torture, &(io1.smb2));
795 2 : CHECK_STATUS(status, NT_STATUS_OK);
796 2 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
797 2 : sinfo.rename_information.in.file.handle = io1.smb2.out.file.handle;
798 2 : sinfo.rename_information.in.overwrite = true;
799 2 : sinfo.rename_information.in.root_fid = 0;
800 2 : sinfo.rename_information.in.new_name =
801 : BASEDIR_MC "\\subdir-name\\subname1-r";
802 2 : status = smb2_setinfo_file(tree2, &sinfo);
803 2 : CHECK_STATUS(status, NT_STATUS_OK);
804 :
805 2 : io1.smb2.in.fname = BASEDIR_MC "\\subdir-name\\subname2";
806 2 : io1.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
807 2 : io1.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
808 2 : status = smb2_create(tree2, torture, &(io1.smb2));
809 2 : CHECK_STATUS(status, NT_STATUS_OK);
810 2 : sinfo.rename_information.in.file.handle = io1.smb2.out.file.handle;
811 2 : sinfo.rename_information.in.new_name = BASEDIR_MC "\\subname2-r";
812 2 : status = smb2_setinfo_file(tree2, &sinfo);
813 2 : CHECK_STATUS(status, NT_STATUS_OK);
814 2 : smb2_util_close(tree2, io1.smb2.out.file.handle);
815 :
816 2 : io1.smb2.in.fname = BASEDIR_MC "\\subname2-r";
817 2 : io1.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
818 2 : status = smb2_create(tree2, torture, &(io1.smb2));
819 2 : CHECK_STATUS(status, NT_STATUS_OK);
820 2 : sinfo.rename_information.in.file.handle = io1.smb2.out.file.handle;
821 2 : sinfo.rename_information.in.new_name = BASEDIR_MC "\\subname3-r";
822 2 : status = smb2_setinfo_file(tree2, &sinfo);
823 2 : CHECK_STATUS(status, NT_STATUS_OK);
824 2 : smb2_util_close(tree2, io1.smb2.out.file.handle);
825 :
826 2 : status = smb2_util_rmdir(tree2, BASEDIR_MC "\\subdir-name\\subname1-r");
827 2 : CHECK_STATUS(status, NT_STATUS_OK);
828 2 : status = smb2_util_rmdir(tree2, BASEDIR_MC "\\subdir-name");
829 2 : CHECK_STATUS(status, NT_STATUS_OK);
830 2 : status = smb2_util_unlink(tree2, BASEDIR_MC "\\subname3-r");
831 2 : CHECK_STATUS(status, NT_STATUS_OK);
832 :
833 2 : status = smb2_notify_recv(req1, torture, &(notify.smb2));
834 2 : CHECK_STATUS(status, NT_STATUS_OK);
835 :
836 2 : CHECK_VAL(notify.smb2.out.num_changes, 1);
837 2 : CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_MODIFIED);
838 2 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subname2-r");
839 :
840 2 : status = smb2_notify_recv(req2, torture, &(notify.smb2));
841 2 : CHECK_STATUS(status, NT_STATUS_OK);
842 :
843 2 : CHECK_VAL(notify.smb2.out.num_changes, 1);
844 2 : CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_MODIFIED);
845 2 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subname3-r");
846 :
847 2 : if (!ret) {
848 0 : goto done;
849 : }
850 :
851 4 : done:
852 2 : smb2_deltree(tree1, BASEDIR_MC);
853 2 : return ret;
854 : }
855 :
856 : /*
857 : testing of mask bits for change notify
858 : */
859 :
860 : #define BASEDIR_MSK BASEDIR "_MSK"
861 :
862 2 : static bool torture_smb2_notify_mask(struct torture_context *torture,
863 : struct smb2_tree *tree1,
864 : struct smb2_tree *tree2)
865 : {
866 2 : bool ret = true;
867 : NTSTATUS status;
868 : union smb_notify notify;
869 : union smb_open io, io1;
870 : struct smb2_handle h1, h2;
871 : uint32_t mask;
872 : int i;
873 2 : char c = 1;
874 : union smb_setfileinfo sinfo;
875 :
876 2 : smb2_deltree(tree1, BASEDIR_MSK);
877 2 : smb2_util_rmdir(tree1, BASEDIR_MSK);
878 :
879 2 : torture_comment(torture, "TESTING CHANGE NOTIFY COMPLETION FILTERS\n");
880 :
881 :
882 2 : ZERO_STRUCT(h1);
883 2 : ZERO_STRUCT(h2);
884 : /*
885 : get a handle on the directory
886 : */
887 2 : ZERO_STRUCT(io.smb2);
888 2 : io.generic.level = RAW_OPEN_SMB2;
889 2 : io.smb2.in.create_flags = 0;
890 2 : io.smb2.in.desired_access = SEC_FILE_ALL;
891 2 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
892 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
893 2 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
894 : NTCREATEX_SHARE_ACCESS_WRITE;
895 2 : io.smb2.in.alloc_size = 0;
896 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
897 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
898 2 : io.smb2.in.security_flags = 0;
899 2 : io.smb2.in.fname = BASEDIR_MSK;
900 :
901 2 : ZERO_STRUCT(notify.smb2);
902 2 : notify.smb2.level = RAW_NOTIFY_SMB2;
903 2 : notify.smb2.in.buffer_size = 1000;
904 2 : notify.smb2.in.recursive = true;
905 :
906 : #define NOTIFY_MASK_TEST(test_name, setup, op, cleanup, Action, \
907 : expected, nchanges) \
908 : do { \
909 : do { for (mask=i=0;i<32;i++) { \
910 : struct smb2_request *req; \
911 : status = smb2_create(tree1, torture, &(io.smb2)); \
912 : CHECK_STATUS(status, NT_STATUS_OK); \
913 : h1 = io.smb2.out.file.handle; \
914 : setup \
915 : notify.smb2.in.file.handle = h1; \
916 : notify.smb2.in.completion_filter = ((uint32_t)1<<i); \
917 : /* cancel initial requests so the buffer is setup */ \
918 : req = smb2_notify_send(tree1, &(notify.smb2)); \
919 : smb2_cancel(req); \
920 : status = smb2_notify_recv(req, torture, &(notify.smb2)); \
921 : CHECK_STATUS(status, NT_STATUS_CANCELLED); \
922 : /* send the change notify request */ \
923 : req = smb2_notify_send(tree1, &(notify.smb2)); \
924 : op \
925 : smb_msleep(200); smb2_cancel(req); \
926 : status = smb2_notify_recv(req, torture, &(notify.smb2)); \
927 : cleanup \
928 : smb2_util_close(tree1, h1); \
929 : if (NT_STATUS_EQUAL(status, NT_STATUS_CANCELLED)) continue; \
930 : CHECK_STATUS(status, NT_STATUS_OK); \
931 : /* special case to cope with file rename behaviour */ \
932 : if (nchanges == 2 && notify.smb2.out.num_changes == 1 && \
933 : notify.smb2.out.changes[0].action == \
934 : NOTIFY_ACTION_MODIFIED && \
935 : ((expected) & FILE_NOTIFY_CHANGE_ATTRIBUTES) && \
936 : Action == NOTIFY_ACTION_OLD_NAME) { \
937 : torture_comment(torture, \
938 : "(rename file special handling OK)\n"); \
939 : } else if (nchanges != notify.smb2.out.num_changes) { \
940 : torture_result(torture, TORTURE_FAIL, \
941 : "ERROR: nchanges=%d expected=%d "\
942 : "action=%d filter=0x%08x\n", \
943 : notify.smb2.out.num_changes, \
944 : nchanges, \
945 : notify.smb2.out.changes[0].action, \
946 : notify.smb2.in.completion_filter); \
947 : ret = false; \
948 : } else if (notify.smb2.out.changes[0].action != Action) { \
949 : torture_result(torture, TORTURE_FAIL, \
950 : "ERROR: nchanges=%d action=%d " \
951 : "expectedAction=%d filter=0x%08x\n", \
952 : notify.smb2.out.num_changes, \
953 : notify.smb2.out.changes[0].action, \
954 : Action, \
955 : notify.smb2.in.completion_filter); \
956 : ret = false; \
957 : } else if (strcmp(notify.smb2.out.changes[0].name.s, \
958 : "tname1") != 0) { \
959 : torture_result(torture, TORTURE_FAIL, \
960 : "ERROR: nchanges=%d action=%d " \
961 : "filter=0x%08x name=%s\n", \
962 : notify.smb2.out.num_changes, \
963 : notify.smb2.out.changes[0].action, \
964 : notify.smb2.in.completion_filter, \
965 : notify.smb2.out.changes[0].name.s); \
966 : ret = false; \
967 : } \
968 : mask |= ((uint32_t)1<<i); \
969 : } \
970 : } while (0); \
971 : } while (0);
972 :
973 2 : torture_comment(torture, "Testing mkdir\n");
974 2 : NOTIFY_MASK_TEST("Testing mkdir",;,
975 : smb2_util_mkdir(tree2, BASEDIR_MSK "\\tname1");,
976 : smb2_util_rmdir(tree2, BASEDIR_MSK "\\tname1");,
977 : NOTIFY_ACTION_ADDED,
978 : FILE_NOTIFY_CHANGE_DIR_NAME, 1);
979 :
980 2 : torture_comment(torture, "Testing create file\n");
981 2 : ZERO_STRUCT(io1.smb2);
982 2 : io1.generic.level = RAW_OPEN_SMB2;
983 2 : io1.smb2.in.create_flags = 0;
984 2 : io1.smb2.in.desired_access = SEC_FILE_ALL;
985 2 : io1.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
986 2 : io1.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
987 : NTCREATEX_SHARE_ACCESS_WRITE;
988 2 : io1.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
989 2 : io1.smb2.in.security_flags = 0;
990 2 : io1.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
991 2 : io1.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
992 2 : io1.smb2.in.fname = BASEDIR_MSK "\\tname1";
993 :
994 2 : NOTIFY_MASK_TEST("Testing create file",;,
995 : smb2_util_close(tree2, custom_smb2_create(tree2,
996 : torture, &(io1.smb2)));,
997 : smb2_util_unlink(tree2, BASEDIR_MSK "\\tname1");,
998 : NOTIFY_ACTION_ADDED,
999 : FILE_NOTIFY_CHANGE_FILE_NAME, 1);
1000 :
1001 2 : torture_comment(torture, "Testing unlink\n");
1002 2 : NOTIFY_MASK_TEST("Testing unlink",
1003 : smb2_util_close(tree2, custom_smb2_create(tree2,
1004 : torture, &(io1.smb2)));,
1005 : smb2_util_unlink(tree2, BASEDIR_MSK "\\tname1");,
1006 : ;,
1007 : NOTIFY_ACTION_REMOVED,
1008 : FILE_NOTIFY_CHANGE_FILE_NAME, 1);
1009 :
1010 2 : torture_comment(torture, "Testing rmdir\n");
1011 2 : NOTIFY_MASK_TEST("Testing rmdir",
1012 : smb2_util_mkdir(tree2, BASEDIR_MSK "\\tname1");,
1013 : smb2_util_rmdir(tree2, BASEDIR_MSK "\\tname1");,
1014 : ;,
1015 : NOTIFY_ACTION_REMOVED,
1016 : FILE_NOTIFY_CHANGE_DIR_NAME, 1);
1017 :
1018 2 : torture_comment(torture, "Testing rename file\n");
1019 2 : ZERO_STRUCT(sinfo);
1020 2 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1021 2 : sinfo.rename_information.in.file.handle = h1;
1022 2 : sinfo.rename_information.in.overwrite = true;
1023 2 : sinfo.rename_information.in.root_fid = 0;
1024 2 : sinfo.rename_information.in.new_name = BASEDIR_MSK "\\tname2";
1025 2 : NOTIFY_MASK_TEST("Testing rename file",
1026 : smb2_util_close(tree2, custom_smb2_create(tree2,
1027 : torture, &(io1.smb2)));,
1028 : smb2_setinfo_file(tree2, &sinfo);,
1029 : smb2_util_unlink(tree2, BASEDIR_MSK "\\tname2");,
1030 : NOTIFY_ACTION_OLD_NAME,
1031 : FILE_NOTIFY_CHANGE_FILE_NAME, 2);
1032 :
1033 2 : torture_comment(torture, "Testing rename dir\n");
1034 2 : ZERO_STRUCT(sinfo);
1035 2 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1036 2 : sinfo.rename_information.in.file.handle = h1;
1037 2 : sinfo.rename_information.in.overwrite = true;
1038 2 : sinfo.rename_information.in.root_fid = 0;
1039 2 : sinfo.rename_information.in.new_name = BASEDIR_MSK "\\tname2";
1040 2 : NOTIFY_MASK_TEST("Testing rename dir",
1041 : smb2_util_mkdir(tree2, BASEDIR_MSK "\\tname1");,
1042 : smb2_setinfo_file(tree2, &sinfo);,
1043 : smb2_util_rmdir(tree2, BASEDIR_MSK "\\tname2");,
1044 : NOTIFY_ACTION_OLD_NAME,
1045 : FILE_NOTIFY_CHANGE_DIR_NAME, 2);
1046 :
1047 2 : torture_comment(torture, "Testing set path attribute\n");
1048 2 : NOTIFY_MASK_TEST("Testing set path attribute",
1049 : smb2_util_close(tree2, custom_smb2_create(tree2,
1050 : torture, &(io.smb2)));,
1051 : smb2_util_setatr(tree2, BASEDIR_MSK "\\tname1",
1052 : FILE_ATTRIBUTE_HIDDEN);,
1053 : smb2_util_unlink(tree2, BASEDIR_MSK "\\tname1");,
1054 : NOTIFY_ACTION_MODIFIED,
1055 : FILE_NOTIFY_CHANGE_ATTRIBUTES, 1);
1056 :
1057 2 : torture_comment(torture, "Testing set path write time\n");
1058 2 : ZERO_STRUCT(sinfo);
1059 2 : sinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1060 2 : sinfo.generic.in.file.handle = h1;
1061 2 : sinfo.basic_info.in.write_time = 1000;
1062 2 : NOTIFY_MASK_TEST("Testing set path write time",
1063 : smb2_util_close(tree2, custom_smb2_create(tree2,
1064 : torture, &(io1.smb2)));,
1065 : smb2_setinfo_file(tree2, &sinfo);,
1066 : smb2_util_unlink(tree2, BASEDIR_MSK "\\tname1");,
1067 : NOTIFY_ACTION_MODIFIED,
1068 : FILE_NOTIFY_CHANGE_LAST_WRITE, 1);
1069 :
1070 2 : if (torture_setting_bool(torture, "samba3", false)) {
1071 2 : torture_comment(torture,
1072 : "Samba3 does not yet support create times "
1073 : "everywhere\n");
1074 : }
1075 : else {
1076 0 : ZERO_STRUCT(sinfo);
1077 0 : sinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1078 0 : sinfo.generic.in.file.handle = h1;
1079 0 : sinfo.basic_info.in.create_time = 0;
1080 0 : torture_comment(torture, "Testing set file create time\n");
1081 0 : NOTIFY_MASK_TEST("Testing set file create time",
1082 : smb2_create_complex_file(torture, tree2,
1083 : BASEDIR_MSK "\\tname1", &h2);,
1084 : smb2_setinfo_file(tree2, &sinfo);,
1085 : (smb2_util_close(tree2, h2),
1086 : smb2_util_unlink(tree2, BASEDIR_MSK "\\tname1"));,
1087 : NOTIFY_ACTION_MODIFIED,
1088 : FILE_NOTIFY_CHANGE_CREATION, 1);
1089 : }
1090 :
1091 2 : ZERO_STRUCT(sinfo);
1092 2 : sinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1093 2 : sinfo.generic.in.file.handle = h1;
1094 2 : sinfo.basic_info.in.access_time = 0;
1095 2 : torture_comment(torture, "Testing set file access time\n");
1096 2 : NOTIFY_MASK_TEST("Testing set file access time",
1097 : smb2_create_complex_file(torture,
1098 : tree2,
1099 : BASEDIR_MSK "\\tname1",
1100 : &h2);,
1101 : smb2_setinfo_file(tree2, &sinfo);,
1102 : (smb2_util_close(tree2, h2),
1103 : smb2_util_unlink(tree2, BASEDIR_MSK "\\tname1"));,
1104 : NOTIFY_ACTION_MODIFIED,
1105 : FILE_NOTIFY_CHANGE_LAST_ACCESS, 1);
1106 :
1107 2 : ZERO_STRUCT(sinfo);
1108 2 : sinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1109 2 : sinfo.generic.in.file.handle = h1;
1110 2 : sinfo.basic_info.in.change_time = 0;
1111 2 : torture_comment(torture, "Testing set file change time\n");
1112 2 : NOTIFY_MASK_TEST("Testing set file change time",
1113 : smb2_create_complex_file(torture,
1114 : tree2,
1115 : BASEDIR_MSK "\\tname1",
1116 : &h2);,
1117 : smb2_setinfo_file(tree2, &sinfo);,
1118 : (smb2_util_close(tree2, h2),
1119 : smb2_util_unlink(tree2, BASEDIR_MSK "\\tname1"));,
1120 : NOTIFY_ACTION_MODIFIED,
1121 : 0, 1);
1122 :
1123 :
1124 2 : torture_comment(torture, "Testing write\n");
1125 2 : NOTIFY_MASK_TEST("Testing write",
1126 : smb2_create_complex_file(torture,
1127 : tree2,
1128 : BASEDIR_MSK "\\tname1",
1129 : &h2);,
1130 : smb2_util_write(tree2, h2, &c, 10000, 1);,
1131 : (smb2_util_close(tree2, h2),
1132 : smb2_util_unlink(tree2, BASEDIR_MSK "\\tname1"));,
1133 : NOTIFY_ACTION_MODIFIED,
1134 : 0, 1);
1135 :
1136 2 : done:
1137 2 : smb2_deltree(tree1, BASEDIR_MSK);
1138 2 : return ret;
1139 : }
1140 :
1141 : #define BASEDIR_FL BASEDIR "_FL"
1142 : /*
1143 : basic testing of change notify on files
1144 : */
1145 2 : static bool torture_smb2_notify_file(struct torture_context *torture,
1146 : struct smb2_tree *tree)
1147 : {
1148 : NTSTATUS status;
1149 2 : bool ret = true;
1150 : union smb_open io;
1151 : union smb_close cl;
1152 : union smb_notify notify;
1153 : struct smb2_request *req;
1154 : struct smb2_handle h1;
1155 2 : const char *fname = BASEDIR_FL "\\file.txt";
1156 :
1157 2 : smb2_deltree(tree, BASEDIR_FL);
1158 2 : smb2_util_rmdir(tree, BASEDIR_FL);
1159 :
1160 2 : torture_comment(torture, "TESTING CHANGE NOTIFY ON FILES\n");
1161 2 : status = torture_smb2_testdir(tree, BASEDIR_FL, &h1);
1162 2 : CHECK_STATUS(status, NT_STATUS_OK);
1163 :
1164 2 : ZERO_STRUCT(io.smb2);
1165 2 : io.generic.level = RAW_OPEN_SMB2;
1166 2 : io.smb2.in.create_flags = 0;
1167 2 : io.smb2.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
1168 2 : io.smb2.in.create_options = 0;
1169 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1170 2 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1171 : NTCREATEX_SHARE_ACCESS_WRITE;
1172 2 : io.smb2.in.alloc_size = 0;
1173 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1174 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1175 2 : io.smb2.in.security_flags = 0;
1176 2 : io.smb2.in.fname = fname;
1177 2 : status = smb2_create(tree, torture, &(io.smb2));
1178 2 : CHECK_STATUS(status, NT_STATUS_OK);
1179 2 : h1 = io.smb2.out.file.handle;
1180 :
1181 : /* ask for a change notify,
1182 : on file or directory name changes */
1183 2 : ZERO_STRUCT(notify.smb2);
1184 2 : notify.smb2.level = RAW_NOTIFY_SMB2;
1185 2 : notify.smb2.in.file.handle = h1;
1186 2 : notify.smb2.in.buffer_size = 1000;
1187 2 : notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_STREAM_NAME;
1188 2 : notify.smb2.in.recursive = false;
1189 :
1190 2 : torture_comment(torture,
1191 : "Testing if notifies on file handles are invalid (should be)\n");
1192 :
1193 2 : req = smb2_notify_send(tree, &(notify.smb2));
1194 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
1195 2 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1196 :
1197 2 : ZERO_STRUCT(cl.smb2);
1198 2 : cl.close.level = RAW_CLOSE_SMB2;
1199 2 : cl.close.in.file.handle = h1;
1200 2 : status = smb2_close(tree, &(cl.smb2));
1201 2 : CHECK_STATUS(status, NT_STATUS_OK);
1202 :
1203 2 : status = smb2_util_unlink(tree, fname);
1204 2 : CHECK_STATUS(status, NT_STATUS_OK);
1205 :
1206 4 : done:
1207 2 : smb2_deltree(tree, BASEDIR_FL);
1208 2 : return ret;
1209 : }
1210 : /*
1211 : basic testing of change notifies followed by a tdis
1212 : */
1213 :
1214 : #define BASEDIR_TD BASEDIR "_TD"
1215 :
1216 2 : static bool torture_smb2_notify_tree_disconnect(
1217 : struct torture_context *torture,
1218 : struct smb2_tree *tree)
1219 : {
1220 2 : bool ret = true;
1221 : NTSTATUS status;
1222 : union smb_notify notify;
1223 : union smb_open io;
1224 : struct smb2_handle h1;
1225 : struct smb2_request *req;
1226 :
1227 2 : smb2_deltree(tree, BASEDIR_TD);
1228 2 : smb2_util_rmdir(tree, BASEDIR_TD);
1229 :
1230 2 : torture_comment(torture, "TESTING CHANGE NOTIFY+CANCEL FOLLOWED BY "
1231 : "TREE-DISCONNECT\n");
1232 :
1233 : /*
1234 : get a handle on the directory
1235 : */
1236 2 : ZERO_STRUCT(io.smb2);
1237 2 : io.generic.level = RAW_OPEN_SMB2;
1238 2 : io.smb2.in.create_flags = 0;
1239 2 : io.smb2.in.desired_access = SEC_FILE_ALL;
1240 2 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1241 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1242 2 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1243 : NTCREATEX_SHARE_ACCESS_WRITE;
1244 2 : io.smb2.in.alloc_size = 0;
1245 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1246 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1247 2 : io.smb2.in.security_flags = 0;
1248 2 : io.smb2.in.fname = BASEDIR_TD;
1249 :
1250 2 : status = smb2_create(tree, torture, &(io.smb2));
1251 2 : CHECK_STATUS(status, NT_STATUS_OK);
1252 2 : h1 = io.smb2.out.file.handle;
1253 :
1254 : /* ask for a change notify,
1255 : on file or directory name changes */
1256 2 : ZERO_STRUCT(notify.smb2);
1257 2 : notify.smb2.level = RAW_NOTIFY_SMB2;
1258 2 : notify.smb2.in.buffer_size = 1000;
1259 2 : notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1260 2 : notify.smb2.in.file.handle = h1;
1261 2 : notify.smb2.in.recursive = true;
1262 :
1263 2 : req = smb2_notify_send(tree, &(notify.smb2));
1264 2 : smb2_cancel(req);
1265 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
1266 :
1267 2 : status = smb2_tdis(tree);
1268 2 : CHECK_STATUS(status, NT_STATUS_OK);
1269 :
1270 2 : req = smb2_notify_send(tree, &(notify.smb2));
1271 :
1272 2 : smb2_notify_recv(req, torture, &(notify.smb2));
1273 2 : CHECK_STATUS(status, NT_STATUS_OK);
1274 2 : CHECK_VAL(notify.smb2.out.num_changes, 0);
1275 :
1276 4 : done:
1277 2 : smb2_deltree(tree, BASEDIR_TD);
1278 2 : return ret;
1279 : }
1280 :
1281 : /*
1282 : testing of change notifies followed by a tdis - no cancel
1283 : */
1284 :
1285 : #define BASEDIR_NTDIS BASEDIR "_NTDIS"
1286 :
1287 2 : static bool torture_smb2_notify_tree_disconnect_1(
1288 : struct torture_context *torture,
1289 : struct smb2_tree *tree)
1290 : {
1291 2 : bool ret = true;
1292 : NTSTATUS status;
1293 : union smb_notify notify;
1294 : union smb_open io;
1295 : struct smb2_handle h1;
1296 : struct smb2_request *req;
1297 :
1298 2 : smb2_deltree(tree, BASEDIR_NTDIS);
1299 2 : smb2_util_rmdir(tree, BASEDIR_NTDIS);
1300 :
1301 2 : torture_comment(torture, "TESTING CHANGE NOTIFY ASYNC FOLLOWED BY "
1302 : "TREE-DISCONNECT\n");
1303 :
1304 : /*
1305 : get a handle on the directory
1306 : */
1307 2 : ZERO_STRUCT(io.smb2);
1308 2 : io.generic.level = RAW_OPEN_SMB2;
1309 2 : io.smb2.in.create_flags = 0;
1310 2 : io.smb2.in.desired_access = SEC_FILE_ALL;
1311 2 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1312 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1313 2 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1314 : NTCREATEX_SHARE_ACCESS_WRITE;
1315 2 : io.smb2.in.alloc_size = 0;
1316 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1317 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1318 2 : io.smb2.in.security_flags = 0;
1319 2 : io.smb2.in.fname = BASEDIR_NTDIS;
1320 :
1321 2 : status = smb2_create(tree, torture, &(io.smb2));
1322 2 : CHECK_STATUS(status, NT_STATUS_OK);
1323 2 : h1 = io.smb2.out.file.handle;
1324 :
1325 : /* ask for a change notify,
1326 : on file or directory name changes */
1327 2 : ZERO_STRUCT(notify.smb2);
1328 2 : notify.smb2.level = RAW_NOTIFY_SMB2;
1329 2 : notify.smb2.in.buffer_size = 1000;
1330 2 : notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1331 2 : notify.smb2.in.file.handle = h1;
1332 2 : notify.smb2.in.recursive = true;
1333 :
1334 2 : req = smb2_notify_send(tree, &(notify.smb2));
1335 8 : WAIT_FOR_ASYNC_RESPONSE(req);
1336 :
1337 2 : status = smb2_tdis(tree);
1338 2 : CHECK_STATUS(status, NT_STATUS_OK);
1339 :
1340 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
1341 2 : CHECK_STATUS(status, NT_STATUS_NOTIFY_CLEANUP);
1342 2 : CHECK_VAL(notify.smb2.out.num_changes, 0);
1343 :
1344 4 : done:
1345 2 : smb2_deltree(tree, BASEDIR_NTDIS);
1346 2 : return ret;
1347 : }
1348 :
1349 : /*
1350 : basic testing of change notifies followed by a close
1351 : */
1352 :
1353 : #define BASEDIR_CNC BASEDIR "_CNC"
1354 :
1355 2 : static bool torture_smb2_notify_close(struct torture_context *torture,
1356 : struct smb2_tree *tree1)
1357 : {
1358 2 : bool ret = true;
1359 : NTSTATUS status;
1360 : union smb_notify notify;
1361 : union smb_open io;
1362 : struct smb2_handle h1;
1363 : struct smb2_request *req;
1364 :
1365 2 : smb2_deltree(tree1, BASEDIR_CNC);
1366 2 : smb2_util_rmdir(tree1, BASEDIR_CNC);
1367 :
1368 2 : torture_comment(torture, "TESTING CHANGE NOTIFY FOLLOWED BY ULOGOFF\n");
1369 :
1370 : /*
1371 : get a handle on the directory
1372 : */
1373 2 : ZERO_STRUCT(io.smb2);
1374 2 : io.generic.level = RAW_OPEN_SMB2;
1375 2 : io.smb2.in.create_flags = 0;
1376 2 : io.smb2.in.desired_access = SEC_FILE_ALL;
1377 2 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1378 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1379 2 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1380 : NTCREATEX_SHARE_ACCESS_WRITE;
1381 2 : io.smb2.in.alloc_size = 0;
1382 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1383 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1384 2 : io.smb2.in.security_flags = 0;
1385 2 : io.smb2.in.fname = BASEDIR_CNC;
1386 :
1387 2 : status = smb2_create(tree1, torture, &(io.smb2));
1388 2 : CHECK_STATUS(status, NT_STATUS_OK);
1389 :
1390 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1391 2 : status = smb2_create(tree1, torture, &(io.smb2));
1392 2 : CHECK_STATUS(status, NT_STATUS_OK);
1393 2 : h1 = io.smb2.out.file.handle;
1394 :
1395 : /* ask for a change notify,
1396 : on file or directory name changes */
1397 2 : ZERO_STRUCT(notify.smb2);
1398 2 : notify.smb2.level = RAW_NOTIFY_SMB2;
1399 2 : notify.smb2.in.buffer_size = 1000;
1400 2 : notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1401 2 : notify.smb2.in.file.handle = h1;
1402 2 : notify.smb2.in.recursive = true;
1403 :
1404 2 : req = smb2_notify_send(tree1, &(notify.smb2));
1405 :
1406 8 : WAIT_FOR_ASYNC_RESPONSE(req);
1407 :
1408 2 : status = smb2_util_close(tree1, h1);
1409 2 : CHECK_STATUS(status, NT_STATUS_OK);
1410 :
1411 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
1412 2 : CHECK_STATUS(status, NT_STATUS_NOTIFY_CLEANUP);
1413 2 : CHECK_VAL(notify.smb2.out.num_changes, 0);
1414 :
1415 4 : done:
1416 2 : smb2_deltree(tree1, BASEDIR_CNC);
1417 2 : return ret;
1418 : }
1419 :
1420 : /*
1421 : basic testing of change notifies followed by a ulogoff
1422 : */
1423 :
1424 : #define BASEDIR_NUL BASEDIR "_NUL"
1425 2 : static bool torture_smb2_notify_ulogoff(struct torture_context *torture,
1426 : struct smb2_tree *tree1)
1427 : {
1428 2 : bool ret = true;
1429 : NTSTATUS status;
1430 : union smb_notify notify;
1431 : union smb_open io;
1432 : struct smb2_handle h1;
1433 : struct smb2_request *req;
1434 :
1435 2 : smb2_deltree(tree1, BASEDIR_NUL);
1436 2 : smb2_util_rmdir(tree1, BASEDIR_NUL);
1437 :
1438 2 : torture_comment(torture, "TESTING CHANGE NOTIFY FOLLOWED BY ULOGOFF\n");
1439 :
1440 : /*
1441 : get a handle on the directory
1442 : */
1443 2 : ZERO_STRUCT(io.smb2);
1444 2 : io.generic.level = RAW_OPEN_SMB2;
1445 2 : io.smb2.in.create_flags = 0;
1446 2 : io.smb2.in.desired_access = SEC_FILE_ALL;
1447 2 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1448 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1449 2 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1450 : NTCREATEX_SHARE_ACCESS_WRITE;
1451 2 : io.smb2.in.alloc_size = 0;
1452 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1453 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1454 2 : io.smb2.in.security_flags = 0;
1455 2 : io.smb2.in.fname = BASEDIR_NUL;
1456 :
1457 2 : status = smb2_create(tree1, torture, &(io.smb2));
1458 2 : CHECK_STATUS(status, NT_STATUS_OK);
1459 :
1460 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1461 2 : status = smb2_create(tree1, torture, &(io.smb2));
1462 2 : CHECK_STATUS(status, NT_STATUS_OK);
1463 2 : h1 = io.smb2.out.file.handle;
1464 :
1465 : /* ask for a change notify,
1466 : on file or directory name changes */
1467 2 : ZERO_STRUCT(notify.smb2);
1468 2 : notify.smb2.level = RAW_NOTIFY_SMB2;
1469 2 : notify.smb2.in.buffer_size = 1000;
1470 2 : notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1471 2 : notify.smb2.in.file.handle = h1;
1472 2 : notify.smb2.in.recursive = true;
1473 :
1474 2 : req = smb2_notify_send(tree1, &(notify.smb2));
1475 :
1476 8 : WAIT_FOR_ASYNC_RESPONSE(req);
1477 :
1478 2 : status = smb2_logoff(tree1->session);
1479 2 : CHECK_STATUS(status, NT_STATUS_OK);
1480 :
1481 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
1482 2 : CHECK_STATUS(status, NT_STATUS_NOTIFY_CLEANUP);
1483 2 : CHECK_VAL(notify.smb2.out.num_changes, 0);
1484 :
1485 4 : done:
1486 2 : smb2_deltree(tree1, BASEDIR_NUL);
1487 2 : return ret;
1488 : }
1489 :
1490 : /*
1491 : basic testing of change notifies followed by a session reconnect
1492 : */
1493 :
1494 : #define BASEDIR_NSR BASEDIR "_NSR"
1495 :
1496 2 : static bool torture_smb2_notify_session_reconnect(struct torture_context *torture,
1497 : struct smb2_tree *tree1)
1498 : {
1499 2 : bool ret = true;
1500 : NTSTATUS status;
1501 : union smb_notify notify;
1502 : union smb_open io;
1503 : struct smb2_handle h1;
1504 : struct smb2_request *req;
1505 2 : uint64_t previous_session_id = 0;
1506 2 : struct smb2_session *session2 = NULL;
1507 :
1508 2 : smb2_deltree(tree1, BASEDIR_NSR);
1509 2 : smb2_util_rmdir(tree1, BASEDIR_NSR);
1510 :
1511 2 : torture_comment(torture, "TESTING CHANGE NOTIFY FOLLOWED BY SESSION RECONNECT\n");
1512 :
1513 : /*
1514 : get a handle on the directory
1515 : */
1516 2 : ZERO_STRUCT(io.smb2);
1517 2 : io.generic.level = RAW_OPEN_SMB2;
1518 2 : io.smb2.in.create_flags = 0;
1519 2 : io.smb2.in.desired_access = SEC_FILE_ALL;
1520 2 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1521 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1522 2 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1523 : NTCREATEX_SHARE_ACCESS_WRITE;
1524 2 : io.smb2.in.alloc_size = 0;
1525 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1526 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1527 2 : io.smb2.in.security_flags = 0;
1528 2 : io.smb2.in.fname = BASEDIR_NSR;
1529 :
1530 2 : status = smb2_create(tree1, torture, &(io.smb2));
1531 2 : CHECK_STATUS(status, NT_STATUS_OK);
1532 :
1533 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1534 2 : status = smb2_create(tree1, torture, &(io.smb2));
1535 2 : CHECK_STATUS(status, NT_STATUS_OK);
1536 2 : h1 = io.smb2.out.file.handle;
1537 :
1538 : /* ask for a change notify,
1539 : on file or directory name changes */
1540 2 : ZERO_STRUCT(notify.smb2);
1541 2 : notify.smb2.level = RAW_NOTIFY_SMB2;
1542 2 : notify.smb2.in.buffer_size = 1000;
1543 2 : notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1544 2 : notify.smb2.in.file.handle = h1;
1545 2 : notify.smb2.in.recursive = true;
1546 :
1547 2 : req = smb2_notify_send(tree1, &(notify.smb2));
1548 :
1549 8 : WAIT_FOR_ASYNC_RESPONSE(req);
1550 :
1551 2 : previous_session_id = smb2cli_session_current_id(tree1->session->smbXcli);
1552 2 : torture_assert(torture, torture_smb2_session_setup(torture,
1553 : tree1->session->transport,
1554 : previous_session_id,
1555 : torture, &session2),
1556 : "session setup with previous_session_id failed");
1557 :
1558 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
1559 2 : CHECK_STATUS(status, NT_STATUS_NOTIFY_CLEANUP);
1560 2 : CHECK_VAL(notify.smb2.out.num_changes, 0);
1561 :
1562 2 : status = smb2_logoff(tree1->session);
1563 2 : CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
1564 :
1565 2 : status = smb2_logoff(session2);
1566 2 : CHECK_STATUS(status, NT_STATUS_OK);
1567 4 : done:
1568 2 : smb2_deltree(tree1, BASEDIR_NSR);
1569 2 : return ret;
1570 : }
1571 :
1572 : /*
1573 : basic testing of change notifies followed by an invalid reauth
1574 : */
1575 :
1576 : #define BASEDIR_IR BASEDIR "_IR"
1577 :
1578 2 : static bool torture_smb2_notify_invalid_reauth(struct torture_context *torture,
1579 : struct smb2_tree *tree1,
1580 : struct smb2_tree *tree2)
1581 : {
1582 2 : bool ret = true;
1583 : NTSTATUS status;
1584 : union smb_notify notify;
1585 : union smb_open io;
1586 : struct smb2_handle h1;
1587 : struct smb2_request *req;
1588 : struct cli_credentials *invalid_creds;
1589 :
1590 2 : smb2_deltree(tree2, BASEDIR_IR);
1591 2 : smb2_util_rmdir(tree2, BASEDIR_IR);
1592 :
1593 2 : torture_comment(torture, "TESTING CHANGE NOTIFY FOLLOWED BY invalid REAUTH\n");
1594 :
1595 : /*
1596 : get a handle on the directory
1597 : */
1598 2 : ZERO_STRUCT(io.smb2);
1599 2 : io.generic.level = RAW_OPEN_SMB2;
1600 2 : io.smb2.in.create_flags = 0;
1601 2 : io.smb2.in.desired_access = SEC_FILE_ALL;
1602 2 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1603 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1604 2 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1605 : NTCREATEX_SHARE_ACCESS_WRITE;
1606 2 : io.smb2.in.alloc_size = 0;
1607 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1608 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1609 2 : io.smb2.in.security_flags = 0;
1610 2 : io.smb2.in.fname = BASEDIR_IR;
1611 :
1612 2 : status = smb2_create(tree1, torture, &(io.smb2));
1613 2 : CHECK_STATUS(status, NT_STATUS_OK);
1614 :
1615 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1616 2 : status = smb2_create(tree1, torture, &(io.smb2));
1617 2 : CHECK_STATUS(status, NT_STATUS_OK);
1618 2 : h1 = io.smb2.out.file.handle;
1619 :
1620 : /* ask for a change notify,
1621 : on file or directory name changes */
1622 2 : ZERO_STRUCT(notify.smb2);
1623 2 : notify.smb2.level = RAW_NOTIFY_SMB2;
1624 2 : notify.smb2.in.buffer_size = 1000;
1625 2 : notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1626 2 : notify.smb2.in.file.handle = h1;
1627 2 : notify.smb2.in.recursive = true;
1628 :
1629 2 : req = smb2_notify_send(tree1, &(notify.smb2));
1630 :
1631 8 : WAIT_FOR_ASYNC_RESPONSE(req);
1632 :
1633 2 : invalid_creds = cli_credentials_init(torture);
1634 2 : torture_assert(torture, (invalid_creds != NULL), "talloc error");
1635 2 : cli_credentials_set_username(invalid_creds, "__none__invalid__none__", CRED_SPECIFIED);
1636 2 : cli_credentials_set_domain(invalid_creds, "__none__invalid__none__", CRED_SPECIFIED);
1637 2 : cli_credentials_set_password(invalid_creds, "__none__invalid__none__", CRED_SPECIFIED);
1638 2 : cli_credentials_set_realm(invalid_creds, NULL, CRED_SPECIFIED);
1639 2 : cli_credentials_set_workstation(invalid_creds, "", CRED_UNINITIALISED);
1640 :
1641 2 : status = smb2_session_setup_spnego(tree1->session,
1642 : invalid_creds,
1643 : 0 /* previous_session_id */);
1644 2 : CHECK_STATUS(status, NT_STATUS_LOGON_FAILURE);
1645 :
1646 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
1647 2 : CHECK_STATUS(status, NT_STATUS_NOTIFY_CLEANUP);
1648 2 : CHECK_VAL(notify.smb2.out.num_changes, 0);
1649 :
1650 : /*
1651 : * Demonstrate that the session is no longer valid.
1652 : */
1653 2 : status = smb2_create(tree1, torture, &(io.smb2));
1654 2 : CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
1655 4 : done:
1656 2 : smb2_deltree(tree2, BASEDIR_IR);
1657 2 : return ret;
1658 : }
1659 :
1660 2 : static void tcp_dis_handler(struct smb2_transport *t, void *p)
1661 : {
1662 2 : struct smb2_tree *tree = (struct smb2_tree *)p;
1663 2 : smb2_transport_dead(tree->session->transport,
1664 2 : NT_STATUS_LOCAL_DISCONNECT);
1665 2 : t = NULL;
1666 2 : tree = NULL;
1667 2 : }
1668 :
1669 : /*
1670 : basic testing of change notifies followed by tcp disconnect
1671 : */
1672 :
1673 : #define BASEDIR_NTCPD BASEDIR "_NTCPD"
1674 :
1675 2 : static bool torture_smb2_notify_tcp_disconnect(
1676 : struct torture_context *torture,
1677 : struct smb2_tree *tree)
1678 : {
1679 2 : bool ret = true;
1680 : NTSTATUS status;
1681 : union smb_notify notify;
1682 : union smb_open io;
1683 : struct smb2_handle h1;
1684 : struct smb2_request *req;
1685 :
1686 2 : smb2_deltree(tree, BASEDIR_NTCPD);
1687 2 : smb2_util_rmdir(tree, BASEDIR_NTCPD);
1688 :
1689 2 : torture_comment(torture,
1690 : "TESTING CHANGE NOTIFY FOLLOWED BY TCP DISCONNECT\n");
1691 :
1692 : /*
1693 : get a handle on the directory
1694 : */
1695 2 : ZERO_STRUCT(io.smb2);
1696 2 : io.generic.level = RAW_OPEN_SMB2;
1697 2 : io.smb2.in.create_flags = 0;
1698 2 : io.smb2.in.desired_access = SEC_FILE_ALL;
1699 2 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1700 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1701 2 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1702 : NTCREATEX_SHARE_ACCESS_WRITE;
1703 2 : io.smb2.in.alloc_size = 0;
1704 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1705 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1706 2 : io.smb2.in.security_flags = 0;
1707 2 : io.smb2.in.fname = BASEDIR_NTCPD;
1708 :
1709 2 : status = smb2_create(tree, torture, &(io.smb2));
1710 2 : CHECK_STATUS(status, NT_STATUS_OK);
1711 2 : h1 = io.smb2.out.file.handle;
1712 :
1713 : /* ask for a change notify,
1714 : on file or directory name changes */
1715 2 : ZERO_STRUCT(notify.smb2);
1716 2 : notify.smb2.level = RAW_NOTIFY_SMB2;
1717 2 : notify.smb2.in.buffer_size = 1000;
1718 2 : notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1719 2 : notify.smb2.in.file.handle = h1;
1720 2 : notify.smb2.in.recursive = true;
1721 :
1722 2 : req = smb2_notify_send(tree, &(notify.smb2));
1723 2 : smb2_cancel(req);
1724 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
1725 2 : CHECK_STATUS(status, NT_STATUS_CANCELLED);
1726 :
1727 2 : notify.smb2.in.recursive = true;
1728 2 : req = smb2_notify_send(tree, &(notify.smb2));
1729 2 : smb2_transport_idle_handler(tree->session->transport,
1730 : tcp_dis_handler, 250000, tree);
1731 2 : tree = NULL;
1732 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
1733 2 : CHECK_STATUS(status, NT_STATUS_LOCAL_DISCONNECT);
1734 :
1735 4 : done:
1736 2 : return ret;
1737 : }
1738 :
1739 : /*
1740 : test setting up two change notify requests on one handle
1741 : */
1742 :
1743 : #define BASEDIR_NDOH BASEDIR "_NDOH"
1744 :
1745 2 : static bool torture_smb2_notify_double(struct torture_context *torture,
1746 : struct smb2_tree *tree1,
1747 : struct smb2_tree *tree2)
1748 : {
1749 2 : bool ret = true;
1750 : NTSTATUS status;
1751 : union smb_notify notify;
1752 : union smb_open io;
1753 : struct smb2_handle h1;
1754 : struct smb2_request *req1, *req2;
1755 :
1756 2 : smb2_deltree(tree1, BASEDIR_NDOH);
1757 2 : smb2_util_rmdir(tree1, BASEDIR_NDOH);
1758 :
1759 2 : torture_comment(torture,
1760 : "TESTING CHANGE NOTIFY TWICE ON ONE DIRECTORY\n");
1761 :
1762 : /*
1763 : get a handle on the directory
1764 : */
1765 2 : ZERO_STRUCT(io.smb2);
1766 2 : io.generic.level = RAW_OPEN_SMB2;
1767 2 : io.smb2.in.create_flags = 0;
1768 2 : io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ|
1769 : SEC_RIGHTS_FILE_WRITE|
1770 : SEC_RIGHTS_FILE_ALL;
1771 2 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1772 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1773 2 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1774 : NTCREATEX_SHARE_ACCESS_WRITE;
1775 2 : io.smb2.in.alloc_size = 0;
1776 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1777 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1778 2 : io.smb2.in.security_flags = 0;
1779 2 : io.smb2.in.fname = BASEDIR_NDOH;
1780 :
1781 2 : status = smb2_create(tree1, torture, &(io.smb2));
1782 2 : CHECK_STATUS(status, NT_STATUS_OK);
1783 2 : h1 = io.smb2.out.file.handle;
1784 :
1785 : /* ask for a change notify,
1786 : on file or directory name changes */
1787 2 : ZERO_STRUCT(notify.smb2);
1788 2 : notify.smb2.level = RAW_NOTIFY_SMB2;
1789 2 : notify.smb2.in.buffer_size = 1000;
1790 2 : notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1791 2 : notify.smb2.in.file.handle = h1;
1792 2 : notify.smb2.in.recursive = true;
1793 :
1794 2 : req1 = smb2_notify_send(tree1, &(notify.smb2));
1795 2 : smb2_cancel(req1);
1796 2 : status = smb2_notify_recv(req1, torture, &(notify.smb2));
1797 2 : CHECK_STATUS(status, NT_STATUS_CANCELLED);
1798 :
1799 2 : req2 = smb2_notify_send(tree1, &(notify.smb2));
1800 2 : smb2_cancel(req2);
1801 2 : status = smb2_notify_recv(req2, torture, &(notify.smb2));
1802 2 : CHECK_STATUS(status, NT_STATUS_CANCELLED);
1803 :
1804 2 : smb2_util_mkdir(tree2, BASEDIR_NDOH "\\subdir-name");
1805 2 : req1 = smb2_notify_send(tree1, &(notify.smb2));
1806 2 : req2 = smb2_notify_send(tree1, &(notify.smb2));
1807 :
1808 2 : status = smb2_notify_recv(req1, torture, &(notify.smb2));
1809 2 : CHECK_STATUS(status, NT_STATUS_OK);
1810 2 : CHECK_VAL(notify.smb2.out.num_changes, 1);
1811 2 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name");
1812 :
1813 2 : smb2_util_mkdir(tree2, BASEDIR_NDOH "\\subdir-name2");
1814 :
1815 2 : status = smb2_notify_recv(req2, torture, &(notify.smb2));
1816 2 : CHECK_STATUS(status, NT_STATUS_OK);
1817 2 : CHECK_VAL(notify.smb2.out.num_changes, 1);
1818 2 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name2");
1819 :
1820 4 : done:
1821 2 : smb2_deltree(tree1, BASEDIR_NDOH);
1822 2 : return ret;
1823 : }
1824 :
1825 :
1826 : /*
1827 : test multiple change notifies at different depths and with/without recursion
1828 : */
1829 :
1830 : #define BASEDIR_TREE BASEDIR "_TREE"
1831 :
1832 2 : static bool torture_smb2_notify_tree(struct torture_context *torture,
1833 : struct smb2_tree *tree)
1834 : {
1835 2 : bool ret = true;
1836 : union smb_notify notify;
1837 : union smb_open io;
1838 : struct smb2_request *req;
1839 : struct timeval tv;
1840 : struct {
1841 : const char *path;
1842 : bool recursive;
1843 : uint32_t filter;
1844 : int expected;
1845 : struct smb2_handle h1;
1846 : int counted;
1847 2 : } dirs[] = {
1848 : {
1849 : .path = BASEDIR_TREE "\\abc",
1850 : .recursive = true,
1851 : .filter = FILE_NOTIFY_CHANGE_NAME,
1852 : .expected = 30,
1853 : },
1854 : {
1855 : .path = BASEDIR_TREE "\\zqy",
1856 : .recursive = true,
1857 : .filter = FILE_NOTIFY_CHANGE_NAME,
1858 : .expected = 8,
1859 : },
1860 : {
1861 : .path = BASEDIR_TREE "\\atsy",
1862 : .recursive = true,
1863 : .filter = FILE_NOTIFY_CHANGE_NAME,
1864 : .expected = 4,
1865 : },
1866 : {
1867 : .path = BASEDIR_TREE "\\abc\\foo",
1868 : .recursive = true,
1869 : .filter = FILE_NOTIFY_CHANGE_NAME,
1870 : .expected = 2,
1871 : },
1872 : {
1873 : .path = BASEDIR_TREE "\\abc\\blah",
1874 : .recursive = true,
1875 : .filter = FILE_NOTIFY_CHANGE_NAME,
1876 : .expected = 13,
1877 : },
1878 : {
1879 : .path = BASEDIR_TREE "\\abc\\blah",
1880 : .recursive = false,
1881 : .filter = FILE_NOTIFY_CHANGE_NAME,
1882 : .expected = 7,
1883 : },
1884 : {
1885 : .path = BASEDIR_TREE "\\abc\\blah\\a",
1886 : .recursive = true,
1887 : .filter = FILE_NOTIFY_CHANGE_NAME,
1888 : .expected = 2,
1889 : },
1890 : {
1891 : .path = BASEDIR_TREE "\\abc\\blah\\b",
1892 : .recursive = true,
1893 : .filter = FILE_NOTIFY_CHANGE_NAME,
1894 : .expected = 2,
1895 : },
1896 : {
1897 : .path = BASEDIR_TREE "\\abc\\blah\\c",
1898 : .recursive = true,
1899 : .filter = FILE_NOTIFY_CHANGE_NAME,
1900 : .expected = 2,
1901 : },
1902 : {
1903 : .path = BASEDIR_TREE "\\abc\\fooblah",
1904 : .recursive = true,
1905 : .filter = FILE_NOTIFY_CHANGE_NAME,
1906 : .expected = 2,
1907 : },
1908 : {
1909 : .path = BASEDIR_TREE "\\zqy\\xx",
1910 : .recursive = true,
1911 : .filter = FILE_NOTIFY_CHANGE_NAME,
1912 : .expected = 2,
1913 : },
1914 : {
1915 : .path = BASEDIR_TREE "\\zqy\\yyy",
1916 : .recursive = true,
1917 : .filter = FILE_NOTIFY_CHANGE_NAME,
1918 : .expected = 2,
1919 : },
1920 : {
1921 : .path = BASEDIR_TREE "\\zqy\\..",
1922 : .recursive = true,
1923 : .filter = FILE_NOTIFY_CHANGE_NAME,
1924 : .expected = 40,
1925 : },
1926 : {
1927 : .path = BASEDIR_TREE,
1928 : .recursive = true,
1929 : .filter = FILE_NOTIFY_CHANGE_NAME,
1930 : .expected = 40,
1931 : },
1932 : {
1933 : .path = BASEDIR_TREE,
1934 : .recursive = false,
1935 : .filter = FILE_NOTIFY_CHANGE_NAME,
1936 : .expected = 6,
1937 : },
1938 : {
1939 : .path = BASEDIR_TREE "\\atsy",
1940 : .recursive = false,
1941 : .filter = FILE_NOTIFY_CHANGE_NAME,
1942 : .expected = 4,
1943 : },
1944 : {
1945 : .path = BASEDIR_TREE "\\abc",
1946 : .recursive = true,
1947 : .filter = FILE_NOTIFY_CHANGE_NAME,
1948 : .expected = 24,
1949 : },
1950 : {
1951 : .path = BASEDIR_TREE "\\abc",
1952 : .recursive = false,
1953 : .filter = FILE_NOTIFY_CHANGE_FILE_NAME,
1954 : .expected = 0,
1955 : },
1956 : {
1957 : .path = BASEDIR_TREE "\\abc",
1958 : .recursive = true,
1959 : .filter = FILE_NOTIFY_CHANGE_FILE_NAME,
1960 : .expected = 0,
1961 : },
1962 : {
1963 : .path = BASEDIR_TREE "\\abc",
1964 : .recursive = true,
1965 : .filter = FILE_NOTIFY_CHANGE_NAME,
1966 : .expected = 24,
1967 : },
1968 : };
1969 : int i;
1970 : NTSTATUS status;
1971 2 : bool all_done = false;
1972 :
1973 2 : smb2_deltree(tree, BASEDIR_TREE);
1974 2 : smb2_util_rmdir(tree, BASEDIR_TREE);
1975 :
1976 2 : torture_comment(torture, "TESTING NOTIFY FOR DIFFERENT DEPTHS\n");
1977 :
1978 2 : ZERO_STRUCT(io.smb2);
1979 2 : io.generic.level = RAW_OPEN_SMB2;
1980 2 : io.smb2.in.create_flags = 0;
1981 2 : io.smb2.in.desired_access = SEC_FILE_ALL;
1982 2 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1983 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1984 2 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1985 : NTCREATEX_SHARE_ACCESS_WRITE;
1986 2 : io.smb2.in.alloc_size = 0;
1987 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1988 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1989 2 : io.smb2.in.security_flags = 0;
1990 2 : io.smb2.in.fname = BASEDIR_TREE;
1991 2 : status = smb2_create(tree, torture, &(io.smb2));
1992 2 : CHECK_STATUS(status, NT_STATUS_OK);
1993 :
1994 2 : ZERO_STRUCT(notify.smb2);
1995 2 : notify.smb2.level = RAW_NOTIFY_SMB2;
1996 2 : notify.smb2.in.buffer_size = 20000;
1997 :
1998 : /*
1999 : setup the directory tree, and the notify buffer on each directory
2000 : */
2001 42 : for (i=0;i<ARRAY_SIZE(dirs);i++) {
2002 40 : io.smb2.in.fname = dirs[i].path;
2003 40 : status = smb2_create(tree, torture, &(io.smb2));
2004 40 : CHECK_STATUS(status, NT_STATUS_OK);
2005 40 : dirs[i].h1 = io.smb2.out.file.handle;
2006 :
2007 40 : notify.smb2.in.completion_filter = dirs[i].filter;
2008 40 : notify.smb2.in.file.handle = dirs[i].h1;
2009 40 : notify.smb2.in.recursive = dirs[i].recursive;
2010 40 : req = smb2_notify_send(tree, &(notify.smb2));
2011 40 : smb2_cancel(req);
2012 40 : status = smb2_notify_recv(req, torture, &(notify.smb2));
2013 40 : CHECK_STATUS(status, NT_STATUS_CANCELLED);
2014 : }
2015 :
2016 : /* trigger 2 events in each dir */
2017 42 : for (i=0;i<ARRAY_SIZE(dirs);i++) {
2018 40 : char *path = talloc_asprintf(torture, "%s\\test.dir",
2019 : dirs[i].path);
2020 40 : smb2_util_mkdir(tree, path);
2021 40 : smb2_util_rmdir(tree, path);
2022 40 : talloc_free(path);
2023 : }
2024 :
2025 : /* give a bit of time for the events to propagate */
2026 2 : tv = timeval_current();
2027 :
2028 : do {
2029 : /* count events that have happened in each dir */
2030 42 : for (i=0;i<ARRAY_SIZE(dirs);i++) {
2031 40 : notify.smb2.in.completion_filter = dirs[i].filter;
2032 40 : notify.smb2.in.file.handle = dirs[i].h1;
2033 40 : notify.smb2.in.recursive = dirs[i].recursive;
2034 40 : req = smb2_notify_send(tree, &(notify.smb2));
2035 40 : smb2_cancel(req);
2036 40 : notify.smb2.out.num_changes = 0;
2037 40 : status = smb2_notify_recv(req, torture,
2038 : &(notify.smb2));
2039 40 : dirs[i].counted += notify.smb2.out.num_changes;
2040 : }
2041 :
2042 2 : all_done = true;
2043 :
2044 42 : for (i=0;i<ARRAY_SIZE(dirs);i++) {
2045 40 : if (dirs[i].counted != dirs[i].expected) {
2046 0 : all_done = false;
2047 : }
2048 : }
2049 2 : } while (!all_done && timeval_elapsed(&tv) < 20);
2050 :
2051 2 : torture_comment(torture, "took %.4f seconds to propagate all events\n",
2052 : timeval_elapsed(&tv));
2053 :
2054 42 : for (i=0;i<ARRAY_SIZE(dirs);i++) {
2055 40 : if (dirs[i].counted != dirs[i].expected) {
2056 0 : torture_comment(torture,
2057 : "ERROR: i=%d expected %d got %d for '%s'\n",
2058 : i, dirs[i].expected, dirs[i].counted,
2059 : dirs[i].path);
2060 0 : ret = false;
2061 : }
2062 : }
2063 :
2064 : /*
2065 : run from the back, closing and deleting
2066 : */
2067 42 : for (i=ARRAY_SIZE(dirs)-1;i>=0;i--) {
2068 40 : smb2_util_close(tree, dirs[i].h1);
2069 40 : smb2_util_rmdir(tree, dirs[i].path);
2070 : }
2071 :
2072 2 : done:
2073 2 : smb2_deltree(tree, BASEDIR_TREE);
2074 2 : smb2_util_rmdir(tree, BASEDIR_TREE);
2075 2 : return ret;
2076 : }
2077 :
2078 : /*
2079 : Test response when cached server events exceed single NT NOTFIY response
2080 : packet size.
2081 : */
2082 :
2083 : #define BASEDIR_OVF BASEDIR "_OVF"
2084 :
2085 2 : static bool torture_smb2_notify_overflow(struct torture_context *torture,
2086 : struct smb2_tree *tree)
2087 : {
2088 2 : bool ret = true;
2089 : NTSTATUS status;
2090 : union smb_notify notify;
2091 : union smb_open io;
2092 : struct smb2_handle h1, h2;
2093 2 : int count = 100;
2094 : struct smb2_request *req1;
2095 : int i;
2096 :
2097 2 : smb2_deltree(tree, BASEDIR_OVF);
2098 2 : smb2_util_rmdir(tree, BASEDIR_OVF);
2099 :
2100 2 : torture_comment(torture, "TESTING CHANGE NOTIFY EVENT OVERFLOW\n");
2101 :
2102 : /* get a handle on the directory */
2103 2 : ZERO_STRUCT(io.smb2);
2104 2 : io.generic.level = RAW_OPEN_SMB2;
2105 2 : io.smb2.in.create_flags = 0;
2106 2 : io.smb2.in.desired_access = SEC_FILE_ALL;
2107 2 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
2108 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2109 2 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
2110 : NTCREATEX_SHARE_ACCESS_WRITE;
2111 2 : io.smb2.in.alloc_size = 0;
2112 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
2113 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
2114 2 : io.smb2.in.security_flags = 0;
2115 2 : io.smb2.in.fname = BASEDIR_OVF;
2116 :
2117 2 : status = smb2_create(tree, torture, &(io.smb2));
2118 2 : CHECK_STATUS(status, NT_STATUS_OK);
2119 2 : h1 = io.smb2.out.file.handle;
2120 :
2121 : /* ask for a change notify, on name changes. */
2122 2 : ZERO_STRUCT(notify.smb2);
2123 2 : notify.smb2.level = RAW_NOTIFY_NTTRANS;
2124 2 : notify.smb2.in.buffer_size = 1000;
2125 2 : notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
2126 2 : notify.smb2.in.file.handle = h1;
2127 :
2128 2 : notify.smb2.in.recursive = true;
2129 2 : req1 = smb2_notify_send(tree, &(notify.smb2));
2130 :
2131 : /* cancel initial requests so the buffer is setup */
2132 2 : smb2_cancel(req1);
2133 2 : status = smb2_notify_recv(req1, torture, &(notify.smb2));
2134 2 : CHECK_STATUS(status, NT_STATUS_CANCELLED);
2135 :
2136 : /* open a lot of files, filling up the server side notify buffer */
2137 2 : torture_comment(torture,
2138 : "Testing overflowed buffer notify on create of %d files\n",
2139 : count);
2140 :
2141 202 : for (i=0;i<count;i++) {
2142 200 : char *fname = talloc_asprintf(torture,
2143 : BASEDIR_OVF "\\test%d.txt", i);
2144 : union smb_open io1;
2145 200 : ZERO_STRUCT(io1.smb2);
2146 200 : io1.generic.level = RAW_OPEN_SMB2;
2147 200 : io1.smb2.in.create_flags = 0;
2148 200 : io1.smb2.in.desired_access = SEC_FILE_ALL;
2149 200 : io1.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
2150 200 : io1.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2151 200 : io1.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
2152 : NTCREATEX_SHARE_ACCESS_WRITE;
2153 200 : io1.smb2.in.alloc_size = 0;
2154 200 : io1.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
2155 200 : io1.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
2156 200 : io1.smb2.in.security_flags = 0;
2157 200 : io1.smb2.in.fname = fname;
2158 :
2159 200 : h2 = custom_smb2_create(tree, torture, &(io1.smb2));
2160 200 : talloc_free(fname);
2161 200 : smb2_util_close(tree, h2);
2162 : }
2163 :
2164 2 : req1 = smb2_notify_send(tree, &(notify.smb2));
2165 2 : status = smb2_notify_recv(req1, torture, &(notify.smb2));
2166 2 : CHECK_STATUS(status, NT_STATUS_NOTIFY_ENUM_DIR);
2167 2 : CHECK_VAL(notify.smb2.out.num_changes, 0);
2168 :
2169 4 : done:
2170 2 : smb2_deltree(tree, BASEDIR_OVF);
2171 2 : return ret;
2172 : }
2173 :
2174 : /*
2175 : Test if notifications are returned for changes to the base directory.
2176 : They shouldn't be.
2177 : */
2178 :
2179 : #define BASEDIR_BAS BASEDIR "_BAS"
2180 :
2181 2 : static bool torture_smb2_notify_basedir(struct torture_context *torture,
2182 : struct smb2_tree *tree1,
2183 : struct smb2_tree *tree2)
2184 : {
2185 2 : bool ret = true;
2186 : NTSTATUS status;
2187 : union smb_notify notify;
2188 : union smb_open io;
2189 : struct smb2_handle h1;
2190 : struct smb2_request *req1;
2191 :
2192 2 : smb2_deltree(tree1, BASEDIR_BAS);
2193 2 : smb2_util_rmdir(tree1, BASEDIR_BAS);
2194 :
2195 2 : torture_comment(torture, "TESTING CHANGE NOTIFY BASEDIR EVENTS\n");
2196 :
2197 : /* get a handle on the directory */
2198 2 : ZERO_STRUCT(io.smb2);
2199 2 : io.generic.level = RAW_OPEN_SMB2;
2200 2 : io.smb2.in.create_flags = 0;
2201 2 : io.smb2.in.desired_access = SEC_FILE_ALL;
2202 2 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
2203 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2204 2 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
2205 : NTCREATEX_SHARE_ACCESS_WRITE;
2206 2 : io.smb2.in.alloc_size = 0;
2207 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
2208 2 : io.smb2.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
2209 2 : io.smb2.in.security_flags = 0;
2210 2 : io.smb2.in.fname = BASEDIR_BAS;
2211 :
2212 2 : status = smb2_create(tree1, torture, &(io.smb2));
2213 2 : CHECK_STATUS(status, NT_STATUS_OK);
2214 2 : h1 = io.smb2.out.file.handle;
2215 :
2216 : /* create a test file that will also be modified */
2217 2 : io.smb2.in.fname = BASEDIR_BAS "\\tname1";
2218 2 : io.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
2219 2 : status = smb2_create(tree2, torture, &(io.smb2));
2220 2 : CHECK_STATUS(status,NT_STATUS_OK);
2221 2 : smb2_util_close(tree2, io.smb2.out.file.handle);
2222 :
2223 : /* ask for a change notify, on attribute changes. */
2224 2 : ZERO_STRUCT(notify.smb2);
2225 2 : notify.smb2.level = RAW_NOTIFY_SMB2;
2226 2 : notify.smb2.in.buffer_size = 1000;
2227 2 : notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_ATTRIBUTES;
2228 2 : notify.smb2.in.file.handle = h1;
2229 2 : notify.smb2.in.recursive = true;
2230 :
2231 2 : req1 = smb2_notify_send(tree1, &(notify.smb2));
2232 :
2233 : /* set attribute on the base dir */
2234 2 : smb2_util_setatr(tree2, BASEDIR_BAS, FILE_ATTRIBUTE_HIDDEN);
2235 :
2236 : /* set attribute on a file to assure we receive a notification */
2237 2 : smb2_util_setatr(tree2, BASEDIR_BAS "\\tname1", FILE_ATTRIBUTE_HIDDEN);
2238 2 : smb_msleep(200);
2239 :
2240 : /* check how many responses were given, expect only 1 for the file */
2241 2 : status = smb2_notify_recv(req1, torture, &(notify.smb2));
2242 2 : CHECK_STATUS(status, NT_STATUS_OK);
2243 2 : CHECK_VAL(notify.smb2.out.num_changes, 1);
2244 2 : CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_MODIFIED);
2245 2 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "tname1");
2246 :
2247 4 : done:
2248 2 : smb2_deltree(tree1, BASEDIR_BAS);
2249 2 : return ret;
2250 : }
2251 :
2252 : /*
2253 : very simple change notify test
2254 : */
2255 :
2256 : #define BASEDIR_TCON BASEDIR "_TCON"
2257 :
2258 2 : static bool torture_smb2_notify_tcon(struct torture_context *torture,
2259 : struct smb2_tree *tree)
2260 : {
2261 2 : bool ret = true;
2262 : NTSTATUS status;
2263 : union smb_notify notify;
2264 : union smb_open io;
2265 2 : struct smb2_handle h1 = {{0}};
2266 2 : struct smb2_request *req = NULL;
2267 2 : struct smb2_tree *tree1 = NULL;
2268 2 : const char *fname = BASEDIR_TCON "\\subdir-name";
2269 :
2270 2 : smb2_deltree(tree, BASEDIR_TCON);
2271 2 : smb2_util_rmdir(tree, BASEDIR_TCON);
2272 :
2273 2 : torture_comment(torture, "TESTING SIMPLE CHANGE NOTIFY\n");
2274 :
2275 : /*
2276 : get a handle on the directory
2277 : */
2278 :
2279 2 : ZERO_STRUCT(io.smb2);
2280 2 : io.generic.level = RAW_OPEN_SMB2;
2281 2 : io.smb2.in.create_flags = 0;
2282 2 : io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
2283 2 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
2284 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL |
2285 : FILE_ATTRIBUTE_DIRECTORY;
2286 2 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
2287 : NTCREATEX_SHARE_ACCESS_WRITE;
2288 2 : io.smb2.in.alloc_size = 0;
2289 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
2290 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
2291 2 : io.smb2.in.security_flags = 0;
2292 2 : io.smb2.in.fname = BASEDIR_TCON;
2293 :
2294 2 : status = smb2_create(tree, torture, &(io.smb2));
2295 2 : CHECK_STATUS(status, NT_STATUS_OK);
2296 2 : h1 = io.smb2.out.file.handle;
2297 :
2298 : /* ask for a change notify,
2299 : on file or directory name changes */
2300 2 : ZERO_STRUCT(notify.smb2);
2301 2 : notify.smb2.level = RAW_NOTIFY_SMB2;
2302 2 : notify.smb2.in.buffer_size = 1000;
2303 2 : notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
2304 2 : notify.smb2.in.file.handle = h1;
2305 2 : notify.smb2.in.recursive = true;
2306 :
2307 2 : torture_comment(torture, "Testing notify mkdir\n");
2308 2 : req = smb2_notify_send(tree, &(notify.smb2));
2309 2 : smb2_cancel(req);
2310 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
2311 2 : CHECK_STATUS(status, NT_STATUS_CANCELLED);
2312 :
2313 2 : notify.smb2.in.recursive = true;
2314 2 : req = smb2_notify_send(tree, &(notify.smb2));
2315 2 : status = smb2_util_mkdir(tree, fname);
2316 2 : CHECK_STATUS(status, NT_STATUS_OK);
2317 :
2318 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
2319 2 : CHECK_STATUS(status, NT_STATUS_OK);
2320 :
2321 2 : CHECK_VAL(notify.smb2.out.num_changes, 1);
2322 2 : CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_ADDED);
2323 2 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name");
2324 :
2325 2 : torture_comment(torture, "Testing notify rmdir\n");
2326 2 : req = smb2_notify_send(tree, &(notify.smb2));
2327 2 : status = smb2_util_rmdir(tree, fname);
2328 2 : CHECK_STATUS(status, NT_STATUS_OK);
2329 :
2330 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
2331 2 : CHECK_STATUS(status, NT_STATUS_OK);
2332 2 : CHECK_VAL(notify.smb2.out.num_changes, 1);
2333 2 : CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_REMOVED);
2334 2 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name");
2335 :
2336 2 : torture_comment(torture, "SIMPLE CHANGE NOTIFY OK\n");
2337 :
2338 2 : torture_comment(torture, "TESTING WITH SECONDARY TCON\n");
2339 2 : if (!torture_smb2_tree_connect(torture, tree->session, tree, &tree1)) {
2340 0 : torture_warning(torture, "couldn't reconnect to share, bailing\n");
2341 0 : ret = false;
2342 0 : goto done;
2343 : }
2344 :
2345 2 : torture_comment(torture, "tid1=%d tid2=%d\n",
2346 : smb2cli_tcon_current_id(tree->smbXcli),
2347 2 : smb2cli_tcon_current_id(tree1->smbXcli));
2348 :
2349 2 : torture_comment(torture, "Testing notify mkdir\n");
2350 2 : req = smb2_notify_send(tree, &(notify.smb2));
2351 2 : smb2_util_mkdir(tree1, fname);
2352 :
2353 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
2354 2 : CHECK_STATUS(status, NT_STATUS_OK);
2355 :
2356 2 : CHECK_VAL(notify.smb2.out.num_changes, 1);
2357 2 : CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_ADDED);
2358 2 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name");
2359 :
2360 2 : torture_comment(torture, "Testing notify rmdir\n");
2361 2 : req = smb2_notify_send(tree, &(notify.smb2));
2362 2 : smb2_util_rmdir(tree, fname);
2363 :
2364 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
2365 2 : CHECK_STATUS(status, NT_STATUS_OK);
2366 2 : CHECK_VAL(notify.smb2.out.num_changes, 1);
2367 2 : CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_REMOVED);
2368 2 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name");
2369 :
2370 2 : torture_comment(torture, "CHANGE NOTIFY WITH TCON OK\n");
2371 :
2372 2 : torture_comment(torture, "Disconnecting secondary tree\n");
2373 2 : status = smb2_tdis(tree1);
2374 2 : CHECK_STATUS(status, NT_STATUS_OK);
2375 2 : talloc_free(tree1);
2376 :
2377 2 : torture_comment(torture, "Testing notify mkdir\n");
2378 2 : req = smb2_notify_send(tree, &(notify.smb2));
2379 2 : smb2_util_mkdir(tree, fname);
2380 :
2381 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
2382 2 : CHECK_STATUS(status, NT_STATUS_OK);
2383 :
2384 2 : CHECK_VAL(notify.smb2.out.num_changes, 1);
2385 2 : CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_ADDED);
2386 2 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name");
2387 :
2388 2 : torture_comment(torture, "Testing notify rmdir\n");
2389 2 : req = smb2_notify_send(tree, &(notify.smb2));
2390 2 : smb2_util_rmdir(tree, fname);
2391 :
2392 2 : status = smb2_notify_recv(req, torture, &(notify.smb2));
2393 2 : CHECK_STATUS(status, NT_STATUS_OK);
2394 2 : CHECK_VAL(notify.smb2.out.num_changes, 1);
2395 2 : CHECK_VAL(notify.smb2.out.changes[0].action, NOTIFY_ACTION_REMOVED);
2396 2 : CHECK_WIRE_STR(notify.smb2.out.changes[0].name, "subdir-name");
2397 :
2398 2 : torture_comment(torture, "CHANGE NOTIFY WITH TDIS OK\n");
2399 2 : done:
2400 2 : smb2_util_close(tree, h1);
2401 2 : smb2_deltree(tree, BASEDIR_TCON);
2402 :
2403 2 : return ret;
2404 : }
2405 :
2406 : #define BASEDIR_RMD BASEDIR "_RMD"
2407 :
2408 8 : static bool torture_smb2_notify_rmdir(struct torture_context *torture,
2409 : struct smb2_tree *tree1,
2410 : struct smb2_tree *tree2,
2411 : bool initial_delete_on_close)
2412 : {
2413 8 : bool ret = true;
2414 : NTSTATUS status;
2415 8 : union smb_notify notify = {};
2416 8 : union smb_setfileinfo sfinfo = {};
2417 8 : union smb_open io = {};
2418 8 : struct smb2_handle h = {};
2419 : struct smb2_request *req;
2420 :
2421 8 : torture_comment(torture, "TESTING NOTIFY CANCEL FOR DELETED DIR\n");
2422 :
2423 8 : smb2_deltree(tree1, BASEDIR_RMD);
2424 8 : smb2_util_rmdir(tree1, BASEDIR_RMD);
2425 :
2426 8 : ZERO_STRUCT(io.smb2);
2427 8 : io.generic.level = RAW_OPEN_SMB2;
2428 8 : io.smb2.in.create_flags = 0;
2429 8 : io.smb2.in.desired_access = SEC_FILE_ALL;
2430 8 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
2431 8 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2432 8 : io.smb2.in.share_access =
2433 : NTCREATEX_SHARE_ACCESS_READ |
2434 : NTCREATEX_SHARE_ACCESS_WRITE |
2435 : NTCREATEX_SHARE_ACCESS_DELETE ;
2436 8 : io.smb2.in.alloc_size = 0;
2437 8 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
2438 8 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
2439 8 : io.smb2.in.security_flags = 0;
2440 8 : io.smb2.in.fname = BASEDIR_RMD;
2441 :
2442 8 : status = smb2_create(tree1, torture, &(io.smb2));
2443 8 : CHECK_STATUS(status, NT_STATUS_OK);
2444 8 : h = io.smb2.out.file.handle;
2445 :
2446 8 : ZERO_STRUCT(notify.smb2);
2447 8 : notify.smb2.level = RAW_NOTIFY_SMB2;
2448 8 : notify.smb2.in.buffer_size = 1000;
2449 8 : notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
2450 8 : notify.smb2.in.file.handle = h;
2451 8 : notify.smb2.in.recursive = false;
2452 :
2453 8 : io.smb2.in.desired_access |= SEC_STD_DELETE;
2454 8 : io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
2455 8 : req = smb2_notify_send(tree1, &(notify.smb2));
2456 :
2457 8 : if (initial_delete_on_close) {
2458 4 : status = smb2_util_rmdir(tree2, BASEDIR_RMD);
2459 4 : CHECK_STATUS(status, NT_STATUS_OK);
2460 : } else {
2461 4 : status = smb2_create(tree2, torture, &(io.smb2));
2462 4 : CHECK_STATUS(status, NT_STATUS_OK);
2463 :
2464 4 : sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION;
2465 4 : sfinfo.generic.in.file.handle = io.smb2.out.file.handle;
2466 4 : sfinfo.disposition_info.in.delete_on_close = 1;
2467 4 : status = smb2_setinfo_file(tree2, &sfinfo);
2468 4 : CHECK_STATUS(status, NT_STATUS_OK);
2469 :
2470 4 : smb2_util_close(tree2, io.smb2.out.file.handle);
2471 : }
2472 :
2473 8 : status = smb2_notify_recv(req, torture, &(notify.smb2));
2474 8 : CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
2475 :
2476 16 : done:
2477 :
2478 8 : smb2_util_close(tree1, h);
2479 8 : smb2_deltree(tree1, BASEDIR_RMD);
2480 :
2481 8 : return ret;
2482 : }
2483 :
2484 2 : static bool torture_smb2_notify_rmdir1(struct torture_context *torture,
2485 : struct smb2_tree *tree)
2486 : {
2487 2 : return torture_smb2_notify_rmdir(torture, tree, tree, false);
2488 : }
2489 :
2490 2 : static bool torture_smb2_notify_rmdir2(struct torture_context *torture,
2491 : struct smb2_tree *tree)
2492 : {
2493 2 : return torture_smb2_notify_rmdir(torture, tree, tree, true);
2494 : }
2495 :
2496 2 : static bool torture_smb2_notify_rmdir3(struct torture_context *torture,
2497 : struct smb2_tree *tree1,
2498 : struct smb2_tree *tree2)
2499 : {
2500 2 : return torture_smb2_notify_rmdir(torture, tree1, tree2, false);
2501 : }
2502 :
2503 2 : static bool torture_smb2_notify_rmdir4(struct torture_context *torture,
2504 : struct smb2_tree *tree1,
2505 : struct smb2_tree *tree2)
2506 : {
2507 2 : return torture_smb2_notify_rmdir(torture, tree1, tree2, true);
2508 : }
2509 :
2510 0 : static void notify_timeout(struct tevent_context *ev,
2511 : struct tevent_timer *te,
2512 : struct timeval current_time,
2513 : void *private_data)
2514 : {
2515 0 : struct smb2_request *req = talloc_get_type_abort(
2516 : private_data, struct smb2_request);
2517 :
2518 0 : smb2_cancel(req);
2519 0 : }
2520 :
2521 : #define BASEDIR_INR BASEDIR "_INR"
2522 :
2523 2 : static bool torture_smb2_inotify_rename(struct torture_context *torture,
2524 : struct smb2_tree *tree1,
2525 : struct smb2_tree *tree2)
2526 : {
2527 : NTSTATUS status;
2528 : struct smb2_notify notify;
2529 2 : struct notify_changes change1 = {0};
2530 2 : struct notify_changes change2 = {0};
2531 : struct smb2_create create;
2532 : union smb_setfileinfo sinfo;
2533 2 : struct smb2_handle h1 = {{0}};
2534 2 : struct smb2_handle h2 = {{0}};
2535 : struct smb2_request *req;
2536 2 : struct tevent_timer *te = NULL;
2537 2 : bool ok = false;
2538 :
2539 2 : smb2_deltree(tree1, BASEDIR_INR);
2540 :
2541 2 : torture_comment(torture, "Testing change notify of a rename with inotify\n");
2542 :
2543 2 : status = torture_smb2_testdir(tree1, BASEDIR_INR, &h1);
2544 2 : torture_assert_ntstatus_ok_goto(torture, status, ok, done, "torture_smb2_testdir failed");
2545 :
2546 2 : ZERO_STRUCT(create);
2547 2 : create.in.desired_access = SEC_RIGHTS_FILE_READ |
2548 : SEC_RIGHTS_FILE_WRITE|
2549 : SEC_RIGHTS_FILE_ALL;
2550 2 : create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
2551 2 : create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2552 2 : create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
2553 : NTCREATEX_SHARE_ACCESS_WRITE |
2554 : NTCREATEX_SHARE_ACCESS_DELETE;
2555 2 : create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
2556 2 : create.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
2557 2 : create.in.fname = BASEDIR_INR "\\subdir-name";
2558 :
2559 2 : status = smb2_create(tree2, torture, &create);
2560 2 : torture_assert_ntstatus_ok_goto(torture, status, ok, done, "smb2_create failed\n");
2561 2 : h2 = create.out.file.handle;
2562 :
2563 2 : ZERO_STRUCT(notify);
2564 2 : notify.level = RAW_NOTIFY_SMB2;
2565 2 : notify.in.buffer_size = 4096;
2566 2 : notify.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
2567 2 : notify.in.file.handle = h1;
2568 2 : notify.in.recursive = true;
2569 2 : req = smb2_notify_send(tree1, ¬ify);
2570 2 : torture_assert_not_null_goto(torture, req, ok, done, "smb2_notify_send failed\n");
2571 :
2572 12 : while (!NT_STATUS_EQUAL(req->status, NT_STATUS_PENDING)) {
2573 8 : if (tevent_loop_once(torture->ev) != 0) {
2574 0 : goto done;
2575 : }
2576 : }
2577 :
2578 2 : ZERO_STRUCT(sinfo);
2579 2 : sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
2580 2 : sinfo.rename_information.in.file.handle = h2;
2581 2 : sinfo.rename_information.in.new_name = BASEDIR_INR "\\subdir-name-r";
2582 :
2583 2 : status = smb2_setinfo_file(tree2, &sinfo);
2584 2 : torture_assert_ntstatus_ok_goto(torture, status, ok, done, "smb2_setinfo_file failed\n");
2585 :
2586 2 : smb2_util_close(tree2, h2);
2587 :
2588 2 : te = tevent_add_timer(torture->ev,
2589 : tree1,
2590 : tevent_timeval_current_ofs(1, 0),
2591 : notify_timeout,
2592 : req);
2593 2 : torture_assert_not_null_goto(torture, te, ok, done, "tevent_add_timer failed\n");
2594 :
2595 2 : status = smb2_notify_recv(req, torture, ¬ify);
2596 2 : torture_assert_ntstatus_ok_goto(torture, status, ok, done, "smb2_notify_recv failed\n");
2597 :
2598 2 : torture_assert_goto(torture, notify.out.num_changes == 1 || notify.out.num_changes == 2,
2599 : ok, done, "bad notify\n");
2600 :
2601 2 : change1 = notify.out.changes[0];
2602 2 : if (notify.out.num_changes == 2) {
2603 2 : change2 = notify.out.changes[1];
2604 : } else {
2605 : /*
2606 : * We may only get one event at a time, so check for the
2607 : * matching second event for the oldname/newname or
2608 : * removed/added pair.
2609 : */
2610 0 : ZERO_STRUCT(notify);
2611 0 : notify.level = RAW_NOTIFY_SMB2;
2612 0 : notify.in.buffer_size = 4096;
2613 0 : notify.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
2614 0 : notify.in.file.handle = h1;
2615 0 : notify.in.recursive = true;
2616 0 : req = smb2_notify_send(tree1, ¬ify);
2617 0 : torture_assert_not_null_goto(torture, req, ok, done, "smb2_notify_send failed\n");
2618 :
2619 0 : status = smb2_notify_recv(req, torture, ¬ify);
2620 0 : torture_assert_ntstatus_ok_goto(torture, status, ok, done, "smb2_notify_recv failed\n");
2621 :
2622 0 : torture_assert_goto(torture, notify.out.num_changes == 1, ok, done,
2623 : "bad notify\n");
2624 :
2625 0 : change2 = notify.out.changes[0];
2626 : }
2627 :
2628 2 : if ((change1.action != NOTIFY_ACTION_OLD_NAME) &&
2629 0 : (change1.action != NOTIFY_ACTION_REMOVED))
2630 : {
2631 0 : torture_fail_goto(torture, done, "bad change notification\n");
2632 : }
2633 2 : torture_assert_str_equal_goto(torture, change1.name.s, "subdir-name",
2634 : ok, done, "bad change notification\n");
2635 :
2636 2 : if ((change2.action != NOTIFY_ACTION_NEW_NAME) &&
2637 0 : (change2.action != NOTIFY_ACTION_ADDED))
2638 : {
2639 0 : torture_fail_goto(torture, done, "bad change notification\n");
2640 : }
2641 2 : torture_assert_str_equal_goto(torture, change2.name.s, "subdir-name-r",
2642 : ok, done, "bad change notification\n");
2643 :
2644 2 : ok = true;
2645 2 : done:
2646 2 : if (!smb2_util_handle_empty(h1)) {
2647 2 : smb2_util_close(tree1, h1);
2648 : }
2649 2 : if (!smb2_util_handle_empty(h2)) {
2650 2 : smb2_util_close(tree2, h2);
2651 : }
2652 :
2653 2 : smb2_deltree(tree1, BASEDIR_INR);
2654 2 : return ok;
2655 : }
2656 :
2657 : /*
2658 : Test asking for a change notify on a handle without permissions.
2659 : */
2660 :
2661 : #define BASEDIR_HPERM BASEDIR "_HPERM"
2662 :
2663 2 : static bool torture_smb2_notify_handle_permissions(
2664 : struct torture_context *torture,
2665 : struct smb2_tree *tree)
2666 : {
2667 2 : bool ret = true;
2668 : NTSTATUS status;
2669 : union smb_notify notify;
2670 : union smb_open io;
2671 2 : struct smb2_handle h1 = {{0}};
2672 : struct smb2_request *req;
2673 :
2674 2 : smb2_deltree(tree, BASEDIR_HPERM);
2675 2 : smb2_util_rmdir(tree, BASEDIR_HPERM);
2676 :
2677 2 : torture_comment(torture,
2678 : "TESTING CHANGE NOTIFY "
2679 : "ON A HANDLE WITHOUT PERMISSIONS\n");
2680 :
2681 : /*
2682 : get a handle on the directory
2683 : */
2684 2 : ZERO_STRUCT(io.smb2);
2685 2 : io.generic.level = RAW_OPEN_SMB2;
2686 2 : io.smb2.in.create_flags = 0;
2687 2 : io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
2688 2 : io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
2689 2 : io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2690 2 : io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
2691 : NTCREATEX_SHARE_ACCESS_WRITE;
2692 2 : io.smb2.in.alloc_size = 0;
2693 2 : io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
2694 2 : io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
2695 2 : io.smb2.in.security_flags = 0;
2696 2 : io.smb2.in.fname = BASEDIR_HPERM;
2697 :
2698 2 : status = smb2_create(tree, torture, &io.smb2);
2699 2 : CHECK_STATUS(status, NT_STATUS_OK);
2700 2 : h1 = io.smb2.out.file.handle;
2701 :
2702 : /* ask for a change notify,
2703 : on file or directory name changes */
2704 2 : ZERO_STRUCT(notify.smb2);
2705 2 : notify.smb2.level = RAW_NOTIFY_SMB2;
2706 2 : notify.smb2.in.buffer_size = 1000;
2707 2 : notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
2708 2 : notify.smb2.in.file.handle = h1;
2709 2 : notify.smb2.in.recursive = true;
2710 :
2711 2 : req = smb2_notify_send(tree, ¬ify.smb2);
2712 2 : torture_assert_goto(torture,
2713 : req != NULL,
2714 : ret,
2715 : done,
2716 : "smb2_notify_send failed\n");
2717 :
2718 : /*
2719 : * Cancel it, we don't really want to wait.
2720 : */
2721 2 : smb2_cancel(req);
2722 2 : status = smb2_notify_recv(req, torture, ¬ify.smb2);
2723 : /* Handle h1 doesn't have permissions for ChangeNotify. */
2724 2 : CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
2725 :
2726 4 : done:
2727 2 : if (!smb2_util_handle_empty(h1)) {
2728 2 : smb2_util_close(tree, h1);
2729 : }
2730 2 : smb2_deltree(tree, BASEDIR_HPERM);
2731 2 : return ret;
2732 : }
2733 :
2734 : /*
2735 : basic testing of SMB2 change notify
2736 : */
2737 2355 : struct torture_suite *torture_smb2_notify_init(TALLOC_CTX *ctx)
2738 : {
2739 2355 : struct torture_suite *suite = torture_suite_create(ctx, "notify");
2740 :
2741 2355 : torture_suite_add_1smb2_test(suite, "valid-req", test_valid_request);
2742 2355 : torture_suite_add_1smb2_test(suite, "tcon", torture_smb2_notify_tcon);
2743 2355 : torture_suite_add_2smb2_test(suite, "dir", torture_smb2_notify_dir);
2744 2355 : torture_suite_add_2smb2_test(suite, "mask", torture_smb2_notify_mask);
2745 2355 : torture_suite_add_1smb2_test(suite, "tdis", torture_smb2_notify_tree_disconnect);
2746 2355 : torture_suite_add_1smb2_test(suite, "tdis1", torture_smb2_notify_tree_disconnect_1);
2747 2355 : torture_suite_add_2smb2_test(suite, "mask-change", torture_smb2_notify_mask_change);
2748 2355 : torture_suite_add_1smb2_test(suite, "close", torture_smb2_notify_close);
2749 2355 : torture_suite_add_1smb2_test(suite, "logoff", torture_smb2_notify_ulogoff);
2750 2355 : torture_suite_add_1smb2_test(suite, "session-reconnect", torture_smb2_notify_session_reconnect);
2751 2355 : torture_suite_add_2smb2_test(suite, "invalid-reauth", torture_smb2_notify_invalid_reauth);
2752 2355 : torture_suite_add_1smb2_test(suite, "tree", torture_smb2_notify_tree);
2753 2355 : torture_suite_add_2smb2_test(suite, "basedir", torture_smb2_notify_basedir);
2754 2355 : torture_suite_add_2smb2_test(suite, "double", torture_smb2_notify_double);
2755 2355 : torture_suite_add_1smb2_test(suite, "file", torture_smb2_notify_file);
2756 2355 : torture_suite_add_1smb2_test(suite, "tcp", torture_smb2_notify_tcp_disconnect);
2757 2355 : torture_suite_add_2smb2_test(suite, "rec", torture_smb2_notify_recursive);
2758 2355 : torture_suite_add_1smb2_test(suite, "overflow", torture_smb2_notify_overflow);
2759 2355 : torture_suite_add_1smb2_test(suite, "rmdir1",
2760 : torture_smb2_notify_rmdir1);
2761 2355 : torture_suite_add_1smb2_test(suite, "rmdir2",
2762 : torture_smb2_notify_rmdir2);
2763 2355 : torture_suite_add_2smb2_test(suite, "rmdir3",
2764 : torture_smb2_notify_rmdir3);
2765 2355 : torture_suite_add_2smb2_test(suite, "rmdir4",
2766 : torture_smb2_notify_rmdir4);
2767 2355 : torture_suite_add_1smb2_test(suite,
2768 : "handle-permissions",
2769 : torture_smb2_notify_handle_permissions);
2770 :
2771 2355 : suite->description = talloc_strdup(suite, "SMB2-NOTIFY tests");
2772 :
2773 2355 : return suite;
2774 : }
2775 :
2776 : /*
2777 : basic testing of SMB2 change notify
2778 : */
2779 2355 : struct torture_suite *torture_smb2_notify_inotify_init(TALLOC_CTX *ctx)
2780 : {
2781 2355 : struct torture_suite *suite = torture_suite_create(ctx, "notify-inotify");
2782 :
2783 2355 : suite->description = talloc_strdup(suite, "SMB2-NOTIFY tests that use inotify");
2784 :
2785 2355 : torture_suite_add_2smb2_test(suite, "inotify-rename", torture_smb2_inotify_rename);
2786 :
2787 2355 : return suite;
2788 : }
|