ctdb-scripts: Support storing statd-callout state in cluster filesystem
[samba4-gss.git] / source4 / torture / smb2 / compound.c
blob622283a7291110deffb8b91cc8f0aeb17a59365c
1 /*
2 Unix SMB/CIFS implementation.
4 test suite for SMB2 compounded requests
6 Copyright (C) Stefan Metzmacher 2009
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "tevent.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "libcli/security/security.h"
29 #include "librpc/gen_ndr/ndr_security.h"
30 #include "../libcli/smb/smbXcli_base.h"
31 #include "lease_break_handler.h"
33 #define CHECK_STATUS(status, correct) do { \
34 if (!NT_STATUS_EQUAL(status, correct)) { \
35 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
36 nt_errstr(status), nt_errstr(correct)); \
37 ret = false; \
38 goto done; \
39 }} while (0)
41 #define CHECK_VAL(v, correct) do { \
42 if ((v) != (correct)) { \
43 torture_result(tctx, TORTURE_FAIL, \
44 "(%s) Incorrect value %s=%d - should be %d\n", \
45 __location__, #v, (int)v, (int)correct); \
46 ret = false; \
47 goto done; \
48 }} while (0)
50 #define CHECK_LEASE(__io, __state, __oplevel, __key, __flags) \
51 do { \
52 CHECK_VAL((__io)->out.lease_response.lease_version, 1); \
53 if (__oplevel) { \
54 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
55 CHECK_VAL((__io)->out.lease_response.lease_key.data[0], (__key)); \
56 CHECK_VAL((__io)->out.lease_response.lease_key.data[1], ~(__key)); \
57 CHECK_VAL((__io)->out.lease_response.lease_state, smb2_util_lease_state(__state)); \
58 } else { \
59 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
60 CHECK_VAL((__io)->out.lease_response.lease_key.data[0], 0); \
61 CHECK_VAL((__io)->out.lease_response.lease_key.data[1], 0); \
62 CHECK_VAL((__io)->out.lease_response.lease_state, 0); \
63 } \
65 CHECK_VAL((__io)->out.lease_response.lease_flags, (__flags)); \
66 CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \
67 CHECK_VAL((__io)->out.lease_response.lease_epoch, 0); \
68 } while(0)
70 #define CHECK_LEASE_V2(__io, __state, __oplevel, __key, __flags, __parent, __epoch) \
71 do { \
72 CHECK_VAL((__io)->out.lease_response_v2.lease_version, 2); \
73 if (__oplevel) { \
74 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
75 CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], (__key)); \
76 CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], ~(__key)); \
77 CHECK_VAL((__io)->out.lease_response_v2.lease_state, smb2_util_lease_state(__state)); \
78 } else { \
79 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
80 CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], 0); \
81 CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], 0); \
82 CHECK_VAL((__io)->out.lease_response_v2.lease_state, 0); \
83 } \
85 CHECK_VAL((__io)->out.lease_response_v2.lease_flags, __flags); \
86 if (__flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET) { \
87 CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[0], (__parent)); \
88 CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[1], ~(__parent)); \
89 } \
90 CHECK_VAL((__io)->out.lease_response_v2.lease_duration, 0); \
91 CHECK_VAL((__io)->out.lease_response_v2.lease_epoch, (__epoch)); \
92 } while(0)
94 #define WAIT_FOR_ASYNC_RESPONSE(req) \
95 while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { \
96 if (tevent_loop_once(tctx->ev) != 0) { \
97 break; \
98 } \
101 static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull;
102 static const uint64_t LEASE2 = 0xDEADBEEFFEEDBEADull;
104 static struct {
105 struct smb2_handle handle;
106 uint8_t level;
107 struct smb2_break br;
108 int count;
109 int failures;
110 NTSTATUS failure_status;
111 } break_info;
113 static void torture_oplock_break_callback(struct smb2_request *req)
115 NTSTATUS status;
116 struct smb2_break br;
118 ZERO_STRUCT(br);
119 status = smb2_break_recv(req, &break_info.br);
120 if (!NT_STATUS_IS_OK(status)) {
121 break_info.failures++;
122 break_info.failure_status = status;
125 return;
128 /* A general oplock break notification handler. This should be used when a
129 * test expects to break from batch or exclusive to a lower level. */
130 static bool torture_oplock_handler(struct smb2_transport *transport,
131 const struct smb2_handle *handle,
132 uint8_t level,
133 void *private_data)
135 struct smb2_tree *tree = private_data;
136 const char *name;
137 struct smb2_request *req;
138 ZERO_STRUCT(break_info.br);
140 break_info.handle = *handle;
141 break_info.level = level;
142 break_info.count++;
144 switch (level) {
145 case SMB2_OPLOCK_LEVEL_II:
146 name = "level II";
147 break;
148 case SMB2_OPLOCK_LEVEL_NONE:
149 name = "none";
150 break;
151 default:
152 name = "unknown";
153 break_info.failures++;
155 printf("Acking to %s [0x%02X] in oplock handler\n", name, level);
157 break_info.br.in.file.handle = *handle;
158 break_info.br.in.oplock_level = level;
159 break_info.br.in.reserved = 0;
160 break_info.br.in.reserved2 = 0;
162 req = smb2_break_send(tree, &break_info.br);
163 req->async.fn = torture_oplock_break_callback;
164 req->async.private_data = NULL;
165 return true;
168 static bool test_compound_break(struct torture_context *tctx,
169 struct smb2_tree *tree)
171 const char *fname1 = "some-file.pptx";
172 NTSTATUS status;
173 bool ret = true;
174 union smb_open io1;
175 struct smb2_create io2;
176 struct smb2_getinfo gf;
177 struct smb2_request *req[2];
178 struct smb2_handle h1;
179 struct smb2_handle h;
181 tree->session->transport->oplock.handler = torture_oplock_handler;
182 tree->session->transport->oplock.private_data = tree;
184 ZERO_STRUCT(break_info);
187 base ntcreatex parms
189 ZERO_STRUCT(io1.smb2);
190 io1.generic.level = RAW_OPEN_SMB2;
191 io1.smb2.in.desired_access = (SEC_STD_SYNCHRONIZE|
192 SEC_STD_READ_CONTROL|
193 SEC_FILE_READ_ATTRIBUTE|
194 SEC_FILE_READ_EA|
195 SEC_FILE_READ_DATA);
196 io1.smb2.in.alloc_size = 0;
197 io1.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
198 io1.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
199 NTCREATEX_SHARE_ACCESS_WRITE|
200 NTCREATEX_SHARE_ACCESS_DELETE;
201 io1.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
202 io1.smb2.in.create_options = 0;
203 io1.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
204 io1.smb2.in.security_flags = 0;
205 io1.smb2.in.fname = fname1;
207 torture_comment(tctx, "TEST2: open a file with an batch "
208 "oplock (share mode: all)\n");
209 io1.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
211 status = smb2_create(tree, tctx, &(io1.smb2));
212 torture_assert_ntstatus_ok(tctx, status, "Error opening the file");
214 h1 = io1.smb2.out.file.handle;
216 torture_comment(tctx, "TEST2: Opening second time with compound\n");
218 ZERO_STRUCT(io2);
220 io2.in.desired_access = (SEC_STD_SYNCHRONIZE|
221 SEC_FILE_READ_ATTRIBUTE|
222 SEC_FILE_READ_EA);
223 io2.in.alloc_size = 0;
224 io2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
225 io2.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
226 NTCREATEX_SHARE_ACCESS_WRITE|
227 NTCREATEX_SHARE_ACCESS_DELETE;
228 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
229 io2.in.create_options = 0;
230 io2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
231 io2.in.security_flags = 0;
232 io2.in.fname = fname1;
233 io2.in.oplock_level = 0;
235 smb2_transport_compound_start(tree->session->transport, 2);
237 req[0] = smb2_create_send(tree, &io2);
239 smb2_transport_compound_set_related(tree->session->transport, true);
241 h.data[0] = UINT64_MAX;
242 h.data[1] = UINT64_MAX;
244 ZERO_STRUCT(gf);
245 gf.in.file.handle = h;
246 gf.in.info_type = SMB2_0_INFO_FILE;
247 gf.in.info_class = 0x16;
248 gf.in.output_buffer_length = 0x1000;
249 gf.in.input_buffer = data_blob_null;
251 req[1] = smb2_getinfo_send(tree, &gf);
253 status = smb2_create_recv(req[0], tree, &io2);
254 CHECK_STATUS(status, NT_STATUS_OK);
256 status = smb2_getinfo_recv(req[1], tree, &gf);
257 CHECK_STATUS(status, NT_STATUS_OK);
259 done:
261 smb2_util_close(tree, h1);
262 smb2_util_unlink(tree, fname1);
263 return ret;
266 static bool test_compound_related1(struct torture_context *tctx,
267 struct smb2_tree *tree)
269 struct smb2_handle hd;
270 struct smb2_create cr;
271 NTSTATUS status;
272 const char *fname = "compound_related1.dat";
273 struct smb2_close cl;
274 bool ret = true;
275 struct smb2_request *req[2];
276 struct smbXcli_tcon *saved_tcon = tree->smbXcli;
277 struct smbXcli_session *saved_session = tree->session->smbXcli;
279 smb2_transport_credits_ask_num(tree->session->transport, 2);
281 smb2_util_unlink(tree, fname);
283 smb2_transport_credits_ask_num(tree->session->transport, 1);
285 ZERO_STRUCT(cr);
286 cr.in.security_flags = 0x00;
287 cr.in.oplock_level = 0;
288 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
289 cr.in.create_flags = 0x00000000;
290 cr.in.reserved = 0x00000000;
291 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
292 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
293 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
294 NTCREATEX_SHARE_ACCESS_WRITE |
295 NTCREATEX_SHARE_ACCESS_DELETE;
296 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
297 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
298 NTCREATEX_OPTIONS_ASYNC_ALERT |
299 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
300 0x00200000;
301 cr.in.fname = fname;
303 smb2_transport_compound_start(tree->session->transport, 2);
305 req[0] = smb2_create_send(tree, &cr);
307 smb2_transport_compound_set_related(tree->session->transport, true);
309 hd.data[0] = UINT64_MAX;
310 hd.data[1] = UINT64_MAX;
312 ZERO_STRUCT(cl);
313 cl.in.file.handle = hd;
315 tree->smbXcli = smbXcli_tcon_create(tree);
316 smb2cli_tcon_set_values(tree->smbXcli,
317 NULL, /* session */
318 0xFFFFFFFF, /* tcon_id */
319 0, /* type */
320 0, /* flags */
321 0, /* capabilities */
322 0 /* maximal_access */);
324 tree->session->smbXcli = smbXcli_session_shallow_copy(tree->session,
325 tree->session->smbXcli);
326 smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0);
328 req[1] = smb2_close_send(tree, &cl);
330 status = smb2_create_recv(req[0], tree, &cr);
331 CHECK_STATUS(status, NT_STATUS_OK);
332 status = smb2_close_recv(req[1], &cl);
333 CHECK_STATUS(status, NT_STATUS_OK);
335 TALLOC_FREE(tree->smbXcli);
336 tree->smbXcli = saved_tcon;
337 TALLOC_FREE(tree->session->smbXcli);
338 tree->session->smbXcli = saved_session;
340 smb2_util_unlink(tree, fname);
341 done:
342 return ret;
345 static bool test_compound_related2(struct torture_context *tctx,
346 struct smb2_tree *tree)
348 struct smb2_handle hd;
349 struct smb2_create cr;
350 NTSTATUS status;
351 const char *fname = "compound_related2.dat";
352 struct smb2_close cl;
353 bool ret = true;
354 struct smb2_request *req[5];
355 struct smbXcli_tcon *saved_tcon = tree->smbXcli;
356 struct smbXcli_session *saved_session = tree->session->smbXcli;
358 smb2_transport_credits_ask_num(tree->session->transport, 5);
360 smb2_util_unlink(tree, fname);
362 smb2_transport_credits_ask_num(tree->session->transport, 1);
364 ZERO_STRUCT(cr);
365 cr.in.security_flags = 0x00;
366 cr.in.oplock_level = 0;
367 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
368 cr.in.create_flags = 0x00000000;
369 cr.in.reserved = 0x00000000;
370 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
371 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
372 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
373 NTCREATEX_SHARE_ACCESS_WRITE |
374 NTCREATEX_SHARE_ACCESS_DELETE;
375 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
376 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
377 NTCREATEX_OPTIONS_ASYNC_ALERT |
378 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
379 0x00200000;
380 cr.in.fname = fname;
382 smb2_transport_compound_start(tree->session->transport, 5);
384 req[0] = smb2_create_send(tree, &cr);
386 hd.data[0] = UINT64_MAX;
387 hd.data[1] = UINT64_MAX;
389 smb2_transport_compound_set_related(tree->session->transport, true);
391 ZERO_STRUCT(cl);
392 cl.in.file.handle = hd;
394 tree->smbXcli = smbXcli_tcon_create(tree);
395 smb2cli_tcon_set_values(tree->smbXcli,
396 NULL, /* session */
397 0xFFFFFFFF, /* tcon_id */
398 0, /* type */
399 0, /* flags */
400 0, /* capabilities */
401 0 /* maximal_access */);
403 tree->session->smbXcli = smbXcli_session_shallow_copy(tree->session,
404 tree->session->smbXcli);
405 smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0);
407 req[1] = smb2_close_send(tree, &cl);
408 req[2] = smb2_close_send(tree, &cl);
409 req[3] = smb2_close_send(tree, &cl);
410 req[4] = smb2_close_send(tree, &cl);
412 status = smb2_create_recv(req[0], tree, &cr);
413 CHECK_STATUS(status, NT_STATUS_OK);
414 status = smb2_close_recv(req[1], &cl);
415 CHECK_STATUS(status, NT_STATUS_OK);
416 status = smb2_close_recv(req[2], &cl);
417 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
418 status = smb2_close_recv(req[3], &cl);
419 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
420 status = smb2_close_recv(req[4], &cl);
421 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
423 TALLOC_FREE(tree->smbXcli);
424 tree->smbXcli = saved_tcon;
425 TALLOC_FREE(tree->session->smbXcli);
426 tree->session->smbXcli = saved_session;
428 smb2_util_unlink(tree, fname);
429 done:
430 return ret;
433 static bool test_compound_related3(struct torture_context *tctx,
434 struct smb2_tree *tree)
436 struct smb2_handle hd;
437 struct smb2_ioctl io;
438 struct smb2_create cr;
439 struct smb2_close cl;
440 const char *fname = "compound_related3.dat";
441 struct smb2_request *req[3];
442 NTSTATUS status;
443 bool ret = false;
445 smb2_util_unlink(tree, fname);
447 ZERO_STRUCT(cr);
448 cr.in.security_flags = 0x00;
449 cr.in.oplock_level = 0;
450 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
451 cr.in.create_flags = 0x00000000;
452 cr.in.reserved = 0x00000000;
453 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
454 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
455 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
456 NTCREATEX_SHARE_ACCESS_WRITE |
457 NTCREATEX_SHARE_ACCESS_DELETE;
458 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
459 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
460 NTCREATEX_OPTIONS_ASYNC_ALERT |
461 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
462 0x00200000;
463 cr.in.fname = fname;
465 smb2_transport_compound_start(tree->session->transport, 3);
467 req[0] = smb2_create_send(tree, &cr);
469 hd.data[0] = UINT64_MAX;
470 hd.data[1] = UINT64_MAX;
472 smb2_transport_compound_set_related(tree->session->transport, true);
474 ZERO_STRUCT(io);
475 io.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID;
476 io.in.file.handle = hd;
477 io.in.reserved2 = 0;
478 io.in.max_output_response = 64;
479 io.in.flags = 1;
481 req[1] = smb2_ioctl_send(tree, &io);
483 ZERO_STRUCT(cl);
484 cl.in.file.handle = hd;
486 req[2] = smb2_close_send(tree, &cl);
488 status = smb2_create_recv(req[0], tree, &cr);
489 CHECK_STATUS(status, NT_STATUS_OK);
490 status = smb2_ioctl_recv(req[1], tree, &io);
491 CHECK_STATUS(status, NT_STATUS_OK);
492 status = smb2_close_recv(req[2], &cl);
493 CHECK_STATUS(status, NT_STATUS_OK);
495 status = smb2_util_unlink(tree, fname);
496 CHECK_STATUS(status, NT_STATUS_OK);
498 ret = true;
499 done:
500 return ret;
503 static bool test_compound_related4(struct torture_context *tctx,
504 struct smb2_tree *tree)
506 const char *fname = "compound_related4.dat";
507 struct security_descriptor *sd = NULL;
508 struct smb2_handle hd;
509 struct smb2_create cr;
510 union smb_setfileinfo set;
511 struct smb2_ioctl io;
512 struct smb2_close cl;
513 struct smb2_request *req[4];
514 NTSTATUS status;
515 bool ret = true;
517 smb2_util_unlink(tree, fname);
519 ZERO_STRUCT(cr);
520 cr.level = RAW_OPEN_SMB2;
521 cr.in.create_flags = 0;
522 cr.in.desired_access = SEC_STD_READ_CONTROL |
523 SEC_STD_WRITE_DAC |
524 SEC_STD_WRITE_OWNER;
525 cr.in.create_options = 0;
526 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
527 cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
528 NTCREATEX_SHARE_ACCESS_READ |
529 NTCREATEX_SHARE_ACCESS_WRITE;
530 cr.in.alloc_size = 0;
531 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
532 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
533 cr.in.security_flags = 0;
534 cr.in.fname = fname;
536 status = smb2_create(tree, tctx, &cr);
537 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n");
539 hd = cr.out.file.handle;
540 torture_comment(tctx, "set a sec desc allowing no write by CREATOR_OWNER\n");
542 sd = security_descriptor_dacl_create(tctx,
543 0, NULL, NULL,
544 SID_CREATOR_OWNER,
545 SEC_ACE_TYPE_ACCESS_ALLOWED,
546 SEC_RIGHTS_FILE_READ | SEC_STD_ALL,
548 NULL);
549 torture_assert_not_null_goto(tctx, sd, ret, done,
550 "security_descriptor_dacl_create failed\n");
552 set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
553 set.set_secdesc.in.file.handle = hd;
554 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
555 set.set_secdesc.in.sd = sd;
557 status = smb2_setinfo_file(tree, &set);
558 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
559 "smb2_setinfo_file failed\n");
561 torture_comment(tctx, "try open for write\n");
562 cr.in.desired_access = SEC_FILE_WRITE_DATA;
563 smb2_transport_compound_start(tree->session->transport, 4);
565 req[0] = smb2_create_send(tree, &cr);
566 torture_assert_not_null_goto(tctx, req[0], ret, done,
567 "smb2_create_send failed\n");
569 hd.data[0] = UINT64_MAX;
570 hd.data[1] = UINT64_MAX;
572 smb2_transport_compound_set_related(tree->session->transport, true);
573 ZERO_STRUCT(io);
574 io.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID;
575 io.in.file.handle = hd;
576 io.in.flags = 1;
578 req[1] = smb2_ioctl_send(tree, &io);
579 torture_assert_not_null_goto(tctx, req[1], ret, done,
580 "smb2_ioctl_send failed\n");
582 ZERO_STRUCT(cl);
583 cl.in.file.handle = hd;
585 req[2] = smb2_close_send(tree, &cl);
586 torture_assert_not_null_goto(tctx, req[2], ret, done,
587 "smb2_create_send failed\n");
589 set.set_secdesc.in.file.handle = hd;
591 req[3] = smb2_setinfo_file_send(tree, &set);
592 torture_assert_not_null_goto(tctx, req[3], ret, done,
593 "smb2_create_send failed\n");
595 status = smb2_create_recv(req[0], tree, &cr);
596 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
597 ret, done,
598 "smb2_create_recv failed\n");
600 status = smb2_ioctl_recv(req[1], tree, &io);
601 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
602 ret, done,
603 "smb2_ioctl_recv failed\n");
605 status = smb2_close_recv(req[2], &cl);
606 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
607 ret, done,
608 "smb2_close_recv failed\n");
610 status = smb2_setinfo_recv(req[3]);
611 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
612 ret, done,
613 "smb2_setinfo_recv failed\n");
615 done:
616 smb2_util_unlink(tree, fname);
617 smb2_tdis(tree);
618 smb2_logoff(tree->session);
619 return ret;
622 static bool test_compound_related5(struct torture_context *tctx,
623 struct smb2_tree *tree)
625 struct smb2_handle hd;
626 struct smb2_ioctl io;
627 struct smb2_close cl;
628 struct smb2_request *req[2];
629 NTSTATUS status;
630 bool ret = false;
632 smb2_transport_compound_start(tree->session->transport, 2);
634 hd.data[0] = UINT64_MAX;
635 hd.data[1] = UINT64_MAX;
637 ZERO_STRUCT(io);
638 io.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID;
639 io.in.file.handle = hd;
640 io.in.flags = 1;
642 req[0] = smb2_ioctl_send(tree, &io);
643 torture_assert_not_null_goto(tctx, req[0], ret, done,
644 "smb2_ioctl_send failed\n");
646 smb2_transport_compound_set_related(tree->session->transport, true);
648 ZERO_STRUCT(cl);
649 cl.in.file.handle = hd;
651 req[1] = smb2_close_send(tree, &cl);
652 torture_assert_not_null_goto(tctx, req[1], ret, done,
653 "smb2_create_send failed\n");
655 status = smb2_ioctl_recv(req[0], tree, &io);
656 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_FILE_CLOSED,
657 ret, done,
658 "smb2_ioctl_recv failed\n");
660 status = smb2_close_recv(req[1], &cl);
661 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_FILE_CLOSED,
662 ret, done,
663 "smb2_close_recv failed\n");
665 ret = true;
667 done:
668 smb2_tdis(tree);
669 smb2_logoff(tree->session);
670 return ret;
673 static bool test_compound_related6(struct torture_context *tctx,
674 struct smb2_tree *tree)
676 struct smb2_handle hd;
677 struct smb2_create cr;
678 struct smb2_read rd;
679 struct smb2_write wr;
680 struct smb2_close cl;
681 NTSTATUS status;
682 const char *fname = "compound_related6.dat";
683 struct smb2_request *req[5];
684 uint8_t buf[64];
685 bool ret = true;
687 smb2_util_unlink(tree, fname);
689 ZERO_STRUCT(cr);
690 cr.level = RAW_OPEN_SMB2;
691 cr.in.create_flags = 0;
692 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
693 cr.in.create_options = 0;
694 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
695 cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
696 NTCREATEX_SHARE_ACCESS_READ |
697 NTCREATEX_SHARE_ACCESS_WRITE;
698 cr.in.alloc_size = 0;
699 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
700 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
701 cr.in.security_flags = 0;
702 cr.in.fname = fname;
704 status = smb2_create(tree, tctx, &cr);
705 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
706 "smb2_create failed\n");
708 hd = cr.out.file.handle;
710 ZERO_STRUCT(buf);
711 status = smb2_util_write(tree, hd, buf, 0, ARRAY_SIZE(buf));
712 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
713 "smb2_util_write failed\n");
715 torture_comment(tctx, "try open for read\n");
716 cr.in.desired_access = SEC_FILE_READ_DATA;
717 smb2_transport_compound_start(tree->session->transport, 5);
719 req[0] = smb2_create_send(tree, &cr);
720 torture_assert_not_null_goto(tctx, req[0], ret, done,
721 "smb2_create_send failed\n");
723 hd.data[0] = UINT64_MAX;
724 hd.data[1] = UINT64_MAX;
726 smb2_transport_compound_set_related(tree->session->transport, true);
728 ZERO_STRUCT(rd);
729 rd.in.file.handle = hd;
730 rd.in.length = 1;
731 rd.in.offset = 0;
733 req[1] = smb2_read_send(tree, &rd);
734 torture_assert_not_null_goto(tctx, req[1], ret, done,
735 "smb2_read_send failed\n");
737 ZERO_STRUCT(wr);
738 wr.in.file.handle = hd;
739 wr.in.offset = 0;
740 wr.in.data = data_blob_talloc(tctx, NULL, 64);
742 req[2] = smb2_write_send(tree, &wr);
743 torture_assert_not_null_goto(tctx, req[2], ret, done,
744 "smb2_write_send failed\n");
746 ZERO_STRUCT(rd);
747 rd.in.file.handle = hd;
748 rd.in.length = 1;
749 rd.in.offset = 0;
751 req[3] = smb2_read_send(tree, &rd);
752 torture_assert_not_null_goto(tctx, req[3], ret, done,
753 "smb2_read_send failed\n");
755 ZERO_STRUCT(cl);
756 cl.in.file.handle = hd;
758 req[4] = smb2_close_send(tree, &cl);
759 torture_assert_not_null_goto(tctx, req[4], ret, done,
760 "smb2_close_send failed\n");
762 status = smb2_create_recv(req[0], tree, &cr);
763 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
764 "smb2_create_recv failed\n");
766 status = smb2_read_recv(req[1], tree, &rd);
767 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
768 "smb2_read_recv failed\n");
770 status = smb2_write_recv(req[2], &wr);
771 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
772 ret, done,
773 "smb2_write_recv failed\n");
775 status = smb2_read_recv(req[3], tree, &rd);
776 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
777 "smb2_read_recv failed\n");
779 status = smb2_close_recv(req[4], &cl);
780 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
781 "smb2_close_recv failed\n");
783 done:
784 smb2_util_unlink(tree, fname);
785 smb2_tdis(tree);
786 smb2_logoff(tree->session);
787 return ret;
790 static bool test_compound_related7(struct torture_context *tctx,
791 struct smb2_tree *tree)
793 const char *fname = "compound_related4.dat";
794 struct security_descriptor *sd = NULL;
795 struct smb2_handle hd;
796 struct smb2_create cr;
797 union smb_setfileinfo set;
798 struct smb2_notify nt;
799 struct smb2_close cl;
800 NTSTATUS status;
801 struct smb2_request *req[4];
802 bool ret = true;
804 smb2_util_unlink(tree, fname);
806 ZERO_STRUCT(cr);
807 cr.level = RAW_OPEN_SMB2;
808 cr.in.create_flags = 0;
809 cr.in.desired_access = SEC_STD_READ_CONTROL |
810 SEC_STD_WRITE_DAC |
811 SEC_STD_WRITE_OWNER;
812 cr.in.create_options = 0;
813 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
814 cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
815 NTCREATEX_SHARE_ACCESS_READ |
816 NTCREATEX_SHARE_ACCESS_WRITE;
817 cr.in.alloc_size = 0;
818 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
819 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
820 cr.in.security_flags = 0;
821 cr.in.fname = fname;
823 status = smb2_create(tree, tctx, &cr);
824 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
825 "smb2_create failed\n");
827 hd = cr.out.file.handle;
828 torture_comment(tctx, "set a sec desc allowing no write by CREATOR_OWNER\n");
829 sd = security_descriptor_dacl_create(tctx,
830 0, NULL, NULL,
831 SID_CREATOR_OWNER,
832 SEC_ACE_TYPE_ACCESS_ALLOWED,
833 SEC_RIGHTS_FILE_READ | SEC_STD_ALL,
835 NULL);
836 torture_assert_not_null_goto(tctx, sd, ret, done,
837 "security_descriptor_dacl_create failed\n");
839 set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
840 set.set_secdesc.in.file.handle = hd;
841 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
842 set.set_secdesc.in.sd = sd;
844 status = smb2_setinfo_file(tree, &set);
845 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
846 "smb2_setinfo_file failed\n");
848 torture_comment(tctx, "try open for write\n");
849 cr.in.desired_access = SEC_FILE_WRITE_DATA;
850 smb2_transport_compound_start(tree->session->transport, 4);
852 req[0] = smb2_create_send(tree, &cr);
853 torture_assert_not_null_goto(tctx, req[0], ret, done,
854 "smb2_create_send failed\n");
856 hd.data[0] = UINT64_MAX;
857 hd.data[1] = UINT64_MAX;
859 smb2_transport_compound_set_related(tree->session->transport, true);
861 ZERO_STRUCT(nt);
862 nt.in.recursive = true;
863 nt.in.buffer_size = 0x1000;
864 nt.in.file.handle = hd;
865 nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
866 nt.in.unknown = 0x00000000;
868 req[1] = smb2_notify_send(tree, &nt);
869 torture_assert_not_null_goto(tctx, req[1], ret, done,
870 "smb2_notify_send failed\n");
872 ZERO_STRUCT(cl);
873 cl.in.file.handle = hd;
875 req[2] = smb2_close_send(tree, &cl);
876 torture_assert_not_null_goto(tctx, req[2], ret, done,
877 "smb2_close_send failed\n");
879 set.set_secdesc.in.file.handle = hd;
881 req[3] = smb2_setinfo_file_send(tree, &set);
882 torture_assert_not_null_goto(tctx, req[3], ret, done,
883 "smb2_setinfo_file_send failed\n");
885 status = smb2_create_recv(req[0], tree, &cr);
886 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
887 ret, done,
888 "smb2_create_recv failed\n");
890 status = smb2_notify_recv(req[1], tree, &nt);
891 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
892 ret, done,
893 "smb2_notify_recv failed\n");
895 status = smb2_close_recv(req[2], &cl);
896 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
897 ret, done,
898 "smb2_close_recv failed\n");
900 status = smb2_setinfo_recv(req[3]);
901 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
902 ret, done,
903 "smb2_setinfo_recv failed\n");
905 done:
906 smb2_util_unlink(tree, fname);
907 smb2_tdis(tree);
908 smb2_logoff(tree->session);
909 return ret;
912 static bool test_compound_related8(struct torture_context *tctx,
913 struct smb2_tree *tree)
915 const char *fname = "compound_related8.dat";
916 const char *fname_nonexisting = "compound_related8.dat.void";
917 struct security_descriptor *sd = NULL;
918 struct smb2_handle hd;
919 struct smb2_create cr;
920 union smb_setfileinfo set;
921 struct smb2_notify nt;
922 struct smb2_close cl;
923 NTSTATUS status;
924 struct smb2_request *req[4];
925 bool ret = true;
927 smb2_util_unlink(tree, fname);
929 ZERO_STRUCT(cr);
930 cr.level = RAW_OPEN_SMB2;
931 cr.in.create_flags = 0;
932 cr.in.desired_access = SEC_STD_READ_CONTROL |
933 SEC_STD_WRITE_DAC |
934 SEC_STD_WRITE_OWNER;
935 cr.in.create_options = 0;
936 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
937 cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
938 NTCREATEX_SHARE_ACCESS_READ |
939 NTCREATEX_SHARE_ACCESS_WRITE;
940 cr.in.alloc_size = 0;
941 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
942 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
943 cr.in.security_flags = 0;
944 cr.in.fname = fname;
946 status = smb2_create(tree, tctx, &cr);
947 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
948 "smb2_create failed\n");
950 hd = cr.out.file.handle;
952 smb2_transport_compound_start(tree->session->transport, 4);
954 torture_comment(tctx, "try open for write\n");
955 cr.in.fname = fname_nonexisting;
956 cr.in.create_disposition = NTCREATEX_DISP_OPEN;
958 req[0] = smb2_create_send(tree, &cr);
959 torture_assert_not_null_goto(tctx, req[0], ret, done,
960 "smb2_create_send failed\n");
962 hd.data[0] = UINT64_MAX;
963 hd.data[1] = UINT64_MAX;
965 smb2_transport_compound_set_related(tree->session->transport, true);
967 ZERO_STRUCT(nt);
968 nt.in.recursive = true;
969 nt.in.buffer_size = 0x1000;
970 nt.in.file.handle = hd;
971 nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
972 nt.in.unknown = 0x00000000;
974 req[1] = smb2_notify_send(tree, &nt);
975 torture_assert_not_null_goto(tctx, req[1], ret, done,
976 "smb2_notify_send failed\n");
978 ZERO_STRUCT(cl);
979 cl.in.file.handle = hd;
981 req[2] = smb2_close_send(tree, &cl);
982 torture_assert_not_null_goto(tctx, req[2], ret, done,
983 "smb2_close_send failed\n");
985 sd = security_descriptor_dacl_create(tctx,
986 0, NULL, NULL,
987 SID_CREATOR_OWNER,
988 SEC_ACE_TYPE_ACCESS_ALLOWED,
989 SEC_RIGHTS_FILE_READ | SEC_STD_ALL,
991 NULL);
992 torture_assert_not_null_goto(tctx, sd, ret, done,
993 "security_descriptor_dacl_create failed\n");
995 set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
996 set.set_secdesc.in.file.handle = hd;
997 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
998 set.set_secdesc.in.sd = sd;
1000 req[3] = smb2_setinfo_file_send(tree, &set);
1001 torture_assert_not_null_goto(tctx, req[3], ret, done,
1002 "smb2_setinfo_file_send failed\n");
1004 status = smb2_create_recv(req[0], tree, &cr);
1005 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
1006 ret, done,
1007 "smb2_create_recv failed\n");
1009 status = smb2_notify_recv(req[1], tree, &nt);
1010 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
1011 ret, done,
1012 "smb2_notify_recv failed\n");
1014 status = smb2_close_recv(req[2], &cl);
1015 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
1016 ret, done,
1017 "smb2_close_recv failed\n");
1019 status = smb2_setinfo_recv(req[3]);
1020 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
1021 ret, done,
1022 "smb2_setinfo_recv failed\n");
1024 done:
1025 smb2_util_unlink(tree, fname);
1026 smb2_tdis(tree);
1027 smb2_logoff(tree->session);
1028 return ret;
1031 static bool test_compound_related9(struct torture_context *tctx,
1032 struct smb2_tree *tree)
1034 const char *fname = "compound_related9.dat";
1035 struct security_descriptor *sd = NULL;
1036 struct smb2_handle hd;
1037 struct smb2_create cr;
1038 union smb_setfileinfo set;
1039 struct smb2_notify nt;
1040 struct smb2_close cl;
1041 NTSTATUS status;
1042 struct smb2_request *req[3];
1043 bool ret = true;
1045 smb2_util_unlink(tree, fname);
1047 ZERO_STRUCT(cr);
1048 cr.level = RAW_OPEN_SMB2;
1049 cr.in.create_flags = 0;
1050 cr.in.desired_access = SEC_STD_READ_CONTROL |
1051 SEC_STD_WRITE_DAC |
1052 SEC_STD_WRITE_OWNER;
1053 cr.in.create_options = 0;
1054 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1055 cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
1056 NTCREATEX_SHARE_ACCESS_READ |
1057 NTCREATEX_SHARE_ACCESS_WRITE;
1058 cr.in.alloc_size = 0;
1059 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1060 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
1061 cr.in.security_flags = 0;
1062 cr.in.fname = fname;
1064 status = smb2_create(tree, tctx, &cr);
1065 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1066 "smb2_create failed\n");
1068 hd = cr.out.file.handle;
1070 smb2_transport_compound_start(tree->session->transport, 3);
1071 smb2_transport_compound_set_related(tree->session->transport, true);
1073 ZERO_STRUCT(nt);
1074 nt.in.recursive = true;
1075 nt.in.buffer_size = 0x1000;
1076 nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1078 req[0] = smb2_notify_send(tree, &nt);
1079 torture_assert_not_null_goto(tctx, req[0], ret, done,
1080 "smb2_notify_send failed\n");
1082 ZERO_STRUCT(cl);
1083 cl.in.file.handle = hd;
1085 req[1] = smb2_close_send(tree, &cl);
1086 torture_assert_not_null_goto(tctx, req[1], ret, done,
1087 "smb2_close_send failed\n");
1089 sd = security_descriptor_dacl_create(tctx,
1090 0, NULL, NULL,
1091 SID_CREATOR_OWNER,
1092 SEC_ACE_TYPE_ACCESS_ALLOWED,
1093 SEC_RIGHTS_FILE_READ | SEC_STD_ALL,
1095 NULL);
1096 torture_assert_not_null_goto(tctx, sd, ret, done,
1097 "security_descriptor_dacl_create failed\n");
1099 set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
1100 set.set_secdesc.in.file.handle = hd;
1101 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
1102 set.set_secdesc.in.sd = sd;
1104 req[2] = smb2_setinfo_file_send(tree, &set);
1105 torture_assert_not_null_goto(tctx, req[2], ret, done,
1106 "smb2_setinfo_file_send failed\n");
1108 status = smb2_notify_recv(req[0], tree, &nt);
1109 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_INVALID_PARAMETER,
1110 ret, done,
1111 "smb2_notify_recv failed\n");
1113 status = smb2_close_recv(req[1], &cl);
1114 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_INVALID_PARAMETER,
1115 ret, done,
1116 "smb2_close_recv failed\n");
1118 status = smb2_setinfo_recv(req[2]);
1119 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_INVALID_PARAMETER,
1120 ret, done,
1121 "smb2_setinfo_recv failed\n");
1123 done:
1124 smb2_util_unlink(tree, fname);
1125 smb2_tdis(tree);
1126 smb2_logoff(tree->session);
1127 return ret;
1130 static bool test_compound_padding(struct torture_context *tctx,
1131 struct smb2_tree *tree)
1133 struct smb2_handle h;
1134 struct smb2_create cr;
1135 struct smb2_read r;
1136 struct smb2_read r2;
1137 const char *fname = "compound_read.dat";
1138 const char *sname = "compound_read.dat:foo";
1139 struct smb2_request *req[3];
1140 NTSTATUS status;
1141 bool ret = false;
1143 smb2_util_unlink(tree, fname);
1145 /* Write file */
1146 ZERO_STRUCT(cr);
1147 cr.in.desired_access = SEC_FILE_WRITE_DATA;
1148 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1149 cr.in.create_disposition = NTCREATEX_DISP_CREATE;
1150 cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1151 cr.in.fname = fname;
1152 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
1153 NTCREATEX_SHARE_ACCESS_WRITE|
1154 NTCREATEX_SHARE_ACCESS_DELETE;
1155 status = smb2_create(tree, tctx, &cr);
1156 CHECK_STATUS(status, NT_STATUS_OK);
1157 h = cr.out.file.handle;
1159 status = smb2_util_write(tree, h, "123", 0, 3);
1160 CHECK_STATUS(status, NT_STATUS_OK);
1162 smb2_util_close(tree, h);
1164 /* Write stream */
1165 ZERO_STRUCT(cr);
1166 cr.in.desired_access = SEC_FILE_WRITE_DATA;
1167 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1168 cr.in.create_disposition = NTCREATEX_DISP_CREATE;
1169 cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1170 cr.in.fname = sname;
1171 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
1172 NTCREATEX_SHARE_ACCESS_WRITE|
1173 NTCREATEX_SHARE_ACCESS_DELETE;
1174 status = smb2_create(tree, tctx, &cr);
1175 CHECK_STATUS(status, NT_STATUS_OK);
1176 h = cr.out.file.handle;
1178 status = smb2_util_write(tree, h, "456", 0, 3);
1179 CHECK_STATUS(status, NT_STATUS_OK);
1181 smb2_util_close(tree, h);
1183 /* Check compound read from basefile */
1184 smb2_transport_compound_start(tree->session->transport, 3);
1186 ZERO_STRUCT(cr);
1187 cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1188 cr.in.desired_access = SEC_FILE_READ_DATA;
1189 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1190 cr.in.create_disposition = NTCREATEX_DISP_OPEN;
1191 cr.in.fname = fname;
1192 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
1193 NTCREATEX_SHARE_ACCESS_WRITE|
1194 NTCREATEX_SHARE_ACCESS_DELETE;
1195 req[0] = smb2_create_send(tree, &cr);
1197 smb2_transport_compound_set_related(tree->session->transport, true);
1200 * We send 2 reads in the compound here as the protocol
1201 * allows the last read to be split off and possibly
1202 * go async. Check the padding on the first read returned,
1203 * not the second as the second may not be part of the
1204 * returned compound.
1207 ZERO_STRUCT(r);
1208 h.data[0] = UINT64_MAX;
1209 h.data[1] = UINT64_MAX;
1210 r.in.file.handle = h;
1211 r.in.length = 3;
1212 r.in.offset = 0;
1213 r.in.min_count = 1;
1214 req[1] = smb2_read_send(tree, &r);
1216 ZERO_STRUCT(r2);
1217 h.data[0] = UINT64_MAX;
1218 h.data[1] = UINT64_MAX;
1219 r2.in.file.handle = h;
1220 r2.in.length = 3;
1221 r2.in.offset = 0;
1222 r2.in.min_count = 1;
1223 req[2] = smb2_read_send(tree, &r2);
1225 status = smb2_create_recv(req[0], tree, &cr);
1226 CHECK_STATUS(status, NT_STATUS_OK);
1229 * We must do a manual smb2_request_receive() in order to be
1230 * able to check the transport layer info, as smb2_read_recv()
1231 * will destroy the req. smb2_read_recv() will call
1232 * smb2_request_receive() again, but that's ok.
1234 if (!smb2_request_receive(req[1]) ||
1235 !smb2_request_is_ok(req[1])) {
1236 torture_fail(tctx, "failed to receive read request");
1240 * size must be 24: 16 byte read response header plus 3
1241 * requested bytes padded to an 8 byte boundary.
1243 CHECK_VAL(req[1]->in.body_size, 24);
1245 status = smb2_read_recv(req[1], tree, &r);
1246 CHECK_STATUS(status, NT_STATUS_OK);
1248 /* Pick up the second, possibly async, read. */
1249 status = smb2_read_recv(req[2], tree, &r2);
1250 CHECK_STATUS(status, NT_STATUS_OK);
1252 smb2_util_close(tree, cr.out.file.handle);
1254 /* Check compound read from stream */
1255 smb2_transport_compound_start(tree->session->transport, 3);
1257 ZERO_STRUCT(cr);
1258 cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1259 cr.in.desired_access = SEC_FILE_READ_DATA;
1260 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1261 cr.in.create_disposition = NTCREATEX_DISP_OPEN;
1262 cr.in.fname = sname;
1263 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
1264 NTCREATEX_SHARE_ACCESS_WRITE|
1265 NTCREATEX_SHARE_ACCESS_DELETE;
1266 req[0] = smb2_create_send(tree, &cr);
1268 smb2_transport_compound_set_related(tree->session->transport, true);
1271 * We send 2 reads in the compound here as the protocol
1272 * allows the last read to be split off and possibly
1273 * go async. Check the padding on the first read returned,
1274 * not the second as the second may not be part of the
1275 * returned compound.
1278 ZERO_STRUCT(r);
1279 h.data[0] = UINT64_MAX;
1280 h.data[1] = UINT64_MAX;
1281 r.in.file.handle = h;
1282 r.in.length = 3;
1283 r.in.offset = 0;
1284 r.in.min_count = 1;
1285 req[1] = smb2_read_send(tree, &r);
1287 ZERO_STRUCT(r2);
1288 h.data[0] = UINT64_MAX;
1289 h.data[1] = UINT64_MAX;
1290 r2.in.file.handle = h;
1291 r2.in.length = 3;
1292 r2.in.offset = 0;
1293 r2.in.min_count = 1;
1294 req[2] = smb2_read_send(tree, &r2);
1296 status = smb2_create_recv(req[0], tree, &cr);
1297 CHECK_STATUS(status, NT_STATUS_OK);
1300 * We must do a manual smb2_request_receive() in order to be
1301 * able to check the transport layer info, as smb2_read_recv()
1302 * will destroy the req. smb2_read_recv() will call
1303 * smb2_request_receive() again, but that's ok.
1305 if (!smb2_request_receive(req[1]) ||
1306 !smb2_request_is_ok(req[1])) {
1307 torture_fail(tctx, "failed to receive read request");
1311 * size must be 24: 16 byte read response header plus 3
1312 * requested bytes padded to an 8 byte boundary.
1314 CHECK_VAL(req[1]->in.body_size, 24);
1316 status = smb2_read_recv(req[1], tree, &r);
1317 CHECK_STATUS(status, NT_STATUS_OK);
1319 /* Pick up the second, possibly async, read. */
1320 status = smb2_read_recv(req[2], tree, &r2);
1321 CHECK_STATUS(status, NT_STATUS_OK);
1323 h = cr.out.file.handle;
1325 /* Check 2 compound (unrelateated) reads from existing stream handle */
1326 smb2_transport_compound_start(tree->session->transport, 2);
1328 ZERO_STRUCT(r);
1329 r.in.file.handle = h;
1330 r.in.length = 3;
1331 r.in.offset = 0;
1332 r.in.min_count = 1;
1333 req[0] = smb2_read_send(tree, &r);
1334 req[1] = smb2_read_send(tree, &r);
1337 * We must do a manual smb2_request_receive() in order to be
1338 * able to check the transport layer info, as smb2_read_recv()
1339 * will destroy the req. smb2_read_recv() will call
1340 * smb2_request_receive() again, but that's ok.
1342 if (!smb2_request_receive(req[0]) ||
1343 !smb2_request_is_ok(req[0])) {
1344 torture_fail(tctx, "failed to receive read request");
1346 if (!smb2_request_receive(req[1]) ||
1347 !smb2_request_is_ok(req[1])) {
1348 torture_fail(tctx, "failed to receive read request");
1352 * size must be 24: 16 byte read response header plus 3
1353 * requested bytes padded to an 8 byte boundary.
1355 CHECK_VAL(req[0]->in.body_size, 24);
1356 CHECK_VAL(req[1]->in.body_size, 24);
1358 status = smb2_read_recv(req[0], tree, &r);
1359 CHECK_STATUS(status, NT_STATUS_OK);
1360 status = smb2_read_recv(req[1], tree, &r);
1361 CHECK_STATUS(status, NT_STATUS_OK);
1364 * now try a single read from the stream and verify there's no padding
1366 ZERO_STRUCT(r);
1367 r.in.file.handle = h;
1368 r.in.length = 3;
1369 r.in.offset = 0;
1370 r.in.min_count = 1;
1371 req[0] = smb2_read_send(tree, &r);
1374 * We must do a manual smb2_request_receive() in order to be
1375 * able to check the transport layer info, as smb2_read_recv()
1376 * will destroy the req. smb2_read_recv() will call
1377 * smb2_request_receive() again, but that's ok.
1379 if (!smb2_request_receive(req[0]) ||
1380 !smb2_request_is_ok(req[0])) {
1381 torture_fail(tctx, "failed to receive read request");
1385 * size must be 19: 16 byte read response header plus 3
1386 * requested bytes without padding.
1388 CHECK_VAL(req[0]->in.body_size, 19);
1390 status = smb2_read_recv(req[0], tree, &r);
1391 CHECK_STATUS(status, NT_STATUS_OK);
1393 smb2_util_close(tree, h);
1395 status = smb2_util_unlink(tree, fname);
1396 CHECK_STATUS(status, NT_STATUS_OK);
1398 ret = true;
1399 done:
1400 return ret;
1403 static bool test_compound_create_write_close(struct torture_context *tctx,
1404 struct smb2_tree *tree)
1406 struct smb2_handle handle = { .data = { UINT64_MAX, UINT64_MAX } };
1407 struct smb2_create create;
1408 struct smb2_write write;
1409 struct smb2_close close;
1410 const char *fname = "compound_create_write_close.dat";
1411 struct smb2_request *req[3];
1412 NTSTATUS status;
1413 bool ret = false;
1415 smb2_util_unlink(tree, fname);
1417 ZERO_STRUCT(create);
1418 create.in.security_flags = 0x00;
1419 create.in.oplock_level = 0;
1420 create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1421 create.in.create_flags = 0x00000000;
1422 create.in.reserved = 0x00000000;
1423 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
1424 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1425 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1426 NTCREATEX_SHARE_ACCESS_WRITE |
1427 NTCREATEX_SHARE_ACCESS_DELETE;
1428 create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1429 create.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1430 NTCREATEX_OPTIONS_ASYNC_ALERT |
1431 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1432 0x00200000;
1433 create.in.fname = fname;
1435 smb2_transport_compound_start(tree->session->transport, 3);
1437 req[0] = smb2_create_send(tree, &create);
1439 smb2_transport_compound_set_related(tree->session->transport, true);
1441 ZERO_STRUCT(write);
1442 write.in.file.handle = handle;
1443 write.in.offset = 0;
1444 write.in.data = data_blob_talloc(tctx, NULL, 1024);
1446 req[1] = smb2_write_send(tree, &write);
1448 ZERO_STRUCT(close);
1449 close.in.file.handle = handle;
1451 req[2] = smb2_close_send(tree, &close);
1453 status = smb2_create_recv(req[0], tree, &create);
1454 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1455 "CREATE failed.");
1457 status = smb2_write_recv(req[1], &write);
1458 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1459 "WRITE failed.");
1461 status = smb2_close_recv(req[2], &close);
1462 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1463 "CLOSE failed.");
1465 status = smb2_util_unlink(tree, fname);
1466 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1467 "File deletion failed.");
1469 ret = true;
1470 done:
1471 return ret;
1474 static bool test_compound_unrelated1(struct torture_context *tctx,
1475 struct smb2_tree *tree)
1477 struct smb2_handle hd;
1478 struct smb2_create cr;
1479 NTSTATUS status;
1480 const char *fname = "compound_unrelated1.dat";
1481 struct smb2_close cl;
1482 bool ret = true;
1483 struct smb2_request *req[5];
1485 smb2_transport_credits_ask_num(tree->session->transport, 5);
1487 smb2_util_unlink(tree, fname);
1489 smb2_transport_credits_ask_num(tree->session->transport, 1);
1491 ZERO_STRUCT(cr);
1492 cr.in.security_flags = 0x00;
1493 cr.in.oplock_level = 0;
1494 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1495 cr.in.create_flags = 0x00000000;
1496 cr.in.reserved = 0x00000000;
1497 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1498 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1499 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1500 NTCREATEX_SHARE_ACCESS_WRITE |
1501 NTCREATEX_SHARE_ACCESS_DELETE;
1502 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1503 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1504 NTCREATEX_OPTIONS_ASYNC_ALERT |
1505 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1506 0x00200000;
1507 cr.in.fname = fname;
1509 smb2_transport_compound_start(tree->session->transport, 5);
1511 req[0] = smb2_create_send(tree, &cr);
1513 hd.data[0] = UINT64_MAX;
1514 hd.data[1] = UINT64_MAX;
1516 ZERO_STRUCT(cl);
1517 cl.in.file.handle = hd;
1518 req[1] = smb2_close_send(tree, &cl);
1519 req[2] = smb2_close_send(tree, &cl);
1520 req[3] = smb2_close_send(tree, &cl);
1521 req[4] = smb2_close_send(tree, &cl);
1523 status = smb2_create_recv(req[0], tree, &cr);
1524 CHECK_STATUS(status, NT_STATUS_OK);
1525 status = smb2_close_recv(req[1], &cl);
1526 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1527 status = smb2_close_recv(req[2], &cl);
1528 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1529 status = smb2_close_recv(req[3], &cl);
1530 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1531 status = smb2_close_recv(req[4], &cl);
1532 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1534 smb2_util_unlink(tree, fname);
1535 done:
1536 return ret;
1539 static bool test_compound_invalid1(struct torture_context *tctx,
1540 struct smb2_tree *tree)
1542 struct smb2_handle hd;
1543 struct smb2_create cr;
1544 NTSTATUS status;
1545 const char *fname = "compound_invalid1.dat";
1546 struct smb2_close cl;
1547 bool ret = true;
1548 struct smb2_request *req[3];
1550 smb2_transport_credits_ask_num(tree->session->transport, 3);
1552 smb2_util_unlink(tree, fname);
1554 smb2_transport_credits_ask_num(tree->session->transport, 1);
1556 ZERO_STRUCT(cr);
1557 cr.in.security_flags = 0x00;
1558 cr.in.oplock_level = 0;
1559 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1560 cr.in.create_flags = 0x00000000;
1561 cr.in.reserved = 0x00000000;
1562 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1563 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1564 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1565 NTCREATEX_SHARE_ACCESS_WRITE |
1566 NTCREATEX_SHARE_ACCESS_DELETE;
1567 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1568 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1569 NTCREATEX_OPTIONS_ASYNC_ALERT |
1570 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1571 0x00200000;
1572 cr.in.fname = fname;
1574 smb2_transport_compound_start(tree->session->transport, 3);
1576 /* passing the first request with the related flag is invalid */
1577 smb2_transport_compound_set_related(tree->session->transport, true);
1579 req[0] = smb2_create_send(tree, &cr);
1581 hd.data[0] = UINT64_MAX;
1582 hd.data[1] = UINT64_MAX;
1584 ZERO_STRUCT(cl);
1585 cl.in.file.handle = hd;
1586 req[1] = smb2_close_send(tree, &cl);
1588 smb2_transport_compound_set_related(tree->session->transport, false);
1589 req[2] = smb2_close_send(tree, &cl);
1591 status = smb2_create_recv(req[0], tree, &cr);
1592 /* TODO: check why this fails with --signing=required */
1593 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1594 status = smb2_close_recv(req[1], &cl);
1595 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1596 status = smb2_close_recv(req[2], &cl);
1597 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1599 smb2_util_unlink(tree, fname);
1600 done:
1601 return ret;
1604 static bool test_compound_invalid2(struct torture_context *tctx,
1605 struct smb2_tree *tree)
1607 struct smb2_handle hd;
1608 struct smb2_create cr;
1609 NTSTATUS status;
1610 const char *fname = "compound_invalid2.dat";
1611 struct smb2_close cl;
1612 bool ret = true;
1613 struct smb2_request *req[5];
1614 struct smbXcli_tcon *saved_tcon = tree->smbXcli;
1615 struct smbXcli_session *saved_session = tree->session->smbXcli;
1617 smb2_transport_credits_ask_num(tree->session->transport, 5);
1619 smb2_util_unlink(tree, fname);
1621 smb2_transport_credits_ask_num(tree->session->transport, 1);
1623 ZERO_STRUCT(cr);
1624 cr.in.security_flags = 0x00;
1625 cr.in.oplock_level = 0;
1626 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1627 cr.in.create_flags = 0x00000000;
1628 cr.in.reserved = 0x00000000;
1629 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1630 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1631 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1632 NTCREATEX_SHARE_ACCESS_WRITE |
1633 NTCREATEX_SHARE_ACCESS_DELETE;
1634 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1635 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1636 NTCREATEX_OPTIONS_ASYNC_ALERT |
1637 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1638 0x00200000;
1639 cr.in.fname = fname;
1641 smb2_transport_compound_start(tree->session->transport, 5);
1643 req[0] = smb2_create_send(tree, &cr);
1645 hd.data[0] = UINT64_MAX;
1646 hd.data[1] = UINT64_MAX;
1648 smb2_transport_compound_set_related(tree->session->transport, true);
1650 ZERO_STRUCT(cl);
1651 cl.in.file.handle = hd;
1653 tree->smbXcli = smbXcli_tcon_create(tree);
1654 smb2cli_tcon_set_values(tree->smbXcli,
1655 NULL, /* session */
1656 0xFFFFFFFF, /* tcon_id */
1657 0, /* type */
1658 0, /* flags */
1659 0, /* capabilities */
1660 0 /* maximal_access */);
1662 tree->session->smbXcli = smbXcli_session_shallow_copy(tree->session,
1663 tree->session->smbXcli);
1664 smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0);
1666 req[1] = smb2_close_send(tree, &cl);
1667 /* strange that this is not generating invalid parameter */
1668 smb2_transport_compound_set_related(tree->session->transport, false);
1669 req[2] = smb2_close_send(tree, &cl);
1670 req[3] = smb2_close_send(tree, &cl);
1671 smb2_transport_compound_set_related(tree->session->transport, true);
1672 req[4] = smb2_close_send(tree, &cl);
1674 status = smb2_create_recv(req[0], tree, &cr);
1675 CHECK_STATUS(status, NT_STATUS_OK);
1676 status = smb2_close_recv(req[1], &cl);
1677 CHECK_STATUS(status, NT_STATUS_OK);
1678 status = smb2_close_recv(req[2], &cl);
1679 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
1680 status = smb2_close_recv(req[3], &cl);
1681 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
1682 status = smb2_close_recv(req[4], &cl);
1683 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1685 TALLOC_FREE(tree->smbXcli);
1686 tree->smbXcli = saved_tcon;
1687 TALLOC_FREE(tree->session->smbXcli);
1688 tree->session->smbXcli = saved_session;
1690 smb2_util_unlink(tree, fname);
1691 done:
1692 return ret;
1695 static bool test_compound_invalid3(struct torture_context *tctx,
1696 struct smb2_tree *tree)
1698 struct smb2_handle hd;
1699 struct smb2_create cr;
1700 NTSTATUS status;
1701 const char *fname = "compound_invalid3.dat";
1702 struct smb2_close cl;
1703 bool ret = true;
1704 struct smb2_request *req[5];
1706 smb2_transport_credits_ask_num(tree->session->transport, 5);
1708 smb2_util_unlink(tree, fname);
1710 smb2_transport_credits_ask_num(tree->session->transport, 1);
1712 ZERO_STRUCT(cr);
1713 cr.in.security_flags = 0x00;
1714 cr.in.oplock_level = 0;
1715 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1716 cr.in.create_flags = 0x00000000;
1717 cr.in.reserved = 0x00000000;
1718 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1719 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1720 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1721 NTCREATEX_SHARE_ACCESS_WRITE |
1722 NTCREATEX_SHARE_ACCESS_DELETE;
1723 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1724 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1725 NTCREATEX_OPTIONS_ASYNC_ALERT |
1726 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1727 0x00200000;
1728 cr.in.fname = fname;
1730 smb2_transport_compound_start(tree->session->transport, 5);
1732 req[0] = smb2_create_send(tree, &cr);
1734 hd.data[0] = UINT64_MAX;
1735 hd.data[1] = UINT64_MAX;
1737 ZERO_STRUCT(cl);
1738 cl.in.file.handle = hd;
1739 req[1] = smb2_close_send(tree, &cl);
1740 req[2] = smb2_close_send(tree, &cl);
1741 /* flipping the related flag is invalid */
1742 smb2_transport_compound_set_related(tree->session->transport, true);
1743 req[3] = smb2_close_send(tree, &cl);
1744 req[4] = smb2_close_send(tree, &cl);
1746 status = smb2_create_recv(req[0], tree, &cr);
1747 CHECK_STATUS(status, NT_STATUS_OK);
1748 status = smb2_close_recv(req[1], &cl);
1749 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1750 status = smb2_close_recv(req[2], &cl);
1751 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1752 status = smb2_close_recv(req[3], &cl);
1753 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1754 status = smb2_close_recv(req[4], &cl);
1755 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1757 smb2_util_unlink(tree, fname);
1758 done:
1759 return ret;
1762 static bool test_compound_invalid4(struct torture_context *tctx,
1763 struct smb2_tree *tree)
1765 struct smb2_create cr;
1766 struct smb2_read rd;
1767 NTSTATUS status;
1768 const char *fname = "compound_invalid4.dat";
1769 struct smb2_close cl;
1770 bool ret = true;
1771 bool ok;
1772 struct smb2_request *req[2];
1774 smb2_transport_credits_ask_num(tree->session->transport, 2);
1776 smb2_util_unlink(tree, fname);
1778 ZERO_STRUCT(cr);
1779 cr.in.security_flags = 0x00;
1780 cr.in.oplock_level = 0;
1781 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1782 cr.in.create_flags = 0x00000000;
1783 cr.in.reserved = 0x00000000;
1784 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1785 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1786 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1787 NTCREATEX_SHARE_ACCESS_WRITE |
1788 NTCREATEX_SHARE_ACCESS_DELETE;
1789 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1790 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1791 NTCREATEX_OPTIONS_ASYNC_ALERT |
1792 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1793 0x00200000;
1794 cr.in.fname = fname;
1796 status = smb2_create(tree, tctx, &cr);
1797 CHECK_STATUS(status, NT_STATUS_OK);
1799 smb2_transport_compound_start(tree->session->transport, 2);
1801 ZERO_STRUCT(rd);
1802 rd.in.file.handle = cr.out.file.handle;
1803 rd.in.length = 1;
1804 rd.in.offset = 0;
1805 req[0] = smb2_read_send(tree, &rd);
1807 smb2_transport_compound_set_related(tree->session->transport, true);
1810 * Send a completely bogus request as second compound
1811 * element. This triggers smbd_smb2_request_error() in in
1812 * smbd_smb2_request_dispatch() before calling
1813 * smbd_smb2_request_dispatch_update_counts().
1816 req[1] = smb2_request_init_tree(tree, 0xff, 0x04, false, 0);
1817 smb2_transport_send(req[1]);
1819 status = smb2_read_recv(req[0], tctx, &rd);
1820 CHECK_STATUS(status, NT_STATUS_END_OF_FILE);
1822 ok = smb2_request_receive(req[1]);
1823 torture_assert(tctx, ok, "Invalid request failed\n");
1824 CHECK_STATUS(req[1]->status, NT_STATUS_INVALID_PARAMETER);
1826 ZERO_STRUCT(cl);
1827 cl.in.file.handle = cr.out.file.handle;
1829 status = smb2_close(tree, &cl);
1830 CHECK_STATUS(status, NT_STATUS_OK);
1832 smb2_util_unlink(tree, fname);
1833 done:
1834 return ret;
1837 /* Send a compound request where we expect the last request (Create, Notify)
1838 * to go asynchronous. This works against a Win7 server and the reply is
1839 * sent in two different packets. */
1840 static bool test_compound_interim1(struct torture_context *tctx,
1841 struct smb2_tree *tree)
1843 struct smb2_handle hd;
1844 struct smb2_create cr;
1845 NTSTATUS status = NT_STATUS_OK;
1846 const char *dname = "compound_interim_dir";
1847 struct smb2_notify nt;
1848 bool ret = true;
1849 struct smb2_request *req[2];
1851 /* Win7 compound request implementation deviates substantially from the
1852 * SMB2 spec as noted in MS-SMB2 <159>, <162>. This, test currently
1853 * verifies the Windows behavior, not the general spec behavior. */
1855 smb2_transport_credits_ask_num(tree->session->transport, 5);
1857 smb2_deltree(tree, dname);
1859 smb2_transport_credits_ask_num(tree->session->transport, 1);
1861 ZERO_STRUCT(cr);
1862 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1863 cr.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1864 cr.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
1865 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1866 NTCREATEX_SHARE_ACCESS_WRITE |
1867 NTCREATEX_SHARE_ACCESS_DELETE;
1868 cr.in.create_disposition = NTCREATEX_DISP_CREATE;
1869 cr.in.fname = dname;
1871 smb2_transport_compound_start(tree->session->transport, 2);
1873 req[0] = smb2_create_send(tree, &cr);
1875 smb2_transport_compound_set_related(tree->session->transport, true);
1877 hd.data[0] = UINT64_MAX;
1878 hd.data[1] = UINT64_MAX;
1880 ZERO_STRUCT(nt);
1881 nt.in.recursive = true;
1882 nt.in.buffer_size = 0x1000;
1883 nt.in.file.handle = hd;
1884 nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1885 nt.in.unknown = 0x00000000;
1887 req[1] = smb2_notify_send(tree, &nt);
1889 status = smb2_create_recv(req[0], tree, &cr);
1890 CHECK_STATUS(status, NT_STATUS_OK);
1892 smb2_cancel(req[1]);
1893 status = smb2_notify_recv(req[1], tree, &nt);
1894 CHECK_STATUS(status, NT_STATUS_CANCELLED);
1896 smb2_util_close(tree, cr.out.file.handle);
1898 smb2_deltree(tree, dname);
1899 done:
1900 return ret;
1903 /* Send a compound request where we expect the middle request (Create, Notify,
1904 * GetInfo) to go asynchronous. Against Win7 the sync request succeed while
1905 * the async fails. All are returned in the same compound response. */
1906 static bool test_compound_interim2(struct torture_context *tctx,
1907 struct smb2_tree *tree)
1909 struct smb2_handle hd;
1910 struct smb2_create cr;
1911 NTSTATUS status = NT_STATUS_OK;
1912 const char *dname = "compound_interim_dir";
1913 struct smb2_getinfo gf;
1914 struct smb2_notify nt;
1915 bool ret = true;
1916 struct smb2_request *req[3];
1918 /* Win7 compound request implementation deviates substantially from the
1919 * SMB2 spec as noted in MS-SMB2 <159>, <162>. This, test currently
1920 * verifies the Windows behavior, not the general spec behavior. */
1922 smb2_transport_credits_ask_num(tree->session->transport, 5);
1924 smb2_deltree(tree, dname);
1926 smb2_transport_credits_ask_num(tree->session->transport, 1);
1928 ZERO_STRUCT(cr);
1929 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1930 cr.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1931 cr.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
1932 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1933 NTCREATEX_SHARE_ACCESS_WRITE |
1934 NTCREATEX_SHARE_ACCESS_DELETE;
1935 cr.in.create_disposition = NTCREATEX_DISP_CREATE;
1936 cr.in.fname = dname;
1938 smb2_transport_compound_start(tree->session->transport, 3);
1940 req[0] = smb2_create_send(tree, &cr);
1942 smb2_transport_compound_set_related(tree->session->transport, true);
1944 hd.data[0] = UINT64_MAX;
1945 hd.data[1] = UINT64_MAX;
1947 ZERO_STRUCT(nt);
1948 nt.in.recursive = true;
1949 nt.in.buffer_size = 0x1000;
1950 nt.in.file.handle = hd;
1951 nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1952 nt.in.unknown = 0x00000000;
1954 req[1] = smb2_notify_send(tree, &nt);
1956 ZERO_STRUCT(gf);
1957 gf.in.file.handle = hd;
1958 gf.in.info_type = SMB2_0_INFO_FILE;
1959 gf.in.info_class = 0x04; /* FILE_BASIC_INFORMATION */
1960 gf.in.output_buffer_length = 0x1000;
1961 gf.in.input_buffer = data_blob_null;
1963 req[2] = smb2_getinfo_send(tree, &gf);
1965 status = smb2_create_recv(req[0], tree, &cr);
1966 CHECK_STATUS(status, NT_STATUS_OK);
1968 status = smb2_notify_recv(req[1], tree, &nt);
1969 CHECK_STATUS(status, NT_STATUS_INTERNAL_ERROR);
1971 status = smb2_getinfo_recv(req[2], tree, &gf);
1972 CHECK_STATUS(status, NT_STATUS_OK);
1974 smb2_util_close(tree, cr.out.file.handle);
1976 smb2_deltree(tree, dname);
1977 done:
1978 return ret;
1982 * Send a compound related series of CREATE+CLOSE+CREATE+NOTIFY and check
1983 * CREATE+CLOSE+CREATE responses come in a separate compound response before the
1984 * STATUS_PENDING for the NOTIFY.
1986 static bool test_compound_interim3(struct torture_context *tctx,
1987 struct smb2_tree *tree)
1989 const char *dname = "test_compound_interim3";
1990 struct smb2_handle hd = {};
1991 struct smb2_create cr = {};
1992 struct smb2_handle h1 = {};
1993 struct smb2_notify nt = {};
1994 struct smb2_request *req[6] = {};
1995 struct smb2_close cl = {};
1996 NTSTATUS status;
1997 int rc;
1998 bool ret = true;
2000 smb2_deltree(tree, dname);
2001 smb2_transport_compound_start(tree->session->transport, 4);
2003 hd.data[0] = UINT64_MAX;
2004 hd.data[1] = UINT64_MAX;
2006 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
2007 cr.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
2008 cr.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2009 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
2010 NTCREATEX_SHARE_ACCESS_WRITE |
2011 NTCREATEX_SHARE_ACCESS_DELETE;
2012 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
2013 cr.in.fname = dname;
2015 nt.in.recursive = true;
2016 nt.in.buffer_size = 0x1000;
2017 nt.in.file.handle = hd;
2018 nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
2019 nt.in.unknown = 0x00000000;
2021 req[0] = smb2_create_send(tree, &cr);
2022 torture_assert_not_null_goto(tctx, req[0], ret, done,
2023 "smb2_create_send failed\n");
2025 smb2_transport_compound_set_related(tree->session->transport, true);
2027 cl.in.file.handle = hd;
2029 req[1] = smb2_close_send(tree, &cl);
2030 torture_assert_not_null_goto(tctx, req[1], ret, done,
2031 "smb2_close_send failed\n");
2033 req[2] = smb2_create_send(tree, &cr);
2034 torture_assert_not_null_goto(tctx, req[2], ret, done,
2035 "smb2_create_send failed\n");
2037 req[3] = smb2_notify_send(tree, &nt);
2038 torture_assert_not_null_goto(tctx, req[3], ret, done,
2039 "smb2_create_send failed\n");
2041 while (req[2]->state < SMB2_REQUEST_DONE) {
2042 rc = tevent_loop_once(tctx->ev);
2043 torture_assert_goto(tctx, rc == 0, ret, done,
2044 "tevent_loop_once failed\n");
2047 torture_assert_goto(tctx, req[0]->state == SMB2_REQUEST_DONE, ret, done,
2048 "state not SMB2_REQUEST_DONE");
2049 torture_assert_goto(tctx, req[1]->state == SMB2_REQUEST_DONE, ret, done,
2050 "state not SMB2_REQUEST_DONE");
2051 torture_assert_goto(tctx, req[2]->state == SMB2_REQUEST_DONE, ret, done,
2052 "state not SMB2_REQUEST_DONE");
2053 torture_assert_goto(tctx, req[3]->state == SMB2_REQUEST_RECV, ret, done,
2054 "state not SMB2_REQUEST_RECV");
2056 WAIT_FOR_ASYNC_RESPONSE(req[3]);
2057 torture_assert_goto(tctx, req[3]->state == SMB2_REQUEST_RECV, ret, done,
2058 "state not SMB2_REQUEST_RECV");
2059 torture_assert_goto(tctx, req[3]->cancel.can_cancel, ret, done, "pending");
2061 status = smb2_create_recv(req[0], tree, &cr);
2062 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2063 "smb2_setinfo_recv failed\n");
2065 status = smb2_close_recv(req[1], &cl);
2066 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2067 "smb2_setinfo_recv failed\n");
2069 status = smb2_create_recv(req[2], tree, &cr);
2070 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2071 "smb2_create_recv failed\n");
2072 h1 = cr.out.file.handle;
2074 smb2_cancel(req[3]);
2075 status = smb2_notify_recv(req[3], tree, &nt);
2076 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_CANCELLED,
2077 ret, done,
2078 "smb2_notify_recv failed\n");
2080 done:
2081 smb2_util_close(tree, h1);
2082 smb2_deltree(tree, dname);
2083 return ret;
2086 /* Test compound related finds */
2087 static bool test_compound_find_related(struct torture_context *tctx,
2088 struct smb2_tree *tree)
2090 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2091 const char *dname = "compound_find_dir";
2092 struct smb2_create create;
2093 struct smb2_find f;
2094 struct smb2_handle h;
2095 struct smb2_request *req[2];
2096 NTSTATUS status;
2097 bool ret = true;
2099 smb2_deltree(tree, dname);
2101 ZERO_STRUCT(create);
2102 create.in.desired_access = SEC_RIGHTS_DIR_ALL;
2103 create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
2104 create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2105 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
2106 NTCREATEX_SHARE_ACCESS_WRITE |
2107 NTCREATEX_SHARE_ACCESS_DELETE;
2108 create.in.create_disposition = NTCREATEX_DISP_CREATE;
2109 create.in.fname = dname;
2111 status = smb2_create(tree, mem_ctx, &create);
2112 h = create.out.file.handle;
2114 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n");
2116 smb2_transport_compound_start(tree->session->transport, 2);
2118 ZERO_STRUCT(f);
2119 f.in.file.handle = h;
2120 f.in.pattern = "*";
2121 f.in.max_response_size = 0x100;
2122 f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO;
2124 req[0] = smb2_find_send(tree, &f);
2126 smb2_transport_compound_set_related(tree->session->transport, true);
2128 req[1] = smb2_find_send(tree, &f);
2130 status = smb2_find_recv(req[0], mem_ctx, &f);
2131 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_recv failed\n");
2133 status = smb2_find_recv(req[1], mem_ctx, &f);
2134 torture_assert_ntstatus_equal_goto(tctx, status, STATUS_NO_MORE_FILES, ret, done, "smb2_find_recv failed\n");
2136 done:
2137 smb2_util_close(tree, h);
2138 smb2_deltree(tree, dname);
2139 TALLOC_FREE(mem_ctx);
2140 return ret;
2143 /* Test compound related finds */
2144 static bool test_compound_find_close(struct torture_context *tctx,
2145 struct smb2_tree *tree)
2147 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2148 const char *dname = "compound_find_dir";
2149 struct smb2_create create;
2150 struct smb2_find f;
2151 struct smb2_handle h;
2152 struct smb2_request *req = NULL;
2153 const int num_files = 5000;
2154 int i;
2155 NTSTATUS status;
2156 bool ret = true;
2158 smb2_deltree(tree, dname);
2160 ZERO_STRUCT(create);
2161 create.in.desired_access = SEC_RIGHTS_DIR_ALL;
2162 create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
2163 create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2164 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
2165 NTCREATEX_SHARE_ACCESS_WRITE |
2166 NTCREATEX_SHARE_ACCESS_DELETE;
2167 create.in.create_disposition = NTCREATEX_DISP_CREATE;
2168 create.in.fname = dname;
2170 smb2cli_conn_set_max_credits(tree->session->transport->conn, 256);
2172 status = smb2_create(tree, mem_ctx, &create);
2173 h = create.out.file.handle;
2175 ZERO_STRUCT(create);
2176 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
2177 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2178 create.in.create_disposition = NTCREATEX_DISP_CREATE;
2180 for (i = 0; i < num_files; i++) {
2181 create.in.fname = talloc_asprintf(mem_ctx, "%s\\file%d",
2182 dname, i);
2183 status = smb2_create(tree, mem_ctx, &create);
2184 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
2185 smb2_util_close(tree, create.out.file.handle);
2188 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n");
2190 ZERO_STRUCT(f);
2191 f.in.file.handle = h;
2192 f.in.pattern = "*";
2193 f.in.max_response_size = 8*1024*1024;
2194 f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO;
2196 req = smb2_find_send(tree, &f);
2198 status = smb2_util_close(tree, h);
2199 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_close failed\n");
2201 status = smb2_find_recv(req, mem_ctx, &f);
2202 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_recv failed\n");
2204 done:
2205 smb2_util_close(tree, h);
2206 smb2_deltree(tree, dname);
2207 TALLOC_FREE(mem_ctx);
2208 return ret;
2211 /* Test compound unrelated finds */
2212 static bool test_compound_find_unrelated(struct torture_context *tctx,
2213 struct smb2_tree *tree)
2215 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2216 const char *dname = "compound_find_dir";
2217 struct smb2_create create;
2218 struct smb2_find f;
2219 struct smb2_handle h;
2220 struct smb2_request *req[2];
2221 NTSTATUS status;
2222 bool ret = true;
2224 smb2_deltree(tree, dname);
2226 ZERO_STRUCT(create);
2227 create.in.desired_access = SEC_RIGHTS_DIR_ALL;
2228 create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
2229 create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2230 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
2231 NTCREATEX_SHARE_ACCESS_WRITE |
2232 NTCREATEX_SHARE_ACCESS_DELETE;
2233 create.in.create_disposition = NTCREATEX_DISP_CREATE;
2234 create.in.fname = dname;
2236 status = smb2_create(tree, mem_ctx, &create);
2237 h = create.out.file.handle;
2239 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n");
2241 smb2_transport_compound_start(tree->session->transport, 2);
2243 ZERO_STRUCT(f);
2244 f.in.file.handle = h;
2245 f.in.pattern = "*";
2246 f.in.max_response_size = 0x100;
2247 f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO;
2249 req[0] = smb2_find_send(tree, &f);
2250 req[1] = smb2_find_send(tree, &f);
2252 status = smb2_find_recv(req[0], mem_ctx, &f);
2253 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_recv failed\n");
2255 status = smb2_find_recv(req[1], mem_ctx, &f);
2256 torture_assert_ntstatus_equal_goto(tctx, status, STATUS_NO_MORE_FILES, ret, done, "smb2_find_recv failed\n");
2258 done:
2259 smb2_util_close(tree, h);
2260 smb2_deltree(tree, dname);
2261 TALLOC_FREE(mem_ctx);
2262 return ret;
2265 static bool test_compound_async_flush_close(struct torture_context *tctx,
2266 struct smb2_tree *tree)
2268 struct smb2_handle fhandle = { .data = { 0, 0 } };
2269 struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } };
2270 struct smb2_close cl;
2271 struct smb2_flush fl;
2272 const char *fname = "compound_async_flush_close";
2273 struct smb2_request *req[2];
2274 NTSTATUS status;
2275 bool ret = false;
2277 /* Start clean. */
2278 smb2_util_unlink(tree, fname);
2280 /* Create a file. */
2281 status = torture_smb2_testfile_access(tree,
2282 fname,
2283 &fhandle,
2284 SEC_RIGHTS_FILE_ALL);
2285 CHECK_STATUS(status, NT_STATUS_OK);
2287 /* Now do a compound flush + close handle. */
2288 smb2_transport_compound_start(tree->session->transport, 2);
2290 ZERO_STRUCT(fl);
2291 fl.in.file.handle = fhandle;
2293 req[0] = smb2_flush_send(tree, &fl);
2294 torture_assert_not_null_goto(tctx, req[0], ret, done,
2295 "smb2_flush_send failed\n");
2297 smb2_transport_compound_set_related(tree->session->transport, true);
2299 ZERO_STRUCT(cl);
2300 cl.in.file.handle = relhandle;
2301 req[1] = smb2_close_send(tree, &cl);
2302 torture_assert_not_null_goto(tctx, req[1], ret, done,
2303 "smb2_close_send failed\n");
2305 status = smb2_flush_recv(req[0], &fl);
2307 * On Windows, this flush will usually
2308 * succeed as we have nothing to flush,
2309 * so allow NT_STATUS_OK. Once bug #15172
2310 * is fixed Samba will do the flush synchronously
2311 * so allow NT_STATUS_OK.
2313 if (!NT_STATUS_IS_OK(status)) {
2315 * If we didn't get NT_STATUS_OK, we *must*
2316 * get NT_STATUS_INTERNAL_ERROR if the flush
2317 * goes async.
2319 * For pre-bugfix #15172 Samba, the flush goes async and
2320 * we should get NT_STATUS_INTERNAL_ERROR.
2322 torture_assert_ntstatus_equal_goto(tctx,
2323 status,
2324 NT_STATUS_INTERNAL_ERROR,
2325 ret,
2326 done,
2327 "smb2_flush_recv didn't return "
2328 "NT_STATUS_INTERNAL_ERROR.\n");
2330 status = smb2_close_recv(req[1], &cl);
2331 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2332 "smb2_close_recv failed.");
2334 ZERO_STRUCT(fhandle);
2337 * Do several more operations on the tree, spaced
2338 * out by 1 sec sleeps to make sure the server didn't
2339 * crash on the close. The sleeps are required to
2340 * make test test for a crash reliable, as we ensure
2341 * the pthread fsync internally finishes and accesses
2342 * freed memory. Without them the test occasionally
2343 * passes as we disconnect before the pthread fsync
2344 * finishes.
2346 status = smb2_util_unlink(tree, fname);
2347 CHECK_STATUS(status, NT_STATUS_OK);
2349 sleep(1);
2350 status = smb2_util_unlink(tree, fname);
2351 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2353 sleep(1);
2354 status = smb2_util_unlink(tree, fname);
2355 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2357 ret = true;
2359 done:
2361 if (fhandle.data[0] != 0) {
2362 smb2_util_close(tree, fhandle);
2365 smb2_util_unlink(tree, fname);
2366 return ret;
2369 static bool test_compound_async_flush_flush(struct torture_context *tctx,
2370 struct smb2_tree *tree)
2372 struct smb2_handle fhandle = { .data = { 0, 0 } };
2373 struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } };
2374 struct smb2_flush fl1;
2375 struct smb2_flush fl2;
2376 const char *fname = "compound_async_flush_flush";
2377 struct smb2_request *req[2];
2378 NTSTATUS status;
2379 bool ret = false;
2381 /* Start clean. */
2382 smb2_util_unlink(tree, fname);
2384 /* Create a file. */
2385 status = torture_smb2_testfile_access(tree,
2386 fname,
2387 &fhandle,
2388 SEC_RIGHTS_FILE_ALL);
2389 CHECK_STATUS(status, NT_STATUS_OK);
2391 /* Now do a compound flush + flush handle. */
2392 smb2_transport_compound_start(tree->session->transport, 2);
2394 ZERO_STRUCT(fl1);
2395 fl1.in.file.handle = fhandle;
2397 req[0] = smb2_flush_send(tree, &fl1);
2398 torture_assert_not_null_goto(tctx, req[0], ret, done,
2399 "smb2_flush_send (1) failed\n");
2401 smb2_transport_compound_set_related(tree->session->transport, true);
2403 ZERO_STRUCT(fl2);
2404 fl2.in.file.handle = relhandle;
2406 req[1] = smb2_flush_send(tree, &fl2);
2407 torture_assert_not_null_goto(tctx, req[1], ret, done,
2408 "smb2_flush_send (2) failed\n");
2410 status = smb2_flush_recv(req[0], &fl1);
2412 * On Windows, this flush will usually
2413 * succeed as we have nothing to flush,
2414 * so allow NT_STATUS_OK. Once bug #15172
2415 * is fixed Samba will do the flush synchronously
2416 * so allow NT_STATUS_OK.
2418 if (!NT_STATUS_IS_OK(status)) {
2420 * If we didn't get NT_STATUS_OK, we *must*
2421 * get NT_STATUS_INTERNAL_ERROR if the flush
2422 * goes async.
2424 * For pre-bugfix #15172 Samba, the flush goes async and
2425 * we should get NT_STATUS_INTERNAL_ERROR.
2427 torture_assert_ntstatus_equal_goto(tctx,
2428 status,
2429 NT_STATUS_INTERNAL_ERROR,
2430 ret,
2431 done,
2432 "smb2_flush_recv (1) didn't return "
2433 "NT_STATUS_INTERNAL_ERROR.\n");
2437 * If the flush is the last entry in a compound,
2438 * it should always succeed even if it goes async.
2440 status = smb2_flush_recv(req[1], &fl2);
2441 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2442 "smb2_flush_recv (2) failed.");
2444 status = smb2_util_close(tree, fhandle);
2445 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2446 "smb2_util_close failed.");
2447 ZERO_STRUCT(fhandle);
2450 * Do several more operations on the tree, spaced
2451 * out by 1 sec sleeps to make sure the server didn't
2452 * crash on the close. The sleeps are required to
2453 * make test test for a crash reliable, as we ensure
2454 * the pthread fsync internally finishes and accesses
2455 * freed memory. Without them the test occasionally
2456 * passes as we disconnect before the pthread fsync
2457 * finishes.
2459 status = smb2_util_unlink(tree, fname);
2460 CHECK_STATUS(status, NT_STATUS_OK);
2462 sleep(1);
2463 status = smb2_util_unlink(tree, fname);
2464 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2466 sleep(1);
2467 status = smb2_util_unlink(tree, fname);
2468 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2470 ret = true;
2472 done:
2474 if (fhandle.data[0] != 0) {
2475 smb2_util_close(tree, fhandle);
2478 smb2_util_unlink(tree, fname);
2479 return ret;
2483 * For Samba/smbd this test must be run against the aio_delay_inject share
2484 * as we need to ensure the last write in the compound takes longer than
2485 * 500 us, which is the threshold for going async in smbd SMB2 writes.
2488 static bool test_compound_async_write_write(struct torture_context *tctx,
2489 struct smb2_tree *tree)
2491 struct smb2_handle fhandle = { .data = { 0, 0 } };
2492 struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } };
2493 struct smb2_write w1;
2494 struct smb2_write w2;
2495 const char *fname = "compound_async_write_write";
2496 struct smb2_request *req[2];
2497 NTSTATUS status;
2498 bool is_smbd = torture_setting_bool(tctx, "smbd", true);
2499 bool ret = false;
2501 /* Start clean. */
2502 smb2_util_unlink(tree, fname);
2504 /* Create a file. */
2505 status = torture_smb2_testfile_access(tree,
2506 fname,
2507 &fhandle,
2508 SEC_RIGHTS_FILE_ALL);
2509 CHECK_STATUS(status, NT_STATUS_OK);
2511 /* Now do a compound write + write handle. */
2512 smb2_transport_compound_start(tree->session->transport, 2);
2514 ZERO_STRUCT(w1);
2515 w1.in.file.handle = fhandle;
2516 w1.in.offset = 0;
2517 w1.in.data = data_blob_talloc_zero(tctx, 64);
2518 req[0] = smb2_write_send(tree, &w1);
2520 torture_assert_not_null_goto(tctx, req[0], ret, done,
2521 "smb2_write_send (1) failed\n");
2523 smb2_transport_compound_set_related(tree->session->transport, true);
2525 ZERO_STRUCT(w2);
2526 w2.in.file.handle = relhandle;
2527 w2.in.offset = 64;
2528 w2.in.data = data_blob_talloc_zero(tctx, 64);
2529 req[1] = smb2_write_send(tree, &w2);
2531 torture_assert_not_null_goto(tctx, req[0], ret, done,
2532 "smb2_write_send (2) failed\n");
2534 status = smb2_write_recv(req[0], &w1);
2535 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2536 "smb2_write_recv (1) failed.");
2538 if (!is_smbd) {
2540 * Windows and other servers don't go async.
2542 status = smb2_write_recv(req[1], &w2);
2543 } else {
2545 * For smbd, the second write should go async
2546 * as it's the last element of a compound.
2548 WAIT_FOR_ASYNC_RESPONSE(req[1]);
2549 CHECK_VAL(req[1]->cancel.can_cancel, true);
2551 * Now pick up the real return.
2553 status = smb2_write_recv(req[1], &w2);
2556 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2557 "smb2_write_recv (2) failed.");
2559 status = smb2_util_close(tree, fhandle);
2560 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2561 "smb2_util_close failed.");
2562 ZERO_STRUCT(fhandle);
2564 ret = true;
2566 done:
2568 if (fhandle.data[0] != 0) {
2569 smb2_util_close(tree, fhandle);
2572 smb2_util_unlink(tree, fname);
2573 return ret;
2577 * For Samba/smbd this test must be run against the aio_delay_inject share
2578 * as we need to ensure the last read in the compound takes longer than
2579 * 500 us, which is the threshold for going async in smbd SMB2 reads.
2582 static bool test_compound_async_read_read(struct torture_context *tctx,
2583 struct smb2_tree *tree)
2585 struct smb2_handle fhandle = { .data = { 0, 0 } };
2586 struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } };
2587 struct smb2_write w;
2588 struct smb2_read r1;
2589 struct smb2_read r2;
2590 const char *fname = "compound_async_read_read";
2591 struct smb2_request *req[2];
2592 NTSTATUS status;
2593 bool is_smbd = torture_setting_bool(tctx, "smbd", true);
2594 bool ret = false;
2596 /* Start clean. */
2597 smb2_util_unlink(tree, fname);
2599 /* Create a file. */
2600 status = torture_smb2_testfile_access(tree,
2601 fname,
2602 &fhandle,
2603 SEC_RIGHTS_FILE_ALL);
2604 CHECK_STATUS(status, NT_STATUS_OK);
2606 /* Write 128 bytes. */
2607 ZERO_STRUCT(w);
2608 w.in.file.handle = fhandle;
2609 w.in.offset = 0;
2610 w.in.data = data_blob_talloc_zero(tctx, 128);
2611 status = smb2_write(tree, &w);
2612 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2613 "smb2_write_recv (1) failed.");
2615 /* Now do a compound read + read handle. */
2616 smb2_transport_compound_start(tree->session->transport, 2);
2618 ZERO_STRUCT(r1);
2619 r1.in.file.handle = fhandle;
2620 r1.in.length = 64;
2621 r1.in.offset = 0;
2622 req[0] = smb2_read_send(tree, &r1);
2624 torture_assert_not_null_goto(tctx, req[0], ret, done,
2625 "smb2_read_send (1) failed\n");
2627 smb2_transport_compound_set_related(tree->session->transport, true);
2629 ZERO_STRUCT(r2);
2630 r2.in.file.handle = relhandle;
2631 r2.in.length = 64;
2632 r2.in.offset = 64;
2633 req[1] = smb2_read_send(tree, &r2);
2635 torture_assert_not_null_goto(tctx, req[0], ret, done,
2636 "smb2_read_send (2) failed\n");
2638 status = smb2_read_recv(req[0], tree, &r1);
2639 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2640 "smb2_read_recv (1) failed.");
2642 if (!is_smbd) {
2644 * Windows and other servers don't go async.
2646 status = smb2_read_recv(req[1], tree, &r2);
2647 } else {
2649 * For smbd, the second write should go async
2650 * as it's the last element of a compound.
2652 WAIT_FOR_ASYNC_RESPONSE(req[1]);
2653 CHECK_VAL(req[1]->cancel.can_cancel, true);
2655 * Now pick up the real return.
2657 status = smb2_read_recv(req[1], tree, &r2);
2660 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2661 "smb2_read_recv (2) failed.");
2663 status = smb2_util_close(tree, fhandle);
2664 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2665 "smb2_util_close failed.");
2666 ZERO_STRUCT(fhandle);
2668 ret = true;
2670 done:
2672 if (fhandle.data[0] != 0) {
2673 smb2_util_close(tree, fhandle);
2676 smb2_util_unlink(tree, fname);
2677 return ret;
2681 * Checks a lease break by a create triggers an pending async response.
2683 static bool test_create_lease_break_async(struct torture_context *tctx,
2684 struct smb2_tree *tree1,
2685 struct smb2_tree *tree2)
2687 struct smb2_request *req = NULL;
2688 struct smb2_create c1 = {};
2689 struct smb2_create c2 = {};
2690 struct smb2_lease ls1 = {};
2691 struct smb2_lease ls2 = {};
2692 struct smb2_handle h1 = {};
2693 struct smb2_handle h2 = {};
2694 struct smb2_lease_break_ack ack = {};
2695 const char *fname_src = "test_create_lease_break_async.dat";
2696 uint32_t caps;
2697 NTSTATUS status;
2698 bool ret = true;
2700 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
2701 if (!(caps & SMB2_CAP_LEASING)) {
2702 torture_skip(tctx, "leases are not supported");
2705 smb2_util_unlink(tree1, fname_src);
2707 tree1->session->transport->lease.handler = torture_lease_handler;
2708 tree1->session->transport->lease.private_data = tree1;
2709 torture_reset_lease_break_info(tctx, &lease_break_info);
2710 lease_break_info.lease_skip_ack = true;
2712 /* First open with a RWH lease. */
2713 smb2_lease_create(&c1,
2714 &ls1,
2715 false,
2716 fname_src,
2717 LEASE1,
2718 smb2_util_lease_state("RWH"));
2720 status = smb2_create(tree1, tree1, &c1);
2721 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2722 "smb2_create failed\n");
2723 CHECK_LEASE(&c1, "RWH", true, LEASE1, 0);
2724 h1 = c1.out.file.handle;
2726 /* Second open, triggers lease break to "RH" */
2728 smb2_lease_create(&c2,
2729 &ls2,
2730 false,
2731 fname_src,
2732 LEASE2,
2733 smb2_util_lease_state("RH"));
2735 req = smb2_create_send(tree2, &c2);
2736 torture_assert_not_null_goto(tctx, req, ret, done,
2737 "smb2_create_send failed\n");
2740 * Check we got the lease break, but defer the ack.
2742 CHECK_BREAK_INFO("RWH", "RH", LEASE1);
2744 ack.in.lease.lease_key =
2745 lease_break_info.lease_break.current_lease.lease_key;
2746 ack.in.lease.lease_state =
2747 lease_break_info.lease_break.new_lease_state;
2748 torture_reset_lease_break_info(tctx, &lease_break_info);
2750 /* Wait for STATUS_PENDING response */
2751 WAIT_FOR_ASYNC_RESPONSE(req);
2752 torture_assert_goto(tctx, req->cancel.can_cancel, ret, done, "pending");
2754 status = smb2_lease_break_ack(tree1, &ack);
2755 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2756 "smb2_lease_break_ack failed\n");
2757 CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1);
2760 status = smb2_create_recv(req, tree2, &c2);
2761 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2762 "smb2_create_recv failed\n");
2763 h2 = c2.out.file.handle;
2765 done:
2766 if (!smb2_util_handle_empty(h1)) {
2767 smb2_util_close(tree1, h1);
2769 if (!smb2_util_handle_empty(h2)) {
2770 smb2_util_close(tree2, h2);
2773 smb2_util_unlink(tree1, fname_src);
2775 return ret;
2779 * Basic test compound related CREATE+GETINFO+CLOSE where
2780 * the CREATE triggers a lease break. Verifies CREATE sees
2781 * an async interim response.
2783 static bool test_compound_getinfo_middle(struct torture_context *tctx,
2784 struct smb2_tree *tree1,
2785 struct smb2_tree *tree2)
2787 struct smb2_create c1 = {};
2788 struct smb2_create c2 = {};
2789 struct smb2_lease ls1 = {};
2790 struct smb2_handle h1 = {};
2791 struct smb2_request *req[3] = {};
2792 union smb_fileinfo info = {};
2793 struct smb2_getinfo rinfo = {};
2794 struct smb2_lease_break_ack ack = {};
2795 struct smb2_close cl = {};
2796 const char *fname_src = "test_compound_getinfo_middle.dat";
2797 uint32_t caps;
2798 NTSTATUS status;
2799 bool ret = true;
2801 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
2802 if (!(caps & SMB2_CAP_LEASING)) {
2803 torture_skip(tctx, "leases are not supported");
2806 smb2_util_unlink(tree1, fname_src);
2808 tree1->session->transport->lease.handler = torture_lease_handler;
2809 tree1->session->transport->lease.private_data = tree1;
2810 torture_reset_lease_break_info(tctx, &lease_break_info);
2811 lease_break_info.lease_skip_ack = true;
2813 /* First open with a RWH lease. */
2814 smb2_lease_create(&c1,
2815 &ls1,
2816 false,
2817 fname_src,
2818 LEASE1,
2819 smb2_util_lease_state("RWH"));
2821 status = smb2_create(tree1, tree1, &c1);
2822 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2823 "smb2_create failed\n");
2824 CHECK_LEASE(&c1, "RWH", true, LEASE1, 0);
2825 h1 = c1.out.file.handle;
2827 /* Second open, triggers a lease break */
2829 smb2_transport_compound_start(tree2->session->transport, 3);
2831 smb2_lease_create(&c2,
2832 NULL,
2833 false,
2834 fname_src,
2836 smb2_util_lease_state(""));
2837 req[0] = smb2_create_send(tree2, &c2);
2838 torture_assert_not_null_goto(tctx, req[0], ret, done,
2839 "smb2_create_send failed\n");
2841 smb2_transport_compound_set_related(tree2->session->transport, true);
2843 ZERO_STRUCT(info);
2844 info.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2845 info.generic.in.file.handle.data[0] = UINT64_MAX;
2846 info.generic.in.file.handle.data[0] = UINT64_MAX;
2847 req[1] = smb2_getinfo_file_send(tree2, &info);
2848 torture_assert(tctx, req[1] != NULL, "smb2_setinfo_file_send");
2850 cl.in.file.handle.data[0] = UINT64_MAX;
2851 cl.in.file.handle.data[1] = UINT64_MAX;
2853 req[2] = smb2_close_send(tree2, &cl);
2854 torture_assert(tctx, req[2] != NULL, "smb2_close_send");
2857 * Check we got the lease break, but defer the ack.
2859 CHECK_BREAK_INFO("RWH", "RH", LEASE1);
2861 ack.in.lease.lease_key =
2862 lease_break_info.lease_break.current_lease.lease_key;
2863 ack.in.lease.lease_state =
2864 lease_break_info.lease_break.new_lease_state;
2865 torture_reset_lease_break_info(tctx, &lease_break_info);
2867 /* Wait for async response */
2868 WAIT_FOR_ASYNC_RESPONSE(req[0]);
2870 torture_assert_goto(tctx, req[0]->state == SMB2_REQUEST_RECV, ret, done,
2871 "smb2_create finished");
2872 torture_assert_goto(tctx, req[1]->state == SMB2_REQUEST_RECV, ret, done,
2873 "smb2_getinfo finished");
2874 torture_assert_goto(tctx, req[2]->state == SMB2_REQUEST_RECV, ret, done,
2875 "smb2_close finished");
2876 torture_assert_goto(tctx, req[0]->cancel.can_cancel, ret, done, "pending");
2877 torture_assert_goto(tctx, !req[1]->cancel.can_cancel, ret, done, "pending");
2878 torture_assert_goto(tctx, !req[2]->cancel.can_cancel, ret, done, "pending");
2880 status = smb2_lease_break_ack(tree1, &ack);
2881 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2882 "smb2_lease_break_ack failed\n");
2883 CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1);
2885 status = smb2_create_recv(req[0], tree2, &c2);
2886 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2887 "smb2_create_recv failed\n");
2889 status = smb2_getinfo_recv(req[1], tree2, &rinfo);
2890 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2891 "smb2_getinfo_recv failed\n");
2893 status = smb2_close_recv(req[2], &cl);
2894 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2895 "smb2_close_recv failed\n");
2897 done:
2898 if (!smb2_util_handle_empty(h1)) {
2899 smb2_util_close(tree1, h1);
2901 smb2_util_unlink(tree1, fname_src);
2903 return ret;
2907 * Checks a lease break by a rename where src and dst name are the same does not
2908 * trigger a pending async response, but does trigger a h-lease break.
2910 static bool test_rename_same_srcdst_non_compound_no_async(
2911 struct torture_context *tctx,
2912 struct smb2_tree *tree1,
2913 struct smb2_tree *tree2)
2915 struct smb2_create c1 = {};
2916 struct smb2_create c2 = {};
2917 struct smb2_lease ls1 = {};
2918 struct smb2_lease ls2 = {};
2919 struct smb2_handle h1 = {};
2920 struct smb2_handle h2 = {};
2921 struct smb2_request *req = NULL;
2922 struct smb2_lease_break_ack ack = {};
2923 union smb_setfileinfo sinfo = {};
2924 const char *fname_src = "test_rename_non_compound_no_async.dat";
2925 const char *fname_dst = "test_rename_non_compound_no_async.dat";
2926 uint32_t caps;
2927 NTSTATUS status;
2928 bool ret = true;
2930 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
2931 if (!(caps & SMB2_CAP_LEASING)) {
2932 torture_skip(tctx, "leases are not supported");
2935 smb2_util_unlink(tree1, fname_src);
2936 smb2_util_unlink(tree1, fname_dst);
2938 tree1->session->transport->lease.handler = torture_lease_handler;
2939 tree1->session->transport->lease.private_data = tree1;
2940 torture_reset_lease_break_info(tctx, &lease_break_info);
2941 lease_break_info.lease_skip_ack = true;
2943 /* First open with a RH lease. */
2944 smb2_lease_create(&c1,
2945 &ls1,
2946 false,
2947 fname_src,
2948 LEASE1,
2949 smb2_util_lease_state("RH"));
2951 status = smb2_create(tree1, tree1, &c1);
2952 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2953 "smb2_create failed\n");
2954 CHECK_LEASE(&c1, "RH", true, LEASE1, 0);
2955 h1 = c1.out.file.handle;
2957 /* Second open, also with a RH lease, this will do the rename */
2959 smb2_lease_create(&c2,
2960 &ls2,
2961 false,
2962 fname_src,
2963 LEASE2,
2964 smb2_util_lease_state("RH"));
2965 status = smb2_create(tree2, tree2, &c2);
2966 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2967 "smb2_create failed\n");
2968 CHECK_LEASE(&c2, "RH", true, LEASE2, 0);
2969 h2 = c2.out.file.handle;
2971 /* Break with a rename. */
2972 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
2973 sinfo.rename_information.in.file.handle = h2;
2974 sinfo.rename_information.in.new_name = fname_dst;
2975 req = smb2_setinfo_file_send(tree2, &sinfo);
2976 torture_assert(tctx, req != NULL, "smb2_setinfo_file_send");
2979 * Check we got the lease break, but defer the ack.
2981 CHECK_BREAK_INFO("RH", "R", LEASE1);
2983 ack.in.lease.lease_key =
2984 lease_break_info.lease_break.current_lease.lease_key;
2985 ack.in.lease.lease_state =
2986 lease_break_info.lease_break.new_lease_state;
2987 torture_reset_lease_break_info(tctx, &lease_break_info);
2989 /* Give the server enough time to possibly send a pending response */
2990 smb_msleep(1000);
2992 status = smb2_lease_break_ack(tree1, &ack);
2993 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2994 "smb2_lease_break_ack failed\n");
2995 CHECK_LEASE_BREAK_ACK(&ack, "R", LEASE1);
2998 * Sending the lease break ACK would have also read the
2999 * NT_STATUS_PENDING interim response if any, but a Windows server
3000 * doesn't send one, check this. This is in contract to a lease break
3001 * triggered by an SMB2-CREATE.
3003 torture_assert_goto(tctx, !req->cancel.can_cancel, ret, done, "pending");
3005 /* Get the rename reply. */
3006 status = smb2_setinfo_recv(req);
3007 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3008 "smb2_setinfo_recv failed\n");
3010 done:
3011 if (!smb2_util_handle_empty(h1)) {
3012 smb2_util_close(tree1, h1);
3014 if (!smb2_util_handle_empty(h2)) {
3015 smb2_util_close(tree2, h2);
3018 smb2_util_unlink(tree1, fname_src);
3019 smb2_util_unlink(tree1, fname_dst);
3021 return ret;
3025 * Checks a lease break by a rename does not trigger a pending async response.
3027 static bool test_rename_non_compound_no_async(struct torture_context *tctx,
3028 struct smb2_tree *tree1,
3029 struct smb2_tree *tree2)
3031 struct smb2_create c1 = {};
3032 struct smb2_create c2 = {};
3033 struct smb2_lease ls1 = {};
3034 struct smb2_lease ls2 = {};
3035 struct smb2_handle h1 = {};
3036 struct smb2_handle h2 = {};
3037 struct smb2_request *req = NULL;
3038 struct smb2_lease_break_ack ack = {};
3039 union smb_setfileinfo sinfo = {};
3040 const char *fname_src = "test_rename_non_compound_no_async_src.dat";
3041 const char *fname_dst = "test_rename_non_compound_no_async_dst.dat";
3042 uint32_t caps;
3043 NTSTATUS status;
3044 bool ret = true;
3046 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
3047 if (!(caps & SMB2_CAP_LEASING)) {
3048 torture_skip(tctx, "leases are not supported");
3051 smb2_util_unlink(tree1, fname_src);
3052 smb2_util_unlink(tree1, fname_dst);
3054 tree1->session->transport->lease.handler = torture_lease_handler;
3055 tree1->session->transport->lease.private_data = tree1;
3056 torture_reset_lease_break_info(tctx, &lease_break_info);
3057 lease_break_info.lease_skip_ack = true;
3059 /* First open with a RH lease. */
3060 smb2_lease_create(&c1,
3061 &ls1,
3062 false,
3063 fname_src,
3064 LEASE1,
3065 smb2_util_lease_state("RH"));
3067 status = smb2_create(tree1, tree1, &c1);
3068 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3069 "smb2_create failed\n");
3070 CHECK_LEASE(&c1, "RH", true, LEASE1, 0);
3071 h1 = c1.out.file.handle;
3073 /* Second open, also with a RH lease, this will to the rename */
3075 smb2_lease_create(&c2,
3076 &ls2,
3077 false,
3078 fname_src,
3079 LEASE2,
3080 smb2_util_lease_state("RH"));
3081 status = smb2_create(tree2, tree2, &c2);
3082 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3083 "smb2_create failed\n");
3084 CHECK_LEASE(&c2, "RH", true, LEASE2, 0);
3085 h2 = c2.out.file.handle;
3087 /* Break with a rename. */
3088 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
3089 sinfo.rename_information.in.file.handle = h2;
3090 sinfo.rename_information.in.new_name = fname_dst;
3091 req = smb2_setinfo_file_send(tree2, &sinfo);
3092 torture_assert(tctx, req != NULL, "smb2_setinfo_file_send");
3095 * Check we got the lease break, but defer the ack.
3097 CHECK_BREAK_INFO("RH", "R", LEASE1);
3099 ack.in.lease.lease_key =
3100 lease_break_info.lease_break.current_lease.lease_key;
3101 ack.in.lease.lease_state =
3102 lease_break_info.lease_break.new_lease_state;
3103 torture_reset_lease_break_info(tctx, &lease_break_info);
3105 /* Give the server enough time to possibly send a pending response */
3106 smb_msleep(1000);
3108 status = smb2_lease_break_ack(tree1, &ack);
3109 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3110 "smb2_lease_break_ack failed\n");
3111 CHECK_LEASE_BREAK_ACK(&ack, "R", LEASE1);
3114 * Sending the lease break ACK would have also read the
3115 * NT_STATUS_PENDING interim response if any, but a Windows server
3116 * doesn't send one, check this. This is in contract to a lease break
3117 * triggered by an SMB2-CREATE.
3119 torture_assert_goto(tctx, !req->cancel.can_cancel, ret, done, "pending");
3121 /* Get the rename reply. */
3122 status = smb2_setinfo_recv(req);
3123 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3124 "smb2_setinfo_recv failed\n");
3126 done:
3127 if (!smb2_util_handle_empty(h1)) {
3128 smb2_util_close(tree1, h1);
3130 if (!smb2_util_handle_empty(h2)) {
3131 smb2_util_close(tree2, h2);
3134 smb2_util_unlink(tree1, fname_src);
3135 smb2_util_unlink(tree1, fname_dst);
3137 return ret;
3141 * Test a compound SMB2-CREATE+SMB2-SETINFO(rename) works and doesn't trigger a
3142 * pending async response.
3144 static bool test_compound_rename_last(struct torture_context *tctx,
3145 struct smb2_tree *tree1,
3146 struct smb2_tree *tree2)
3148 struct smb2_create c1 = {};
3149 struct smb2_create c2 = {};
3150 struct smb2_lease ls1 = {};
3151 struct smb2_lease ls2 = {};
3152 struct smb2_handle h1 = {};
3153 struct smb2_handle h2 = {};
3154 struct smb2_request *req[2] = {};
3155 union smb_setfileinfo sinfo = {};
3156 struct smb2_lease_break_ack ack = {};
3157 const char *fname_src = "test_compound_rename_last_src.dat";
3158 const char *fname_dst = "test_compound_rename_last_dst.dat";
3159 uint32_t caps;
3160 NTSTATUS status;
3161 bool ret = true;
3163 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
3164 if (!(caps & SMB2_CAP_LEASING)) {
3165 torture_skip(tctx, "leases are not supported");
3168 smb2_util_unlink(tree1, fname_src);
3169 smb2_util_unlink(tree1, fname_dst);
3171 tree1->session->transport->lease.handler = torture_lease_handler;
3172 tree1->session->transport->lease.private_data = tree1;
3173 torture_reset_lease_break_info(tctx, &lease_break_info);
3174 lease_break_info.lease_skip_ack = true;
3176 /* First open with a RH lease. */
3177 smb2_lease_create(&c1,
3178 &ls1,
3179 false,
3180 fname_src,
3181 LEASE1,
3182 smb2_util_lease_state("RH"));
3184 status = smb2_create(tree1, tree1, &c1);
3185 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3186 "smb2_create failed\n");
3187 CHECK_LEASE(&c1, "RH", true, LEASE1, 0);
3188 h1 = c1.out.file.handle;
3190 /* Second open, also with a RH lease, this will to the rename */
3192 smb2_transport_compound_start(tree2->session->transport, 2);
3194 smb2_lease_create(&c2,
3195 &ls2,
3196 false,
3197 fname_src,
3198 LEASE2,
3199 smb2_util_lease_state(""));
3200 req[0] = smb2_create_send(tree2, &c2);
3201 torture_assert_not_null_goto(tctx, req[0], ret, done,
3202 "smb2_create_send failed\n");
3204 smb2_transport_compound_set_related(tree2->session->transport, true);
3206 /* Break with a rename. */
3207 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
3208 sinfo.rename_information.in.file.handle.data[0] = UINT64_MAX;
3209 sinfo.rename_information.in.file.handle.data[1] = UINT64_MAX;
3210 sinfo.rename_information.in.new_name = fname_dst;
3211 req[1] = smb2_setinfo_file_send(tree2, &sinfo);
3212 torture_assert(tctx, req[1] != NULL, "smb2_setinfo_file_send");
3215 * Check we got the lease break, but defer the ack.
3217 CHECK_BREAK_INFO("RH", "R", LEASE1);
3219 ack.in.lease.lease_key =
3220 lease_break_info.lease_break.current_lease.lease_key;
3221 ack.in.lease.lease_state =
3222 lease_break_info.lease_break.new_lease_state;
3223 torture_reset_lease_break_info(tctx, &lease_break_info);
3225 /* Give the server enough time to possibly send a pending response */
3226 smb_msleep(1000);
3228 status = smb2_lease_break_ack(tree1, &ack);
3229 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3230 "smb2_lease_break_ack failed\n");
3231 CHECK_LEASE_BREAK_ACK(&ack, "R", LEASE1);
3234 * Sending the lease break ACK would have also read the
3235 * NT_STATUS_PENDING interim response if any, but a Windows server
3236 * doesn't send one, check this. This is in contract to a lease break
3237 * triggered by an SMB2-CREATE.
3239 torture_assert_goto(tctx, req[0]->state == SMB2_REQUEST_RECV, ret, done,
3240 "state not SMB2_REQUEST_RECV");
3241 torture_assert_goto(tctx, req[1]->state == SMB2_REQUEST_RECV, ret, done,
3242 "state not SMB2_REQUEST_RECV");
3243 torture_assert_goto(tctx, !req[1]->cancel.can_cancel, ret, done, "pending");
3245 status = smb2_create_recv(req[0], tree2, &c2);
3246 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3247 "smb2_setinfo_recv failed\n");
3248 h2 = c2.out.file.handle;
3250 /* Get the rename reply. */
3251 status = smb2_setinfo_recv(req[1]);
3252 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3253 "smb2_setinfo_recv failed\n");
3255 done:
3256 if (!smb2_util_handle_empty(h1)) {
3257 smb2_util_close(tree1, h1);
3259 if (!smb2_util_handle_empty(h2)) {
3260 smb2_util_close(tree2, h2);
3263 smb2_util_unlink(tree1, fname_src);
3264 smb2_util_unlink(tree1, fname_dst);
3266 return ret;
3270 * Compound related CREATE + SETINFO(rename) + CLOSE, rename triggers a lease
3271 * break. Verify we don't get an async interim response for the SETINFO and all
3272 * responses are received in a single compound response.
3274 static bool test_compound_rename_middle(struct torture_context *tctx,
3275 struct smb2_tree *tree1,
3276 struct smb2_tree *tree2)
3278 struct smb2_create c1 = {};
3279 struct smb2_create c2 = {};
3280 struct smb2_lease ls1 = {};
3281 struct smb2_handle h1 = {};
3282 struct smb2_request *req[3] = {};
3283 union smb_setfileinfo sinfo = {};
3284 struct smb2_lease_break_ack ack = {};
3285 struct smb2_close cl = {};
3286 const char *fname_src = "test_compound_rename_middle_src.dat";
3287 const char *fname_dst = "test_compound_rename_middle_dst.dat";
3288 uint32_t caps;
3289 NTSTATUS status;
3290 bool ret = true;
3292 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
3293 if (!(caps & SMB2_CAP_LEASING)) {
3294 torture_skip(tctx, "leases are not supported");
3297 smb2_util_unlink(tree1, fname_src);
3298 smb2_util_unlink(tree1, fname_dst);
3300 tree1->session->transport->lease.handler = torture_lease_handler;
3301 tree1->session->transport->lease.private_data = tree1;
3302 torture_reset_lease_break_info(tctx, &lease_break_info);
3303 lease_break_info.lease_skip_ack = true;
3305 /* First open with a RH lease. */
3306 smb2_lease_create(&c1,
3307 &ls1,
3308 false,
3309 fname_src,
3310 LEASE1,
3311 smb2_util_lease_state("RH"));
3313 status = smb2_create(tree1, tree1, &c1);
3314 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3315 "smb2_create failed\n");
3316 CHECK_LEASE(&c1, "RH", true, LEASE1, 0);
3317 h1 = c1.out.file.handle;
3319 /* Second open, this will to the rename */
3321 smb2_transport_compound_start(tree2->session->transport, 3);
3323 smb2_lease_create(&c2,
3324 NULL,
3325 false,
3326 fname_src,
3328 smb2_util_lease_state(""));
3329 req[0] = smb2_create_send(tree2, &c2);
3330 torture_assert_not_null_goto(tctx, req[0], ret, done,
3331 "smb2_create_send failed\n");
3333 smb2_transport_compound_set_related(tree2->session->transport, true);
3335 /* Break with a rename. */
3336 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
3337 sinfo.rename_information.in.file.handle.data[0] = UINT64_MAX;
3338 sinfo.rename_information.in.file.handle.data[1] = UINT64_MAX;
3339 sinfo.rename_information.in.new_name = fname_dst;
3340 req[1] = smb2_setinfo_file_send(tree2, &sinfo);
3341 torture_assert(tctx, req[1] != NULL, "smb2_setinfo_file_send");
3343 cl.in.file.handle.data[0] = UINT64_MAX;
3344 cl.in.file.handle.data[1] = UINT64_MAX;
3346 req[2] = smb2_close_send(tree2, &cl);
3347 torture_assert(tctx, req[2] != NULL, "smb2_close_send");
3349 /* Give the server enough time to possibly send a pending response */
3350 smb_msleep(1000);
3353 * Check we got the lease break, but defer the ack.
3355 CHECK_BREAK_INFO("RH", "R", LEASE1);
3357 ack.in.lease.lease_key =
3358 lease_break_info.lease_break.current_lease.lease_key;
3359 ack.in.lease.lease_state =
3360 lease_break_info.lease_break.new_lease_state;
3361 torture_reset_lease_break_info(tctx, &lease_break_info);
3363 torture_assert_goto(tctx, !req[0]->cancel.can_cancel, ret, done, "pending");
3364 torture_assert_goto(tctx, !req[1]->cancel.can_cancel, ret, done, "pending");
3365 torture_assert_goto(tctx, !req[2]->cancel.can_cancel, ret, done, "pending");
3366 torture_assert_goto(tctx, req[0]->state == SMB2_REQUEST_RECV, ret, done,
3367 "state not SMB2_REQUEST_RECV");
3368 torture_assert_goto(tctx, req[1]->state == SMB2_REQUEST_RECV, ret, done,
3369 "state not SMB2_REQUEST_RECV");
3370 torture_assert_goto(tctx, req[2]->state == SMB2_REQUEST_RECV, ret, done,
3371 "state not SMB2_REQUEST_RECV");
3373 status = smb2_lease_break_ack(tree1, &ack);
3374 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3375 "smb2_lease_break_ack failed\n");
3376 CHECK_LEASE_BREAK_ACK(&ack, "R", LEASE1);
3379 status = smb2_create_recv(req[0], tree2, &c2);
3380 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3381 "smb2_setinfo_recv failed\n");
3383 /* Get the rename reply. */
3384 status = smb2_setinfo_recv(req[1]);
3385 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3386 "smb2_setinfo_recv failed\n");
3388 status = smb2_close_recv(req[2], &cl);
3389 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3390 "smb2_setinfo_recv failed\n");
3392 done:
3393 if (!smb2_util_handle_empty(h1)) {
3394 smb2_util_close(tree1, h1);
3397 smb2_util_unlink(tree1, fname_src);
3398 smb2_util_unlink(tree1, fname_dst);
3400 return ret;
3403 struct torture_suite *torture_smb2_compound_init(TALLOC_CTX *ctx)
3405 struct torture_suite *suite = torture_suite_create(ctx, "compound");
3407 torture_suite_add_1smb2_test(suite, "related1", test_compound_related1);
3408 torture_suite_add_1smb2_test(suite, "related2", test_compound_related2);
3409 torture_suite_add_1smb2_test(suite, "related3",
3410 test_compound_related3);
3411 torture_suite_add_1smb2_test(suite, "related4",
3412 test_compound_related4);
3413 torture_suite_add_1smb2_test(suite, "related5",
3414 test_compound_related5);
3415 torture_suite_add_1smb2_test(suite, "related6",
3416 test_compound_related6);
3417 torture_suite_add_1smb2_test(suite, "related7",
3418 test_compound_related7);
3419 torture_suite_add_1smb2_test(suite, "related8",
3420 test_compound_related8);
3421 torture_suite_add_1smb2_test(suite, "related9",
3422 test_compound_related9);
3423 torture_suite_add_1smb2_test(suite, "unrelated1", test_compound_unrelated1);
3424 torture_suite_add_1smb2_test(suite, "invalid1", test_compound_invalid1);
3425 torture_suite_add_1smb2_test(suite, "invalid2", test_compound_invalid2);
3426 torture_suite_add_1smb2_test(suite, "invalid3", test_compound_invalid3);
3427 torture_suite_add_1smb2_test(
3428 suite, "invalid4", test_compound_invalid4);
3429 torture_suite_add_1smb2_test(suite, "interim1", test_compound_interim1);
3430 torture_suite_add_1smb2_test(suite, "interim2", test_compound_interim2);
3431 torture_suite_add_1smb2_test(suite, "interim3", test_compound_interim3);
3432 torture_suite_add_1smb2_test(suite, "compound-break", test_compound_break);
3433 torture_suite_add_1smb2_test(suite, "compound-padding", test_compound_padding);
3434 torture_suite_add_1smb2_test(suite, "create-write-close",
3435 test_compound_create_write_close);
3437 suite->description = talloc_strdup(suite, "SMB2-COMPOUND tests");
3439 return suite;
3442 struct torture_suite *torture_smb2_compound_find_init(TALLOC_CTX *ctx)
3444 struct torture_suite *suite = torture_suite_create(ctx, "compound_find");
3446 torture_suite_add_1smb2_test(suite, "compound_find_related", test_compound_find_related);
3447 torture_suite_add_1smb2_test(suite, "compound_find_unrelated", test_compound_find_unrelated);
3448 torture_suite_add_1smb2_test(suite, "compound_find_close", test_compound_find_close);
3450 suite->description = talloc_strdup(suite, "SMB2-COMPOUND-FIND tests");
3452 return suite;
3455 struct torture_suite *torture_smb2_compound_async_init(TALLOC_CTX *ctx)
3457 struct torture_suite *suite = torture_suite_create(ctx,
3458 "compound_async");
3460 torture_suite_add_1smb2_test(suite, "flush_close",
3461 test_compound_async_flush_close);
3462 torture_suite_add_1smb2_test(suite, "flush_flush",
3463 test_compound_async_flush_flush);
3464 torture_suite_add_1smb2_test(suite, "write_write",
3465 test_compound_async_write_write);
3466 torture_suite_add_1smb2_test(suite, "read_read",
3467 test_compound_async_read_read);
3468 torture_suite_add_2smb2_test(suite, "create_lease_break_async",
3469 test_create_lease_break_async);
3470 torture_suite_add_2smb2_test(suite, "getinfo_middle",
3471 test_compound_getinfo_middle);
3472 torture_suite_add_2smb2_test(suite, "rename_same_srcdst_non_compound_no_async",
3473 test_rename_same_srcdst_non_compound_no_async);
3474 torture_suite_add_2smb2_test(suite, "rename_non_compound_no_async",
3475 test_rename_non_compound_no_async);
3476 torture_suite_add_2smb2_test(suite, "rename_last",
3477 test_compound_rename_last);
3478 torture_suite_add_2smb2_test(suite, "rename_middle",
3479 test_compound_rename_middle);
3481 suite->description = talloc_strdup(suite, "SMB2-COMPOUND-ASYNC tests");
3483 return suite;