2 Unix SMB/CIFS implementation.
4 test alternate data streams
6 Copyright (C) Andrew Tridgell 2004
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/>.
23 #include "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25 #include "libcli/smb_composite/smb_composite.h"
26 #include "libcli/raw/libcliraw.h"
27 #include "libcli/raw/raw_proto.h"
28 #include "libcli/libcli.h"
30 #include "torture/torture.h"
31 #include "torture/smb2/proto.h"
32 #include "torture/util.h"
34 #include "system/filesys.h"
35 #include "system/locale.h"
37 #define DNAME "teststreams"
39 #define CHECK_STATUS(status, correct) do { \
40 if (!NT_STATUS_EQUAL(status, correct)) { \
41 torture_result(tctx, TORTURE_FAIL, \
42 "(%s) Incorrect status %s - should be %s\n", \
43 __location__, nt_errstr(status), nt_errstr(correct)); \
48 #define CHECK_VALUE(v, correct) do { \
49 if ((v) != (correct)) { \
50 torture_result(tctx, TORTURE_FAIL, \
51 "(%s) Incorrect value %s=%d - should be %d\n", \
52 __location__, #v, (int)v, (int)correct); \
56 #define CHECK_NTTIME(v, correct) do { \
57 if ((v) != (correct)) { \
58 torture_result(tctx, TORTURE_FAIL, \
59 "(%s) Incorrect value %s=%llu - should be %llu\n", \
60 __location__, #v, (unsigned long long)v, \
61 (unsigned long long)correct); \
65 #define CHECK_STR(v, correct) do { \
67 if ((v) && !(correct)) { \
69 } else if (!(v) && (correct)) { \
71 } else if (!(v) && !(correct)) { \
73 } else if (strcmp((v), (correct)) == 0) { \
79 torture_comment(tctx,"(%s) Incorrect value %s='%s' - " \
81 __location__, #v, (v)?(v):"NULL", \
82 (correct)?(correct):"NULL"); \
87 static int qsort_string(const void *v1,
90 char * const *s1 = v1;
91 char * const *s2 = v2;
92 return strcmp(*s1, *s2);
95 static int qsort_stream(const void *v1,
98 const struct stream_struct * s1 = v1;
99 const struct stream_struct * s2 = v2;
100 return strcmp(s1->stream_name.s, s2->stream_name.s);
103 static bool check_stream(struct smb2_tree *tree,
104 const char *location,
110 struct smb2_handle handle;
111 struct smb2_create create;
114 const char *full_name;
116 full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname);
119 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
120 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
121 create.in.create_disposition = NTCREATEX_DISP_OPEN;
122 create.in.fname = full_name;
124 status = smb2_create(tree, mem_ctx, &create);
125 if (!NT_STATUS_IS_OK(status)) {
129 torture_comment(mem_ctx, "Unable to open stream %s\n",
135 handle = create.out.file.handle;
142 r.in.file.handle = handle;
143 r.in.length = strlen(value)+11;
146 status = smb2_read(tree, tree, &r);
148 if (!NT_STATUS_IS_OK(status)) {
149 torture_comment(mem_ctx, "(%s) Failed to read %lu bytes from "
150 "stream '%s'\n", location, (long)strlen(value), full_name);
154 if (memcmp(r.out.data.data, value, strlen(value)) != 0) {
155 torture_comment(mem_ctx, "(%s) Bad data in stream\n", location);
159 smb2_util_close(tree, handle);
163 static bool check_stream_list(struct smb2_tree *tree,
164 struct torture_context *tctx,
168 struct smb2_handle h)
170 union smb_fileinfo finfo;
173 TALLOC_CTX *tmp_ctx = talloc_new(tctx);
175 struct stream_struct *stream_sort;
178 finfo.generic.level = RAW_FILEINFO_STREAM_INFORMATION;
179 finfo.generic.in.file.handle = h;
181 status = smb2_getinfo_file(tree, tctx, &finfo);
182 if (!NT_STATUS_IS_OK(status)) {
183 torture_comment(tctx, "(%s) smb_raw_pathinfo failed: %s\n",
184 __location__, nt_errstr(status));
188 if (finfo.stream_info.out.num_streams != num_exp) {
189 torture_comment(tctx, "(%s) expected %d streams, got %d\n",
190 __location__, num_exp, finfo.stream_info.out.num_streams);
199 exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
201 if (exp_sort == NULL) {
205 qsort(exp_sort, num_exp, sizeof(*exp_sort), qsort_string);
207 stream_sort = talloc_memdup(tmp_ctx, finfo.stream_info.out.streams,
208 finfo.stream_info.out.num_streams *
209 sizeof(*stream_sort));
211 if (stream_sort == NULL) {
215 qsort(stream_sort, finfo.stream_info.out.num_streams,
216 sizeof(*stream_sort), qsort_stream);
218 for (i=0; i<num_exp; i++) {
219 if (strcmp(exp_sort[i], stream_sort[i].stream_name.s) != 0) {
220 torture_comment(tctx,
221 "(%s) expected stream name %s, got %s\n",
222 __location__, exp_sort[i],
223 stream_sort[i].stream_name.s);
230 talloc_free(tmp_ctx);
235 static bool test_stream_dir(struct torture_context *tctx,
236 struct smb2_tree *tree)
238 TALLOC_CTX *mem_ctx = talloc_new(tctx);
241 const char *fname = DNAME "\\stream.txt";
244 const char *basedir_data;
245 struct smb2_handle h;
247 smb2_util_unlink(tree, fname);
248 smb2_deltree(tree, DNAME);
250 status = torture_smb2_testdir(tree, DNAME, &h);
251 CHECK_STATUS(status, NT_STATUS_OK);
253 basedir_data = talloc_asprintf(mem_ctx, "%s::$DATA", DNAME);
254 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
255 torture_comment(tctx, "%s\n", sname1);
257 torture_comment(tctx, "(%s) opening non-existant directory stream\n",
259 ZERO_STRUCT(io.smb2);
260 io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
261 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
262 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
263 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
264 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
265 io.smb2.in.share_access = 0;
266 io.smb2.in.alloc_size = 0;
267 io.smb2.in.security_flags = 0;
268 io.smb2.in.fname = sname1;
269 io.smb2.in.create_flags = 0;
270 status = smb2_create(tree, mem_ctx, &(io.smb2));
271 CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
273 torture_comment(tctx, "(%s) opening basedir stream\n", __location__);
274 ZERO_STRUCT(io.smb2);
275 io.smb2.in.create_flags = 0;
276 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
277 io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
278 io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
279 io.smb2.in.share_access = 0;
280 io.smb2.in.alloc_size = 0;
281 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
282 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
283 io.smb2.in.security_flags = 0;
284 io.smb2.in.fname = basedir_data;
285 status = smb2_create(tree, mem_ctx, &(io.smb2));
286 CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
288 torture_comment(tctx, "(%s) opening basedir ::$DATA stream\n",
290 ZERO_STRUCT(io.smb2);
291 io.smb2.in.create_flags = 0x10;
292 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
293 io.smb2.in.create_options = 0;
294 io.smb2.in.file_attributes = 0;
295 io.smb2.in.share_access = 0;
296 io.smb2.in.alloc_size = 0;
297 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
298 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
299 io.smb2.in.security_flags = 0;
300 io.smb2.in.fname = basedir_data;
301 status = smb2_create(tree, mem_ctx, &(io.smb2));
302 CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
304 torture_comment(tctx, "(%s) list the streams on the basedir\n",
306 ret &= check_stream_list(tree, mem_ctx, DNAME, 0, NULL, h);
308 smb2_util_unlink(tree, fname);
309 smb2_deltree(tree, DNAME);
310 talloc_free(mem_ctx);
315 static bool test_stream_io(struct torture_context *tctx,
316 struct smb2_tree *tree)
318 TALLOC_CTX *mem_ctx = talloc_new(tctx);
321 const char *fname = DNAME "\\stream.txt";
322 const char *sname1, *sname2;
324 struct smb2_handle h, h2;
326 const char *one[] = { "::$DATA" };
327 const char *two[] = { "::$DATA", ":Second Stream:$DATA" };
328 const char *three[] = { "::$DATA", ":Stream One:$DATA",
329 ":Second Stream:$DATA" };
331 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
332 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname,
335 smb2_util_unlink(tree, fname);
336 smb2_deltree(tree, DNAME);
338 status = torture_smb2_testdir(tree, DNAME, &h);
339 CHECK_STATUS(status, NT_STATUS_OK);
341 torture_comment(tctx, "(%s) creating a stream on a non-existant file\n",
344 ZERO_STRUCT(io.smb2);
345 io.smb2.in.create_flags = 0;
346 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
347 io.smb2.in.create_options = 0;
348 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
349 io.smb2.in.share_access = 0;
350 io.smb2.in.alloc_size = 0;
351 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
352 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
353 io.smb2.in.security_flags = 0;
354 io.smb2.in.fname = sname1;
355 status = smb2_create(tree, mem_ctx, &(io.smb2));
356 CHECK_STATUS(status, NT_STATUS_OK);
357 h2 = io.smb2.out.file.handle;
359 ret &= check_stream(tree, __location__, mem_ctx, fname,
362 torture_comment(tctx, "(%s) check that open of base file is allowed\n", __location__);
363 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
364 io.smb2.in.fname = fname;
365 status = smb2_create(tree, mem_ctx, &(io.smb2));
366 CHECK_STATUS(status, NT_STATUS_OK);
367 smb2_util_close(tree, io.smb2.out.file.handle);
369 torture_comment(tctx, "(%s) writing to stream\n", __location__);
370 status = smb2_util_write(tree, h2, "test data", 0, 9);
371 CHECK_STATUS(status, NT_STATUS_OK);
373 smb2_util_close(tree, h2);
375 ret &= check_stream(tree, __location__, mem_ctx, fname,
376 "Stream One", "test data");
378 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
379 io.smb2.in.fname = sname1;
380 status = smb2_create(tree, mem_ctx, &(io.smb2));
381 CHECK_STATUS(status, NT_STATUS_OK);
382 h2 = io.smb2.out.file.handle;
384 torture_comment(tctx, "(%s) modifying stream\n", __location__);
385 status = smb2_util_write(tree, h2, "MORE DATA ", 5, 10);
386 CHECK_STATUS(status, NT_STATUS_OK);
388 smb2_util_close(tree, h2);
390 ret &= check_stream(tree, __location__, mem_ctx, fname,
391 "Stream One:$FOO", NULL);
393 torture_comment(tctx, "(%s) creating a stream2 on a existing file\n",
395 io.smb2.in.fname = sname2;
396 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
397 status = smb2_create(tree, mem_ctx, &(io.smb2));
398 CHECK_STATUS(status, NT_STATUS_OK);
399 h2 = io.smb2.out.file.handle;
401 torture_comment(tctx, "(%s) modifying stream\n", __location__);
402 status= smb2_util_write(tree, h2, "SECOND STREAM", 0, 13);
403 CHECK_STATUS(status, NT_STATUS_OK);
404 smb2_util_close(tree, h2);
406 ret &= check_stream(tree, __location__, mem_ctx, fname,
407 "Stream One", "test MORE DATA ");
409 ret &= check_stream(tree, __location__, mem_ctx, fname,
410 "Stream One:$DATA", "test MORE DATA ");
412 ret &= check_stream(tree, __location__, mem_ctx, fname,
413 "Stream One:", NULL);
415 ret &= check_stream(tree, __location__, mem_ctx, fname,
416 "Second Stream", "SECOND STREAM");
418 if (!torture_setting_bool(tctx, "samba4", false)) {
419 ret &= check_stream(tree, __location__, mem_ctx, fname,
420 "SECOND STREAM:$DATA", "SECOND STREAM");
422 ret &= check_stream(tree, __location__, mem_ctx, fname,
423 "Second Stream:$DATA", "SECOND STREAM");
425 ret &= check_stream(tree, __location__, mem_ctx, fname,
426 "Second Stream:", NULL);
428 ret &= check_stream(tree, __location__, mem_ctx, fname,
429 "Second Stream:$FOO", NULL);
431 io.smb2.in.fname = sname2;
432 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
433 status = smb2_create(tree, mem_ctx, &(io.smb2));
434 CHECK_STATUS(status, NT_STATUS_OK);
435 h2 = io.smb2.out.file.handle;
436 check_stream_list(tree, tctx, fname, 3, three, h2);
438 smb2_util_close(tree, h2);
440 torture_comment(tctx, "(%s) deleting stream\n", __location__);
441 status = smb2_util_unlink(tree, sname1);
442 CHECK_STATUS(status, NT_STATUS_OK);
444 io.smb2.in.fname = sname2;
445 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
446 status = smb2_create(tree, mem_ctx, &(io.smb2));
447 CHECK_STATUS(status, NT_STATUS_OK);
448 h2 = io.smb2.out.file.handle;
449 check_stream_list(tree, tctx, fname, 2, two, h2);
450 smb2_util_close(tree, h2);
452 torture_comment(tctx, "(%s) delete a stream via delete-on-close\n",
454 io.smb2.in.fname = sname2;
455 io.smb2.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
456 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
457 io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
458 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
460 status = smb2_create(tree, mem_ctx, &(io.smb2));
461 CHECK_STATUS(status, NT_STATUS_OK);
462 h2 = io.smb2.out.file.handle;
464 smb2_util_close(tree, h2);
465 status = smb2_util_unlink(tree, sname2);
466 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
468 io.smb2.in.fname = fname;
469 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
470 status = smb2_create(tree, mem_ctx, &(io.smb2));
471 h2 = io.smb2.out.file.handle;
472 check_stream_list(tree,tctx, fname, 1, one, h2);
473 smb2_util_close(tree, h2);
475 if (!torture_setting_bool(tctx, "samba4", false)) {
476 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
477 io.smb2.in.fname = sname1;
478 status = smb2_create(tree, mem_ctx, &(io.smb2));
479 CHECK_STATUS(status, NT_STATUS_OK);
480 smb2_util_close(tree, io.ntcreatex.out.file.handle);
481 io.smb2.in.fname = sname2;
482 status = smb2_create(tree, mem_ctx, &(io.smb2));
483 CHECK_STATUS(status, NT_STATUS_OK);
484 smb2_util_close(tree, io.ntcreatex.out.file.handle);
487 torture_comment(tctx, "(%s) deleting file\n", __location__);
488 status = smb2_util_unlink(tree, fname);
489 CHECK_STATUS(status, NT_STATUS_OK);
492 smb2_util_close(tree, h2);
493 smb2_deltree(tree, DNAME);
494 talloc_free(mem_ctx);
500 test stream sharemodes
502 static bool test_stream_sharemodes(struct torture_context *tctx,
503 struct smb2_tree *tree)
505 TALLOC_CTX *mem_ctx = talloc_new(tctx);
508 const char *fname = DNAME "\\stream_share.txt";
509 const char *sname1, *sname2;
511 struct smb2_handle h, h1, h2;
513 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
514 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname,
517 smb2_util_unlink(tree, fname);
518 smb2_deltree(tree, DNAME);
520 status = torture_smb2_testdir(tree, DNAME, &h);
521 CHECK_STATUS(status, NT_STATUS_OK);
523 torture_comment(tctx, "(%s) Testing stream share mode conflicts\n",
525 ZERO_STRUCT(io.smb2);
526 io.generic.level = RAW_OPEN_SMB2;
527 io.smb2.in.create_flags = 0;
528 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
529 io.smb2.in.create_options = 0;
530 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
531 io.smb2.in.share_access = 0;
532 io.smb2.in.alloc_size = 0;
533 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
534 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
535 io.smb2.in.security_flags = 0;
536 io.smb2.in.fname = sname1;
538 status = smb2_create(tree, mem_ctx, &(io.smb2));
539 CHECK_STATUS(status, NT_STATUS_OK);
540 h1 = io.smb2.out.file.handle;
543 * A different stream does not give a sharing violation
546 io.smb2.in.fname = sname2;
547 status = smb2_create(tree, mem_ctx, &(io.smb2));
548 CHECK_STATUS(status, NT_STATUS_OK);
549 h2 = io.smb2.out.file.handle;
552 * ... whereas the same stream does with unchanged access/share_access
556 io.smb2.in.fname = sname1;
557 io.smb2.in.create_disposition = 0;
558 status = smb2_create(tree, mem_ctx, &(io.smb2));
559 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
561 io.smb2.in.fname = sname2;
562 status = smb2_create(tree, mem_ctx, &(io.smb2));
563 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
566 smb2_util_close(tree, h1);
567 smb2_util_close(tree, h2);
568 status = smb2_util_unlink(tree, fname);
569 smb2_deltree(tree, DNAME);
570 talloc_free(mem_ctx);
576 * Test FILE_SHARE_DELETE on streams
578 * A stream opened with !FILE_SHARE_DELETE prevents the main file to be opened
579 * with SEC_STD_DELETE.
581 * The main file opened with !FILE_SHARE_DELETE does *not* prevent a stream to
582 * be opened with SEC_STD_DELETE.
584 * A stream held open with FILE_SHARE_DELETE allows the file to be
585 * deleted. After the main file is deleted, access to the open file descriptor
586 * still works, but all name-based access to both the main file as well as the
587 * stream is denied with DELETE pending.
589 * This means, an open of the main file with SEC_STD_DELETE should walk all
590 * streams and also open them with SEC_STD_DELETE. If any of these opens gives
591 * SHARING_VIOLATION, the main open fails.
593 * Closing the main file after delete_on_close has been set does not really
594 * unlink it but leaves the corresponding share mode entry with
595 * delete_on_close being set around until all streams are closed.
597 * Opening a stream must also look at the main file's share mode entry, look
598 * at the delete_on_close bit and potentially return DELETE_PENDING.
601 static bool test_stream_delete(struct torture_context *tctx,
602 struct smb2_tree *tree)
604 TALLOC_CTX *mem_ctx = talloc_new(tctx);
607 const char *fname = DNAME "\\stream_delete.txt";
610 struct smb2_handle h, h1;
613 if (!torture_setting_bool(tctx, "samba4", true)) {
614 torture_comment(tctx, "Skipping test as samba4 is enabled\n");
618 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
621 smb2_util_unlink(tree, fname);
622 smb2_deltree(tree, fname);
623 smb2_deltree(tree, DNAME);
625 status = torture_smb2_testdir(tree, DNAME, &h);
626 CHECK_STATUS(status, NT_STATUS_OK);
628 torture_comment(tctx, "(%s) opening non-existant file stream\n",
630 ZERO_STRUCT(io.smb2);
631 io.smb2.in.create_flags = 0;
632 io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
633 io.smb2.in.create_options = 0;
634 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
635 io.smb2.in.share_access = 0;
636 io.smb2.in.alloc_size = 0;
637 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
638 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
639 io.smb2.in.security_flags = 0;
640 io.smb2.in.fname = sname1;
642 status = smb2_create(tree, mem_ctx, &(io.smb2));
643 CHECK_STATUS(status, NT_STATUS_OK);
644 h1 = io.smb2.out.file.handle;
646 status = smb2_util_write(tree, h1, "test data", 0, 9);
647 CHECK_STATUS(status, NT_STATUS_OK);
650 * One stream opened without FILE_SHARE_DELETE prevents the main file
651 * to be deleted or even opened with DELETE access
654 status = smb2_util_unlink(tree, fname);
655 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
657 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
658 io.smb2.in.fname = fname;
659 io.smb2.in.desired_access = SEC_STD_DELETE;
660 status = smb2_create(tree, mem_ctx, &(io.smb2));
661 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
663 smb2_util_close(tree, h1);
666 * ... but unlink works if a stream is opened with FILE_SHARE_DELETE
669 io.smb2.in.fname = sname1;
670 io.smb2.in.desired_access = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
671 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
672 NTCREATEX_SHARE_ACCESS_READ |
673 NTCREATEX_SHARE_ACCESS_WRITE;
674 status = smb2_create(tree, mem_ctx, &(io.smb2));
675 CHECK_STATUS(status, NT_STATUS_OK);
676 h1 = io.smb2.out.file.handle;
678 status = smb2_util_unlink(tree, fname);
679 CHECK_STATUS(status, NT_STATUS_OK);
682 * file access still works on the stream while the main file is closed
685 r.in.file.handle = h1;
689 status = smb2_read(tree, tree, &r);
690 CHECK_STATUS(status, NT_STATUS_OK);
693 * name-based access to both the main file and the stream does not
694 * work anymore but gives DELETE_PENDING
697 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
698 io.smb2.in.fname = fname;
699 status = smb2_create(tree, mem_ctx, &(io.smb2));
700 CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
703 * older S3 doesn't do this
706 io.smb2.in.fname = sname1;
707 status = smb2_create(tree, mem_ctx, &(io.smb2));
708 CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
710 smb2_util_close(tree, h1);
713 * After closing the stream the file is really gone.
716 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
717 io.smb2.in.fname = fname;
718 status = smb2_create(tree, mem_ctx, &(io.smb2));
719 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
722 smb2_util_close(tree, h1);
723 smb2_util_unlink(tree, fname);
724 smb2_deltree(tree, DNAME);
725 talloc_free(mem_ctx);
733 static bool test_stream_names(struct torture_context *tctx,
734 struct smb2_tree *tree)
736 TALLOC_CTX *mem_ctx = talloc_new(tctx);
739 union smb_fileinfo finfo;
740 union smb_fileinfo stinfo;
741 union smb_setfileinfo sinfo;
742 const char *fname = DNAME "\\stream_names.txt";
743 const char *sname1, *sname1b, *sname1c, *sname1d;
744 const char *sname2, *snamew, *snamew2;
745 const char *snamer1, *snamer2;
747 struct smb2_handle h, h1, h2, h3;
749 const char *four[4] = {
751 ":\x05Stream\n One:$DATA",
752 ":MStream Two:$DATA",
755 const char *five1[5] = {
757 ":\x05Stream\n One:$DATA",
758 ":BeforeRename:$DATA",
759 ":MStream Two:$DATA",
762 const char *five2[5] = {
764 ":\x05Stream\n One:$DATA",
765 ":AfterRename:$DATA",
766 ":MStream Two:$DATA",
770 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "\x05Stream\n One");
771 sname1b = talloc_asprintf(mem_ctx, "%s:", sname1);
772 sname1c = talloc_asprintf(mem_ctx, "%s:$FOO", sname1);
773 sname1d = talloc_asprintf(mem_ctx, "%s:?D*a", sname1);
774 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "MStream Two");
775 snamew = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "?Stream*");
776 snamew2 = talloc_asprintf(mem_ctx, "%s\\stream*:%s:$DATA", DNAME,
778 snamer1 = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname,
780 snamer2 = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "AfterRename");
783 smb2_util_unlink(tree, fname);
784 smb2_deltree(tree, fname);
785 smb2_deltree(tree, DNAME);
787 status = torture_smb2_testdir(tree, DNAME, &h);
788 CHECK_STATUS(status, NT_STATUS_OK);
790 torture_comment(tctx, "(%s) testing stream names\n", __location__);
791 ZERO_STRUCT(io.smb2);
792 io.smb2.in.create_flags = 0;
793 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
794 io.smb2.in.create_options = 0;
795 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
796 io.smb2.in.share_access = 0;
797 io.smb2.in.alloc_size = 0;
798 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
799 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
800 io.smb2.in.security_flags = 0;
801 io.smb2.in.fname = sname1;
803 status = smb2_create(tree, mem_ctx, &(io.smb2));
804 CHECK_STATUS(status, NT_STATUS_OK);
805 h1 = io.smb2.out.file.handle;
808 * A different stream does not give a sharing violation
811 io.smb2.in.fname = sname2;
812 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
813 status = smb2_create(tree, mem_ctx, &(io.smb2));
814 CHECK_STATUS(status, NT_STATUS_OK);
815 h2 = io.smb2.out.file.handle;
818 * ... whereas the same stream does with unchanged access/share_access
822 io.smb2.in.fname = sname1;
823 io.smb2.in.create_disposition = NTCREATEX_DISP_SUPERSEDE;
824 status = smb2_create(tree, mem_ctx, &(io.smb2));
825 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
827 io.smb2.in.fname = sname1b;
828 status = smb2_create(tree, mem_ctx, &(io.smb2));
829 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
831 io.smb2.in.fname = sname1c;
832 status = smb2_create(tree, mem_ctx, &(io.smb2));
833 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
834 /* w2k returns INVALID_PARAMETER */
835 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
837 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
840 io.smb2.in.fname = sname1d;
841 status = smb2_create(tree, mem_ctx, &(io.smb2));
842 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
843 /* w2k returns INVALID_PARAMETER */
844 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
846 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
849 io.smb2.in.fname = sname2;
850 status = smb2_create(tree, mem_ctx, &(io.smb2));
851 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
853 io.smb2.in.fname = snamew;
854 status = smb2_create(tree, mem_ctx, &(io.smb2));
855 CHECK_STATUS(status, NT_STATUS_OK);
856 h3 = io.smb2.out.file.handle;
858 io.smb2.in.fname = snamew2;
859 status = smb2_create(tree, mem_ctx, &(io.smb2));
860 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
862 io.smb2.in.fname = fname;
863 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
864 status = smb2_create(tree, mem_ctx, &(io.smb2));
865 CHECK_STATUS(status, NT_STATUS_OK);
866 ret &= check_stream_list(tree, tctx, fname, 4, four,
867 io.smb2.out.file.handle);
869 smb2_util_close(tree, h1);
870 smb2_util_close(tree, h2);
871 smb2_util_close(tree, h3);
873 if (torture_setting_bool(tctx, "samba4", true)) {
877 finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
878 finfo.generic.in.file.handle = io.smb2.out.file.handle;
879 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
880 CHECK_STATUS(status, NT_STATUS_OK);
881 ret &= check_stream_list(tree, tctx, fname, 4, four,
882 io.smb2.out.file.handle);
884 for (i=0; i < 4; i++) {
886 uint64_t stream_size;
887 char *path = talloc_asprintf(tctx, "%s%s",
890 char *rpath = talloc_strdup(path, path);
891 char *p = strrchr(rpath, ':');
899 torture_comment(tctx, "(%s): i[%u][%s]\n",
900 __location__, i,path);
901 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
902 io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
903 SEC_FILE_WRITE_ATTRIBUTE |
905 io.smb2.in.fname = path;
906 status = smb2_create(tree, mem_ctx, &(io.smb2));
907 CHECK_STATUS(status, NT_STATUS_OK);
908 h1 = io.smb2.out.file.handle;
910 finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
911 finfo.generic.in.file.path = fname;
912 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
913 CHECK_STATUS(status, NT_STATUS_OK);
915 stinfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
916 stinfo.generic.in.file.handle = h1;
917 status = smb2_getinfo_file(tree, mem_ctx, &stinfo);
918 CHECK_STATUS(status, NT_STATUS_OK);
919 if (!torture_setting_bool(tctx, "samba3", false)) {
920 CHECK_NTTIME(stinfo.all_info.out.create_time,
921 finfo.all_info.out.create_time);
922 CHECK_NTTIME(stinfo.all_info.out.access_time,
923 finfo.all_info.out.access_time);
924 CHECK_NTTIME(stinfo.all_info.out.write_time,
925 finfo.all_info.out.write_time);
926 CHECK_NTTIME(stinfo.all_info.out.change_time,
927 finfo.all_info.out.change_time);
929 CHECK_VALUE(stinfo.all_info.out.attrib,
930 finfo.all_info.out.attrib);
931 CHECK_VALUE(stinfo.all_info.out.size,
932 finfo.all_info.out.size);
933 CHECK_VALUE(stinfo.all_info.out.delete_pending,
934 finfo.all_info.out.delete_pending);
935 CHECK_VALUE(stinfo.all_info.out.directory,
936 finfo.all_info.out.directory);
937 CHECK_VALUE(stinfo.all_info.out.ea_size,
938 finfo.all_info.out.ea_size);
940 stinfo.generic.level = RAW_FILEINFO_NAME_INFORMATION;
941 stinfo.generic.in.file.handle = h1;
942 status = smb2_getinfo_file(tree, mem_ctx, &stinfo);
943 CHECK_STATUS(status, NT_STATUS_OK);
944 if (!torture_setting_bool(tctx, "samba3", false)) {
945 CHECK_STR(rpath, stinfo.name_info.out.fname.s);
948 write_time = finfo.all_info.out.write_time;
949 write_time += i*1000000;
950 write_time /= 1000000;
951 write_time *= 1000000;
954 sinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
955 sinfo.basic_info.in.file.handle = h1;
956 sinfo.basic_info.in.write_time = write_time;
957 sinfo.basic_info.in.attrib = stinfo.all_info.out.attrib;
958 status = smb2_setinfo_file(tree, &sinfo);
959 CHECK_STATUS(status, NT_STATUS_OK);
961 stream_size = i*8192;
964 sinfo.end_of_file_info.level =
965 RAW_SFILEINFO_END_OF_FILE_INFORMATION;
966 sinfo.end_of_file_info.in.file.handle = h1;
967 sinfo.end_of_file_info.in.size = stream_size;
968 status = smb2_setinfo_file(tree, &sinfo);
969 CHECK_STATUS(status, NT_STATUS_OK);
971 stinfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
972 stinfo.generic.in.file.handle = h1;
973 status = smb2_getinfo_file(tree, mem_ctx, &stinfo);
974 CHECK_STATUS(status, NT_STATUS_OK);
975 if (!torture_setting_bool(tctx, "samba3", false)) {
976 CHECK_NTTIME(stinfo.all_info.out.write_time,
978 CHECK_VALUE(stinfo.all_info.out.attrib,
979 finfo.all_info.out.attrib);
981 CHECK_VALUE(stinfo.all_info.out.size,
983 CHECK_VALUE(stinfo.all_info.out.delete_pending,
984 finfo.all_info.out.delete_pending);
985 CHECK_VALUE(stinfo.all_info.out.directory,
986 finfo.all_info.out.directory);
987 CHECK_VALUE(stinfo.all_info.out.ea_size,
988 finfo.all_info.out.ea_size);
990 io.smb2.in.fname = fname;
991 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
992 status = smb2_create(tree, mem_ctx, &(io.smb2));
993 CHECK_STATUS(status, NT_STATUS_OK);
994 ret &= check_stream_list(tree, tctx, fname, 4, four,
995 io.smb2.out.file.handle);
997 smb2_util_close(tree, h1);
1001 torture_comment(tctx, "(%s): testing stream renames\n", __location__);
1002 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1003 io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
1004 SEC_FILE_WRITE_ATTRIBUTE |
1005 SEC_RIGHTS_FILE_ALL;
1006 io.smb2.in.fname = snamer1;
1007 status = smb2_create(tree, mem_ctx, &(io.smb2));
1008 CHECK_STATUS(status, NT_STATUS_OK);
1009 h1 = io.smb2.out.file.handle;
1010 ret &= check_stream_list(tree,tctx, fname, 5, five1,
1011 io.smb2.out.file.handle);
1014 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1015 sinfo.rename_information.in.file.handle = h1;
1016 sinfo.rename_information.in.overwrite = true;
1017 sinfo.rename_information.in.root_fid = 0;
1018 sinfo.rename_information.in.new_name = ":AfterRename:$DATA";
1019 status = smb2_setinfo_file(tree, &sinfo);
1020 CHECK_STATUS(status, NT_STATUS_OK);
1022 ret &= check_stream_list(tree,tctx, fname, 5, five2,
1023 io.smb2.out.file.handle);
1026 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1027 sinfo.rename_information.in.file.handle = h1;
1028 sinfo.rename_information.in.overwrite = false;
1029 sinfo.rename_information.in.root_fid = 0;
1030 sinfo.rename_information.in.new_name = ":MStream Two:$DATA";
1031 status = smb2_setinfo_file(tree, &sinfo);
1032 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
1034 ret &= check_stream_list(tree,tctx, fname, 5, five2,
1035 io.smb2.out.file.handle);
1038 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1039 sinfo.rename_information.in.file.handle = h1;
1040 sinfo.rename_information.in.overwrite = true;
1041 sinfo.rename_information.in.root_fid = 0;
1042 sinfo.rename_information.in.new_name = ":MStream Two:$DATA";
1043 status = smb2_setinfo_file(tree, &sinfo);
1044 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1046 ret &= check_stream_list(tree,tctx, fname, 5, five2,
1047 io.smb2.out.file.handle);
1049 /* TODO: we need to test more rename combinations */
1052 smb2_util_close(tree, h1);
1053 status = smb2_util_unlink(tree, fname);
1054 smb2_deltree(tree, DNAME);
1055 talloc_free(mem_ctx);
1063 static bool test_stream_names2(struct torture_context *tctx,
1064 struct smb2_tree *tree)
1066 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1069 const char *fname = DNAME "\\stream_names2.txt";
1071 struct smb2_handle h, h1;
1074 smb2_util_unlink(tree, fname);
1075 smb2_deltree(tree, DNAME);
1077 status = torture_smb2_testdir(tree, DNAME, &h);
1078 CHECK_STATUS(status, NT_STATUS_OK);
1080 torture_comment(tctx, "(%s) testing stream names\n", __location__);
1081 ZERO_STRUCT(io.smb2);
1082 io.smb2.in.create_flags = 0;
1083 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
1084 io.smb2.in.create_options = 0;
1085 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1086 io.smb2.in.share_access = 0;
1087 io.smb2.in.alloc_size = 0;
1088 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1089 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1090 io.smb2.in.security_flags = 0;
1091 io.smb2.in.fname = fname;
1092 status = smb2_create(tree, mem_ctx, &(io.smb2));
1093 CHECK_STATUS(status, NT_STATUS_OK);
1094 h1 = io.smb2.out.file.handle;
1096 for (i=0x01; i < 0x7F; i++) {
1097 char *path = talloc_asprintf(mem_ctx, "%s:Stream%c0x%02X:$DATA",
1105 expected = NT_STATUS_OBJECT_NAME_INVALID;
1108 expected = NT_STATUS_OBJECT_NAME_NOT_FOUND;
1113 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1114 io.smb2.in.fname = path;
1115 status = smb2_create(tree, mem_ctx, &(io.smb2));
1116 if (!NT_STATUS_EQUAL(status, expected)) {
1117 torture_comment(tctx,
1118 "(%s) %s:Stream%c0x%02X:$DATA%s => expected[%s]\n",
1119 __location__, fname, isprint(i)?(char)i:' ', i,
1120 isprint(i)?"":" (not printable)",
1121 nt_errstr(expected));
1123 CHECK_STATUS(status, expected);
1129 smb2_util_close(tree, h1);
1130 status = smb2_util_unlink(tree, fname);
1131 smb2_deltree(tree, DNAME);
1132 talloc_free(mem_ctx);
1137 #define CHECK_CALL_HANDLE(call, rightstatus) do { \
1138 check_handle = true; \
1139 call_name = #call; \
1140 sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
1141 sfinfo.generic.in.file.handle = h1; \
1142 status = smb2_setinfo_file(tree, &sfinfo); \
1143 if (!NT_STATUS_EQUAL(status, rightstatus)) { \
1144 torture_comment(tctx,"(%s) %s - %s (should be %s)\n", \
1145 __location__, #call, \
1146 nt_errstr(status), nt_errstr(rightstatus)); \
1149 finfo1.generic.level = RAW_FILEINFO_ALL_INFORMATION; \
1150 finfo1.generic.in.file.handle = h1; \
1151 status2 = smb2_getinfo_file(tree, tctx, &finfo1); \
1152 if (!NT_STATUS_IS_OK(status2)) { \
1153 torture_comment(tctx,"(%s) %s pathinfo - %s\n", \
1154 __location__, #call, nt_errstr(status)); \
1161 static bool test_stream_rename(struct torture_context *tctx,
1162 struct smb2_tree *tree)
1164 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1165 NTSTATUS status, status2;
1167 const char *fname = DNAME "\\stream_rename.txt";
1168 const char *sname1, *sname2;
1169 union smb_fileinfo finfo1;
1170 union smb_setfileinfo sfinfo;
1172 struct smb2_handle h, h1;
1174 const char *call_name;
1176 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
1177 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname,
1180 smb2_util_unlink(tree, fname);
1181 smb2_deltree(tree, DNAME);
1183 status = torture_smb2_testdir(tree, DNAME, &h);
1184 CHECK_STATUS(status, NT_STATUS_OK);
1186 torture_comment(tctx, "(%s) testing stream renames\n", __location__);
1187 ZERO_STRUCT(io.smb2);
1188 io.smb2.in.create_flags = 0;
1189 io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
1190 SEC_FILE_WRITE_ATTRIBUTE |
1191 SEC_RIGHTS_FILE_ALL;
1192 io.smb2.in.create_options = 0;
1193 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1194 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1195 NTCREATEX_SHARE_ACCESS_WRITE |
1196 NTCREATEX_SHARE_ACCESS_DELETE;
1197 io.smb2.in.alloc_size = 0;
1198 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1199 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1200 io.smb2.in.security_flags = 0;
1201 io.smb2.in.fname = sname1;
1203 /* Create two streams. */
1204 status = smb2_create(tree, mem_ctx, &(io.smb2));
1205 CHECK_STATUS(status, NT_STATUS_OK);
1206 h1 = io.smb2.out.file.handle;
1207 smb2_util_close(tree, h1);
1209 io.smb2.in.fname = sname2;
1210 status = smb2_create(tree, mem_ctx, &(io.smb2));
1211 CHECK_STATUS(status, NT_STATUS_OK);
1212 h1 = io.smb2.out.file.handle;
1214 smb2_util_close(tree, h1);
1217 * Open the second stream.
1220 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1221 status = smb2_create(tree, mem_ctx, &(io.smb2));
1222 CHECK_STATUS(status, NT_STATUS_OK);
1223 h1 = io.smb2.out.file.handle;
1226 * Now rename the second stream onto the first.
1229 ZERO_STRUCT(sfinfo);
1231 sfinfo.rename_information.in.overwrite = 1;
1232 sfinfo.rename_information.in.root_fid = 0;
1233 sfinfo.rename_information.in.new_name = ":Stream One";
1234 CHECK_CALL_HANDLE(RENAME_INFORMATION, NT_STATUS_OK);
1236 smb2_util_close(tree, h1);
1237 status = smb2_util_unlink(tree, fname);
1238 smb2_deltree(tree, DNAME);
1239 talloc_free(mem_ctx);
1244 static bool test_stream_rename2(struct torture_context *tctx,
1245 struct smb2_tree *tree)
1247 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1250 const char *fname1 = DNAME "\\stream_rename2.txt";
1251 const char *fname2 = DNAME "\\stream2_rename2.txt";
1252 const char *stream_name1 = ":Stream One:$DATA";
1253 const char *stream_name2 = ":Stream Two:$DATA";
1254 const char *stream_name_default = "::$DATA";
1258 struct smb2_handle h, h1;
1259 union smb_setfileinfo sinfo;
1261 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream One");
1262 sname2 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream Two");
1264 smb2_util_unlink(tree, fname1);
1265 smb2_util_unlink(tree, fname2);
1266 smb2_deltree(tree, DNAME);
1268 status = torture_smb2_testdir(tree, DNAME, &h);
1269 CHECK_STATUS(status, NT_STATUS_OK);
1271 ZERO_STRUCT(io.smb2);
1272 io.smb2.in.create_flags = 0;
1273 io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1274 SEC_FILE_WRITE_DATA |
1276 SEC_FILE_APPEND_DATA |
1277 SEC_STD_READ_CONTROL;
1278 io.smb2.in.create_options = 0;
1279 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1280 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1281 NTCREATEX_SHARE_ACCESS_WRITE |
1282 NTCREATEX_SHARE_ACCESS_DELETE;
1283 io.smb2.in.alloc_size = 0;
1284 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1285 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1286 io.smb2.in.security_flags = 0;
1287 io.smb2.in.fname = sname1;
1289 /* Open/create new stream. */
1290 status = smb2_create(tree, mem_ctx, &(io.smb2));
1291 CHECK_STATUS(status, NT_STATUS_OK);
1293 smb2_util_close(tree, io.smb2.out.file.handle);
1296 * Reopen the stream for SMB2 renames.
1298 io.smb2.in.fname = sname1;
1299 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1300 status = smb2_create(tree, mem_ctx, &(io.smb2));
1301 CHECK_STATUS(status, NT_STATUS_OK);
1302 h1 = io.smb2.out.file.handle;
1305 * Check SMB2 rename of a stream using :<stream>.
1307 torture_comment(tctx, "(%s) Checking SMB2 rename of a stream using "
1308 ":<stream>\n", __location__);
1310 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION_SMB2;
1311 sinfo.rename_information.in.file.handle = h1;
1312 sinfo.rename_information.in.overwrite = 1;
1313 sinfo.rename_information.in.root_fid = 0;
1314 sinfo.rename_information.in.new_name = stream_name1;
1315 status = smb2_setinfo_file(tree, &sinfo);
1316 CHECK_STATUS(status, NT_STATUS_OK);
1319 * Check SMB2 rename of an overwriting stream using :<stream>.
1321 torture_comment(tctx, "(%s) Checking SMB2 rename of an overwriting "
1322 "stream using :<stream>\n", __location__);
1324 /* Create second stream. */
1325 io.smb2.in.fname = sname2;
1326 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1327 status = smb2_create(tree, mem_ctx, &(io.smb2));
1328 CHECK_STATUS(status, NT_STATUS_OK);
1329 smb2_util_close(tree, io.smb2.out.file.handle);
1331 /* Rename the first stream onto the second. */
1332 sinfo.rename_information.in.file.handle = h1;
1333 sinfo.rename_information.in.new_name = stream_name2;
1334 status = smb2_setinfo_file(tree, &sinfo);
1335 CHECK_STATUS(status, NT_STATUS_OK);
1337 smb2_util_close(tree, h1);
1340 * Reopen the stream with the new name.
1342 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1343 io.smb2.in.fname = sname2;
1344 status = smb2_create(tree, mem_ctx, &(io.smb2));
1345 CHECK_STATUS(status, NT_STATUS_OK);
1346 h1 = io.smb2.out.file.handle;
1349 * Check SMB2 rename of a stream using <base>:<stream>.
1351 torture_comment(tctx, "(%s) Checking SMB2 rename of a stream using "
1352 "<base>:<stream>\n", __location__);
1353 sinfo.rename_information.in.file.handle = h1;
1354 sinfo.rename_information.in.new_name = sname1;
1355 status = smb2_setinfo_file(tree, &sinfo);
1356 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
1359 * Check SMB2 rename to the default stream using :<stream>.
1361 torture_comment(tctx, "(%s) Checking SMB2 rename to defaualt stream "
1362 "using :<stream>\n", __location__);
1363 sinfo.rename_information.in.file.handle = h1;
1364 sinfo.rename_information.in.new_name = stream_name_default;
1365 status = smb2_setinfo_file(tree, &sinfo);
1366 CHECK_STATUS(status, NT_STATUS_OK);
1368 smb2_util_close(tree, h1);
1371 smb2_util_close(tree, h1);
1372 status = smb2_util_unlink(tree, fname1);
1373 status = smb2_util_unlink(tree, fname2);
1374 smb2_deltree(tree, DNAME);
1375 talloc_free(mem_ctx);
1380 static bool create_file_with_stream(struct torture_context *tctx,
1381 struct smb2_tree *tree,
1382 TALLOC_CTX *mem_ctx,
1383 const char *base_fname,
1390 /* Create a file with a stream */
1391 ZERO_STRUCT(io.smb2);
1392 io.smb2.in.create_flags = 0;
1393 io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1394 SEC_FILE_WRITE_DATA |
1395 SEC_FILE_APPEND_DATA |
1396 SEC_STD_READ_CONTROL;
1397 io.smb2.in.create_options = 0;
1398 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1399 io.smb2.in.share_access = 0;
1400 io.smb2.in.alloc_size = 0;
1401 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1402 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1403 io.smb2.in.security_flags = 0;
1404 io.smb2.in.fname = stream;
1406 status = smb2_create(tree, mem_ctx, &(io.smb2));
1407 CHECK_STATUS(status, NT_STATUS_OK);
1410 smb2_util_close(tree, io.smb2.out.file.handle);
1415 /* Test how streams interact with create dispositions */
1416 static bool test_stream_create_disposition(struct torture_context *tctx,
1417 struct smb2_tree *tree)
1419 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1422 const char *fname = DNAME "\\stream_create_disp.txt";
1423 const char *stream = "Stream One:$DATA";
1424 const char *fname_stream;
1425 const char *default_stream_name = "::$DATA";
1426 const char *stream_list[2];
1428 struct smb2_handle h, h1;
1430 /* clean slate .. */
1431 smb2_util_unlink(tree, fname);
1432 smb2_deltree(tree, fname);
1433 smb2_deltree(tree, DNAME);
1435 status = torture_smb2_testdir(tree, DNAME, &h);
1436 CHECK_STATUS(status, NT_STATUS_OK);
1438 fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream);
1440 stream_list[0] = talloc_asprintf(mem_ctx, ":%s", stream);
1441 stream_list[1] = default_stream_name;
1443 if (!create_file_with_stream(tctx, tree, mem_ctx, fname,
1448 /* Open the base file with OPEN */
1449 ZERO_STRUCT(io.smb2);
1450 io.smb2.in.create_flags = 0;
1451 io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1452 SEC_FILE_WRITE_DATA |
1453 SEC_FILE_APPEND_DATA |
1454 SEC_STD_READ_CONTROL;
1455 io.smb2.in.create_options = 0;
1456 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1457 io.smb2.in.share_access = 0;
1458 io.smb2.in.alloc_size = 0;
1459 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1460 io.smb2.in.security_flags = 0;
1461 io.smb2.in.fname = fname;
1464 * check create open: sanity check
1466 torture_comment(tctx, "(%s) Checking create disp: open\n",
1468 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1469 status = smb2_create(tree, mem_ctx, &(io.smb2));
1470 CHECK_STATUS(status, NT_STATUS_OK);
1471 if (!check_stream_list(tree, tctx, fname, 2, stream_list,
1472 io.smb2.out.file.handle)) {
1475 smb2_util_close(tree, io.smb2.out.file.handle);
1478 * check create overwrite
1480 torture_comment(tctx, "(%s) Checking create disp: overwrite\n",
1482 io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
1483 status = smb2_create(tree, mem_ctx, &(io.smb2));
1484 CHECK_STATUS(status, NT_STATUS_OK);
1485 if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name,
1486 io.smb2.out.file.handle)) {
1489 smb2_util_close(tree, io.smb2.out.file.handle);
1492 * check create overwrite_if
1494 torture_comment(tctx, "(%s) Checking create disp: overwrite_if\n",
1496 smb2_util_unlink(tree, fname);
1497 if (!create_file_with_stream(tctx, tree, mem_ctx, fname, fname_stream))
1500 io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1501 status = smb2_create(tree, mem_ctx, &(io.smb2));
1502 CHECK_STATUS(status, NT_STATUS_OK);
1503 if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name,
1504 io.smb2.out.file.handle)) {
1507 smb2_util_close(tree, io.smb2.out.file.handle);
1510 * check create supersede
1512 torture_comment(tctx, "(%s) Checking create disp: supersede\n",
1514 smb2_util_unlink(tree, fname);
1515 if (!create_file_with_stream(tctx, tree, mem_ctx, fname,
1520 io.smb2.in.create_disposition = NTCREATEX_DISP_SUPERSEDE;
1521 status = smb2_create(tree, mem_ctx, &(io.smb2));
1522 CHECK_STATUS(status, NT_STATUS_OK);
1523 if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name,
1524 io.smb2.out.file.handle)) {
1527 smb2_util_close(tree, io.smb2.out.file.handle);
1530 * check create overwrite_if on a stream.
1532 torture_comment(tctx, "(%s) Checking create disp: overwrite_if on "
1533 "stream\n", __location__);
1534 smb2_util_unlink(tree, fname);
1535 if (!create_file_with_stream(tctx, tree, mem_ctx, fname,
1540 io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1541 io.smb2.in.fname = fname_stream;
1542 status = smb2_create(tree, mem_ctx, &(io.smb2));
1543 CHECK_STATUS(status, NT_STATUS_OK);
1544 if (!check_stream_list(tree, tctx, fname, 2, stream_list,
1545 io.smb2.out.file.handle)) {
1548 smb2_util_close(tree, io.smb2.out.file.handle);
1550 smb2_util_close(tree, h1);
1551 smb2_util_unlink(tree, fname);
1552 smb2_deltree(tree, DNAME);
1553 talloc_free(mem_ctx);
1558 static bool open_stream(struct smb2_tree *tree,
1559 struct torture_context *mem_ctx,
1561 struct smb2_handle *h_out)
1566 ZERO_STRUCT(io.smb2);
1567 io.smb2.in.create_flags = 0;
1568 io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1569 SEC_FILE_WRITE_DATA |
1570 SEC_FILE_APPEND_DATA |
1571 SEC_STD_READ_CONTROL |
1572 SEC_FILE_WRITE_ATTRIBUTE;
1573 io.smb2.in.create_options = 0;
1574 io.smb2.in.file_attributes = 0;
1575 io.smb2.in.share_access = 0;
1576 io.smb2.in.alloc_size = 0;
1577 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1578 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1579 io.smb2.in.security_flags = 0;
1580 io.smb2.in.fname = fname;
1582 status = smb2_create(tree, mem_ctx, &(io.smb2));
1583 if (!NT_STATUS_IS_OK(status)) {
1586 *h_out = io.smb2.out.file.handle;
1591 /* Test the effect of setting attributes on a stream. */
1592 static bool test_stream_attributes(struct torture_context *tctx,
1593 struct smb2_tree *tree)
1595 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1599 const char *fname = DNAME "\\stream_attr.txt";
1600 const char *stream = "Stream One:$DATA";
1601 const char *fname_stream;
1602 struct smb2_handle h, h1;
1603 union smb_fileinfo finfo;
1604 union smb_setfileinfo sfinfo;
1605 time_t basetime = (time(NULL) - 86400) & ~1;
1607 torture_comment(tctx, "(%s) testing attribute setting on stream\n",
1610 /* clean slate .. */
1611 smb2_util_unlink(tree, fname);
1612 smb2_deltree(tree, fname);
1613 smb2_deltree(tree, DNAME);
1615 status = torture_smb2_testdir(tree, DNAME, &h);
1616 CHECK_STATUS(status, NT_STATUS_OK);
1618 fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream);
1620 /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */
1621 ret = create_file_with_stream(tctx, tree, mem_ctx, fname,
1627 ZERO_STRUCT(io.smb2);
1628 io.smb2.in.fname = fname;
1629 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1630 io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
1631 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1632 NTCREATEX_SHARE_ACCESS_WRITE |
1633 NTCREATEX_SHARE_ACCESS_DELETE;
1634 status = smb2_create(tree, mem_ctx, &(io.smb2));
1635 CHECK_STATUS(status, NT_STATUS_OK);
1638 finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1639 finfo.generic.in.file.handle = io.smb2.out.file.handle;
1640 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1641 CHECK_STATUS(status, NT_STATUS_OK);
1643 if (finfo.basic_info.out.attrib != FILE_ATTRIBUTE_ARCHIVE) {
1644 torture_comment(tctx, "(%s) Incorrect attrib %x - should be "
1645 "%x\n", __location__,
1646 (unsigned int)finfo.basic_info.out.attrib,
1647 (unsigned int)FILE_ATTRIBUTE_ARCHIVE);
1652 smb2_util_close(tree, io.smb2.out.file.handle);
1653 /* Now open the stream name. */
1655 if (!open_stream(tree, tctx, fname_stream, &h1)) {
1659 /* Change the time on the stream. */
1660 ZERO_STRUCT(sfinfo);
1661 unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime);
1662 sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1663 sfinfo.generic.in.file.handle = h1;
1664 status = smb2_setinfo_file(tree, &sfinfo);
1665 if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
1666 torture_comment(tctx, "(%s) %s - %s (should be %s)\n",
1667 __location__, "SETATTR",
1668 nt_errstr(status), nt_errstr(NT_STATUS_OK));
1673 smb2_util_close(tree, h1);
1675 ZERO_STRUCT(io.smb2);
1676 io.smb2.in.fname = fname;
1677 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1678 io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
1679 status = smb2_create(tree, mem_ctx, &(io.smb2));
1680 CHECK_STATUS(status, NT_STATUS_OK);
1681 h1 = io.smb2.out.file.handle;
1684 finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1685 finfo.generic.in.file.handle = h1;
1686 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1687 if (!NT_STATUS_IS_OK(status)) {
1688 torture_comment(tctx, "(%s) %s pathinfo - %s\n",
1689 __location__, "SETATTRE", nt_errstr(status));
1694 if (nt_time_to_unix(finfo.basic_info.out.write_time) != basetime) {
1695 torture_comment(tctx, "(%s) time incorrect.\n", __location__);
1699 smb2_util_close(tree, h1);
1701 if (!open_stream(tree, tctx, fname_stream, &h1)) {
1705 /* Changing attributes on stream */
1706 ZERO_STRUCT(sfinfo);
1707 sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY;
1709 sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1710 sfinfo.generic.in.file.handle = h1;
1711 status = smb2_setinfo_file(tree, &sfinfo);
1712 if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
1713 torture_comment(tctx, "(%s) %s - %s (should be %s)\n",
1714 __location__, "SETATTR",
1715 nt_errstr(status), nt_errstr(NT_STATUS_OK));
1720 smb2_util_close(tree, h1);
1722 ZERO_STRUCT(io.smb2);
1723 io.smb2.in.fname = fname;
1724 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1725 io.smb2.in.desired_access = SEC_FILE_READ_DATA;
1726 status = smb2_create(tree, mem_ctx, &(io.smb2));
1727 CHECK_STATUS(status, NT_STATUS_OK);
1728 h1 = io.smb2.out.file.handle;
1731 finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1732 finfo.generic.in.file.handle = h1;
1733 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1734 CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
1737 smb2_util_close(tree, h1);
1738 smb2_util_unlink(tree, fname);
1739 smb2_deltree(tree, DNAME);
1740 talloc_free(mem_ctx);
1747 basic testing of streams calls SMB2
1749 struct torture_suite *torture_smb2_streams_init(void)
1751 struct torture_suite *suite =
1752 torture_suite_create(talloc_autofree_context(), "STREAMS");
1754 torture_suite_add_1smb2_test(suite, "DIR", test_stream_dir);
1755 torture_suite_add_1smb2_test(suite, "IO", test_stream_io);
1756 torture_suite_add_1smb2_test(suite, "SHAREMODES", test_stream_sharemodes);
1757 torture_suite_add_1smb2_test(suite, "NAMES", test_stream_names);
1758 torture_suite_add_1smb2_test(suite, "NAMES2", test_stream_names2);
1759 torture_suite_add_1smb2_test(suite, "RENAME", test_stream_rename);
1760 torture_suite_add_1smb2_test(suite, "RENAME2", test_stream_rename2);
1761 torture_suite_add_1smb2_test(suite, "CREATE-DISPOSITION", test_stream_create_disposition);
1762 torture_suite_add_1smb2_test(suite, "ATTRIBUTES", test_stream_attributes);
1763 torture_suite_add_1smb2_test(suite, "DELETE", test_stream_delete);
1765 suite->description = talloc_strdup(suite, "SMB2-STREAM tests");