s4: torture: Add compound_async.read_read test to show we don't go async on the last...
[Samba.git] / source4 / torture / smb2 / compound.c
bloba9dfd727a7f54fc73ff866d38abf289712425047
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"
32 #define CHECK_STATUS(status, correct) do { \
33 if (!NT_STATUS_EQUAL(status, correct)) { \
34 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
35 nt_errstr(status), nt_errstr(correct)); \
36 ret = false; \
37 goto done; \
38 }} while (0)
40 #define CHECK_VALUE(v, correct) do { \
41 if ((v) != (correct)) { \
42 torture_result(tctx, TORTURE_FAIL, \
43 "(%s) Incorrect value %s=%d - should be %d\n", \
44 __location__, #v, (int)v, (int)correct); \
45 ret = false; \
46 }} while (0)
48 #define WAIT_FOR_ASYNC_RESPONSE(req) \
49 while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { \
50 if (tevent_loop_once(tctx->ev) != 0) { \
51 break; \
52 } \
55 static struct {
56 struct smb2_handle handle;
57 uint8_t level;
58 struct smb2_break br;
59 int count;
60 int failures;
61 NTSTATUS failure_status;
62 } break_info;
64 static void torture_oplock_break_callback(struct smb2_request *req)
66 NTSTATUS status;
67 struct smb2_break br;
69 ZERO_STRUCT(br);
70 status = smb2_break_recv(req, &break_info.br);
71 if (!NT_STATUS_IS_OK(status)) {
72 break_info.failures++;
73 break_info.failure_status = status;
76 return;
79 /* A general oplock break notification handler. This should be used when a
80 * test expects to break from batch or exclusive to a lower level. */
81 static bool torture_oplock_handler(struct smb2_transport *transport,
82 const struct smb2_handle *handle,
83 uint8_t level,
84 void *private_data)
86 struct smb2_tree *tree = private_data;
87 const char *name;
88 struct smb2_request *req;
89 ZERO_STRUCT(break_info.br);
91 break_info.handle = *handle;
92 break_info.level = level;
93 break_info.count++;
95 switch (level) {
96 case SMB2_OPLOCK_LEVEL_II:
97 name = "level II";
98 break;
99 case SMB2_OPLOCK_LEVEL_NONE:
100 name = "none";
101 break;
102 default:
103 name = "unknown";
104 break_info.failures++;
106 printf("Acking to %s [0x%02X] in oplock handler\n", name, level);
108 break_info.br.in.file.handle = *handle;
109 break_info.br.in.oplock_level = level;
110 break_info.br.in.reserved = 0;
111 break_info.br.in.reserved2 = 0;
113 req = smb2_break_send(tree, &break_info.br);
114 req->async.fn = torture_oplock_break_callback;
115 req->async.private_data = NULL;
116 return true;
119 static bool test_compound_break(struct torture_context *tctx,
120 struct smb2_tree *tree)
122 const char *fname1 = "some-file.pptx";
123 NTSTATUS status;
124 bool ret = true;
125 union smb_open io1;
126 struct smb2_create io2;
127 struct smb2_getinfo gf;
128 struct smb2_request *req[2];
129 struct smb2_handle h1;
130 struct smb2_handle h;
132 tree->session->transport->oplock.handler = torture_oplock_handler;
133 tree->session->transport->oplock.private_data = tree;
135 ZERO_STRUCT(break_info);
138 base ntcreatex parms
140 ZERO_STRUCT(io1.smb2);
141 io1.generic.level = RAW_OPEN_SMB2;
142 io1.smb2.in.desired_access = (SEC_STD_SYNCHRONIZE|
143 SEC_STD_READ_CONTROL|
144 SEC_FILE_READ_ATTRIBUTE|
145 SEC_FILE_READ_EA|
146 SEC_FILE_READ_DATA);
147 io1.smb2.in.alloc_size = 0;
148 io1.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
149 io1.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
150 NTCREATEX_SHARE_ACCESS_WRITE|
151 NTCREATEX_SHARE_ACCESS_DELETE;
152 io1.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
153 io1.smb2.in.create_options = 0;
154 io1.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
155 io1.smb2.in.security_flags = 0;
156 io1.smb2.in.fname = fname1;
158 torture_comment(tctx, "TEST2: open a file with an batch "
159 "oplock (share mode: all)\n");
160 io1.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
162 status = smb2_create(tree, tctx, &(io1.smb2));
163 torture_assert_ntstatus_ok(tctx, status, "Error opening the file");
165 h1 = io1.smb2.out.file.handle;
167 torture_comment(tctx, "TEST2: Opening second time with compound\n");
169 ZERO_STRUCT(io2);
171 io2.in.desired_access = (SEC_STD_SYNCHRONIZE|
172 SEC_FILE_READ_ATTRIBUTE|
173 SEC_FILE_READ_EA);
174 io2.in.alloc_size = 0;
175 io2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
176 io2.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
177 NTCREATEX_SHARE_ACCESS_WRITE|
178 NTCREATEX_SHARE_ACCESS_DELETE;
179 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
180 io2.in.create_options = 0;
181 io2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
182 io2.in.security_flags = 0;
183 io2.in.fname = fname1;
184 io2.in.oplock_level = 0;
186 smb2_transport_compound_start(tree->session->transport, 2);
188 req[0] = smb2_create_send(tree, &io2);
190 smb2_transport_compound_set_related(tree->session->transport, true);
192 h.data[0] = UINT64_MAX;
193 h.data[1] = UINT64_MAX;
195 ZERO_STRUCT(gf);
196 gf.in.file.handle = h;
197 gf.in.info_type = SMB2_0_INFO_FILE;
198 gf.in.info_class = 0x16;
199 gf.in.output_buffer_length = 0x1000;
200 gf.in.input_buffer = data_blob_null;
202 req[1] = smb2_getinfo_send(tree, &gf);
204 status = smb2_create_recv(req[0], tree, &io2);
205 CHECK_STATUS(status, NT_STATUS_OK);
207 status = smb2_getinfo_recv(req[1], tree, &gf);
208 CHECK_STATUS(status, NT_STATUS_OK);
210 done:
212 smb2_util_close(tree, h1);
213 smb2_util_unlink(tree, fname1);
214 return ret;
217 static bool test_compound_related1(struct torture_context *tctx,
218 struct smb2_tree *tree)
220 struct smb2_handle hd;
221 struct smb2_create cr;
222 NTSTATUS status;
223 const char *fname = "compound_related1.dat";
224 struct smb2_close cl;
225 bool ret = true;
226 struct smb2_request *req[2];
227 struct smbXcli_tcon *saved_tcon = tree->smbXcli;
228 struct smbXcli_session *saved_session = tree->session->smbXcli;
230 smb2_transport_credits_ask_num(tree->session->transport, 2);
232 smb2_util_unlink(tree, fname);
234 smb2_transport_credits_ask_num(tree->session->transport, 1);
236 ZERO_STRUCT(cr);
237 cr.in.security_flags = 0x00;
238 cr.in.oplock_level = 0;
239 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
240 cr.in.create_flags = 0x00000000;
241 cr.in.reserved = 0x00000000;
242 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
243 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
244 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
245 NTCREATEX_SHARE_ACCESS_WRITE |
246 NTCREATEX_SHARE_ACCESS_DELETE;
247 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
248 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
249 NTCREATEX_OPTIONS_ASYNC_ALERT |
250 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
251 0x00200000;
252 cr.in.fname = fname;
254 smb2_transport_compound_start(tree->session->transport, 2);
256 req[0] = smb2_create_send(tree, &cr);
258 smb2_transport_compound_set_related(tree->session->transport, true);
260 hd.data[0] = UINT64_MAX;
261 hd.data[1] = UINT64_MAX;
263 ZERO_STRUCT(cl);
264 cl.in.file.handle = hd;
266 tree->smbXcli = smbXcli_tcon_create(tree);
267 smb2cli_tcon_set_values(tree->smbXcli,
268 NULL, /* session */
269 0xFFFFFFFF, /* tcon_id */
270 0, /* type */
271 0, /* flags */
272 0, /* capabilities */
273 0 /* maximal_access */);
275 tree->session->smbXcli = smbXcli_session_shallow_copy(tree->session,
276 tree->session->smbXcli);
277 smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0);
279 req[1] = smb2_close_send(tree, &cl);
281 status = smb2_create_recv(req[0], tree, &cr);
282 CHECK_STATUS(status, NT_STATUS_OK);
283 status = smb2_close_recv(req[1], &cl);
284 CHECK_STATUS(status, NT_STATUS_OK);
286 TALLOC_FREE(tree->smbXcli);
287 tree->smbXcli = saved_tcon;
288 TALLOC_FREE(tree->session->smbXcli);
289 tree->session->smbXcli = saved_session;
291 smb2_util_unlink(tree, fname);
292 done:
293 return ret;
296 static bool test_compound_related2(struct torture_context *tctx,
297 struct smb2_tree *tree)
299 struct smb2_handle hd;
300 struct smb2_create cr;
301 NTSTATUS status;
302 const char *fname = "compound_related2.dat";
303 struct smb2_close cl;
304 bool ret = true;
305 struct smb2_request *req[5];
306 struct smbXcli_tcon *saved_tcon = tree->smbXcli;
307 struct smbXcli_session *saved_session = tree->session->smbXcli;
309 smb2_transport_credits_ask_num(tree->session->transport, 5);
311 smb2_util_unlink(tree, fname);
313 smb2_transport_credits_ask_num(tree->session->transport, 1);
315 ZERO_STRUCT(cr);
316 cr.in.security_flags = 0x00;
317 cr.in.oplock_level = 0;
318 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
319 cr.in.create_flags = 0x00000000;
320 cr.in.reserved = 0x00000000;
321 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
322 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
323 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
324 NTCREATEX_SHARE_ACCESS_WRITE |
325 NTCREATEX_SHARE_ACCESS_DELETE;
326 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
327 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
328 NTCREATEX_OPTIONS_ASYNC_ALERT |
329 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
330 0x00200000;
331 cr.in.fname = fname;
333 smb2_transport_compound_start(tree->session->transport, 5);
335 req[0] = smb2_create_send(tree, &cr);
337 hd.data[0] = UINT64_MAX;
338 hd.data[1] = UINT64_MAX;
340 smb2_transport_compound_set_related(tree->session->transport, true);
342 ZERO_STRUCT(cl);
343 cl.in.file.handle = hd;
345 tree->smbXcli = smbXcli_tcon_create(tree);
346 smb2cli_tcon_set_values(tree->smbXcli,
347 NULL, /* session */
348 0xFFFFFFFF, /* tcon_id */
349 0, /* type */
350 0, /* flags */
351 0, /* capabilities */
352 0 /* maximal_access */);
354 tree->session->smbXcli = smbXcli_session_shallow_copy(tree->session,
355 tree->session->smbXcli);
356 smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0);
358 req[1] = smb2_close_send(tree, &cl);
359 req[2] = smb2_close_send(tree, &cl);
360 req[3] = smb2_close_send(tree, &cl);
361 req[4] = smb2_close_send(tree, &cl);
363 status = smb2_create_recv(req[0], tree, &cr);
364 CHECK_STATUS(status, NT_STATUS_OK);
365 status = smb2_close_recv(req[1], &cl);
366 CHECK_STATUS(status, NT_STATUS_OK);
367 status = smb2_close_recv(req[2], &cl);
368 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
369 status = smb2_close_recv(req[3], &cl);
370 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
371 status = smb2_close_recv(req[4], &cl);
372 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
374 TALLOC_FREE(tree->smbXcli);
375 tree->smbXcli = saved_tcon;
376 TALLOC_FREE(tree->session->smbXcli);
377 tree->session->smbXcli = saved_session;
379 smb2_util_unlink(tree, fname);
380 done:
381 return ret;
384 static bool test_compound_related3(struct torture_context *tctx,
385 struct smb2_tree *tree)
387 struct smb2_handle hd;
388 struct smb2_ioctl io;
389 struct smb2_create cr;
390 struct smb2_close cl;
391 const char *fname = "compound_related3.dat";
392 struct smb2_request *req[3];
393 NTSTATUS status;
394 bool ret = false;
396 smb2_util_unlink(tree, fname);
398 ZERO_STRUCT(cr);
399 cr.in.security_flags = 0x00;
400 cr.in.oplock_level = 0;
401 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
402 cr.in.create_flags = 0x00000000;
403 cr.in.reserved = 0x00000000;
404 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
405 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
406 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
407 NTCREATEX_SHARE_ACCESS_WRITE |
408 NTCREATEX_SHARE_ACCESS_DELETE;
409 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
410 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
411 NTCREATEX_OPTIONS_ASYNC_ALERT |
412 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
413 0x00200000;
414 cr.in.fname = fname;
416 smb2_transport_compound_start(tree->session->transport, 3);
418 req[0] = smb2_create_send(tree, &cr);
420 hd.data[0] = UINT64_MAX;
421 hd.data[1] = UINT64_MAX;
423 smb2_transport_compound_set_related(tree->session->transport, true);
425 ZERO_STRUCT(io);
426 io.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID;
427 io.in.file.handle = hd;
428 io.in.reserved2 = 0;
429 io.in.max_output_response = 64;
430 io.in.flags = 1;
432 req[1] = smb2_ioctl_send(tree, &io);
434 ZERO_STRUCT(cl);
435 cl.in.file.handle = hd;
437 req[2] = smb2_close_send(tree, &cl);
439 status = smb2_create_recv(req[0], tree, &cr);
440 CHECK_STATUS(status, NT_STATUS_OK);
441 status = smb2_ioctl_recv(req[1], tree, &io);
442 CHECK_STATUS(status, NT_STATUS_OK);
443 status = smb2_close_recv(req[2], &cl);
444 CHECK_STATUS(status, NT_STATUS_OK);
446 status = smb2_util_unlink(tree, fname);
447 CHECK_STATUS(status, NT_STATUS_OK);
449 ret = true;
450 done:
451 return ret;
454 static bool test_compound_related4(struct torture_context *tctx,
455 struct smb2_tree *tree)
457 const char *fname = "compound_related4.dat";
458 struct security_descriptor *sd = NULL;
459 struct smb2_handle hd;
460 struct smb2_create cr;
461 union smb_setfileinfo set;
462 struct smb2_ioctl io;
463 struct smb2_close cl;
464 struct smb2_request *req[4];
465 NTSTATUS status;
466 bool ret = true;
468 smb2_util_unlink(tree, fname);
470 ZERO_STRUCT(cr);
471 cr.level = RAW_OPEN_SMB2;
472 cr.in.create_flags = 0;
473 cr.in.desired_access = SEC_STD_READ_CONTROL |
474 SEC_STD_WRITE_DAC |
475 SEC_STD_WRITE_OWNER;
476 cr.in.create_options = 0;
477 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
478 cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
479 NTCREATEX_SHARE_ACCESS_READ |
480 NTCREATEX_SHARE_ACCESS_WRITE;
481 cr.in.alloc_size = 0;
482 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
483 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
484 cr.in.security_flags = 0;
485 cr.in.fname = fname;
487 status = smb2_create(tree, tctx, &cr);
488 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n");
490 hd = cr.out.file.handle;
491 torture_comment(tctx, "set a sec desc allowing no write by CREATOR_OWNER\n");
493 sd = security_descriptor_dacl_create(tctx,
494 0, NULL, NULL,
495 SID_CREATOR_OWNER,
496 SEC_ACE_TYPE_ACCESS_ALLOWED,
497 SEC_RIGHTS_FILE_READ | SEC_STD_ALL,
499 NULL);
500 torture_assert_not_null_goto(tctx, sd, ret, done,
501 "security_descriptor_dacl_create failed\n");
503 set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
504 set.set_secdesc.in.file.handle = hd;
505 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
506 set.set_secdesc.in.sd = sd;
508 status = smb2_setinfo_file(tree, &set);
509 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
510 "smb2_setinfo_file failed\n");
512 torture_comment(tctx, "try open for write\n");
513 cr.in.desired_access = SEC_FILE_WRITE_DATA;
514 smb2_transport_compound_start(tree->session->transport, 4);
516 req[0] = smb2_create_send(tree, &cr);
517 torture_assert_not_null_goto(tctx, req[0], ret, done,
518 "smb2_create_send failed\n");
520 hd.data[0] = UINT64_MAX;
521 hd.data[1] = UINT64_MAX;
523 smb2_transport_compound_set_related(tree->session->transport, true);
524 ZERO_STRUCT(io);
525 io.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID;
526 io.in.file.handle = hd;
527 io.in.flags = 1;
529 req[1] = smb2_ioctl_send(tree, &io);
530 torture_assert_not_null_goto(tctx, req[1], ret, done,
531 "smb2_ioctl_send failed\n");
533 ZERO_STRUCT(cl);
534 cl.in.file.handle = hd;
536 req[2] = smb2_close_send(tree, &cl);
537 torture_assert_not_null_goto(tctx, req[2], ret, done,
538 "smb2_create_send failed\n");
540 set.set_secdesc.in.file.handle = hd;
542 req[3] = smb2_setinfo_file_send(tree, &set);
543 torture_assert_not_null_goto(tctx, req[3], ret, done,
544 "smb2_create_send failed\n");
546 status = smb2_create_recv(req[0], tree, &cr);
547 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
548 ret, done,
549 "smb2_create_recv failed\n");
551 status = smb2_ioctl_recv(req[1], tree, &io);
552 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
553 ret, done,
554 "smb2_ioctl_recv failed\n");
556 status = smb2_close_recv(req[2], &cl);
557 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
558 ret, done,
559 "smb2_close_recv failed\n");
561 status = smb2_setinfo_recv(req[3]);
562 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
563 ret, done,
564 "smb2_setinfo_recv failed\n");
566 done:
567 smb2_util_unlink(tree, fname);
568 smb2_tdis(tree);
569 smb2_logoff(tree->session);
570 return ret;
573 static bool test_compound_related5(struct torture_context *tctx,
574 struct smb2_tree *tree)
576 struct smb2_handle hd;
577 struct smb2_ioctl io;
578 struct smb2_close cl;
579 struct smb2_request *req[2];
580 NTSTATUS status;
581 bool ret = false;
583 smb2_transport_compound_start(tree->session->transport, 2);
585 hd.data[0] = UINT64_MAX;
586 hd.data[1] = UINT64_MAX;
588 ZERO_STRUCT(io);
589 io.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID;
590 io.in.file.handle = hd;
591 io.in.flags = 1;
593 req[0] = smb2_ioctl_send(tree, &io);
594 torture_assert_not_null_goto(tctx, req[0], ret, done,
595 "smb2_ioctl_send failed\n");
597 smb2_transport_compound_set_related(tree->session->transport, true);
599 ZERO_STRUCT(cl);
600 cl.in.file.handle = hd;
602 req[1] = smb2_close_send(tree, &cl);
603 torture_assert_not_null_goto(tctx, req[1], ret, done,
604 "smb2_create_send failed\n");
606 status = smb2_ioctl_recv(req[0], tree, &io);
607 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_FILE_CLOSED,
608 ret, done,
609 "smb2_ioctl_recv failed\n");
611 status = smb2_close_recv(req[1], &cl);
612 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_FILE_CLOSED,
613 ret, done,
614 "smb2_close_recv failed\n");
616 ret = true;
618 done:
619 smb2_tdis(tree);
620 smb2_logoff(tree->session);
621 return ret;
624 static bool test_compound_related6(struct torture_context *tctx,
625 struct smb2_tree *tree)
627 struct smb2_handle hd;
628 struct smb2_create cr;
629 struct smb2_read rd;
630 struct smb2_write wr;
631 struct smb2_close cl;
632 NTSTATUS status;
633 const char *fname = "compound_related6.dat";
634 struct smb2_request *req[5];
635 uint8_t buf[64];
636 bool ret = true;
638 smb2_util_unlink(tree, fname);
640 ZERO_STRUCT(cr);
641 cr.level = RAW_OPEN_SMB2;
642 cr.in.create_flags = 0;
643 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
644 cr.in.create_options = 0;
645 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
646 cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
647 NTCREATEX_SHARE_ACCESS_READ |
648 NTCREATEX_SHARE_ACCESS_WRITE;
649 cr.in.alloc_size = 0;
650 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
651 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
652 cr.in.security_flags = 0;
653 cr.in.fname = fname;
655 status = smb2_create(tree, tctx, &cr);
656 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
657 "smb2_create failed\n");
659 hd = cr.out.file.handle;
661 ZERO_STRUCT(buf);
662 status = smb2_util_write(tree, hd, buf, 0, ARRAY_SIZE(buf));
663 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
664 "smb2_util_write failed\n");
666 torture_comment(tctx, "try open for read\n");
667 cr.in.desired_access = SEC_FILE_READ_DATA;
668 smb2_transport_compound_start(tree->session->transport, 5);
670 req[0] = smb2_create_send(tree, &cr);
671 torture_assert_not_null_goto(tctx, req[0], ret, done,
672 "smb2_create_send failed\n");
674 hd.data[0] = UINT64_MAX;
675 hd.data[1] = UINT64_MAX;
677 smb2_transport_compound_set_related(tree->session->transport, true);
679 ZERO_STRUCT(rd);
680 rd.in.file.handle = hd;
681 rd.in.length = 1;
682 rd.in.offset = 0;
684 req[1] = smb2_read_send(tree, &rd);
685 torture_assert_not_null_goto(tctx, req[1], ret, done,
686 "smb2_read_send failed\n");
688 ZERO_STRUCT(wr);
689 wr.in.file.handle = hd;
690 wr.in.offset = 0;
691 wr.in.data = data_blob_talloc(tctx, NULL, 64);
693 req[2] = smb2_write_send(tree, &wr);
694 torture_assert_not_null_goto(tctx, req[2], ret, done,
695 "smb2_write_send failed\n");
697 ZERO_STRUCT(rd);
698 rd.in.file.handle = hd;
699 rd.in.length = 1;
700 rd.in.offset = 0;
702 req[3] = smb2_read_send(tree, &rd);
703 torture_assert_not_null_goto(tctx, req[3], ret, done,
704 "smb2_read_send failed\n");
706 ZERO_STRUCT(cl);
707 cl.in.file.handle = hd;
709 req[4] = smb2_close_send(tree, &cl);
710 torture_assert_not_null_goto(tctx, req[4], ret, done,
711 "smb2_close_send failed\n");
713 status = smb2_create_recv(req[0], tree, &cr);
714 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
715 "smb2_create_recv failed\n");
717 status = smb2_read_recv(req[1], tree, &rd);
718 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
719 "smb2_read_recv failed\n");
721 status = smb2_write_recv(req[2], &wr);
722 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
723 ret, done,
724 "smb2_write_recv failed\n");
726 status = smb2_read_recv(req[3], tree, &rd);
727 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
728 "smb2_read_recv failed\n");
730 status = smb2_close_recv(req[4], &cl);
731 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
732 "smb2_close_recv failed\n");
734 done:
735 smb2_util_unlink(tree, fname);
736 smb2_tdis(tree);
737 smb2_logoff(tree->session);
738 return ret;
741 static bool test_compound_related7(struct torture_context *tctx,
742 struct smb2_tree *tree)
744 const char *fname = "compound_related4.dat";
745 struct security_descriptor *sd = NULL;
746 struct smb2_handle hd;
747 struct smb2_create cr;
748 union smb_setfileinfo set;
749 struct smb2_notify nt;
750 struct smb2_close cl;
751 NTSTATUS status;
752 struct smb2_request *req[4];
753 bool ret = true;
755 smb2_util_unlink(tree, fname);
757 ZERO_STRUCT(cr);
758 cr.level = RAW_OPEN_SMB2;
759 cr.in.create_flags = 0;
760 cr.in.desired_access = SEC_STD_READ_CONTROL |
761 SEC_STD_WRITE_DAC |
762 SEC_STD_WRITE_OWNER;
763 cr.in.create_options = 0;
764 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
765 cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
766 NTCREATEX_SHARE_ACCESS_READ |
767 NTCREATEX_SHARE_ACCESS_WRITE;
768 cr.in.alloc_size = 0;
769 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
770 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
771 cr.in.security_flags = 0;
772 cr.in.fname = fname;
774 status = smb2_create(tree, tctx, &cr);
775 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
776 "smb2_create failed\n");
778 hd = cr.out.file.handle;
779 torture_comment(tctx, "set a sec desc allowing no write by CREATOR_OWNER\n");
780 sd = security_descriptor_dacl_create(tctx,
781 0, NULL, NULL,
782 SID_CREATOR_OWNER,
783 SEC_ACE_TYPE_ACCESS_ALLOWED,
784 SEC_RIGHTS_FILE_READ | SEC_STD_ALL,
786 NULL);
787 torture_assert_not_null_goto(tctx, sd, ret, done,
788 "security_descriptor_dacl_create failed\n");
790 set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
791 set.set_secdesc.in.file.handle = hd;
792 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
793 set.set_secdesc.in.sd = sd;
795 status = smb2_setinfo_file(tree, &set);
796 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
797 "smb2_setinfo_file failed\n");
799 torture_comment(tctx, "try open for write\n");
800 cr.in.desired_access = SEC_FILE_WRITE_DATA;
801 smb2_transport_compound_start(tree->session->transport, 4);
803 req[0] = smb2_create_send(tree, &cr);
804 torture_assert_not_null_goto(tctx, req[0], ret, done,
805 "smb2_create_send failed\n");
807 hd.data[0] = UINT64_MAX;
808 hd.data[1] = UINT64_MAX;
810 smb2_transport_compound_set_related(tree->session->transport, true);
812 ZERO_STRUCT(nt);
813 nt.in.recursive = true;
814 nt.in.buffer_size = 0x1000;
815 nt.in.file.handle = hd;
816 nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
817 nt.in.unknown = 0x00000000;
819 req[1] = smb2_notify_send(tree, &nt);
820 torture_assert_not_null_goto(tctx, req[1], ret, done,
821 "smb2_notify_send failed\n");
823 ZERO_STRUCT(cl);
824 cl.in.file.handle = hd;
826 req[2] = smb2_close_send(tree, &cl);
827 torture_assert_not_null_goto(tctx, req[2], ret, done,
828 "smb2_close_send failed\n");
830 set.set_secdesc.in.file.handle = hd;
832 req[3] = smb2_setinfo_file_send(tree, &set);
833 torture_assert_not_null_goto(tctx, req[3], ret, done,
834 "smb2_setinfo_file_send failed\n");
836 status = smb2_create_recv(req[0], tree, &cr);
837 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
838 ret, done,
839 "smb2_create_recv failed\n");
841 status = smb2_notify_recv(req[1], tree, &nt);
842 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
843 ret, done,
844 "smb2_notify_recv failed\n");
846 status = smb2_close_recv(req[2], &cl);
847 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
848 ret, done,
849 "smb2_close_recv failed\n");
851 status = smb2_setinfo_recv(req[3]);
852 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED,
853 ret, done,
854 "smb2_setinfo_recv failed\n");
856 done:
857 smb2_util_unlink(tree, fname);
858 smb2_tdis(tree);
859 smb2_logoff(tree->session);
860 return ret;
863 static bool test_compound_related8(struct torture_context *tctx,
864 struct smb2_tree *tree)
866 const char *fname = "compound_related8.dat";
867 const char *fname_nonexisting = "compound_related8.dat.void";
868 struct security_descriptor *sd = NULL;
869 struct smb2_handle hd;
870 struct smb2_create cr;
871 union smb_setfileinfo set;
872 struct smb2_notify nt;
873 struct smb2_close cl;
874 NTSTATUS status;
875 struct smb2_request *req[4];
876 bool ret = true;
878 smb2_util_unlink(tree, fname);
880 ZERO_STRUCT(cr);
881 cr.level = RAW_OPEN_SMB2;
882 cr.in.create_flags = 0;
883 cr.in.desired_access = SEC_STD_READ_CONTROL |
884 SEC_STD_WRITE_DAC |
885 SEC_STD_WRITE_OWNER;
886 cr.in.create_options = 0;
887 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
888 cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
889 NTCREATEX_SHARE_ACCESS_READ |
890 NTCREATEX_SHARE_ACCESS_WRITE;
891 cr.in.alloc_size = 0;
892 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
893 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
894 cr.in.security_flags = 0;
895 cr.in.fname = fname;
897 status = smb2_create(tree, tctx, &cr);
898 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
899 "smb2_create failed\n");
901 hd = cr.out.file.handle;
903 smb2_transport_compound_start(tree->session->transport, 4);
905 torture_comment(tctx, "try open for write\n");
906 cr.in.fname = fname_nonexisting;
907 cr.in.create_disposition = NTCREATEX_DISP_OPEN;
909 req[0] = smb2_create_send(tree, &cr);
910 torture_assert_not_null_goto(tctx, req[0], ret, done,
911 "smb2_create_send failed\n");
913 hd.data[0] = UINT64_MAX;
914 hd.data[1] = UINT64_MAX;
916 smb2_transport_compound_set_related(tree->session->transport, true);
918 ZERO_STRUCT(nt);
919 nt.in.recursive = true;
920 nt.in.buffer_size = 0x1000;
921 nt.in.file.handle = hd;
922 nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
923 nt.in.unknown = 0x00000000;
925 req[1] = smb2_notify_send(tree, &nt);
926 torture_assert_not_null_goto(tctx, req[1], ret, done,
927 "smb2_notify_send failed\n");
929 ZERO_STRUCT(cl);
930 cl.in.file.handle = hd;
932 req[2] = smb2_close_send(tree, &cl);
933 torture_assert_not_null_goto(tctx, req[2], ret, done,
934 "smb2_close_send failed\n");
936 sd = security_descriptor_dacl_create(tctx,
937 0, NULL, NULL,
938 SID_CREATOR_OWNER,
939 SEC_ACE_TYPE_ACCESS_ALLOWED,
940 SEC_RIGHTS_FILE_READ | SEC_STD_ALL,
942 NULL);
943 torture_assert_not_null_goto(tctx, sd, ret, done,
944 "security_descriptor_dacl_create failed\n");
946 set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
947 set.set_secdesc.in.file.handle = hd;
948 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
949 set.set_secdesc.in.sd = sd;
951 req[3] = smb2_setinfo_file_send(tree, &set);
952 torture_assert_not_null_goto(tctx, req[3], ret, done,
953 "smb2_setinfo_file_send failed\n");
955 status = smb2_create_recv(req[0], tree, &cr);
956 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
957 ret, done,
958 "smb2_create_recv failed\n");
960 status = smb2_notify_recv(req[1], tree, &nt);
961 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
962 ret, done,
963 "smb2_notify_recv failed\n");
965 status = smb2_close_recv(req[2], &cl);
966 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
967 ret, done,
968 "smb2_close_recv failed\n");
970 status = smb2_setinfo_recv(req[3]);
971 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
972 ret, done,
973 "smb2_setinfo_recv failed\n");
975 done:
976 smb2_util_unlink(tree, fname);
977 smb2_tdis(tree);
978 smb2_logoff(tree->session);
979 return ret;
982 static bool test_compound_related9(struct torture_context *tctx,
983 struct smb2_tree *tree)
985 const char *fname = "compound_related9.dat";
986 struct security_descriptor *sd = NULL;
987 struct smb2_handle hd;
988 struct smb2_create cr;
989 union smb_setfileinfo set;
990 struct smb2_notify nt;
991 struct smb2_close cl;
992 NTSTATUS status;
993 struct smb2_request *req[3];
994 bool ret = true;
996 smb2_util_unlink(tree, fname);
998 ZERO_STRUCT(cr);
999 cr.level = RAW_OPEN_SMB2;
1000 cr.in.create_flags = 0;
1001 cr.in.desired_access = SEC_STD_READ_CONTROL |
1002 SEC_STD_WRITE_DAC |
1003 SEC_STD_WRITE_OWNER;
1004 cr.in.create_options = 0;
1005 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1006 cr.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
1007 NTCREATEX_SHARE_ACCESS_READ |
1008 NTCREATEX_SHARE_ACCESS_WRITE;
1009 cr.in.alloc_size = 0;
1010 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1011 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS;
1012 cr.in.security_flags = 0;
1013 cr.in.fname = fname;
1015 status = smb2_create(tree, tctx, &cr);
1016 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1017 "smb2_create failed\n");
1019 hd = cr.out.file.handle;
1021 smb2_transport_compound_start(tree->session->transport, 3);
1022 smb2_transport_compound_set_related(tree->session->transport, true);
1024 ZERO_STRUCT(nt);
1025 nt.in.recursive = true;
1026 nt.in.buffer_size = 0x1000;
1027 nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1029 req[0] = smb2_notify_send(tree, &nt);
1030 torture_assert_not_null_goto(tctx, req[0], ret, done,
1031 "smb2_notify_send failed\n");
1033 ZERO_STRUCT(cl);
1034 cl.in.file.handle = hd;
1036 req[1] = smb2_close_send(tree, &cl);
1037 torture_assert_not_null_goto(tctx, req[1], ret, done,
1038 "smb2_close_send failed\n");
1040 sd = security_descriptor_dacl_create(tctx,
1041 0, NULL, NULL,
1042 SID_CREATOR_OWNER,
1043 SEC_ACE_TYPE_ACCESS_ALLOWED,
1044 SEC_RIGHTS_FILE_READ | SEC_STD_ALL,
1046 NULL);
1047 torture_assert_not_null_goto(tctx, sd, ret, done,
1048 "security_descriptor_dacl_create failed\n");
1050 set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
1051 set.set_secdesc.in.file.handle = hd;
1052 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
1053 set.set_secdesc.in.sd = sd;
1055 req[2] = smb2_setinfo_file_send(tree, &set);
1056 torture_assert_not_null_goto(tctx, req[2], ret, done,
1057 "smb2_setinfo_file_send failed\n");
1059 status = smb2_notify_recv(req[0], tree, &nt);
1060 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_INVALID_PARAMETER,
1061 ret, done,
1062 "smb2_notify_recv failed\n");
1064 status = smb2_close_recv(req[1], &cl);
1065 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_INVALID_PARAMETER,
1066 ret, done,
1067 "smb2_close_recv failed\n");
1069 status = smb2_setinfo_recv(req[2]);
1070 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_INVALID_PARAMETER,
1071 ret, done,
1072 "smb2_setinfo_recv failed\n");
1074 done:
1075 smb2_util_unlink(tree, fname);
1076 smb2_tdis(tree);
1077 smb2_logoff(tree->session);
1078 return ret;
1081 static bool test_compound_padding(struct torture_context *tctx,
1082 struct smb2_tree *tree)
1084 struct smb2_handle h;
1085 struct smb2_create cr;
1086 struct smb2_read r;
1087 struct smb2_read r2;
1088 const char *fname = "compound_read.dat";
1089 const char *sname = "compound_read.dat:foo";
1090 struct smb2_request *req[3];
1091 NTSTATUS status;
1092 bool ret = false;
1094 smb2_util_unlink(tree, fname);
1096 /* Write file */
1097 ZERO_STRUCT(cr);
1098 cr.in.desired_access = SEC_FILE_WRITE_DATA;
1099 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1100 cr.in.create_disposition = NTCREATEX_DISP_CREATE;
1101 cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1102 cr.in.fname = fname;
1103 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
1104 NTCREATEX_SHARE_ACCESS_WRITE|
1105 NTCREATEX_SHARE_ACCESS_DELETE;
1106 status = smb2_create(tree, tctx, &cr);
1107 CHECK_STATUS(status, NT_STATUS_OK);
1108 h = cr.out.file.handle;
1110 status = smb2_util_write(tree, h, "123", 0, 3);
1111 CHECK_STATUS(status, NT_STATUS_OK);
1113 smb2_util_close(tree, h);
1115 /* Write stream */
1116 ZERO_STRUCT(cr);
1117 cr.in.desired_access = SEC_FILE_WRITE_DATA;
1118 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1119 cr.in.create_disposition = NTCREATEX_DISP_CREATE;
1120 cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1121 cr.in.fname = sname;
1122 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
1123 NTCREATEX_SHARE_ACCESS_WRITE|
1124 NTCREATEX_SHARE_ACCESS_DELETE;
1125 status = smb2_create(tree, tctx, &cr);
1126 CHECK_STATUS(status, NT_STATUS_OK);
1127 h = cr.out.file.handle;
1129 status = smb2_util_write(tree, h, "456", 0, 3);
1130 CHECK_STATUS(status, NT_STATUS_OK);
1132 smb2_util_close(tree, h);
1134 /* Check compound read from basefile */
1135 smb2_transport_compound_start(tree->session->transport, 3);
1137 ZERO_STRUCT(cr);
1138 cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1139 cr.in.desired_access = SEC_FILE_READ_DATA;
1140 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1141 cr.in.create_disposition = NTCREATEX_DISP_OPEN;
1142 cr.in.fname = fname;
1143 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
1144 NTCREATEX_SHARE_ACCESS_WRITE|
1145 NTCREATEX_SHARE_ACCESS_DELETE;
1146 req[0] = smb2_create_send(tree, &cr);
1148 smb2_transport_compound_set_related(tree->session->transport, true);
1151 * We send 2 reads in the compound here as the protocol
1152 * allows the last read to be split off and possibly
1153 * go async. Check the padding on the first read returned,
1154 * not the second as the second may not be part of the
1155 * returned compound.
1158 ZERO_STRUCT(r);
1159 h.data[0] = UINT64_MAX;
1160 h.data[1] = UINT64_MAX;
1161 r.in.file.handle = h;
1162 r.in.length = 3;
1163 r.in.offset = 0;
1164 r.in.min_count = 1;
1165 req[1] = smb2_read_send(tree, &r);
1167 ZERO_STRUCT(r2);
1168 h.data[0] = UINT64_MAX;
1169 h.data[1] = UINT64_MAX;
1170 r2.in.file.handle = h;
1171 r2.in.length = 3;
1172 r2.in.offset = 0;
1173 r2.in.min_count = 1;
1174 req[2] = smb2_read_send(tree, &r2);
1176 status = smb2_create_recv(req[0], tree, &cr);
1177 CHECK_STATUS(status, NT_STATUS_OK);
1180 * We must do a manual smb2_request_receive() in order to be
1181 * able to check the transport layer info, as smb2_read_recv()
1182 * will destroy the req. smb2_read_recv() will call
1183 * smb2_request_receive() again, but that's ok.
1185 if (!smb2_request_receive(req[1]) ||
1186 !smb2_request_is_ok(req[1])) {
1187 torture_fail(tctx, "failed to receive read request");
1191 * size must be 24: 16 byte read response header plus 3
1192 * requested bytes padded to an 8 byte boundary.
1194 CHECK_VALUE(req[1]->in.body_size, 24);
1196 status = smb2_read_recv(req[1], tree, &r);
1197 CHECK_STATUS(status, NT_STATUS_OK);
1199 /* Pick up the second, possibly async, read. */
1200 status = smb2_read_recv(req[2], tree, &r2);
1201 CHECK_STATUS(status, NT_STATUS_OK);
1203 smb2_util_close(tree, cr.out.file.handle);
1205 /* Check compound read from stream */
1206 smb2_transport_compound_start(tree->session->transport, 3);
1208 ZERO_STRUCT(cr);
1209 cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1210 cr.in.desired_access = SEC_FILE_READ_DATA;
1211 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1212 cr.in.create_disposition = NTCREATEX_DISP_OPEN;
1213 cr.in.fname = sname;
1214 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
1215 NTCREATEX_SHARE_ACCESS_WRITE|
1216 NTCREATEX_SHARE_ACCESS_DELETE;
1217 req[0] = smb2_create_send(tree, &cr);
1219 smb2_transport_compound_set_related(tree->session->transport, true);
1222 * We send 2 reads in the compound here as the protocol
1223 * allows the last read to be split off and possibly
1224 * go async. Check the padding on the first read returned,
1225 * not the second as the second may not be part of the
1226 * returned compound.
1229 ZERO_STRUCT(r);
1230 h.data[0] = UINT64_MAX;
1231 h.data[1] = UINT64_MAX;
1232 r.in.file.handle = h;
1233 r.in.length = 3;
1234 r.in.offset = 0;
1235 r.in.min_count = 1;
1236 req[1] = smb2_read_send(tree, &r);
1238 ZERO_STRUCT(r2);
1239 h.data[0] = UINT64_MAX;
1240 h.data[1] = UINT64_MAX;
1241 r2.in.file.handle = h;
1242 r2.in.length = 3;
1243 r2.in.offset = 0;
1244 r2.in.min_count = 1;
1245 req[2] = smb2_read_send(tree, &r2);
1247 status = smb2_create_recv(req[0], tree, &cr);
1248 CHECK_STATUS(status, NT_STATUS_OK);
1251 * We must do a manual smb2_request_receive() in order to be
1252 * able to check the transport layer info, as smb2_read_recv()
1253 * will destroy the req. smb2_read_recv() will call
1254 * smb2_request_receive() again, but that's ok.
1256 if (!smb2_request_receive(req[1]) ||
1257 !smb2_request_is_ok(req[1])) {
1258 torture_fail(tctx, "failed to receive read request");
1262 * size must be 24: 16 byte read response header plus 3
1263 * requested bytes padded to an 8 byte boundary.
1265 CHECK_VALUE(req[1]->in.body_size, 24);
1267 status = smb2_read_recv(req[1], tree, &r);
1268 CHECK_STATUS(status, NT_STATUS_OK);
1270 /* Pick up the second, possibly async, read. */
1271 status = smb2_read_recv(req[2], tree, &r2);
1272 CHECK_STATUS(status, NT_STATUS_OK);
1274 h = cr.out.file.handle;
1276 /* Check 2 compound (unrelateated) reads from existing stream handle */
1277 smb2_transport_compound_start(tree->session->transport, 2);
1279 ZERO_STRUCT(r);
1280 r.in.file.handle = h;
1281 r.in.length = 3;
1282 r.in.offset = 0;
1283 r.in.min_count = 1;
1284 req[0] = smb2_read_send(tree, &r);
1285 req[1] = smb2_read_send(tree, &r);
1288 * We must do a manual smb2_request_receive() in order to be
1289 * able to check the transport layer info, as smb2_read_recv()
1290 * will destroy the req. smb2_read_recv() will call
1291 * smb2_request_receive() again, but that's ok.
1293 if (!smb2_request_receive(req[0]) ||
1294 !smb2_request_is_ok(req[0])) {
1295 torture_fail(tctx, "failed to receive read request");
1297 if (!smb2_request_receive(req[1]) ||
1298 !smb2_request_is_ok(req[1])) {
1299 torture_fail(tctx, "failed to receive read request");
1303 * size must be 24: 16 byte read response header plus 3
1304 * requested bytes padded to an 8 byte boundary.
1306 CHECK_VALUE(req[0]->in.body_size, 24);
1307 CHECK_VALUE(req[1]->in.body_size, 24);
1309 status = smb2_read_recv(req[0], tree, &r);
1310 CHECK_STATUS(status, NT_STATUS_OK);
1311 status = smb2_read_recv(req[1], tree, &r);
1312 CHECK_STATUS(status, NT_STATUS_OK);
1315 * now try a single read from the stream and verify there's no padding
1317 ZERO_STRUCT(r);
1318 r.in.file.handle = h;
1319 r.in.length = 3;
1320 r.in.offset = 0;
1321 r.in.min_count = 1;
1322 req[0] = smb2_read_send(tree, &r);
1325 * We must do a manual smb2_request_receive() in order to be
1326 * able to check the transport layer info, as smb2_read_recv()
1327 * will destroy the req. smb2_read_recv() will call
1328 * smb2_request_receive() again, but that's ok.
1330 if (!smb2_request_receive(req[0]) ||
1331 !smb2_request_is_ok(req[0])) {
1332 torture_fail(tctx, "failed to receive read request");
1336 * size must be 19: 16 byte read response header plus 3
1337 * requested bytes without padding.
1339 CHECK_VALUE(req[0]->in.body_size, 19);
1341 status = smb2_read_recv(req[0], tree, &r);
1342 CHECK_STATUS(status, NT_STATUS_OK);
1344 smb2_util_close(tree, h);
1346 status = smb2_util_unlink(tree, fname);
1347 CHECK_STATUS(status, NT_STATUS_OK);
1349 ret = true;
1350 done:
1351 return ret;
1354 static bool test_compound_create_write_close(struct torture_context *tctx,
1355 struct smb2_tree *tree)
1357 struct smb2_handle handle = { .data = { UINT64_MAX, UINT64_MAX } };
1358 struct smb2_create create;
1359 struct smb2_write write;
1360 struct smb2_close close;
1361 const char *fname = "compound_create_write_close.dat";
1362 struct smb2_request *req[3];
1363 NTSTATUS status;
1364 bool ret = false;
1366 smb2_util_unlink(tree, fname);
1368 ZERO_STRUCT(create);
1369 create.in.security_flags = 0x00;
1370 create.in.oplock_level = 0;
1371 create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1372 create.in.create_flags = 0x00000000;
1373 create.in.reserved = 0x00000000;
1374 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
1375 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1376 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1377 NTCREATEX_SHARE_ACCESS_WRITE |
1378 NTCREATEX_SHARE_ACCESS_DELETE;
1379 create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1380 create.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1381 NTCREATEX_OPTIONS_ASYNC_ALERT |
1382 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1383 0x00200000;
1384 create.in.fname = fname;
1386 smb2_transport_compound_start(tree->session->transport, 3);
1388 req[0] = smb2_create_send(tree, &create);
1390 smb2_transport_compound_set_related(tree->session->transport, true);
1392 ZERO_STRUCT(write);
1393 write.in.file.handle = handle;
1394 write.in.offset = 0;
1395 write.in.data = data_blob_talloc(tctx, NULL, 1024);
1397 req[1] = smb2_write_send(tree, &write);
1399 ZERO_STRUCT(close);
1400 close.in.file.handle = handle;
1402 req[2] = smb2_close_send(tree, &close);
1404 status = smb2_create_recv(req[0], tree, &create);
1405 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1406 "CREATE failed.");
1408 status = smb2_write_recv(req[1], &write);
1409 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1410 "WRITE failed.");
1412 status = smb2_close_recv(req[2], &close);
1413 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1414 "CLOSE failed.");
1416 status = smb2_util_unlink(tree, fname);
1417 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1418 "File deletion failed.");
1420 ret = true;
1421 done:
1422 return ret;
1425 static bool test_compound_unrelated1(struct torture_context *tctx,
1426 struct smb2_tree *tree)
1428 struct smb2_handle hd;
1429 struct smb2_create cr;
1430 NTSTATUS status;
1431 const char *fname = "compound_unrelated1.dat";
1432 struct smb2_close cl;
1433 bool ret = true;
1434 struct smb2_request *req[5];
1436 smb2_transport_credits_ask_num(tree->session->transport, 5);
1438 smb2_util_unlink(tree, fname);
1440 smb2_transport_credits_ask_num(tree->session->transport, 1);
1442 ZERO_STRUCT(cr);
1443 cr.in.security_flags = 0x00;
1444 cr.in.oplock_level = 0;
1445 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1446 cr.in.create_flags = 0x00000000;
1447 cr.in.reserved = 0x00000000;
1448 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1449 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1450 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1451 NTCREATEX_SHARE_ACCESS_WRITE |
1452 NTCREATEX_SHARE_ACCESS_DELETE;
1453 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1454 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1455 NTCREATEX_OPTIONS_ASYNC_ALERT |
1456 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1457 0x00200000;
1458 cr.in.fname = fname;
1460 smb2_transport_compound_start(tree->session->transport, 5);
1462 req[0] = smb2_create_send(tree, &cr);
1464 hd.data[0] = UINT64_MAX;
1465 hd.data[1] = UINT64_MAX;
1467 ZERO_STRUCT(cl);
1468 cl.in.file.handle = hd;
1469 req[1] = smb2_close_send(tree, &cl);
1470 req[2] = smb2_close_send(tree, &cl);
1471 req[3] = smb2_close_send(tree, &cl);
1472 req[4] = smb2_close_send(tree, &cl);
1474 status = smb2_create_recv(req[0], tree, &cr);
1475 CHECK_STATUS(status, NT_STATUS_OK);
1476 status = smb2_close_recv(req[1], &cl);
1477 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1478 status = smb2_close_recv(req[2], &cl);
1479 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1480 status = smb2_close_recv(req[3], &cl);
1481 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1482 status = smb2_close_recv(req[4], &cl);
1483 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1485 smb2_util_unlink(tree, fname);
1486 done:
1487 return ret;
1490 static bool test_compound_invalid1(struct torture_context *tctx,
1491 struct smb2_tree *tree)
1493 struct smb2_handle hd;
1494 struct smb2_create cr;
1495 NTSTATUS status;
1496 const char *fname = "compound_invalid1.dat";
1497 struct smb2_close cl;
1498 bool ret = true;
1499 struct smb2_request *req[3];
1501 smb2_transport_credits_ask_num(tree->session->transport, 3);
1503 smb2_util_unlink(tree, fname);
1505 smb2_transport_credits_ask_num(tree->session->transport, 1);
1507 ZERO_STRUCT(cr);
1508 cr.in.security_flags = 0x00;
1509 cr.in.oplock_level = 0;
1510 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1511 cr.in.create_flags = 0x00000000;
1512 cr.in.reserved = 0x00000000;
1513 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1514 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1515 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1516 NTCREATEX_SHARE_ACCESS_WRITE |
1517 NTCREATEX_SHARE_ACCESS_DELETE;
1518 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1519 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1520 NTCREATEX_OPTIONS_ASYNC_ALERT |
1521 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1522 0x00200000;
1523 cr.in.fname = fname;
1525 smb2_transport_compound_start(tree->session->transport, 3);
1527 /* passing the first request with the related flag is invalid */
1528 smb2_transport_compound_set_related(tree->session->transport, true);
1530 req[0] = smb2_create_send(tree, &cr);
1532 hd.data[0] = UINT64_MAX;
1533 hd.data[1] = UINT64_MAX;
1535 ZERO_STRUCT(cl);
1536 cl.in.file.handle = hd;
1537 req[1] = smb2_close_send(tree, &cl);
1539 smb2_transport_compound_set_related(tree->session->transport, false);
1540 req[2] = smb2_close_send(tree, &cl);
1542 status = smb2_create_recv(req[0], tree, &cr);
1543 /* TODO: check why this fails with --signing=required */
1544 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1545 status = smb2_close_recv(req[1], &cl);
1546 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1547 status = smb2_close_recv(req[2], &cl);
1548 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1550 smb2_util_unlink(tree, fname);
1551 done:
1552 return ret;
1555 static bool test_compound_invalid2(struct torture_context *tctx,
1556 struct smb2_tree *tree)
1558 struct smb2_handle hd;
1559 struct smb2_create cr;
1560 NTSTATUS status;
1561 const char *fname = "compound_invalid2.dat";
1562 struct smb2_close cl;
1563 bool ret = true;
1564 struct smb2_request *req[5];
1565 struct smbXcli_tcon *saved_tcon = tree->smbXcli;
1566 struct smbXcli_session *saved_session = tree->session->smbXcli;
1568 smb2_transport_credits_ask_num(tree->session->transport, 5);
1570 smb2_util_unlink(tree, fname);
1572 smb2_transport_credits_ask_num(tree->session->transport, 1);
1574 ZERO_STRUCT(cr);
1575 cr.in.security_flags = 0x00;
1576 cr.in.oplock_level = 0;
1577 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1578 cr.in.create_flags = 0x00000000;
1579 cr.in.reserved = 0x00000000;
1580 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1581 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1582 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1583 NTCREATEX_SHARE_ACCESS_WRITE |
1584 NTCREATEX_SHARE_ACCESS_DELETE;
1585 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1586 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1587 NTCREATEX_OPTIONS_ASYNC_ALERT |
1588 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1589 0x00200000;
1590 cr.in.fname = fname;
1592 smb2_transport_compound_start(tree->session->transport, 5);
1594 req[0] = smb2_create_send(tree, &cr);
1596 hd.data[0] = UINT64_MAX;
1597 hd.data[1] = UINT64_MAX;
1599 smb2_transport_compound_set_related(tree->session->transport, true);
1601 ZERO_STRUCT(cl);
1602 cl.in.file.handle = hd;
1604 tree->smbXcli = smbXcli_tcon_create(tree);
1605 smb2cli_tcon_set_values(tree->smbXcli,
1606 NULL, /* session */
1607 0xFFFFFFFF, /* tcon_id */
1608 0, /* type */
1609 0, /* flags */
1610 0, /* capabilities */
1611 0 /* maximal_access */);
1613 tree->session->smbXcli = smbXcli_session_shallow_copy(tree->session,
1614 tree->session->smbXcli);
1615 smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0);
1617 req[1] = smb2_close_send(tree, &cl);
1618 /* strange that this is not generating invalid parameter */
1619 smb2_transport_compound_set_related(tree->session->transport, false);
1620 req[2] = smb2_close_send(tree, &cl);
1621 req[3] = smb2_close_send(tree, &cl);
1622 smb2_transport_compound_set_related(tree->session->transport, true);
1623 req[4] = smb2_close_send(tree, &cl);
1625 status = smb2_create_recv(req[0], tree, &cr);
1626 CHECK_STATUS(status, NT_STATUS_OK);
1627 status = smb2_close_recv(req[1], &cl);
1628 CHECK_STATUS(status, NT_STATUS_OK);
1629 status = smb2_close_recv(req[2], &cl);
1630 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
1631 status = smb2_close_recv(req[3], &cl);
1632 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
1633 status = smb2_close_recv(req[4], &cl);
1634 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1636 TALLOC_FREE(tree->smbXcli);
1637 tree->smbXcli = saved_tcon;
1638 TALLOC_FREE(tree->session->smbXcli);
1639 tree->session->smbXcli = saved_session;
1641 smb2_util_unlink(tree, fname);
1642 done:
1643 return ret;
1646 static bool test_compound_invalid3(struct torture_context *tctx,
1647 struct smb2_tree *tree)
1649 struct smb2_handle hd;
1650 struct smb2_create cr;
1651 NTSTATUS status;
1652 const char *fname = "compound_invalid3.dat";
1653 struct smb2_close cl;
1654 bool ret = true;
1655 struct smb2_request *req[5];
1657 smb2_transport_credits_ask_num(tree->session->transport, 5);
1659 smb2_util_unlink(tree, fname);
1661 smb2_transport_credits_ask_num(tree->session->transport, 1);
1663 ZERO_STRUCT(cr);
1664 cr.in.security_flags = 0x00;
1665 cr.in.oplock_level = 0;
1666 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1667 cr.in.create_flags = 0x00000000;
1668 cr.in.reserved = 0x00000000;
1669 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1670 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1671 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1672 NTCREATEX_SHARE_ACCESS_WRITE |
1673 NTCREATEX_SHARE_ACCESS_DELETE;
1674 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1675 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1676 NTCREATEX_OPTIONS_ASYNC_ALERT |
1677 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1678 0x00200000;
1679 cr.in.fname = fname;
1681 smb2_transport_compound_start(tree->session->transport, 5);
1683 req[0] = smb2_create_send(tree, &cr);
1685 hd.data[0] = UINT64_MAX;
1686 hd.data[1] = UINT64_MAX;
1688 ZERO_STRUCT(cl);
1689 cl.in.file.handle = hd;
1690 req[1] = smb2_close_send(tree, &cl);
1691 req[2] = smb2_close_send(tree, &cl);
1692 /* flipping the related flag is invalid */
1693 smb2_transport_compound_set_related(tree->session->transport, true);
1694 req[3] = smb2_close_send(tree, &cl);
1695 req[4] = smb2_close_send(tree, &cl);
1697 status = smb2_create_recv(req[0], tree, &cr);
1698 CHECK_STATUS(status, NT_STATUS_OK);
1699 status = smb2_close_recv(req[1], &cl);
1700 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1701 status = smb2_close_recv(req[2], &cl);
1702 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1703 status = smb2_close_recv(req[3], &cl);
1704 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1705 status = smb2_close_recv(req[4], &cl);
1706 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1708 smb2_util_unlink(tree, fname);
1709 done:
1710 return ret;
1713 static bool test_compound_invalid4(struct torture_context *tctx,
1714 struct smb2_tree *tree)
1716 struct smb2_create cr;
1717 struct smb2_read rd;
1718 NTSTATUS status;
1719 const char *fname = "compound_invalid4.dat";
1720 struct smb2_close cl;
1721 bool ret = true;
1722 bool ok;
1723 struct smb2_request *req[2];
1725 smb2_transport_credits_ask_num(tree->session->transport, 2);
1727 smb2_util_unlink(tree, fname);
1729 ZERO_STRUCT(cr);
1730 cr.in.security_flags = 0x00;
1731 cr.in.oplock_level = 0;
1732 cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1733 cr.in.create_flags = 0x00000000;
1734 cr.in.reserved = 0x00000000;
1735 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1736 cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1737 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1738 NTCREATEX_SHARE_ACCESS_WRITE |
1739 NTCREATEX_SHARE_ACCESS_DELETE;
1740 cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1741 cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1742 NTCREATEX_OPTIONS_ASYNC_ALERT |
1743 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1744 0x00200000;
1745 cr.in.fname = fname;
1747 status = smb2_create(tree, tctx, &cr);
1748 CHECK_STATUS(status, NT_STATUS_OK);
1750 smb2_transport_compound_start(tree->session->transport, 2);
1752 ZERO_STRUCT(rd);
1753 rd.in.file.handle = cr.out.file.handle;
1754 rd.in.length = 1;
1755 rd.in.offset = 0;
1756 req[0] = smb2_read_send(tree, &rd);
1758 smb2_transport_compound_set_related(tree->session->transport, true);
1761 * Send a completely bogus request as second compound
1762 * element. This triggers smbd_smb2_request_error() in in
1763 * smbd_smb2_request_dispatch() before calling
1764 * smbd_smb2_request_dispatch_update_counts().
1767 req[1] = smb2_request_init_tree(tree, 0xff, 0x04, false, 0);
1768 smb2_transport_send(req[1]);
1770 status = smb2_read_recv(req[0], tctx, &rd);
1771 CHECK_STATUS(status, NT_STATUS_END_OF_FILE);
1773 ok = smb2_request_receive(req[1]);
1774 torture_assert(tctx, ok, "Invalid request failed\n");
1775 CHECK_STATUS(req[1]->status, NT_STATUS_INVALID_PARAMETER);
1777 ZERO_STRUCT(cl);
1778 cl.in.file.handle = cr.out.file.handle;
1780 status = smb2_close(tree, &cl);
1781 CHECK_STATUS(status, NT_STATUS_OK);
1783 smb2_util_unlink(tree, fname);
1784 done:
1785 return ret;
1788 /* Send a compound request where we expect the last request (Create, Notify)
1789 * to go asynchronous. This works against a Win7 server and the reply is
1790 * sent in two different packets. */
1791 static bool test_compound_interim1(struct torture_context *tctx,
1792 struct smb2_tree *tree)
1794 struct smb2_handle hd;
1795 struct smb2_create cr;
1796 NTSTATUS status = NT_STATUS_OK;
1797 const char *dname = "compound_interim_dir";
1798 struct smb2_notify nt;
1799 bool ret = true;
1800 struct smb2_request *req[2];
1802 /* Win7 compound request implementation deviates substantially from the
1803 * SMB2 spec as noted in MS-SMB2 <159>, <162>. This, test currently
1804 * verifies the Windows behavior, not the general spec behavior. */
1806 smb2_transport_credits_ask_num(tree->session->transport, 5);
1808 smb2_deltree(tree, dname);
1810 smb2_transport_credits_ask_num(tree->session->transport, 1);
1812 ZERO_STRUCT(cr);
1813 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1814 cr.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1815 cr.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
1816 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1817 NTCREATEX_SHARE_ACCESS_WRITE |
1818 NTCREATEX_SHARE_ACCESS_DELETE;
1819 cr.in.create_disposition = NTCREATEX_DISP_CREATE;
1820 cr.in.fname = dname;
1822 smb2_transport_compound_start(tree->session->transport, 2);
1824 req[0] = smb2_create_send(tree, &cr);
1826 smb2_transport_compound_set_related(tree->session->transport, true);
1828 hd.data[0] = UINT64_MAX;
1829 hd.data[1] = UINT64_MAX;
1831 ZERO_STRUCT(nt);
1832 nt.in.recursive = true;
1833 nt.in.buffer_size = 0x1000;
1834 nt.in.file.handle = hd;
1835 nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1836 nt.in.unknown = 0x00000000;
1838 req[1] = smb2_notify_send(tree, &nt);
1840 status = smb2_create_recv(req[0], tree, &cr);
1841 CHECK_STATUS(status, NT_STATUS_OK);
1843 smb2_cancel(req[1]);
1844 status = smb2_notify_recv(req[1], tree, &nt);
1845 CHECK_STATUS(status, NT_STATUS_CANCELLED);
1847 smb2_util_close(tree, cr.out.file.handle);
1849 smb2_deltree(tree, dname);
1850 done:
1851 return ret;
1854 /* Send a compound request where we expect the middle request (Create, Notify,
1855 * GetInfo) to go asynchronous. Against Win7 the sync request succeed while
1856 * the async fails. All are returned in the same compound response. */
1857 static bool test_compound_interim2(struct torture_context *tctx,
1858 struct smb2_tree *tree)
1860 struct smb2_handle hd;
1861 struct smb2_create cr;
1862 NTSTATUS status = NT_STATUS_OK;
1863 const char *dname = "compound_interim_dir";
1864 struct smb2_getinfo gf;
1865 struct smb2_notify nt;
1866 bool ret = true;
1867 struct smb2_request *req[3];
1869 /* Win7 compound request implementation deviates substantially from the
1870 * SMB2 spec as noted in MS-SMB2 <159>, <162>. This, test currently
1871 * verifies the Windows behavior, not the general spec behavior. */
1873 smb2_transport_credits_ask_num(tree->session->transport, 5);
1875 smb2_deltree(tree, dname);
1877 smb2_transport_credits_ask_num(tree->session->transport, 1);
1879 ZERO_STRUCT(cr);
1880 cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
1881 cr.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1882 cr.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
1883 cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1884 NTCREATEX_SHARE_ACCESS_WRITE |
1885 NTCREATEX_SHARE_ACCESS_DELETE;
1886 cr.in.create_disposition = NTCREATEX_DISP_CREATE;
1887 cr.in.fname = dname;
1889 smb2_transport_compound_start(tree->session->transport, 3);
1891 req[0] = smb2_create_send(tree, &cr);
1893 smb2_transport_compound_set_related(tree->session->transport, true);
1895 hd.data[0] = UINT64_MAX;
1896 hd.data[1] = UINT64_MAX;
1898 ZERO_STRUCT(nt);
1899 nt.in.recursive = true;
1900 nt.in.buffer_size = 0x1000;
1901 nt.in.file.handle = hd;
1902 nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME;
1903 nt.in.unknown = 0x00000000;
1905 req[1] = smb2_notify_send(tree, &nt);
1907 ZERO_STRUCT(gf);
1908 gf.in.file.handle = hd;
1909 gf.in.info_type = SMB2_0_INFO_FILE;
1910 gf.in.info_class = 0x04; /* FILE_BASIC_INFORMATION */
1911 gf.in.output_buffer_length = 0x1000;
1912 gf.in.input_buffer = data_blob_null;
1914 req[2] = smb2_getinfo_send(tree, &gf);
1916 status = smb2_create_recv(req[0], tree, &cr);
1917 CHECK_STATUS(status, NT_STATUS_OK);
1919 status = smb2_notify_recv(req[1], tree, &nt);
1920 CHECK_STATUS(status, NT_STATUS_INTERNAL_ERROR);
1922 status = smb2_getinfo_recv(req[2], tree, &gf);
1923 CHECK_STATUS(status, NT_STATUS_OK);
1925 smb2_util_close(tree, cr.out.file.handle);
1927 smb2_deltree(tree, dname);
1928 done:
1929 return ret;
1932 /* Test compound related finds */
1933 static bool test_compound_find_related(struct torture_context *tctx,
1934 struct smb2_tree *tree)
1936 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1937 const char *dname = "compound_find_dir";
1938 struct smb2_create create;
1939 struct smb2_find f;
1940 struct smb2_handle h;
1941 struct smb2_request *req[2];
1942 NTSTATUS status;
1943 bool ret = true;
1945 smb2_deltree(tree, dname);
1947 ZERO_STRUCT(create);
1948 create.in.desired_access = SEC_RIGHTS_DIR_ALL;
1949 create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1950 create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
1951 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1952 NTCREATEX_SHARE_ACCESS_WRITE |
1953 NTCREATEX_SHARE_ACCESS_DELETE;
1954 create.in.create_disposition = NTCREATEX_DISP_CREATE;
1955 create.in.fname = dname;
1957 status = smb2_create(tree, mem_ctx, &create);
1958 h = create.out.file.handle;
1960 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n");
1962 smb2_transport_compound_start(tree->session->transport, 2);
1964 ZERO_STRUCT(f);
1965 f.in.file.handle = h;
1966 f.in.pattern = "*";
1967 f.in.max_response_size = 0x100;
1968 f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO;
1970 req[0] = smb2_find_send(tree, &f);
1972 smb2_transport_compound_set_related(tree->session->transport, true);
1974 req[1] = smb2_find_send(tree, &f);
1976 status = smb2_find_recv(req[0], mem_ctx, &f);
1977 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_recv failed\n");
1979 status = smb2_find_recv(req[1], mem_ctx, &f);
1980 torture_assert_ntstatus_equal_goto(tctx, status, STATUS_NO_MORE_FILES, ret, done, "smb2_find_recv failed\n");
1982 done:
1983 smb2_util_close(tree, h);
1984 smb2_deltree(tree, dname);
1985 TALLOC_FREE(mem_ctx);
1986 return ret;
1989 /* Test compound related finds */
1990 static bool test_compound_find_close(struct torture_context *tctx,
1991 struct smb2_tree *tree)
1993 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1994 const char *dname = "compound_find_dir";
1995 struct smb2_create create;
1996 struct smb2_find f;
1997 struct smb2_handle h;
1998 struct smb2_request *req = NULL;
1999 const int num_files = 5000;
2000 int i;
2001 NTSTATUS status;
2002 bool ret = true;
2004 smb2_deltree(tree, dname);
2006 ZERO_STRUCT(create);
2007 create.in.desired_access = SEC_RIGHTS_DIR_ALL;
2008 create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
2009 create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2010 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
2011 NTCREATEX_SHARE_ACCESS_WRITE |
2012 NTCREATEX_SHARE_ACCESS_DELETE;
2013 create.in.create_disposition = NTCREATEX_DISP_CREATE;
2014 create.in.fname = dname;
2016 smb2cli_conn_set_max_credits(tree->session->transport->conn, 256);
2018 status = smb2_create(tree, mem_ctx, &create);
2019 h = create.out.file.handle;
2021 ZERO_STRUCT(create);
2022 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
2023 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2024 create.in.create_disposition = NTCREATEX_DISP_CREATE;
2026 for (i = 0; i < num_files; i++) {
2027 create.in.fname = talloc_asprintf(mem_ctx, "%s\\file%d",
2028 dname, i);
2029 status = smb2_create(tree, mem_ctx, &create);
2030 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
2031 smb2_util_close(tree, create.out.file.handle);
2034 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n");
2036 ZERO_STRUCT(f);
2037 f.in.file.handle = h;
2038 f.in.pattern = "*";
2039 f.in.max_response_size = 8*1024*1024;
2040 f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO;
2042 req = smb2_find_send(tree, &f);
2044 status = smb2_util_close(tree, h);
2045 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_close failed\n");
2047 status = smb2_find_recv(req, mem_ctx, &f);
2048 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_recv failed\n");
2050 done:
2051 smb2_util_close(tree, h);
2052 smb2_deltree(tree, dname);
2053 TALLOC_FREE(mem_ctx);
2054 return ret;
2057 /* Test compound unrelated finds */
2058 static bool test_compound_find_unrelated(struct torture_context *tctx,
2059 struct smb2_tree *tree)
2061 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2062 const char *dname = "compound_find_dir";
2063 struct smb2_create create;
2064 struct smb2_find f;
2065 struct smb2_handle h;
2066 struct smb2_request *req[2];
2067 NTSTATUS status;
2068 bool ret = true;
2070 smb2_deltree(tree, dname);
2072 ZERO_STRUCT(create);
2073 create.in.desired_access = SEC_RIGHTS_DIR_ALL;
2074 create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
2075 create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2076 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
2077 NTCREATEX_SHARE_ACCESS_WRITE |
2078 NTCREATEX_SHARE_ACCESS_DELETE;
2079 create.in.create_disposition = NTCREATEX_DISP_CREATE;
2080 create.in.fname = dname;
2082 status = smb2_create(tree, mem_ctx, &create);
2083 h = create.out.file.handle;
2085 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n");
2087 smb2_transport_compound_start(tree->session->transport, 2);
2089 ZERO_STRUCT(f);
2090 f.in.file.handle = h;
2091 f.in.pattern = "*";
2092 f.in.max_response_size = 0x100;
2093 f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO;
2095 req[0] = smb2_find_send(tree, &f);
2096 req[1] = smb2_find_send(tree, &f);
2098 status = smb2_find_recv(req[0], mem_ctx, &f);
2099 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_recv failed\n");
2101 status = smb2_find_recv(req[1], mem_ctx, &f);
2102 torture_assert_ntstatus_equal_goto(tctx, status, STATUS_NO_MORE_FILES, ret, done, "smb2_find_recv failed\n");
2104 done:
2105 smb2_util_close(tree, h);
2106 smb2_deltree(tree, dname);
2107 TALLOC_FREE(mem_ctx);
2108 return ret;
2111 static bool test_compound_async_flush_close(struct torture_context *tctx,
2112 struct smb2_tree *tree)
2114 struct smb2_handle fhandle = { .data = { 0, 0 } };
2115 struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } };
2116 struct smb2_close cl;
2117 struct smb2_flush fl;
2118 const char *fname = "compound_async_flush_close";
2119 struct smb2_request *req[2];
2120 NTSTATUS status;
2121 bool ret = false;
2123 /* Start clean. */
2124 smb2_util_unlink(tree, fname);
2126 /* Create a file. */
2127 status = torture_smb2_testfile_access(tree,
2128 fname,
2129 &fhandle,
2130 SEC_RIGHTS_FILE_ALL);
2131 CHECK_STATUS(status, NT_STATUS_OK);
2133 /* Now do a compound flush + close handle. */
2134 smb2_transport_compound_start(tree->session->transport, 2);
2136 ZERO_STRUCT(fl);
2137 fl.in.file.handle = fhandle;
2139 req[0] = smb2_flush_send(tree, &fl);
2140 torture_assert_not_null_goto(tctx, req[0], ret, done,
2141 "smb2_flush_send failed\n");
2143 smb2_transport_compound_set_related(tree->session->transport, true);
2145 ZERO_STRUCT(cl);
2146 cl.in.file.handle = relhandle;
2147 req[1] = smb2_close_send(tree, &cl);
2148 torture_assert_not_null_goto(tctx, req[1], ret, done,
2149 "smb2_close_send failed\n");
2151 status = smb2_flush_recv(req[0], &fl);
2153 * On Windows, this flush will usually
2154 * succeed as we have nothing to flush,
2155 * so allow NT_STATUS_OK. Once bug #15172
2156 * is fixed Samba will do the flush synchronously
2157 * so allow NT_STATUS_OK.
2159 if (!NT_STATUS_IS_OK(status)) {
2161 * If we didn't get NT_STATUS_OK, we *must*
2162 * get NT_STATUS_INTERNAL_ERROR if the flush
2163 * goes async.
2165 * For pre-bugfix #15172 Samba, the flush goes async and
2166 * we should get NT_STATUS_INTERNAL_ERROR.
2168 torture_assert_ntstatus_equal_goto(tctx,
2169 status,
2170 NT_STATUS_INTERNAL_ERROR,
2171 ret,
2172 done,
2173 "smb2_flush_recv didn't return "
2174 "NT_STATUS_INTERNAL_ERROR.\n");
2176 status = smb2_close_recv(req[1], &cl);
2177 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2178 "smb2_close_recv failed.");
2180 ZERO_STRUCT(fhandle);
2183 * Do several more operations on the tree, spaced
2184 * out by 1 sec sleeps to make sure the server didn't
2185 * crash on the close. The sleeps are required to
2186 * make test test for a crash reliable, as we ensure
2187 * the pthread fsync internally finishes and accesses
2188 * freed memory. Without them the test occassionally
2189 * passes as we disconnect before the pthread fsync
2190 * finishes.
2192 status = smb2_util_unlink(tree, fname);
2193 CHECK_STATUS(status, NT_STATUS_OK);
2195 sleep(1);
2196 status = smb2_util_unlink(tree, fname);
2197 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2199 sleep(1);
2200 status = smb2_util_unlink(tree, fname);
2201 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2203 ret = true;
2205 done:
2207 if (fhandle.data[0] != 0) {
2208 smb2_util_close(tree, fhandle);
2211 smb2_util_unlink(tree, fname);
2212 return ret;
2215 static bool test_compound_async_flush_flush(struct torture_context *tctx,
2216 struct smb2_tree *tree)
2218 struct smb2_handle fhandle = { .data = { 0, 0 } };
2219 struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } };
2220 struct smb2_flush fl1;
2221 struct smb2_flush fl2;
2222 const char *fname = "compound_async_flush_flush";
2223 struct smb2_request *req[2];
2224 NTSTATUS status;
2225 bool ret = false;
2227 /* Start clean. */
2228 smb2_util_unlink(tree, fname);
2230 /* Create a file. */
2231 status = torture_smb2_testfile_access(tree,
2232 fname,
2233 &fhandle,
2234 SEC_RIGHTS_FILE_ALL);
2235 CHECK_STATUS(status, NT_STATUS_OK);
2237 /* Now do a compound flush + flush handle. */
2238 smb2_transport_compound_start(tree->session->transport, 2);
2240 ZERO_STRUCT(fl1);
2241 fl1.in.file.handle = fhandle;
2243 req[0] = smb2_flush_send(tree, &fl1);
2244 torture_assert_not_null_goto(tctx, req[0], ret, done,
2245 "smb2_flush_send (1) failed\n");
2247 smb2_transport_compound_set_related(tree->session->transport, true);
2249 ZERO_STRUCT(fl2);
2250 fl2.in.file.handle = relhandle;
2252 req[1] = smb2_flush_send(tree, &fl2);
2253 torture_assert_not_null_goto(tctx, req[1], ret, done,
2254 "smb2_flush_send (2) failed\n");
2256 status = smb2_flush_recv(req[0], &fl1);
2258 * On Windows, this flush will usually
2259 * succeed as we have nothing to flush,
2260 * so allow NT_STATUS_OK. Once bug #15172
2261 * is fixed Samba will do the flush synchronously
2262 * so allow NT_STATUS_OK.
2264 if (!NT_STATUS_IS_OK(status)) {
2266 * If we didn't get NT_STATUS_OK, we *must*
2267 * get NT_STATUS_INTERNAL_ERROR if the flush
2268 * goes async.
2270 * For pre-bugfix #15172 Samba, the flush goes async and
2271 * we should get NT_STATUS_INTERNAL_ERROR.
2273 torture_assert_ntstatus_equal_goto(tctx,
2274 status,
2275 NT_STATUS_INTERNAL_ERROR,
2276 ret,
2277 done,
2278 "smb2_flush_recv (1) didn't return "
2279 "NT_STATUS_INTERNAL_ERROR.\n");
2283 * If the flush is the last entry in a compound,
2284 * it should always succeed even if it goes async.
2286 status = smb2_flush_recv(req[1], &fl2);
2287 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2288 "smb2_flush_recv (2) failed.");
2290 status = smb2_util_close(tree, fhandle);
2291 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2292 "smb2_util_close failed.");
2293 ZERO_STRUCT(fhandle);
2296 * Do several more operations on the tree, spaced
2297 * out by 1 sec sleeps to make sure the server didn't
2298 * crash on the close. The sleeps are required to
2299 * make test test for a crash reliable, as we ensure
2300 * the pthread fsync internally finishes and accesses
2301 * freed memory. Without them the test occassionally
2302 * passes as we disconnect before the pthread fsync
2303 * finishes.
2305 status = smb2_util_unlink(tree, fname);
2306 CHECK_STATUS(status, NT_STATUS_OK);
2308 sleep(1);
2309 status = smb2_util_unlink(tree, fname);
2310 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2312 sleep(1);
2313 status = smb2_util_unlink(tree, fname);
2314 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2316 ret = true;
2318 done:
2320 if (fhandle.data[0] != 0) {
2321 smb2_util_close(tree, fhandle);
2324 smb2_util_unlink(tree, fname);
2325 return ret;
2329 * For Samba/smbd this test must be run against the aio_delay_inject share
2330 * as we need to ensure the last write in the compound takes longer than
2331 * 500 us, which is the threshold for going async in smbd SMB2 writes.
2334 static bool test_compound_async_write_write(struct torture_context *tctx,
2335 struct smb2_tree *tree)
2337 struct smb2_handle fhandle = { .data = { 0, 0 } };
2338 struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } };
2339 struct smb2_write w1;
2340 struct smb2_write w2;
2341 const char *fname = "compound_async_write_write";
2342 struct smb2_request *req[2];
2343 NTSTATUS status;
2344 bool is_smbd = torture_setting_bool(tctx, "smbd", true);
2345 bool ret = false;
2347 /* Start clean. */
2348 smb2_util_unlink(tree, fname);
2350 /* Create a file. */
2351 status = torture_smb2_testfile_access(tree,
2352 fname,
2353 &fhandle,
2354 SEC_RIGHTS_FILE_ALL);
2355 CHECK_STATUS(status, NT_STATUS_OK);
2357 /* Now do a compound write + write handle. */
2358 smb2_transport_compound_start(tree->session->transport, 2);
2360 ZERO_STRUCT(w1);
2361 w1.in.file.handle = fhandle;
2362 w1.in.offset = 0;
2363 w1.in.data = data_blob_talloc_zero(tctx, 64);
2364 req[0] = smb2_write_send(tree, &w1);
2366 torture_assert_not_null_goto(tctx, req[0], ret, done,
2367 "smb2_write_send (1) failed\n");
2369 smb2_transport_compound_set_related(tree->session->transport, true);
2371 ZERO_STRUCT(w2);
2372 w2.in.file.handle = relhandle;
2373 w2.in.offset = 64;
2374 w2.in.data = data_blob_talloc_zero(tctx, 64);
2375 req[1] = smb2_write_send(tree, &w2);
2377 torture_assert_not_null_goto(tctx, req[0], ret, done,
2378 "smb2_write_send (2) failed\n");
2380 status = smb2_write_recv(req[0], &w1);
2381 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2382 "smb2_write_recv (1) failed.");
2384 if (!is_smbd) {
2386 * Windows and other servers don't go async.
2388 status = smb2_write_recv(req[1], &w2);
2389 } else {
2391 * For smbd, the second write should go async
2392 * as it's the last element of a compound.
2394 WAIT_FOR_ASYNC_RESPONSE(req[1]);
2395 CHECK_VALUE(req[1]->cancel.can_cancel, true);
2397 * Now pick up the real return.
2399 status = smb2_write_recv(req[1], &w2);
2402 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2403 "smb2_write_recv (2) failed.");
2405 status = smb2_util_close(tree, fhandle);
2406 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2407 "smb2_util_close failed.");
2408 ZERO_STRUCT(fhandle);
2410 ret = true;
2412 done:
2414 if (fhandle.data[0] != 0) {
2415 smb2_util_close(tree, fhandle);
2418 smb2_util_unlink(tree, fname);
2419 return ret;
2423 * For Samba/smbd this test must be run against the aio_delay_inject share
2424 * as we need to ensure the last read in the compound takes longer than
2425 * 500 us, which is the threshold for going async in smbd SMB2 reads.
2428 static bool test_compound_async_read_read(struct torture_context *tctx,
2429 struct smb2_tree *tree)
2431 struct smb2_handle fhandle = { .data = { 0, 0 } };
2432 struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } };
2433 struct smb2_write w;
2434 struct smb2_read r1;
2435 struct smb2_read r2;
2436 const char *fname = "compound_async_read_read";
2437 struct smb2_request *req[2];
2438 NTSTATUS status;
2439 bool is_smbd = torture_setting_bool(tctx, "smbd", true);
2440 bool ret = false;
2442 /* Start clean. */
2443 smb2_util_unlink(tree, fname);
2445 /* Create a file. */
2446 status = torture_smb2_testfile_access(tree,
2447 fname,
2448 &fhandle,
2449 SEC_RIGHTS_FILE_ALL);
2450 CHECK_STATUS(status, NT_STATUS_OK);
2452 /* Write 128 bytes. */
2453 ZERO_STRUCT(w);
2454 w.in.file.handle = fhandle;
2455 w.in.offset = 0;
2456 w.in.data = data_blob_talloc_zero(tctx, 128);
2457 status = smb2_write(tree, &w);
2458 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2459 "smb2_write_recv (1) failed.");
2461 /* Now do a compound read + read handle. */
2462 smb2_transport_compound_start(tree->session->transport, 2);
2464 ZERO_STRUCT(r1);
2465 r1.in.file.handle = fhandle;
2466 r1.in.length = 64;
2467 r1.in.offset = 0;
2468 req[0] = smb2_read_send(tree, &r1);
2470 torture_assert_not_null_goto(tctx, req[0], ret, done,
2471 "smb2_read_send (1) failed\n");
2473 smb2_transport_compound_set_related(tree->session->transport, true);
2475 ZERO_STRUCT(r2);
2476 r2.in.file.handle = relhandle;
2477 r2.in.length = 64;
2478 r2.in.offset = 64;
2479 req[1] = smb2_read_send(tree, &r2);
2481 torture_assert_not_null_goto(tctx, req[0], ret, done,
2482 "smb2_read_send (2) failed\n");
2484 status = smb2_read_recv(req[0], tree, &r1);
2485 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2486 "smb2_read_recv (1) failed.");
2488 if (!is_smbd) {
2490 * Windows and other servers don't go async.
2492 status = smb2_read_recv(req[1], tree, &r2);
2493 } else {
2495 * For smbd, the second write should go async
2496 * as it's the last element of a compound.
2498 WAIT_FOR_ASYNC_RESPONSE(req[1]);
2499 CHECK_VALUE(req[1]->cancel.can_cancel, true);
2501 * Now pick up the real return.
2503 status = smb2_read_recv(req[1], tree, &r2);
2506 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2507 "smb2_read_recv (2) failed.");
2509 status = smb2_util_close(tree, fhandle);
2510 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2511 "smb2_util_close failed.");
2512 ZERO_STRUCT(fhandle);
2514 ret = true;
2516 done:
2518 if (fhandle.data[0] != 0) {
2519 smb2_util_close(tree, fhandle);
2522 smb2_util_unlink(tree, fname);
2523 return ret;
2527 struct torture_suite *torture_smb2_compound_init(TALLOC_CTX *ctx)
2529 struct torture_suite *suite = torture_suite_create(ctx, "compound");
2531 torture_suite_add_1smb2_test(suite, "related1", test_compound_related1);
2532 torture_suite_add_1smb2_test(suite, "related2", test_compound_related2);
2533 torture_suite_add_1smb2_test(suite, "related3",
2534 test_compound_related3);
2535 torture_suite_add_1smb2_test(suite, "related4",
2536 test_compound_related4);
2537 torture_suite_add_1smb2_test(suite, "related5",
2538 test_compound_related5);
2539 torture_suite_add_1smb2_test(suite, "related6",
2540 test_compound_related6);
2541 torture_suite_add_1smb2_test(suite, "related7",
2542 test_compound_related7);
2543 torture_suite_add_1smb2_test(suite, "related8",
2544 test_compound_related8);
2545 torture_suite_add_1smb2_test(suite, "related9",
2546 test_compound_related9);
2547 torture_suite_add_1smb2_test(suite, "unrelated1", test_compound_unrelated1);
2548 torture_suite_add_1smb2_test(suite, "invalid1", test_compound_invalid1);
2549 torture_suite_add_1smb2_test(suite, "invalid2", test_compound_invalid2);
2550 torture_suite_add_1smb2_test(suite, "invalid3", test_compound_invalid3);
2551 torture_suite_add_1smb2_test(
2552 suite, "invalid4", test_compound_invalid4);
2553 torture_suite_add_1smb2_test(suite, "interim1", test_compound_interim1);
2554 torture_suite_add_1smb2_test(suite, "interim2", test_compound_interim2);
2555 torture_suite_add_1smb2_test(suite, "compound-break", test_compound_break);
2556 torture_suite_add_1smb2_test(suite, "compound-padding", test_compound_padding);
2557 torture_suite_add_1smb2_test(suite, "create-write-close",
2558 test_compound_create_write_close);
2560 suite->description = talloc_strdup(suite, "SMB2-COMPOUND tests");
2562 return suite;
2565 struct torture_suite *torture_smb2_compound_find_init(TALLOC_CTX *ctx)
2567 struct torture_suite *suite = torture_suite_create(ctx, "compound_find");
2569 torture_suite_add_1smb2_test(suite, "compound_find_related", test_compound_find_related);
2570 torture_suite_add_1smb2_test(suite, "compound_find_unrelated", test_compound_find_unrelated);
2571 torture_suite_add_1smb2_test(suite, "compound_find_close", test_compound_find_close);
2573 suite->description = talloc_strdup(suite, "SMB2-COMPOUND-FIND tests");
2575 return suite;
2578 struct torture_suite *torture_smb2_compound_async_init(TALLOC_CTX *ctx)
2580 struct torture_suite *suite = torture_suite_create(ctx,
2581 "compound_async");
2583 torture_suite_add_1smb2_test(suite, "flush_close",
2584 test_compound_async_flush_close);
2585 torture_suite_add_1smb2_test(suite, "flush_flush",
2586 test_compound_async_flush_flush);
2587 torture_suite_add_1smb2_test(suite, "write_write",
2588 test_compound_async_write_write);
2589 torture_suite_add_1smb2_test(suite, "read_read",
2590 test_compound_async_read_read);
2592 suite->description = talloc_strdup(suite, "SMB2-COMPOUND-ASYNC tests");
2594 return suite;