11fed0bb40cc54992ddb468ca85e19ac61fe9b1d
[samba.git] / source4 / torture / smb2 / compound.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 compounded requests
5
6    Copyright (C) Stefan Metzmacher 2009
7
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.
12
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.
17
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/>.
20 */
21
22 #include "includes.h"
23 #include "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25 #include "torture/torture.h"
26 #include "torture/smb2/proto.h"
27 #include "../libcli/smb/smbXcli_base.h"
28
29 #define CHECK_STATUS(status, correct) do { \
30         if (!NT_STATUS_EQUAL(status, correct)) { \
31                 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
32                        nt_errstr(status), nt_errstr(correct)); \
33                 ret = false; \
34                 goto done; \
35         }} while (0)
36
37 #define CHECK_VALUE(v, correct) do { \
38         if ((v) != (correct)) { \
39                 torture_result(tctx, TORTURE_FAIL, \
40                     "(%s) Incorrect value %s=%d - should be %d\n", \
41                     __location__, #v, (int)v, (int)correct); \
42                 ret = false; \
43         }} while (0)
44
45 static struct {
46         struct smb2_handle handle;
47         uint8_t level;
48         struct smb2_break br;
49         int count;
50         int failures;
51         NTSTATUS failure_status;
52 } break_info;
53
54 static void torture_oplock_break_callback(struct smb2_request *req)
55 {
56         NTSTATUS status;
57         struct smb2_break br;
58
59         ZERO_STRUCT(br);
60         status = smb2_break_recv(req, &break_info.br);
61         if (!NT_STATUS_IS_OK(status)) {
62                 break_info.failures++;
63                 break_info.failure_status = status;
64         }
65
66         return;
67 }
68
69 /* A general oplock break notification handler.  This should be used when a
70  * test expects to break from batch or exclusive to a lower level. */
71 static bool torture_oplock_handler(struct smb2_transport *transport,
72                                    const struct smb2_handle *handle,
73                                    uint8_t level,
74                                    void *private_data)
75 {
76         struct smb2_tree *tree = private_data;
77         const char *name;
78         struct smb2_request *req;
79         ZERO_STRUCT(break_info.br);
80
81         break_info.handle       = *handle;
82         break_info.level        = level;
83         break_info.count++;
84
85         switch (level) {
86         case SMB2_OPLOCK_LEVEL_II:
87                 name = "level II";
88                 break;
89         case SMB2_OPLOCK_LEVEL_NONE:
90                 name = "none";
91                 break;
92         default:
93                 name = "unknown";
94                 break_info.failures++;
95         }
96         printf("Acking to %s [0x%02X] in oplock handler\n", name, level);
97
98         break_info.br.in.file.handle    = *handle;
99         break_info.br.in.oplock_level   = level;
100         break_info.br.in.reserved       = 0;
101         break_info.br.in.reserved2      = 0;
102
103         req = smb2_break_send(tree, &break_info.br);
104         req->async.fn = torture_oplock_break_callback;
105         req->async.private_data = NULL;
106         return true;
107 }
108
109 static bool test_compound_break(struct torture_context *tctx,
110                                  struct smb2_tree *tree)
111 {
112         const char *fname1 = "some-file.pptx";
113         NTSTATUS status;
114         bool ret = true;
115         union smb_open io1;
116         struct smb2_create io2;
117         struct smb2_getinfo gf;
118         struct smb2_request *req[2];
119         struct smb2_handle h1;
120         struct smb2_handle h;
121
122         tree->session->transport->oplock.handler = torture_oplock_handler;
123         tree->session->transport->oplock.private_data = tree;
124
125         ZERO_STRUCT(break_info);
126
127         /*
128           base ntcreatex parms
129         */
130         ZERO_STRUCT(io1.smb2);
131         io1.generic.level = RAW_OPEN_SMB2;
132         io1.smb2.in.desired_access = (SEC_STD_SYNCHRONIZE|
133                                         SEC_STD_READ_CONTROL|
134                                         SEC_FILE_READ_ATTRIBUTE|
135                                         SEC_FILE_READ_EA|
136                                         SEC_FILE_READ_DATA);
137         io1.smb2.in.alloc_size = 0;
138         io1.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
139         io1.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
140                         NTCREATEX_SHARE_ACCESS_WRITE|
141                         NTCREATEX_SHARE_ACCESS_DELETE;
142         io1.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
143         io1.smb2.in.create_options = 0;
144         io1.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
145         io1.smb2.in.security_flags = 0;
146         io1.smb2.in.fname = fname1;
147
148         torture_comment(tctx, "TEST2: open a file with an batch "
149                         "oplock (share mode: all)\n");
150         io1.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
151
152         status = smb2_create(tree, tctx, &(io1.smb2));
153         torture_assert_ntstatus_ok(tctx, status, "Error opening the file");
154
155         h1 = io1.smb2.out.file.handle;
156
157         torture_comment(tctx, "TEST2: Opening second time with compound\n");
158
159         ZERO_STRUCT(io2);
160
161         io2.in.desired_access = (SEC_STD_SYNCHRONIZE|
162                                 SEC_FILE_READ_ATTRIBUTE|
163                                 SEC_FILE_READ_EA);
164         io2.in.alloc_size = 0;
165         io2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
166         io2.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
167                         NTCREATEX_SHARE_ACCESS_WRITE|
168                         NTCREATEX_SHARE_ACCESS_DELETE;
169         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
170         io2.in.create_options = 0;
171         io2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
172         io2.in.security_flags = 0;
173         io2.in.fname = fname1;
174         io2.in.oplock_level = 0;
175
176         smb2_transport_compound_start(tree->session->transport, 2);
177
178         req[0] = smb2_create_send(tree, &io2);
179
180         smb2_transport_compound_set_related(tree->session->transport, true);
181
182         h.data[0] = UINT64_MAX;
183         h.data[1] = UINT64_MAX;
184
185         ZERO_STRUCT(gf);
186         gf.in.file.handle = h;
187         gf.in.info_type = SMB2_GETINFO_FILE;
188         gf.in.info_class = 0x16;
189         gf.in.output_buffer_length = 0x1000;
190         gf.in.input_buffer_length = 0;
191
192         req[1] = smb2_getinfo_send(tree, &gf);
193
194         status = smb2_create_recv(req[0], tree, &io2);
195         CHECK_STATUS(status, NT_STATUS_OK);
196
197         status = smb2_getinfo_recv(req[1], tree, &gf);
198         CHECK_STATUS(status, NT_STATUS_OK);
199
200 done:
201
202         smb2_util_close(tree, h1);
203         smb2_util_unlink(tree, fname1);
204         return ret;
205 }
206
207 static bool test_compound_related1(struct torture_context *tctx,
208                                    struct smb2_tree *tree)
209 {
210         struct smb2_handle hd;
211         struct smb2_create cr;
212         NTSTATUS status;
213         const char *fname = "compound_related1.dat";
214         struct smb2_close cl;
215         bool ret = true;
216         struct smb2_request *req[2];
217         struct smbXcli_tcon *saved_tcon = tree->smbXcli;
218         struct smbXcli_session *saved_session = tree->session->smbXcli;
219
220         smb2_transport_credits_ask_num(tree->session->transport, 2);
221
222         smb2_util_unlink(tree, fname);
223
224         smb2_transport_credits_ask_num(tree->session->transport, 1);
225
226         ZERO_STRUCT(cr);
227         cr.in.security_flags            = 0x00;
228         cr.in.oplock_level              = 0;
229         cr.in.impersonation_level       = NTCREATEX_IMPERSONATION_IMPERSONATION;
230         cr.in.create_flags              = 0x00000000;
231         cr.in.reserved                  = 0x00000000;
232         cr.in.desired_access            = SEC_RIGHTS_FILE_ALL;
233         cr.in.file_attributes           = FILE_ATTRIBUTE_NORMAL;
234         cr.in.share_access              = NTCREATEX_SHARE_ACCESS_READ |
235                                           NTCREATEX_SHARE_ACCESS_WRITE |
236                                           NTCREATEX_SHARE_ACCESS_DELETE;
237         cr.in.create_disposition        = NTCREATEX_DISP_OPEN_IF;
238         cr.in.create_options            = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
239                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
240                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
241                                           0x00200000;
242         cr.in.fname                     = fname;
243
244         smb2_transport_compound_start(tree->session->transport, 2);
245
246         req[0] = smb2_create_send(tree, &cr);
247
248         smb2_transport_compound_set_related(tree->session->transport, true);
249
250         hd.data[0] = UINT64_MAX;
251         hd.data[1] = UINT64_MAX;
252
253         ZERO_STRUCT(cl);
254         cl.in.file.handle = hd;
255
256         tree->smbXcli = smbXcli_tcon_create(tree);
257         smb2cli_tcon_set_values(tree->smbXcli,
258                                 NULL, /* session */
259                                 0xFFFFFFFF, /* tcon_id */
260                                 0, /* type */
261                                 0, /* flags */
262                                 0, /* capabilities */
263                                 0 /* maximal_access */);
264
265         tree->session->smbXcli = smbXcli_session_copy(tree->session,
266                                                         tree->session->smbXcli);
267         smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0);
268
269         req[1] = smb2_close_send(tree, &cl);
270
271         status = smb2_create_recv(req[0], tree, &cr);
272         CHECK_STATUS(status, NT_STATUS_OK);
273         status = smb2_close_recv(req[1], &cl);
274         CHECK_STATUS(status, NT_STATUS_OK);
275
276         TALLOC_FREE(tree->smbXcli);
277         tree->smbXcli = saved_tcon;
278         TALLOC_FREE(tree->session->smbXcli);
279         tree->session->smbXcli = saved_session;
280
281         smb2_util_unlink(tree, fname);
282 done:
283         return ret;
284 }
285
286 static bool test_compound_related2(struct torture_context *tctx,
287                                    struct smb2_tree *tree)
288 {
289         struct smb2_handle hd;
290         struct smb2_create cr;
291         NTSTATUS status;
292         const char *fname = "compound_related2.dat";
293         struct smb2_close cl;
294         bool ret = true;
295         struct smb2_request *req[5];
296         struct smbXcli_tcon *saved_tcon = tree->smbXcli;
297         struct smbXcli_session *saved_session = tree->session->smbXcli;
298
299         smb2_transport_credits_ask_num(tree->session->transport, 5);
300
301         smb2_util_unlink(tree, fname);
302
303         smb2_transport_credits_ask_num(tree->session->transport, 1);
304
305         ZERO_STRUCT(cr);
306         cr.in.security_flags            = 0x00;
307         cr.in.oplock_level              = 0;
308         cr.in.impersonation_level       = NTCREATEX_IMPERSONATION_IMPERSONATION;
309         cr.in.create_flags              = 0x00000000;
310         cr.in.reserved                  = 0x00000000;
311         cr.in.desired_access            = SEC_RIGHTS_FILE_ALL;
312         cr.in.file_attributes           = FILE_ATTRIBUTE_NORMAL;
313         cr.in.share_access              = NTCREATEX_SHARE_ACCESS_READ |
314                                           NTCREATEX_SHARE_ACCESS_WRITE |
315                                           NTCREATEX_SHARE_ACCESS_DELETE;
316         cr.in.create_disposition        = NTCREATEX_DISP_OPEN_IF;
317         cr.in.create_options            = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
318                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
319                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
320                                           0x00200000;
321         cr.in.fname                     = fname;
322
323         smb2_transport_compound_start(tree->session->transport, 5);
324
325         req[0] = smb2_create_send(tree, &cr);
326
327         hd.data[0] = UINT64_MAX;
328         hd.data[1] = UINT64_MAX;
329
330         smb2_transport_compound_set_related(tree->session->transport, true);
331
332         ZERO_STRUCT(cl);
333         cl.in.file.handle = hd;
334
335         tree->smbXcli = smbXcli_tcon_create(tree);
336         smb2cli_tcon_set_values(tree->smbXcli,
337                                 NULL, /* session */
338                                 0xFFFFFFFF, /* tcon_id */
339                                 0, /* type */
340                                 0, /* flags */
341                                 0, /* capabilities */
342                                 0 /* maximal_access */);
343
344         tree->session->smbXcli = smbXcli_session_copy(tree->session,
345                                                         tree->session->smbXcli);
346         smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0);
347
348         req[1] = smb2_close_send(tree, &cl);
349         req[2] = smb2_close_send(tree, &cl);
350         req[3] = smb2_close_send(tree, &cl);
351         req[4] = smb2_close_send(tree, &cl);
352
353         status = smb2_create_recv(req[0], tree, &cr);
354         CHECK_STATUS(status, NT_STATUS_OK);
355         status = smb2_close_recv(req[1], &cl);
356         CHECK_STATUS(status, NT_STATUS_OK);
357         status = smb2_close_recv(req[2], &cl);
358         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
359         status = smb2_close_recv(req[3], &cl);
360         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
361         status = smb2_close_recv(req[4], &cl);
362         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
363
364         TALLOC_FREE(tree->smbXcli);
365         tree->smbXcli = saved_tcon;
366         TALLOC_FREE(tree->session->smbXcli);
367         tree->session->smbXcli = saved_session;
368
369         smb2_util_unlink(tree, fname);
370 done:
371         return ret;
372 }
373
374 static bool test_compound_related3(struct torture_context *tctx,
375                                    struct smb2_tree *tree)
376 {
377         struct smb2_handle hd;
378         struct smb2_ioctl io;
379         struct smb2_create cr;
380         struct smb2_close cl;
381         const char *fname = "compound_related3.dat";
382         struct smb2_request *req[3];
383         NTSTATUS status;
384         bool ret = false;
385
386         smb2_util_unlink(tree, fname);
387
388         ZERO_STRUCT(cr);
389         cr.in.security_flags    = 0x00;
390         cr.in.oplock_level      = 0;
391         cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
392         cr.in.create_flags      = 0x00000000;
393         cr.in.reserved          = 0x00000000;
394         cr.in.desired_access    = SEC_RIGHTS_FILE_ALL;
395         cr.in.file_attributes   = FILE_ATTRIBUTE_NORMAL;
396         cr.in.share_access      = NTCREATEX_SHARE_ACCESS_READ |
397                                   NTCREATEX_SHARE_ACCESS_WRITE |
398                                   NTCREATEX_SHARE_ACCESS_DELETE;
399         cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
400         cr.in.create_options    = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
401                                   NTCREATEX_OPTIONS_ASYNC_ALERT |
402                                   NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
403                                   0x00200000;
404         cr.in.fname             = fname;
405
406         smb2_transport_compound_start(tree->session->transport, 3);
407
408         req[0] = smb2_create_send(tree, &cr);
409
410         hd.data[0] = UINT64_MAX;
411         hd.data[1] = UINT64_MAX;
412
413         smb2_transport_compound_set_related(tree->session->transport, true);
414
415         ZERO_STRUCT(io);
416         io.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID;
417         io.in.file.handle = hd;
418         io.in.unknown2 = 0;
419         io.in.max_response_size = 64;
420         io.in.flags = 1;
421
422         req[1] = smb2_ioctl_send(tree, &io);
423
424         ZERO_STRUCT(cl);
425         cl.in.file.handle = hd;
426
427         req[2] = smb2_close_send(tree, &cl);
428
429         status = smb2_create_recv(req[0], tree, &cr);
430         CHECK_STATUS(status, NT_STATUS_OK);
431         status = smb2_ioctl_recv(req[1], tree, &io);
432         CHECK_STATUS(status, NT_STATUS_OK);
433         status = smb2_close_recv(req[2], &cl);
434         CHECK_STATUS(status, NT_STATUS_OK);
435
436         status = smb2_util_unlink(tree, fname);
437         CHECK_STATUS(status, NT_STATUS_OK);
438
439         ret = true;
440 done:
441         return ret;
442 }
443
444 static bool test_compound_padding(struct torture_context *tctx,
445                                   struct smb2_tree *tree)
446 {
447         struct smb2_handle h;
448         struct smb2_create cr;
449         struct smb2_read r;
450         const char *fname = "compound_read.dat";
451         const char *sname = "compound_read.dat:foo";
452         struct smb2_request *req[3];
453         NTSTATUS status;
454         bool ret = false;
455
456         smb2_util_unlink(tree, fname);
457
458         /* Write file */
459         ZERO_STRUCT(cr);
460         cr.in.desired_access = SEC_FILE_WRITE_DATA;
461         cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
462         cr.in.create_disposition = NTCREATEX_DISP_CREATE;
463         cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
464         cr.in.fname = fname;
465         cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
466                 NTCREATEX_SHARE_ACCESS_WRITE|
467                 NTCREATEX_SHARE_ACCESS_DELETE;
468         status = smb2_create(tree, tctx, &cr);
469         CHECK_STATUS(status, NT_STATUS_OK);
470         h = cr.out.file.handle;
471
472         status = smb2_util_write(tree, h, "123", 0, 3);
473         CHECK_STATUS(status, NT_STATUS_OK);
474
475         smb2_util_close(tree, h);
476
477         /* Write stream */
478         ZERO_STRUCT(cr);
479         cr.in.desired_access = SEC_FILE_WRITE_DATA;
480         cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
481         cr.in.create_disposition = NTCREATEX_DISP_CREATE;
482         cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
483         cr.in.fname = sname;
484         cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
485                 NTCREATEX_SHARE_ACCESS_WRITE|
486                 NTCREATEX_SHARE_ACCESS_DELETE;
487         status = smb2_create(tree, tctx, &cr);
488         CHECK_STATUS(status, NT_STATUS_OK);
489         h = cr.out.file.handle;
490
491         status = smb2_util_write(tree, h, "456", 0, 3);
492         CHECK_STATUS(status, NT_STATUS_OK);
493
494         smb2_util_close(tree, h);
495
496         /* Check compound read from basefile */
497         smb2_transport_compound_start(tree->session->transport, 2);
498
499         ZERO_STRUCT(cr);
500         cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
501         cr.in.desired_access    = SEC_FILE_READ_DATA;
502         cr.in.file_attributes   = FILE_ATTRIBUTE_NORMAL;
503         cr.in.create_disposition = NTCREATEX_DISP_OPEN;
504         cr.in.fname             = fname;
505         cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
506                 NTCREATEX_SHARE_ACCESS_WRITE|
507                 NTCREATEX_SHARE_ACCESS_DELETE;
508         req[0] = smb2_create_send(tree, &cr);
509
510         smb2_transport_compound_set_related(tree->session->transport, true);
511
512         ZERO_STRUCT(r);
513         h.data[0] = UINT64_MAX;
514         h.data[1] = UINT64_MAX;
515         r.in.file.handle = h;
516         r.in.length      = 3;
517         r.in.offset      = 0;
518         r.in.min_count      = 1;
519         req[1] = smb2_read_send(tree, &r);
520
521         status = smb2_create_recv(req[0], tree, &cr);
522         CHECK_STATUS(status, NT_STATUS_OK);
523
524         /*
525          * We must do a manual smb2_request_receive() in order to be
526          * able to check the transport layer info, as smb2_read_recv()
527          * will destroy the req. smb2_read_recv() will call
528          * smb2_request_receive() again, but that's ok.
529          */
530         if (!smb2_request_receive(req[1]) ||
531             !smb2_request_is_ok(req[1])) {
532                 torture_fail(tctx, "failed to receive read request");
533         }
534
535         /*
536          * size must be 24: 16 byte read response header plus 3
537          * requested bytes padded to an 8 byte boundary.
538          */
539         CHECK_VALUE(req[1]->in.body_size, 24);
540
541         status = smb2_read_recv(req[1], tree, &r);
542         CHECK_STATUS(status, NT_STATUS_OK);
543
544         smb2_util_close(tree, cr.out.file.handle);
545
546         /* Check compound read from stream */
547         smb2_transport_compound_start(tree->session->transport, 2);
548
549         ZERO_STRUCT(cr);
550         cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
551         cr.in.desired_access    = SEC_FILE_READ_DATA;
552         cr.in.file_attributes   = FILE_ATTRIBUTE_NORMAL;
553         cr.in.create_disposition = NTCREATEX_DISP_OPEN;
554         cr.in.fname             = sname;
555         cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
556                 NTCREATEX_SHARE_ACCESS_WRITE|
557                 NTCREATEX_SHARE_ACCESS_DELETE;
558         req[0] = smb2_create_send(tree, &cr);
559
560         smb2_transport_compound_set_related(tree->session->transport, true);
561
562         ZERO_STRUCT(r);
563         h.data[0] = UINT64_MAX;
564         h.data[1] = UINT64_MAX;
565         r.in.file.handle = h;
566         r.in.length      = 3;
567         r.in.offset      = 0;
568         r.in.min_count   = 1;
569         req[1] = smb2_read_send(tree, &r);
570
571         status = smb2_create_recv(req[0], tree, &cr);
572         CHECK_STATUS(status, NT_STATUS_OK);
573
574         /*
575          * We must do a manual smb2_request_receive() in order to be
576          * able to check the transport layer info, as smb2_read_recv()
577          * will destroy the req. smb2_read_recv() will call
578          * smb2_request_receive() again, but that's ok.
579          */
580         if (!smb2_request_receive(req[1]) ||
581             !smb2_request_is_ok(req[1])) {
582                 torture_fail(tctx, "failed to receive read request");
583         }
584
585         /*
586          * size must be 24: 16 byte read response header plus 3
587          * requested bytes padded to an 8 byte boundary.
588          */
589         CHECK_VALUE(req[1]->in.body_size, 24);
590
591         status = smb2_read_recv(req[1], tree, &r);
592         CHECK_STATUS(status, NT_STATUS_OK);
593
594         h = cr.out.file.handle;
595
596         /* Check 2 compound (unrelateated) reads from existing stream handle */
597         smb2_transport_compound_start(tree->session->transport, 2);
598
599         ZERO_STRUCT(r);
600         r.in.file.handle = h;
601         r.in.length      = 3;
602         r.in.offset      = 0;
603         r.in.min_count   = 1;
604         req[0] = smb2_read_send(tree, &r);
605         req[1] = smb2_read_send(tree, &r);
606
607         /*
608          * We must do a manual smb2_request_receive() in order to be
609          * able to check the transport layer info, as smb2_read_recv()
610          * will destroy the req. smb2_read_recv() will call
611          * smb2_request_receive() again, but that's ok.
612          */
613         if (!smb2_request_receive(req[0]) ||
614             !smb2_request_is_ok(req[0])) {
615                 torture_fail(tctx, "failed to receive read request");
616         }
617         if (!smb2_request_receive(req[1]) ||
618             !smb2_request_is_ok(req[1])) {
619                 torture_fail(tctx, "failed to receive read request");
620         }
621
622         /*
623          * size must be 24: 16 byte read response header plus 3
624          * requested bytes padded to an 8 byte boundary.
625          */
626         CHECK_VALUE(req[0]->in.body_size, 24);
627         CHECK_VALUE(req[1]->in.body_size, 24);
628
629         status = smb2_read_recv(req[0], tree, &r);
630         CHECK_STATUS(status, NT_STATUS_OK);
631         status = smb2_read_recv(req[1], tree, &r);
632         CHECK_STATUS(status, NT_STATUS_OK);
633
634         /*
635          * now try a single read from the stream and verify there's no padding
636          */
637         ZERO_STRUCT(r);
638         r.in.file.handle = h;
639         r.in.length      = 3;
640         r.in.offset      = 0;
641         r.in.min_count   = 1;
642         req[0] = smb2_read_send(tree, &r);
643
644         /*
645          * We must do a manual smb2_request_receive() in order to be
646          * able to check the transport layer info, as smb2_read_recv()
647          * will destroy the req. smb2_read_recv() will call
648          * smb2_request_receive() again, but that's ok.
649          */
650         if (!smb2_request_receive(req[0]) ||
651             !smb2_request_is_ok(req[0])) {
652                 torture_fail(tctx, "failed to receive read request");
653         }
654
655         /*
656          * size must be 19: 16 byte read response header plus 3
657          * requested bytes without padding.
658          */
659         CHECK_VALUE(req[0]->in.body_size, 19);
660
661         status = smb2_read_recv(req[0], tree, &r);
662         CHECK_STATUS(status, NT_STATUS_OK);
663
664         smb2_util_close(tree, h);
665
666         status = smb2_util_unlink(tree, fname);
667         CHECK_STATUS(status, NT_STATUS_OK);
668
669         ret = true;
670 done:
671         return ret;
672 }
673
674 static bool test_compound_create_write_close(struct torture_context *tctx,
675                                              struct smb2_tree *tree)
676 {
677         struct smb2_handle handle = { .data = { UINT64_MAX, UINT64_MAX } };
678         struct smb2_create create;
679         struct smb2_write write;
680         struct smb2_close close;
681         const char *fname = "compound_create_write_close.dat";
682         struct smb2_request *req[3];
683         NTSTATUS status;
684         bool ret = false;
685
686         smb2_util_unlink(tree, fname);
687
688         ZERO_STRUCT(create);
689         create.in.security_flags = 0x00;
690         create.in.oplock_level = 0;
691         create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
692         create.in.create_flags = 0x00000000;
693         create.in.reserved = 0x00000000;
694         create.in.desired_access = SEC_RIGHTS_FILE_ALL;
695         create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
696         create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
697                 NTCREATEX_SHARE_ACCESS_WRITE |
698                 NTCREATEX_SHARE_ACCESS_DELETE;
699         create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
700         create.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
701                 NTCREATEX_OPTIONS_ASYNC_ALERT |
702                 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
703                 0x00200000;
704         create.in.fname = fname;
705
706         smb2_transport_compound_start(tree->session->transport, 3);
707
708         req[0] = smb2_create_send(tree, &create);
709
710         smb2_transport_compound_set_related(tree->session->transport, true);
711
712         ZERO_STRUCT(write);
713         write.in.file.handle = handle;
714         write.in.offset = 0;
715         write.in.data = data_blob_talloc(tctx, NULL, 1024);
716
717         req[1] = smb2_write_send(tree, &write);
718
719         ZERO_STRUCT(close);
720         close.in.file.handle = handle;
721
722         req[2] = smb2_close_send(tree, &close);
723
724         status = smb2_create_recv(req[0], tree, &create);
725         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
726                                         "CREATE failed.");
727
728         status = smb2_write_recv(req[1], &write);
729         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
730                                         "WRITE failed.");
731
732         status = smb2_close_recv(req[2], &close);
733         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
734                                         "CLOSE failed.");
735
736         status = smb2_util_unlink(tree, fname);
737         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
738                                         "File deletion failed.");
739
740         ret = true;
741 done:
742         return ret;
743 }
744
745 static bool test_compound_unrelated1(struct torture_context *tctx,
746                                      struct smb2_tree *tree)
747 {
748         struct smb2_handle hd;
749         struct smb2_create cr;
750         NTSTATUS status;
751         const char *fname = "compound_unrelated1.dat";
752         struct smb2_close cl;
753         bool ret = true;
754         struct smb2_request *req[5];
755
756         smb2_transport_credits_ask_num(tree->session->transport, 5);
757
758         smb2_util_unlink(tree, fname);
759
760         smb2_transport_credits_ask_num(tree->session->transport, 1);
761
762         ZERO_STRUCT(cr);
763         cr.in.security_flags            = 0x00;
764         cr.in.oplock_level              = 0;
765         cr.in.impersonation_level       = NTCREATEX_IMPERSONATION_IMPERSONATION;
766         cr.in.create_flags              = 0x00000000;
767         cr.in.reserved                  = 0x00000000;
768         cr.in.desired_access            = SEC_RIGHTS_FILE_ALL;
769         cr.in.file_attributes           = FILE_ATTRIBUTE_NORMAL;
770         cr.in.share_access              = NTCREATEX_SHARE_ACCESS_READ |
771                                           NTCREATEX_SHARE_ACCESS_WRITE |
772                                           NTCREATEX_SHARE_ACCESS_DELETE;
773         cr.in.create_disposition        = NTCREATEX_DISP_OPEN_IF;
774         cr.in.create_options            = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
775                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
776                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
777                                           0x00200000;
778         cr.in.fname                     = fname;
779
780         smb2_transport_compound_start(tree->session->transport, 5);
781
782         req[0] = smb2_create_send(tree, &cr);
783
784         hd.data[0] = UINT64_MAX;
785         hd.data[1] = UINT64_MAX;
786
787         ZERO_STRUCT(cl);
788         cl.in.file.handle = hd;
789         req[1] = smb2_close_send(tree, &cl);
790         req[2] = smb2_close_send(tree, &cl);
791         req[3] = smb2_close_send(tree, &cl);
792         req[4] = smb2_close_send(tree, &cl);
793
794         status = smb2_create_recv(req[0], tree, &cr);
795         CHECK_STATUS(status, NT_STATUS_OK);
796         status = smb2_close_recv(req[1], &cl);
797         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
798         status = smb2_close_recv(req[2], &cl);
799         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
800         status = smb2_close_recv(req[3], &cl);
801         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
802         status = smb2_close_recv(req[4], &cl);
803         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
804
805         smb2_util_unlink(tree, fname);
806 done:
807         return ret;
808 }
809
810 static bool test_compound_invalid1(struct torture_context *tctx,
811                                    struct smb2_tree *tree)
812 {
813         struct smb2_handle hd;
814         struct smb2_create cr;
815         NTSTATUS status;
816         const char *fname = "compound_invalid1.dat";
817         struct smb2_close cl;
818         bool ret = true;
819         struct smb2_request *req[3];
820
821         smb2_transport_credits_ask_num(tree->session->transport, 3);
822
823         smb2_util_unlink(tree, fname);
824
825         smb2_transport_credits_ask_num(tree->session->transport, 1);
826
827         ZERO_STRUCT(cr);
828         cr.in.security_flags            = 0x00;
829         cr.in.oplock_level              = 0;
830         cr.in.impersonation_level       = NTCREATEX_IMPERSONATION_IMPERSONATION;
831         cr.in.create_flags              = 0x00000000;
832         cr.in.reserved                  = 0x00000000;
833         cr.in.desired_access            = SEC_RIGHTS_FILE_ALL;
834         cr.in.file_attributes           = FILE_ATTRIBUTE_NORMAL;
835         cr.in.share_access              = NTCREATEX_SHARE_ACCESS_READ |
836                                           NTCREATEX_SHARE_ACCESS_WRITE |
837                                           NTCREATEX_SHARE_ACCESS_DELETE;
838         cr.in.create_disposition        = NTCREATEX_DISP_OPEN_IF;
839         cr.in.create_options            = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
840                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
841                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
842                                           0x00200000;
843         cr.in.fname                     = fname;
844
845         smb2_transport_compound_start(tree->session->transport, 3);
846
847         /* passing the first request with the related flag is invalid */
848         smb2_transport_compound_set_related(tree->session->transport, true);
849
850         req[0] = smb2_create_send(tree, &cr);
851
852         hd.data[0] = UINT64_MAX;
853         hd.data[1] = UINT64_MAX;
854
855         ZERO_STRUCT(cl);
856         cl.in.file.handle = hd;
857         req[1] = smb2_close_send(tree, &cl);
858
859         smb2_transport_compound_set_related(tree->session->transport, false);
860         req[2] = smb2_close_send(tree, &cl);
861
862         status = smb2_create_recv(req[0], tree, &cr);
863         /* TODO: check why this fails with --signing=required */
864         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
865         status = smb2_close_recv(req[1], &cl);
866         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
867         status = smb2_close_recv(req[2], &cl);
868         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
869
870         smb2_util_unlink(tree, fname);
871 done:
872         return ret;
873 }
874
875 static bool test_compound_invalid2(struct torture_context *tctx,
876                                    struct smb2_tree *tree)
877 {
878         struct smb2_handle hd;
879         struct smb2_create cr;
880         NTSTATUS status;
881         const char *fname = "compound_invalid2.dat";
882         struct smb2_close cl;
883         bool ret = true;
884         struct smb2_request *req[5];
885         struct smbXcli_tcon *saved_tcon = tree->smbXcli;
886         struct smbXcli_session *saved_session = tree->session->smbXcli;
887
888         smb2_transport_credits_ask_num(tree->session->transport, 5);
889
890         smb2_util_unlink(tree, fname);
891
892         smb2_transport_credits_ask_num(tree->session->transport, 1);
893
894         ZERO_STRUCT(cr);
895         cr.in.security_flags            = 0x00;
896         cr.in.oplock_level              = 0;
897         cr.in.impersonation_level       = NTCREATEX_IMPERSONATION_IMPERSONATION;
898         cr.in.create_flags              = 0x00000000;
899         cr.in.reserved                  = 0x00000000;
900         cr.in.desired_access            = SEC_RIGHTS_FILE_ALL;
901         cr.in.file_attributes           = FILE_ATTRIBUTE_NORMAL;
902         cr.in.share_access              = NTCREATEX_SHARE_ACCESS_READ |
903                                           NTCREATEX_SHARE_ACCESS_WRITE |
904                                           NTCREATEX_SHARE_ACCESS_DELETE;
905         cr.in.create_disposition        = NTCREATEX_DISP_OPEN_IF;
906         cr.in.create_options            = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
907                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
908                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
909                                           0x00200000;
910         cr.in.fname                     = fname;
911
912         smb2_transport_compound_start(tree->session->transport, 5);
913
914         req[0] = smb2_create_send(tree, &cr);
915
916         hd.data[0] = UINT64_MAX;
917         hd.data[1] = UINT64_MAX;
918
919         smb2_transport_compound_set_related(tree->session->transport, true);
920
921         ZERO_STRUCT(cl);
922         cl.in.file.handle = hd;
923
924         tree->smbXcli = smbXcli_tcon_create(tree);
925         smb2cli_tcon_set_values(tree->smbXcli,
926                                 NULL, /* session */
927                                 0xFFFFFFFF, /* tcon_id */
928                                 0, /* type */
929                                 0, /* flags */
930                                 0, /* capabilities */
931                                 0 /* maximal_access */);
932
933         tree->session->smbXcli = smbXcli_session_copy(tree->session,
934                                                         tree->session->smbXcli);
935         smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0);
936
937         req[1] = smb2_close_send(tree, &cl);
938         /* strange that this is not generating invalid parameter */
939         smb2_transport_compound_set_related(tree->session->transport, false);
940         req[2] = smb2_close_send(tree, &cl);
941         req[3] = smb2_close_send(tree, &cl);
942         smb2_transport_compound_set_related(tree->session->transport, true);
943         req[4] = smb2_close_send(tree, &cl);
944
945         status = smb2_create_recv(req[0], tree, &cr);
946         CHECK_STATUS(status, NT_STATUS_OK);
947         status = smb2_close_recv(req[1], &cl);
948         CHECK_STATUS(status, NT_STATUS_OK);
949         status = smb2_close_recv(req[2], &cl);
950         CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
951         status = smb2_close_recv(req[3], &cl);
952         CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
953         status = smb2_close_recv(req[4], &cl);
954         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
955
956         TALLOC_FREE(tree->smbXcli);
957         tree->smbXcli = saved_tcon;
958         TALLOC_FREE(tree->session->smbXcli);
959         tree->session->smbXcli = saved_session;
960
961         smb2_util_unlink(tree, fname);
962 done:
963         return ret;
964 }
965
966 static bool test_compound_invalid3(struct torture_context *tctx,
967                                    struct smb2_tree *tree)
968 {
969         struct smb2_handle hd;
970         struct smb2_create cr;
971         NTSTATUS status;
972         const char *fname = "compound_invalid3.dat";
973         struct smb2_close cl;
974         bool ret = true;
975         struct smb2_request *req[5];
976
977         smb2_transport_credits_ask_num(tree->session->transport, 5);
978
979         smb2_util_unlink(tree, fname);
980
981         smb2_transport_credits_ask_num(tree->session->transport, 1);
982
983         ZERO_STRUCT(cr);
984         cr.in.security_flags            = 0x00;
985         cr.in.oplock_level              = 0;
986         cr.in.impersonation_level       = NTCREATEX_IMPERSONATION_IMPERSONATION;
987         cr.in.create_flags              = 0x00000000;
988         cr.in.reserved                  = 0x00000000;
989         cr.in.desired_access            = SEC_RIGHTS_FILE_ALL;
990         cr.in.file_attributes           = FILE_ATTRIBUTE_NORMAL;
991         cr.in.share_access              = NTCREATEX_SHARE_ACCESS_READ |
992                                           NTCREATEX_SHARE_ACCESS_WRITE |
993                                           NTCREATEX_SHARE_ACCESS_DELETE;
994         cr.in.create_disposition        = NTCREATEX_DISP_OPEN_IF;
995         cr.in.create_options            = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
996                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
997                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
998                                           0x00200000;
999         cr.in.fname                     = fname;
1000
1001         smb2_transport_compound_start(tree->session->transport, 5);
1002
1003         req[0] = smb2_create_send(tree, &cr);
1004
1005         hd.data[0] = UINT64_MAX;
1006         hd.data[1] = UINT64_MAX;
1007
1008         ZERO_STRUCT(cl);
1009         cl.in.file.handle = hd;
1010         req[1] = smb2_close_send(tree, &cl);
1011         req[2] = smb2_close_send(tree, &cl);
1012         /* flipping the related flag is invalid */
1013         smb2_transport_compound_set_related(tree->session->transport, true);
1014         req[3] = smb2_close_send(tree, &cl);
1015         req[4] = smb2_close_send(tree, &cl);
1016
1017         status = smb2_create_recv(req[0], tree, &cr);
1018         CHECK_STATUS(status, NT_STATUS_OK);
1019         status = smb2_close_recv(req[1], &cl);
1020         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1021         status = smb2_close_recv(req[2], &cl);
1022         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1023         status = smb2_close_recv(req[3], &cl);
1024         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1025         status = smb2_close_recv(req[4], &cl);
1026         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1027
1028         smb2_util_unlink(tree, fname);
1029 done:
1030         return ret;
1031 }
1032
1033 static bool test_compound_invalid4(struct torture_context *tctx,
1034                                    struct smb2_tree *tree)
1035 {
1036         struct smb2_create cr;
1037         struct smb2_read rd;
1038         NTSTATUS status;
1039         const char *fname = "compound_invalid4.dat";
1040         struct smb2_close cl;
1041         bool ret = true;
1042         bool ok;
1043         struct smb2_request *req[2];
1044
1045         smb2_transport_credits_ask_num(tree->session->transport, 2);
1046
1047         smb2_util_unlink(tree, fname);
1048
1049         ZERO_STRUCT(cr);
1050         cr.in.security_flags      = 0x00;
1051         cr.in.oplock_level        = 0;
1052         cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
1053         cr.in.create_flags        = 0x00000000;
1054         cr.in.reserved            = 0x00000000;
1055         cr.in.desired_access      = SEC_RIGHTS_FILE_ALL;
1056         cr.in.file_attributes     = FILE_ATTRIBUTE_NORMAL;
1057         cr.in.share_access        = NTCREATEX_SHARE_ACCESS_READ |
1058                                     NTCREATEX_SHARE_ACCESS_WRITE |
1059                                     NTCREATEX_SHARE_ACCESS_DELETE;
1060         cr.in.create_disposition  = NTCREATEX_DISP_OPEN_IF;
1061         cr.in.create_options      = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
1062                                     NTCREATEX_OPTIONS_ASYNC_ALERT       |
1063                                     NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
1064                                     0x00200000;
1065         cr.in.fname               = fname;
1066
1067         status = smb2_create(tree, tctx, &cr);
1068         CHECK_STATUS(status, NT_STATUS_OK);
1069
1070         smb2_transport_compound_start(tree->session->transport, 2);
1071
1072         ZERO_STRUCT(rd);
1073         rd.in.file.handle = cr.out.file.handle;
1074         rd.in.length      = 1;
1075         rd.in.offset      = 0;
1076         req[0] = smb2_read_send(tree, &rd);
1077
1078         smb2_transport_compound_set_related(tree->session->transport, true);
1079
1080         /*
1081          * Send a completely bogus request as second compound
1082          * element. This triggers smbd_smb2_request_error() in in
1083          * smbd_smb2_request_dispatch() before calling
1084          * smbd_smb2_request_dispatch_update_counts().
1085          */
1086
1087         req[1] = smb2_request_init_tree(tree, 0xff, 0x04, false, 0);
1088         smb2_transport_send(req[1]);
1089
1090         status = smb2_read_recv(req[0], tctx, &rd);
1091         CHECK_STATUS(status, NT_STATUS_END_OF_FILE);
1092
1093         ok = smb2_request_receive(req[1]);
1094         torture_assert(tctx, ok, "Invalid request failed\n");
1095         CHECK_STATUS(req[1]->status, NT_STATUS_INVALID_PARAMETER);
1096
1097         ZERO_STRUCT(cl);
1098         cl.in.file.handle = cr.out.file.handle;
1099
1100         status = smb2_close(tree, &cl);
1101         CHECK_STATUS(status, NT_STATUS_OK);
1102
1103         smb2_util_unlink(tree, fname);
1104 done:
1105         return ret;
1106 }
1107
1108 /* Send a compound request where we expect the last request (Create, Notify)
1109  * to go asynchronous. This works against a Win7 server and the reply is
1110  * sent in two different packets. */
1111 static bool test_compound_interim1(struct torture_context *tctx,
1112                                    struct smb2_tree *tree)
1113 {
1114     struct smb2_handle hd;
1115     struct smb2_create cr;
1116     NTSTATUS status = NT_STATUS_OK;
1117     const char *dname = "compound_interim_dir";
1118     struct smb2_notify nt;
1119     bool ret = true;
1120     struct smb2_request *req[2];
1121
1122     /* Win7 compound request implementation deviates substantially from the
1123      * SMB2 spec as noted in MS-SMB2 <159>, <162>.  This, test currently
1124      * verifies the Windows behavior, not the general spec behavior. */
1125
1126     smb2_transport_credits_ask_num(tree->session->transport, 5);
1127
1128     smb2_deltree(tree, dname);
1129
1130     smb2_transport_credits_ask_num(tree->session->transport, 1);
1131
1132     ZERO_STRUCT(cr);
1133     cr.in.desired_access        = SEC_RIGHTS_FILE_ALL;
1134     cr.in.create_options        = NTCREATEX_OPTIONS_DIRECTORY;
1135     cr.in.file_attributes       = FILE_ATTRIBUTE_DIRECTORY;
1136     cr.in.share_access          = NTCREATEX_SHARE_ACCESS_READ |
1137                                   NTCREATEX_SHARE_ACCESS_WRITE |
1138                                   NTCREATEX_SHARE_ACCESS_DELETE;
1139     cr.in.create_disposition    = NTCREATEX_DISP_CREATE;
1140     cr.in.fname                 = dname;
1141
1142     smb2_transport_compound_start(tree->session->transport, 2);
1143
1144     req[0] = smb2_create_send(tree, &cr);
1145
1146     smb2_transport_compound_set_related(tree->session->transport, true);
1147
1148     hd.data[0] = UINT64_MAX;
1149     hd.data[1] = UINT64_MAX;
1150
1151     ZERO_STRUCT(nt);
1152     nt.in.recursive          = true;
1153     nt.in.buffer_size        = 0x1000;
1154     nt.in.file.handle        = hd;
1155     nt.in.completion_filter  = FILE_NOTIFY_CHANGE_NAME;
1156     nt.in.unknown            = 0x00000000;
1157
1158     req[1] = smb2_notify_send(tree, &nt);
1159
1160     status = smb2_create_recv(req[0], tree, &cr);
1161     CHECK_STATUS(status, NT_STATUS_OK);
1162
1163     smb2_cancel(req[1]);
1164     status = smb2_notify_recv(req[1], tree, &nt);
1165     CHECK_STATUS(status, NT_STATUS_CANCELLED);
1166
1167     smb2_util_close(tree, cr.out.file.handle);
1168
1169     smb2_deltree(tree, dname);
1170 done:
1171     return ret;
1172 }
1173
1174 /* Send a compound request where we expect the middle request (Create, Notify,
1175  * GetInfo) to go asynchronous. Against Win7 the sync request succeed while
1176  * the async fails. All are returned in the same compound response. */
1177 static bool test_compound_interim2(struct torture_context *tctx,
1178                                    struct smb2_tree *tree)
1179 {
1180     struct smb2_handle hd;
1181     struct smb2_create cr;
1182     NTSTATUS status = NT_STATUS_OK;
1183     const char *dname = "compound_interim_dir";
1184     struct smb2_getinfo gf;
1185     struct smb2_notify  nt;
1186     bool ret = true;
1187     struct smb2_request *req[3];
1188
1189     /* Win7 compound request implementation deviates substantially from the
1190      * SMB2 spec as noted in MS-SMB2 <159>, <162>.  This, test currently
1191      * verifies the Windows behavior, not the general spec behavior. */
1192
1193     smb2_transport_credits_ask_num(tree->session->transport, 5);
1194
1195     smb2_deltree(tree, dname);
1196
1197     smb2_transport_credits_ask_num(tree->session->transport, 1);
1198
1199     ZERO_STRUCT(cr);
1200     cr.in.desired_access        = SEC_RIGHTS_FILE_ALL;
1201     cr.in.create_options        = NTCREATEX_OPTIONS_DIRECTORY;
1202     cr.in.file_attributes       = FILE_ATTRIBUTE_DIRECTORY;
1203     cr.in.share_access      = NTCREATEX_SHARE_ACCESS_READ |
1204                       NTCREATEX_SHARE_ACCESS_WRITE |
1205                       NTCREATEX_SHARE_ACCESS_DELETE;
1206     cr.in.create_disposition    = NTCREATEX_DISP_CREATE;
1207     cr.in.fname         = dname;
1208
1209     smb2_transport_compound_start(tree->session->transport, 3);
1210
1211     req[0] = smb2_create_send(tree, &cr);
1212
1213     smb2_transport_compound_set_related(tree->session->transport, true);
1214
1215     hd.data[0] = UINT64_MAX;
1216     hd.data[1] = UINT64_MAX;
1217
1218     ZERO_STRUCT(nt);
1219     nt.in.recursive          = true;
1220     nt.in.buffer_size        = 0x1000;
1221     nt.in.file.handle        = hd;
1222     nt.in.completion_filter  = FILE_NOTIFY_CHANGE_NAME;
1223     nt.in.unknown            = 0x00000000;
1224
1225     req[1] = smb2_notify_send(tree, &nt);
1226
1227     ZERO_STRUCT(gf);
1228     gf.in.file.handle = hd;
1229     gf.in.info_type   = SMB2_GETINFO_FILE;
1230     gf.in.info_class  = 0x04; /* FILE_BASIC_INFORMATION */
1231     gf.in.output_buffer_length = 0x1000;
1232     gf.in.input_buffer_length = 0;
1233
1234     req[2] = smb2_getinfo_send(tree, &gf);
1235
1236     status = smb2_create_recv(req[0], tree, &cr);
1237     CHECK_STATUS(status, NT_STATUS_OK);
1238
1239     status = smb2_notify_recv(req[1], tree, &nt);
1240     CHECK_STATUS(status, NT_STATUS_INTERNAL_ERROR);
1241
1242     status = smb2_getinfo_recv(req[2], tree, &gf);
1243     CHECK_STATUS(status, NT_STATUS_OK);
1244
1245     smb2_util_close(tree, cr.out.file.handle);
1246
1247     smb2_deltree(tree, dname);
1248 done:
1249     return ret;
1250 }
1251
1252 /* Test compound related finds */
1253 static bool test_compound_find_related(struct torture_context *tctx,
1254                                        struct smb2_tree *tree)
1255 {
1256         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1257         const char *dname = "compound_find_dir";
1258         struct smb2_create create;
1259         struct smb2_find f;
1260         struct smb2_handle h;
1261         struct smb2_request *req[2];
1262         NTSTATUS status;
1263         bool ret = true;
1264
1265         smb2_deltree(tree, dname);
1266
1267         ZERO_STRUCT(create);
1268         create.in.desired_access = SEC_RIGHTS_DIR_ALL;
1269         create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1270         create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
1271         create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1272                                  NTCREATEX_SHARE_ACCESS_WRITE |
1273                                  NTCREATEX_SHARE_ACCESS_DELETE;
1274         create.in.create_disposition = NTCREATEX_DISP_CREATE;
1275         create.in.fname = dname;
1276
1277         status = smb2_create(tree, mem_ctx, &create);
1278         h = create.out.file.handle;
1279
1280         torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n");
1281
1282         smb2_transport_compound_start(tree->session->transport, 2);
1283
1284         ZERO_STRUCT(f);
1285         f.in.file.handle        = h;
1286         f.in.pattern            = "*";
1287         f.in.max_response_size  = 0x100;
1288         f.in.level              = SMB2_FIND_BOTH_DIRECTORY_INFO;
1289
1290         req[0] = smb2_find_send(tree, &f);
1291
1292         smb2_transport_compound_set_related(tree->session->transport, true);
1293
1294         req[1] = smb2_find_send(tree, &f);
1295
1296         status = smb2_find_recv(req[0], mem_ctx, &f);
1297         torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_recv failed\n");
1298
1299         status = smb2_find_recv(req[1], mem_ctx, &f);
1300         torture_assert_ntstatus_equal_goto(tctx, status, STATUS_NO_MORE_FILES, ret, done, "smb2_find_recv failed\n");
1301
1302 done:
1303         smb2_util_close(tree, h);
1304         smb2_deltree(tree, dname);
1305         TALLOC_FREE(mem_ctx);
1306         return ret;
1307 }
1308
1309 /* Test compound related finds */
1310 static bool test_compound_find_close(struct torture_context *tctx,
1311                                      struct smb2_tree *tree)
1312 {
1313         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1314         const char *dname = "compound_find_dir";
1315         struct smb2_create create;
1316         struct smb2_find f;
1317         struct smb2_handle h;
1318         struct smb2_request *req = NULL;
1319         const int num_files = 5000;
1320         int i;
1321         NTSTATUS status;
1322         bool ret = true;
1323
1324         smb2_deltree(tree, dname);
1325
1326         ZERO_STRUCT(create);
1327         create.in.desired_access = SEC_RIGHTS_DIR_ALL;
1328         create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1329         create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
1330         create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1331                                  NTCREATEX_SHARE_ACCESS_WRITE |
1332                                  NTCREATEX_SHARE_ACCESS_DELETE;
1333         create.in.create_disposition = NTCREATEX_DISP_CREATE;
1334         create.in.fname = dname;
1335
1336         smb2cli_conn_set_max_credits(tree->session->transport->conn, 256);
1337
1338         status = smb2_create(tree, mem_ctx, &create);
1339         h = create.out.file.handle;
1340
1341         ZERO_STRUCT(create);
1342         create.in.desired_access = SEC_RIGHTS_FILE_ALL;
1343         create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1344         create.in.create_disposition = NTCREATEX_DISP_CREATE;
1345
1346         for (i = 0; i < num_files; i++) {
1347                 create.in.fname = talloc_asprintf(mem_ctx, "%s\\file%d",
1348                                                   dname, i);
1349                 status = smb2_create(tree, mem_ctx, &create);
1350                 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
1351                 smb2_util_close(tree, create.out.file.handle);
1352         }
1353
1354         torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n");
1355
1356         ZERO_STRUCT(f);
1357         f.in.file.handle        = h;
1358         f.in.pattern            = "*";
1359         f.in.max_response_size  = 8*1024*1024;
1360         f.in.level              = SMB2_FIND_BOTH_DIRECTORY_INFO;
1361
1362         req = smb2_find_send(tree, &f);
1363
1364         status = smb2_util_close(tree, h);
1365         torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_close failed\n");
1366
1367         status = smb2_find_recv(req, mem_ctx, &f);
1368         torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_recv failed\n");
1369
1370 done:
1371         smb2_util_close(tree, h);
1372         smb2_deltree(tree, dname);
1373         TALLOC_FREE(mem_ctx);
1374         return ret;
1375 }
1376
1377 /* Test compound unrelated finds */
1378 static bool test_compound_find_unrelated(struct torture_context *tctx,
1379                                          struct smb2_tree *tree)
1380 {
1381         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1382         const char *dname = "compound_find_dir";
1383         struct smb2_create create;
1384         struct smb2_find f;
1385         struct smb2_handle h;
1386         struct smb2_request *req[2];
1387         NTSTATUS status;
1388         bool ret = true;
1389
1390         smb2_deltree(tree, dname);
1391
1392         ZERO_STRUCT(create);
1393         create.in.desired_access = SEC_RIGHTS_DIR_ALL;
1394         create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1395         create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
1396         create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1397                                  NTCREATEX_SHARE_ACCESS_WRITE |
1398                                  NTCREATEX_SHARE_ACCESS_DELETE;
1399         create.in.create_disposition = NTCREATEX_DISP_CREATE;
1400         create.in.fname = dname;
1401
1402         status = smb2_create(tree, mem_ctx, &create);
1403         h = create.out.file.handle;
1404
1405         torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed\n");
1406
1407         smb2_transport_compound_start(tree->session->transport, 2);
1408
1409         ZERO_STRUCT(f);
1410         f.in.file.handle        = h;
1411         f.in.pattern            = "*";
1412         f.in.max_response_size  = 0x100;
1413         f.in.level              = SMB2_FIND_BOTH_DIRECTORY_INFO;
1414
1415         req[0] = smb2_find_send(tree, &f);
1416         req[1] = smb2_find_send(tree, &f);
1417
1418         status = smb2_find_recv(req[0], mem_ctx, &f);
1419         torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_find_recv failed\n");
1420
1421         status = smb2_find_recv(req[1], mem_ctx, &f);
1422         torture_assert_ntstatus_equal_goto(tctx, status, STATUS_NO_MORE_FILES, ret, done, "smb2_find_recv failed\n");
1423
1424 done:
1425         smb2_util_close(tree, h);
1426         smb2_deltree(tree, dname);
1427         TALLOC_FREE(mem_ctx);
1428         return ret;
1429 }
1430
1431 struct torture_suite *torture_smb2_compound_init(TALLOC_CTX *ctx)
1432 {
1433         struct torture_suite *suite = torture_suite_create(ctx, "compound");
1434
1435         torture_suite_add_1smb2_test(suite, "related1", test_compound_related1);
1436         torture_suite_add_1smb2_test(suite, "related2", test_compound_related2);
1437         torture_suite_add_1smb2_test(suite, "related3",
1438                                      test_compound_related3);
1439         torture_suite_add_1smb2_test(suite, "unrelated1", test_compound_unrelated1);
1440         torture_suite_add_1smb2_test(suite, "invalid1", test_compound_invalid1);
1441         torture_suite_add_1smb2_test(suite, "invalid2", test_compound_invalid2);
1442         torture_suite_add_1smb2_test(suite, "invalid3", test_compound_invalid3);
1443         torture_suite_add_1smb2_test(
1444                 suite, "invalid4", test_compound_invalid4);
1445         torture_suite_add_1smb2_test(suite, "interim1",  test_compound_interim1);
1446         torture_suite_add_1smb2_test(suite, "interim2",  test_compound_interim2);
1447         torture_suite_add_1smb2_test(suite, "compound-break", test_compound_break);
1448         torture_suite_add_1smb2_test(suite, "compound-padding", test_compound_padding);
1449         torture_suite_add_1smb2_test(suite, "create-write-close",
1450                                      test_compound_create_write_close);
1451
1452         suite->description = talloc_strdup(suite, "SMB2-COMPOUND tests");
1453
1454         return suite;
1455 }
1456
1457 struct torture_suite *torture_smb2_compound_find_init(TALLOC_CTX *ctx)
1458 {
1459         struct torture_suite *suite = torture_suite_create(ctx, "compound_find");
1460
1461         torture_suite_add_1smb2_test(suite, "compound_find_related", test_compound_find_related);
1462         torture_suite_add_1smb2_test(suite, "compound_find_unrelated", test_compound_find_unrelated);
1463         torture_suite_add_1smb2_test(suite, "compound_find_close", test_compound_find_close);
1464
1465         suite->description = talloc_strdup(suite, "SMB2-COMPOUND-FIND tests");
1466
1467         return suite;
1468 }