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"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
29 #include "system/filesys.h"
30 #include "system/locale.h"
31 #include "lib/util/tsort.h"
33 #define DNAME "teststreams"
35 #define CHECK_STATUS(status, correct) do { \
36 if (!NT_STATUS_EQUAL(status, correct)) { \
37 torture_result(tctx, TORTURE_FAIL, \
38 "(%s) Incorrect status %s - should be %s\n", \
39 __location__, nt_errstr(status), nt_errstr(correct)); \
44 #define CHECK_VALUE(v, correct) do { \
45 if ((v) != (correct)) { \
46 torture_result(tctx, TORTURE_FAIL, \
47 "(%s) Incorrect value %s=%d - should be %d\n", \
48 __location__, #v, (int)v, (int)correct); \
52 #define CHECK_NTTIME(v, correct) do { \
53 if ((v) != (correct)) { \
54 torture_result(tctx, TORTURE_FAIL, \
55 "(%s) Incorrect value %s=%llu - should be %llu\n", \
56 __location__, #v, (unsigned long long)v, \
57 (unsigned long long)correct); \
61 #define CHECK_STR(v, correct) do { \
63 if ((v) && !(correct)) { \
65 } else if (!(v) && (correct)) { \
67 } else if (!(v) && !(correct)) { \
69 } else if (strcmp((v), (correct)) == 0) { \
75 torture_result(tctx, TORTURE_FAIL, \
76 "(%s) Incorrect value %s='%s' - " \
78 __location__, #v, (v)?(v):"NULL", \
79 (correct)?(correct):"NULL"); \
84 static int qsort_string(char * const *s1, char * const *s2)
86 return strcmp(*s1, *s2);
89 static int qsort_stream(const struct stream_struct * s1, const struct stream_struct *s2)
91 return strcmp(s1->stream_name.s, s2->stream_name.s);
94 static bool check_stream(struct smb2_tree *tree,
101 struct smb2_handle handle;
102 struct smb2_create create;
105 const char *full_name;
107 full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname);
110 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
111 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
112 create.in.create_disposition = NTCREATEX_DISP_OPEN;
113 create.in.fname = full_name;
115 status = smb2_create(tree, mem_ctx, &create);
116 if (!NT_STATUS_IS_OK(status)) {
120 torture_comment(mem_ctx, "Unable to open stream %s\n",
126 handle = create.out.file.handle;
133 r.in.file.handle = handle;
134 r.in.length = strlen(value)+11;
137 status = smb2_read(tree, tree, &r);
139 if (!NT_STATUS_IS_OK(status)) {
140 torture_comment(mem_ctx, "(%s) Failed to read %lu bytes from "
141 "stream '%s'\n", location, (long)strlen(value), full_name);
145 if (memcmp(r.out.data.data, value, strlen(value)) != 0) {
146 torture_comment(mem_ctx, "(%s) Bad data in stream\n", location);
150 smb2_util_close(tree, handle);
154 static bool check_stream_list(struct smb2_tree *tree,
155 struct torture_context *tctx,
159 struct smb2_handle h)
161 union smb_fileinfo finfo;
164 TALLOC_CTX *tmp_ctx = talloc_new(tctx);
166 struct stream_struct *stream_sort;
169 finfo.generic.level = RAW_FILEINFO_STREAM_INFORMATION;
170 finfo.generic.in.file.handle = h;
172 status = smb2_getinfo_file(tree, tctx, &finfo);
173 if (!NT_STATUS_IS_OK(status)) {
174 torture_comment(tctx, "(%s) smb_raw_pathinfo failed: %s\n",
175 __location__, nt_errstr(status));
179 if (finfo.stream_info.out.num_streams != num_exp) {
180 torture_comment(tctx, "(%s) expected %d streams, got %d\n",
181 __location__, num_exp, finfo.stream_info.out.num_streams);
190 exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
192 if (exp_sort == NULL) {
196 TYPESAFE_QSORT(exp_sort, num_exp, qsort_string);
198 stream_sort = talloc_memdup(tmp_ctx, finfo.stream_info.out.streams,
199 finfo.stream_info.out.num_streams *
200 sizeof(*stream_sort));
202 if (stream_sort == NULL) {
206 TYPESAFE_QSORT(stream_sort, finfo.stream_info.out.num_streams, qsort_stream);
208 for (i=0; i<num_exp; i++) {
209 if (strcmp(exp_sort[i], stream_sort[i].stream_name.s) != 0) {
210 torture_comment(tctx,
211 "(%s) expected stream name %s, got %s\n",
212 __location__, exp_sort[i],
213 stream_sort[i].stream_name.s);
220 talloc_free(tmp_ctx);
225 static bool test_stream_dir(struct torture_context *tctx,
226 struct smb2_tree *tree)
228 TALLOC_CTX *mem_ctx = talloc_new(tctx);
231 const char *fname = DNAME "\\stream.txt";
234 const char *basedir_data;
235 struct smb2_handle h;
237 smb2_util_unlink(tree, fname);
238 smb2_deltree(tree, DNAME);
240 status = torture_smb2_testdir(tree, DNAME, &h);
241 CHECK_STATUS(status, NT_STATUS_OK);
243 basedir_data = talloc_asprintf(mem_ctx, "%s::$DATA", DNAME);
244 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
245 torture_comment(tctx, "%s\n", sname1);
247 torture_comment(tctx, "(%s) opening non-existent directory stream\n",
249 ZERO_STRUCT(io.smb2);
250 io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
251 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
252 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
253 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
254 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
255 io.smb2.in.share_access = 0;
256 io.smb2.in.alloc_size = 0;
257 io.smb2.in.security_flags = 0;
258 io.smb2.in.fname = sname1;
259 io.smb2.in.create_flags = 0;
260 status = smb2_create(tree, mem_ctx, &(io.smb2));
261 CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
263 torture_comment(tctx, "(%s) opening basedir stream\n", __location__);
264 ZERO_STRUCT(io.smb2);
265 io.smb2.in.create_flags = 0;
266 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
267 io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
268 io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
269 io.smb2.in.share_access = 0;
270 io.smb2.in.alloc_size = 0;
271 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
272 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
273 io.smb2.in.security_flags = 0;
274 io.smb2.in.fname = basedir_data;
275 status = smb2_create(tree, mem_ctx, &(io.smb2));
276 CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
278 torture_comment(tctx, "(%s) opening basedir ::$DATA stream\n",
280 ZERO_STRUCT(io.smb2);
281 io.smb2.in.create_flags = 0x10;
282 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
283 io.smb2.in.create_options = 0;
284 io.smb2.in.file_attributes = 0;
285 io.smb2.in.share_access = 0;
286 io.smb2.in.alloc_size = 0;
287 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
288 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
289 io.smb2.in.security_flags = 0;
290 io.smb2.in.fname = basedir_data;
291 status = smb2_create(tree, mem_ctx, &(io.smb2));
292 CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
294 torture_comment(tctx, "(%s) list the streams on the basedir\n",
296 ret &= check_stream_list(tree, mem_ctx, DNAME, 0, NULL, h);
298 smb2_util_unlink(tree, fname);
299 smb2_deltree(tree, DNAME);
300 talloc_free(mem_ctx);
305 static bool test_stream_io(struct torture_context *tctx,
306 struct smb2_tree *tree)
308 TALLOC_CTX *mem_ctx = talloc_new(tctx);
311 const char *fname = DNAME "\\stream.txt";
312 const char *sname1, *sname2;
314 struct smb2_handle h, h2;
316 const char *one[] = { "::$DATA" };
317 const char *two[] = { "::$DATA", ":Second Stream:$DATA" };
318 const char *three[] = { "::$DATA", ":Stream One:$DATA",
319 ":Second Stream:$DATA" };
324 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
325 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname,
328 smb2_util_unlink(tree, fname);
329 smb2_deltree(tree, DNAME);
331 status = torture_smb2_testdir(tree, DNAME, &h);
332 CHECK_STATUS(status, NT_STATUS_OK);
334 torture_comment(tctx, "(%s) creating a stream on a non-existent file\n",
337 ZERO_STRUCT(io.smb2);
338 io.smb2.in.create_flags = 0;
339 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
340 io.smb2.in.create_options = 0;
341 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
342 io.smb2.in.share_access = 0;
343 io.smb2.in.alloc_size = 0;
344 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
345 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
346 io.smb2.in.security_flags = 0;
347 io.smb2.in.fname = sname1;
348 status = smb2_create(tree, mem_ctx, &(io.smb2));
349 CHECK_STATUS(status, NT_STATUS_OK);
350 h2 = io.smb2.out.file.handle;
352 ret &= check_stream(tree, __location__, mem_ctx, fname,
355 torture_comment(tctx, "(%s) check that open of base file is allowed\n", __location__);
356 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
357 io.smb2.in.fname = fname;
358 status = smb2_create(tree, mem_ctx, &(io.smb2));
359 CHECK_STATUS(status, NT_STATUS_OK);
360 smb2_util_close(tree, io.smb2.out.file.handle);
362 torture_comment(tctx, "(%s) writing to stream\n", __location__);
363 status = smb2_util_write(tree, h2, "test data", 0, 9);
364 CHECK_STATUS(status, NT_STATUS_OK);
366 smb2_util_close(tree, h2);
368 ret &= check_stream(tree, __location__, mem_ctx, fname,
369 "Stream One", "test data");
371 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
372 io.smb2.in.fname = sname1;
373 status = smb2_create(tree, mem_ctx, &(io.smb2));
374 CHECK_STATUS(status, NT_STATUS_OK);
375 h2 = io.smb2.out.file.handle;
377 torture_comment(tctx, "(%s) modifying stream\n", __location__);
378 status = smb2_util_write(tree, h2, "MORE DATA ", 5, 10);
379 CHECK_STATUS(status, NT_STATUS_OK);
381 smb2_util_close(tree, h2);
383 ret &= check_stream(tree, __location__, mem_ctx, fname,
384 "Stream One:$FOO", NULL);
386 torture_comment(tctx, "(%s) creating a stream2 on a existing file\n",
388 io.smb2.in.fname = sname2;
389 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
390 status = smb2_create(tree, mem_ctx, &(io.smb2));
391 CHECK_STATUS(status, NT_STATUS_OK);
392 h2 = io.smb2.out.file.handle;
394 torture_comment(tctx, "(%s) modifying stream\n", __location__);
395 status= smb2_util_write(tree, h2, "SECOND STREAM", 0, 13);
396 CHECK_STATUS(status, NT_STATUS_OK);
397 smb2_util_close(tree, h2);
399 ret &= check_stream(tree, __location__, mem_ctx, fname,
400 "Stream One", "test MORE DATA ");
402 ret &= check_stream(tree, __location__, mem_ctx, fname,
403 "Stream One:$DATA", "test MORE DATA ");
405 ret &= check_stream(tree, __location__, mem_ctx, fname,
406 "Stream One:", NULL);
408 ret &= check_stream(tree, __location__, mem_ctx, fname,
409 "Second Stream", "SECOND STREAM");
411 ret &= check_stream(tree, __location__, mem_ctx, fname,
412 "SECOND STREAM:$DATA", "SECOND STREAM");
413 ret &= check_stream(tree, __location__, mem_ctx, fname,
414 "Second Stream:$DATA", "SECOND STREAM");
416 ret &= check_stream(tree, __location__, mem_ctx, fname,
417 "Second Stream:", NULL);
419 ret &= check_stream(tree, __location__, mem_ctx, fname,
420 "Second Stream:$FOO", NULL);
422 io.smb2.in.fname = sname2;
423 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
424 status = smb2_create(tree, mem_ctx, &(io.smb2));
425 CHECK_STATUS(status, NT_STATUS_OK);
426 h2 = io.smb2.out.file.handle;
427 check_stream_list(tree, tctx, fname, 3, three, h2);
429 smb2_util_close(tree, h2);
431 torture_comment(tctx, "(%s) deleting stream\n", __location__);
432 status = smb2_util_unlink(tree, sname1);
433 CHECK_STATUS(status, NT_STATUS_OK);
435 io.smb2.in.fname = sname2;
436 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
437 status = smb2_create(tree, mem_ctx, &(io.smb2));
438 CHECK_STATUS(status, NT_STATUS_OK);
439 h2 = io.smb2.out.file.handle;
440 check_stream_list(tree, tctx, fname, 2, two, h2);
441 smb2_util_close(tree, h2);
443 torture_comment(tctx, "(%s) delete a stream via delete-on-close\n",
445 io.smb2.in.fname = sname2;
446 io.smb2.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
447 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
448 io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
449 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
451 status = smb2_create(tree, mem_ctx, &(io.smb2));
452 CHECK_STATUS(status, NT_STATUS_OK);
453 h2 = io.smb2.out.file.handle;
455 smb2_util_close(tree, h2);
456 status = smb2_util_unlink(tree, sname2);
457 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
459 io.smb2.in.fname = fname;
460 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
461 status = smb2_create(tree, mem_ctx, &(io.smb2));
462 h2 = io.smb2.out.file.handle;
463 check_stream_list(tree,tctx, fname, 1, one, h2);
464 smb2_util_close(tree, h2);
466 if (!torture_setting_bool(tctx, "samba4", false)) {
467 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
468 io.smb2.in.fname = sname1;
469 status = smb2_create(tree, mem_ctx, &(io.smb2));
470 CHECK_STATUS(status, NT_STATUS_OK);
471 smb2_util_close(tree, io.ntcreatex.out.file.handle);
472 io.smb2.in.fname = sname2;
473 status = smb2_create(tree, mem_ctx, &(io.smb2));
474 CHECK_STATUS(status, NT_STATUS_OK);
475 smb2_util_close(tree, io.ntcreatex.out.file.handle);
476 torture_comment(tctx, "(%s) deleting file\n", __location__);
477 status = smb2_util_unlink(tree, fname);
478 CHECK_STATUS(status, NT_STATUS_OK);
483 smb2_util_close(tree, h2);
484 smb2_deltree(tree, DNAME);
485 talloc_free(mem_ctx);
490 static bool test_zero_byte_stream(struct torture_context *tctx,
491 struct smb2_tree *tree)
493 TALLOC_CTX *mem_ctx = talloc_new(tctx);
496 const char *fname = DNAME "\\stream.txt";
499 struct smb2_handle h, bh;
500 const char *streams[] = { "::$DATA", ":foo:$DATA" };
502 sname = talloc_asprintf(mem_ctx, "%s:%s", fname, "foo");
504 smb2_util_unlink(tree, fname);
505 smb2_deltree(tree, DNAME);
507 status = torture_smb2_testdir(tree, DNAME, &h);
508 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "testdir");
509 smb2_util_close(tree, h);
511 torture_comment(tctx, "(%s) Check 0 byte named stream\n",
514 /* Create basefile */
516 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
517 io.smb2.in.fname = fname;
518 io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
519 SEC_FILE_WRITE_ATTRIBUTE |
522 status = smb2_create(tree, mem_ctx, &(io.smb2));
523 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "create");
524 smb2_util_close(tree, io.smb2.out.file.handle);
526 /* Create named stream and close it */
528 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
529 io.smb2.in.fname = sname;
530 io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
531 SEC_FILE_WRITE_ATTRIBUTE |
534 status = smb2_create(tree, mem_ctx, &(io.smb2));
535 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "create");
536 smb2_util_close(tree, io.smb2.out.file.handle);
539 * Check stream list, the 0 byte stream MUST be returned by
543 io.smb2.in.fname = fname;
544 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
545 io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
546 SEC_FILE_WRITE_ATTRIBUTE |
549 status = smb2_create(tree, mem_ctx, &(io.smb2));
550 bh = io.smb2.out.file.handle;
552 ret = check_stream_list(tree,tctx, fname, 2, streams, bh);
553 torture_assert_goto(tctx, ret == true, ret, done, "smb2_create");
554 smb2_util_close(tree, bh);
557 smb2_deltree(tree, DNAME);
558 talloc_free(mem_ctx);
564 test stream sharemodes
566 static bool test_stream_sharemodes(struct torture_context *tctx,
567 struct smb2_tree *tree)
569 TALLOC_CTX *mem_ctx = talloc_new(tctx);
572 const char *fname = DNAME "\\stream_share.txt";
573 const char *sname1, *sname2;
575 struct smb2_handle h, h1, h2;
581 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
582 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname,
585 smb2_util_unlink(tree, fname);
586 smb2_deltree(tree, DNAME);
588 status = torture_smb2_testdir(tree, DNAME, &h);
589 CHECK_STATUS(status, NT_STATUS_OK);
591 torture_comment(tctx, "(%s) Testing stream share mode conflicts\n",
593 ZERO_STRUCT(io.smb2);
594 io.generic.level = RAW_OPEN_SMB2;
595 io.smb2.in.create_flags = 0;
596 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
597 io.smb2.in.create_options = 0;
598 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
599 io.smb2.in.share_access = 0;
600 io.smb2.in.alloc_size = 0;
601 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
602 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
603 io.smb2.in.security_flags = 0;
604 io.smb2.in.fname = sname1;
606 status = smb2_create(tree, mem_ctx, &(io.smb2));
607 CHECK_STATUS(status, NT_STATUS_OK);
608 h1 = io.smb2.out.file.handle;
611 * A different stream does not give a sharing violation
614 io.smb2.in.fname = sname2;
615 status = smb2_create(tree, mem_ctx, &(io.smb2));
616 CHECK_STATUS(status, NT_STATUS_OK);
617 h2 = io.smb2.out.file.handle;
620 * ... whereas the same stream does with unchanged access/share_access
624 io.smb2.in.fname = sname1;
625 io.smb2.in.create_disposition = 0;
626 status = smb2_create(tree, mem_ctx, &(io.smb2));
627 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
629 io.smb2.in.fname = sname2;
630 status = smb2_create(tree, mem_ctx, &(io.smb2));
631 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
634 smb2_util_close(tree, h1);
635 smb2_util_close(tree, h2);
636 status = smb2_util_unlink(tree, fname);
637 smb2_deltree(tree, DNAME);
638 talloc_free(mem_ctx);
644 * Test FILE_SHARE_DELETE on streams
646 * A stream opened with !FILE_SHARE_DELETE prevents the main file to be opened
647 * with SEC_STD_DELETE.
649 * The main file opened with !FILE_SHARE_DELETE does *not* prevent a stream to
650 * be opened with SEC_STD_DELETE.
652 * A stream held open with FILE_SHARE_DELETE allows the file to be
653 * deleted. After the main file is deleted, access to the open file descriptor
654 * still works, but all name-based access to both the main file as well as the
655 * stream is denied with DELETE pending.
657 * This means, an open of the main file with SEC_STD_DELETE should walk all
658 * streams and also open them with SEC_STD_DELETE. If any of these opens gives
659 * SHARING_VIOLATION, the main open fails.
661 * Closing the main file after delete_on_close has been set does not really
662 * unlink it but leaves the corresponding share mode entry with
663 * delete_on_close being set around until all streams are closed.
665 * Opening a stream must also look at the main file's share mode entry, look
666 * at the delete_on_close bit and potentially return DELETE_PENDING.
669 static bool test_stream_delete(struct torture_context *tctx,
670 struct smb2_tree *tree)
672 TALLOC_CTX *mem_ctx = talloc_new(tctx);
675 const char *fname = DNAME "\\stream_delete.txt";
678 struct smb2_handle h = {{0}};
679 struct smb2_handle h1 = {{0}};
682 if (torture_setting_bool(tctx, "samba4", false)) {
683 torture_comment(tctx, "Skipping test as samba4 is enabled\n");
690 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
693 smb2_util_unlink(tree, fname);
694 smb2_deltree(tree, fname);
695 smb2_deltree(tree, DNAME);
697 status = torture_smb2_testdir(tree, DNAME, &h);
698 CHECK_STATUS(status, NT_STATUS_OK);
700 torture_comment(tctx, "(%s) opening non-existent file stream\n",
702 ZERO_STRUCT(io.smb2);
703 io.smb2.in.create_flags = 0;
704 io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
705 io.smb2.in.create_options = 0;
706 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
707 io.smb2.in.share_access = 0;
708 io.smb2.in.alloc_size = 0;
709 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
710 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
711 io.smb2.in.security_flags = 0;
712 io.smb2.in.fname = sname1;
714 status = smb2_create(tree, mem_ctx, &(io.smb2));
715 CHECK_STATUS(status, NT_STATUS_OK);
716 h1 = io.smb2.out.file.handle;
718 status = smb2_util_write(tree, h1, "test data", 0, 9);
719 CHECK_STATUS(status, NT_STATUS_OK);
722 * One stream opened without FILE_SHARE_DELETE prevents the main file
723 * to be deleted or even opened with DELETE access
726 status = smb2_util_unlink(tree, fname);
727 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
729 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
730 io.smb2.in.fname = fname;
731 io.smb2.in.desired_access = SEC_STD_DELETE;
732 status = smb2_create(tree, mem_ctx, &(io.smb2));
733 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
735 smb2_util_close(tree, h1);
738 * ... but unlink works if a stream is opened with FILE_SHARE_DELETE
741 io.smb2.in.fname = sname1;
742 io.smb2.in.desired_access = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
743 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
744 NTCREATEX_SHARE_ACCESS_READ |
745 NTCREATEX_SHARE_ACCESS_WRITE;
746 status = smb2_create(tree, mem_ctx, &(io.smb2));
747 CHECK_STATUS(status, NT_STATUS_OK);
748 h1 = io.smb2.out.file.handle;
750 status = smb2_util_unlink(tree, fname);
751 CHECK_STATUS(status, NT_STATUS_OK);
754 * file access still works on the stream while the main file is closed
757 r.in.file.handle = h1;
761 status = smb2_read(tree, tree, &r);
762 CHECK_STATUS(status, NT_STATUS_OK);
765 * name-based access to both the main file and the stream does not
766 * work anymore but gives DELETE_PENDING
769 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
770 io.smb2.in.fname = fname;
771 status = smb2_create(tree, mem_ctx, &(io.smb2));
772 CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
775 * older S3 doesn't do this
778 io.smb2.in.fname = sname1;
779 status = smb2_create(tree, mem_ctx, &(io.smb2));
780 CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
782 smb2_util_close(tree, h1);
786 * After closing the stream the file is really gone.
789 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
790 io.smb2.in.fname = fname;
791 status = smb2_create(tree, mem_ctx, &(io.smb2));
792 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
795 if (!smb2_util_handle_empty(h1)) {
796 smb2_util_close(tree, h1);
798 smb2_util_unlink(tree, fname);
799 smb2_deltree(tree, DNAME);
800 talloc_free(mem_ctx);
808 static bool test_stream_names(struct torture_context *tctx,
809 struct smb2_tree *tree)
811 TALLOC_CTX *mem_ctx = talloc_new(tctx);
814 union smb_fileinfo finfo;
815 union smb_fileinfo stinfo;
816 union smb_setfileinfo sinfo;
817 const char *fname = DNAME "\\stream_names.txt";
818 const char *sname1, *sname1b, *sname1c, *sname1d;
819 const char *sname2, *snamew, *snamew2;
822 struct smb2_handle h, h1, h2, h3;
824 const char *four[4] = {
826 ":\x05Stream\n One:$DATA",
827 ":MStream Two:$DATA",
830 const char *five1[5] = {
832 ":\x05Stream\n One:$DATA",
833 ":BeforeRename:$DATA",
834 ":MStream Two:$DATA",
837 const char *five2[5] = {
839 ":\x05Stream\n One:$DATA",
840 ":AfterRename:$DATA",
841 ":MStream Two:$DATA",
850 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "\x05Stream\n One");
851 sname1b = talloc_asprintf(mem_ctx, "%s:", sname1);
852 sname1c = talloc_asprintf(mem_ctx, "%s:$FOO", sname1);
853 sname1d = talloc_asprintf(mem_ctx, "%s:?D*a", sname1);
854 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "MStream Two");
855 snamew = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "?Stream*");
856 snamew2 = talloc_asprintf(mem_ctx, "%s\\stream*:%s:$DATA", DNAME,
858 snamer1 = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname,
862 smb2_util_unlink(tree, fname);
863 smb2_deltree(tree, fname);
864 smb2_deltree(tree, DNAME);
866 status = torture_smb2_testdir(tree, DNAME, &h);
867 CHECK_STATUS(status, NT_STATUS_OK);
869 torture_comment(tctx, "(%s) testing stream names\n", __location__);
870 ZERO_STRUCT(io.smb2);
871 io.smb2.in.create_flags = 0;
872 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
873 io.smb2.in.create_options = 0;
874 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
875 io.smb2.in.share_access = 0;
876 io.smb2.in.alloc_size = 0;
877 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
878 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
879 io.smb2.in.security_flags = 0;
880 io.smb2.in.fname = sname1;
882 status = smb2_create(tree, mem_ctx, &(io.smb2));
883 CHECK_STATUS(status, NT_STATUS_OK);
884 h1 = io.smb2.out.file.handle;
887 * A different stream does not give a sharing violation
890 io.smb2.in.fname = sname2;
891 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
892 status = smb2_create(tree, mem_ctx, &(io.smb2));
893 CHECK_STATUS(status, NT_STATUS_OK);
894 h2 = io.smb2.out.file.handle;
897 * ... whereas the same stream does with unchanged access/share_access
901 io.smb2.in.fname = sname1;
902 io.smb2.in.create_disposition = NTCREATEX_DISP_SUPERSEDE;
903 status = smb2_create(tree, mem_ctx, &(io.smb2));
904 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
906 io.smb2.in.fname = sname1b;
907 status = smb2_create(tree, mem_ctx, &(io.smb2));
908 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
910 io.smb2.in.fname = sname1c;
911 status = smb2_create(tree, mem_ctx, &(io.smb2));
912 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
913 /* w2k returns INVALID_PARAMETER */
914 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
916 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
919 io.smb2.in.fname = sname1d;
920 status = smb2_create(tree, mem_ctx, &(io.smb2));
921 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
922 /* w2k returns INVALID_PARAMETER */
923 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
925 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
928 io.smb2.in.fname = sname2;
929 status = smb2_create(tree, mem_ctx, &(io.smb2));
930 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
932 io.smb2.in.fname = snamew;
933 status = smb2_create(tree, mem_ctx, &(io.smb2));
934 CHECK_STATUS(status, NT_STATUS_OK);
935 h3 = io.smb2.out.file.handle;
937 io.smb2.in.fname = snamew2;
938 status = smb2_create(tree, mem_ctx, &(io.smb2));
939 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
941 io.smb2.in.fname = fname;
942 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
943 status = smb2_create(tree, mem_ctx, &(io.smb2));
944 CHECK_STATUS(status, NT_STATUS_OK);
945 ret &= check_stream_list(tree, tctx, fname, 4, four,
946 io.smb2.out.file.handle);
948 smb2_util_close(tree, h1);
949 smb2_util_close(tree, h2);
950 smb2_util_close(tree, h3);
952 if (torture_setting_bool(tctx, "samba4", true)) {
956 finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
957 finfo.generic.in.file.handle = io.smb2.out.file.handle;
958 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
959 CHECK_STATUS(status, NT_STATUS_OK);
960 ret &= check_stream_list(tree, tctx, fname, 4, four,
961 io.smb2.out.file.handle);
963 for (i=0; i < 4; i++) {
965 uint64_t stream_size;
966 char *path = talloc_asprintf(tctx, "%s%s",
969 char *rpath = talloc_strdup(path, path);
970 char *p = strrchr(rpath, ':');
978 torture_comment(tctx, "(%s): i[%u][%s]\n",
979 __location__, i,path);
980 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
981 io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
982 SEC_FILE_WRITE_ATTRIBUTE |
984 io.smb2.in.fname = path;
985 status = smb2_create(tree, mem_ctx, &(io.smb2));
986 CHECK_STATUS(status, NT_STATUS_OK);
987 h1 = io.smb2.out.file.handle;
989 finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
990 finfo.generic.in.file.path = fname;
991 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
992 CHECK_STATUS(status, NT_STATUS_OK);
994 stinfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
995 stinfo.generic.in.file.handle = h1;
996 status = smb2_getinfo_file(tree, mem_ctx, &stinfo);
997 CHECK_STATUS(status, NT_STATUS_OK);
998 if (!torture_setting_bool(tctx, "samba3", false)) {
999 CHECK_NTTIME(stinfo.all_info.out.create_time,
1000 finfo.all_info.out.create_time);
1001 CHECK_NTTIME(stinfo.all_info.out.access_time,
1002 finfo.all_info.out.access_time);
1003 CHECK_NTTIME(stinfo.all_info.out.write_time,
1004 finfo.all_info.out.write_time);
1005 CHECK_NTTIME(stinfo.all_info.out.change_time,
1006 finfo.all_info.out.change_time);
1008 CHECK_VALUE(stinfo.all_info.out.attrib,
1009 finfo.all_info.out.attrib);
1010 CHECK_VALUE(stinfo.all_info.out.size,
1011 finfo.all_info.out.size);
1012 CHECK_VALUE(stinfo.all_info.out.delete_pending,
1013 finfo.all_info.out.delete_pending);
1014 CHECK_VALUE(stinfo.all_info.out.directory,
1015 finfo.all_info.out.directory);
1016 CHECK_VALUE(stinfo.all_info.out.ea_size,
1017 finfo.all_info.out.ea_size);
1019 stinfo.generic.level = RAW_FILEINFO_NAME_INFORMATION;
1020 stinfo.generic.in.file.handle = h1;
1021 status = smb2_getinfo_file(tree, mem_ctx, &stinfo);
1022 CHECK_STATUS(status, NT_STATUS_OK);
1023 if (!torture_setting_bool(tctx, "samba3", false)) {
1024 CHECK_STR(rpath, stinfo.name_info.out.fname.s);
1027 write_time = finfo.all_info.out.write_time;
1028 write_time += i*1000000;
1029 write_time /= 1000000;
1030 write_time *= 1000000;
1033 sinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
1034 sinfo.basic_info.in.file.handle = h1;
1035 sinfo.basic_info.in.write_time = write_time;
1036 sinfo.basic_info.in.attrib = stinfo.all_info.out.attrib;
1037 status = smb2_setinfo_file(tree, &sinfo);
1038 CHECK_STATUS(status, NT_STATUS_OK);
1040 stream_size = i*8192;
1043 sinfo.end_of_file_info.level =
1044 RAW_SFILEINFO_END_OF_FILE_INFORMATION;
1045 sinfo.end_of_file_info.in.file.handle = h1;
1046 sinfo.end_of_file_info.in.size = stream_size;
1047 status = smb2_setinfo_file(tree, &sinfo);
1048 CHECK_STATUS(status, NT_STATUS_OK);
1050 stinfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
1051 stinfo.generic.in.file.handle = h1;
1052 status = smb2_getinfo_file(tree, mem_ctx, &stinfo);
1053 CHECK_STATUS(status, NT_STATUS_OK);
1054 if (!torture_setting_bool(tctx, "samba3", false)) {
1055 CHECK_NTTIME(stinfo.all_info.out.write_time,
1057 CHECK_VALUE(stinfo.all_info.out.attrib,
1058 finfo.all_info.out.attrib);
1060 CHECK_VALUE(stinfo.all_info.out.size,
1062 CHECK_VALUE(stinfo.all_info.out.delete_pending,
1063 finfo.all_info.out.delete_pending);
1064 CHECK_VALUE(stinfo.all_info.out.directory,
1065 finfo.all_info.out.directory);
1066 CHECK_VALUE(stinfo.all_info.out.ea_size,
1067 finfo.all_info.out.ea_size);
1069 io.smb2.in.fname = fname;
1070 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1071 status = smb2_create(tree, mem_ctx, &(io.smb2));
1072 CHECK_STATUS(status, NT_STATUS_OK);
1073 ret &= check_stream_list(tree, tctx, fname, 4, four,
1074 io.smb2.out.file.handle);
1076 smb2_util_close(tree, h1);
1080 torture_comment(tctx, "(%s): testing stream renames\n", __location__);
1081 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1082 io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
1083 SEC_FILE_WRITE_ATTRIBUTE |
1084 SEC_RIGHTS_FILE_ALL;
1085 io.smb2.in.fname = snamer1;
1086 status = smb2_create(tree, mem_ctx, &(io.smb2));
1087 CHECK_STATUS(status, NT_STATUS_OK);
1088 h1 = io.smb2.out.file.handle;
1089 ret &= check_stream_list(tree,tctx, fname, 5, five1,
1090 io.smb2.out.file.handle);
1093 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1094 sinfo.rename_information.in.file.handle = h1;
1095 sinfo.rename_information.in.overwrite = true;
1096 sinfo.rename_information.in.root_fid = 0;
1097 sinfo.rename_information.in.new_name = ":AfterRename:$DATA";
1098 status = smb2_setinfo_file(tree, &sinfo);
1099 CHECK_STATUS(status, NT_STATUS_OK);
1101 ret &= check_stream_list(tree,tctx, fname, 5, five2,
1102 io.smb2.out.file.handle);
1105 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1106 sinfo.rename_information.in.file.handle = h1;
1107 sinfo.rename_information.in.overwrite = false;
1108 sinfo.rename_information.in.root_fid = 0;
1109 sinfo.rename_information.in.new_name = ":MStream Two:$DATA";
1110 status = smb2_setinfo_file(tree, &sinfo);
1111 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
1113 ret &= check_stream_list(tree,tctx, fname, 5, five2,
1114 io.smb2.out.file.handle);
1117 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1118 sinfo.rename_information.in.file.handle = h1;
1119 sinfo.rename_information.in.overwrite = true;
1120 sinfo.rename_information.in.root_fid = 0;
1121 sinfo.rename_information.in.new_name = ":MStream Two:$DATA";
1122 status = smb2_setinfo_file(tree, &sinfo);
1123 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1125 ret &= check_stream_list(tree,tctx, fname, 5, five2,
1126 io.smb2.out.file.handle);
1128 /* TODO: we need to test more rename combinations */
1131 smb2_util_close(tree, h1);
1132 status = smb2_util_unlink(tree, fname);
1133 smb2_deltree(tree, DNAME);
1134 talloc_free(mem_ctx);
1142 static bool test_stream_names2(struct torture_context *tctx,
1143 struct smb2_tree *tree)
1145 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1148 const char *fname = DNAME "\\stream_names2.txt";
1150 struct smb2_handle h = {{0}};
1151 struct smb2_handle h1 = {{0}};
1154 smb2_util_unlink(tree, fname);
1155 smb2_deltree(tree, DNAME);
1157 status = torture_smb2_testdir(tree, DNAME, &h);
1158 CHECK_STATUS(status, NT_STATUS_OK);
1160 torture_comment(tctx, "(%s) testing stream names\n", __location__);
1161 ZERO_STRUCT(io.smb2);
1162 io.smb2.in.create_flags = 0;
1163 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
1164 io.smb2.in.create_options = 0;
1165 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1166 io.smb2.in.share_access = 0;
1167 io.smb2.in.alloc_size = 0;
1168 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1169 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1170 io.smb2.in.security_flags = 0;
1171 io.smb2.in.fname = fname;
1172 status = smb2_create(tree, mem_ctx, &(io.smb2));
1173 CHECK_STATUS(status, NT_STATUS_OK);
1174 h1 = io.smb2.out.file.handle;
1176 for (i=0x01; i < 0x7F; i++) {
1177 char *path = talloc_asprintf(mem_ctx, "%s:Stream%c0x%02X:$DATA",
1185 expected = NT_STATUS_OBJECT_NAME_INVALID;
1188 expected = NT_STATUS_OBJECT_NAME_NOT_FOUND;
1193 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1194 io.smb2.in.fname = path;
1195 status = smb2_create(tree, mem_ctx, &(io.smb2));
1196 if (!NT_STATUS_EQUAL(status, expected)) {
1197 torture_comment(tctx,
1198 "(%s) %s:Stream%c0x%02X:$DATA%s => expected[%s]\n",
1199 __location__, fname, isprint(i)?(char)i:' ', i,
1200 isprint(i)?"":" (not printable)",
1201 nt_errstr(expected));
1203 CHECK_STATUS(status, expected);
1209 smb2_util_close(tree, h1);
1210 status = smb2_util_unlink(tree, fname);
1211 smb2_deltree(tree, DNAME);
1212 talloc_free(mem_ctx);
1217 #define CHECK_CALL_HANDLE(call, rightstatus) do { \
1218 sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
1219 sfinfo.generic.in.file.handle = h1; \
1220 status = smb2_setinfo_file(tree, &sfinfo); \
1221 if (!NT_STATUS_EQUAL(status, rightstatus)) { \
1222 torture_result(tctx, TORTURE_FAIL, \
1223 "(%s) %s - %s (should be %s)\n", \
1224 __location__, #call, \
1225 nt_errstr(status), nt_errstr(rightstatus)); \
1228 finfo1.generic.level = RAW_FILEINFO_ALL_INFORMATION; \
1229 finfo1.generic.in.file.handle = h1; \
1230 status2 = smb2_getinfo_file(tree, tctx, &finfo1); \
1231 if (!NT_STATUS_IS_OK(status2)) { \
1232 torture_result(tctx, TORTURE_FAIL, \
1233 "(%s) %s pathinfo - %s\n", \
1234 __location__, #call, nt_errstr(status)); \
1241 static bool test_stream_rename(struct torture_context *tctx,
1242 struct smb2_tree *tree)
1244 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1245 NTSTATUS status, status2;
1247 const char *fname = DNAME "\\stream_rename.txt";
1248 const char *sname1, *sname2;
1249 union smb_fileinfo finfo1;
1250 union smb_setfileinfo sfinfo;
1252 struct smb2_handle h = {{0}};
1253 struct smb2_handle h1 = {{0}};
1255 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
1256 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname,
1259 smb2_util_unlink(tree, fname);
1260 smb2_deltree(tree, DNAME);
1262 status = torture_smb2_testdir(tree, DNAME, &h);
1263 CHECK_STATUS(status, NT_STATUS_OK);
1265 torture_comment(tctx, "(%s) testing stream renames\n", __location__);
1266 ZERO_STRUCT(io.smb2);
1267 io.smb2.in.create_flags = 0;
1268 io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
1269 SEC_FILE_WRITE_ATTRIBUTE |
1270 SEC_RIGHTS_FILE_ALL;
1271 io.smb2.in.create_options = 0;
1272 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1273 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1274 NTCREATEX_SHARE_ACCESS_WRITE |
1275 NTCREATEX_SHARE_ACCESS_DELETE;
1276 io.smb2.in.alloc_size = 0;
1277 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1278 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1279 io.smb2.in.security_flags = 0;
1280 io.smb2.in.fname = sname1;
1282 /* Create two streams. */
1283 status = smb2_create(tree, mem_ctx, &(io.smb2));
1284 CHECK_STATUS(status, NT_STATUS_OK);
1285 h1 = io.smb2.out.file.handle;
1286 smb2_util_close(tree, h1);
1288 io.smb2.in.fname = sname2;
1289 status = smb2_create(tree, mem_ctx, &(io.smb2));
1290 CHECK_STATUS(status, NT_STATUS_OK);
1291 h1 = io.smb2.out.file.handle;
1293 smb2_util_close(tree, h1);
1296 * Open the second stream.
1299 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1300 status = smb2_create(tree, mem_ctx, &(io.smb2));
1301 CHECK_STATUS(status, NT_STATUS_OK);
1302 h1 = io.smb2.out.file.handle;
1305 * Now rename the second stream onto the first.
1308 ZERO_STRUCT(sfinfo);
1310 sfinfo.rename_information.in.overwrite = 1;
1311 sfinfo.rename_information.in.root_fid = 0;
1312 sfinfo.rename_information.in.new_name = ":Stream One";
1313 CHECK_CALL_HANDLE(RENAME_INFORMATION, NT_STATUS_OK);
1315 smb2_util_close(tree, h1);
1316 status = smb2_util_unlink(tree, fname);
1317 smb2_deltree(tree, DNAME);
1318 talloc_free(mem_ctx);
1323 static bool test_stream_rename2(struct torture_context *tctx,
1324 struct smb2_tree *tree)
1326 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1329 const char *fname1 = DNAME "\\stream_rename2.txt";
1330 const char *fname2 = DNAME "\\stream2_rename2.txt";
1331 const char *stream_name1 = ":Stream One:$DATA";
1332 const char *stream_name2 = ":Stream Two:$DATA";
1333 const char *stream_name_default = "::$DATA";
1337 struct smb2_handle h, h1;
1338 union smb_setfileinfo sinfo;
1343 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream One");
1344 sname2 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream Two");
1346 smb2_util_unlink(tree, fname1);
1347 smb2_util_unlink(tree, fname2);
1348 smb2_deltree(tree, DNAME);
1350 status = torture_smb2_testdir(tree, DNAME, &h);
1351 CHECK_STATUS(status, NT_STATUS_OK);
1353 ZERO_STRUCT(io.smb2);
1354 io.smb2.in.create_flags = 0;
1355 io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1356 SEC_FILE_WRITE_DATA |
1358 SEC_FILE_APPEND_DATA |
1359 SEC_STD_READ_CONTROL;
1360 io.smb2.in.create_options = 0;
1361 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1362 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1363 NTCREATEX_SHARE_ACCESS_WRITE |
1364 NTCREATEX_SHARE_ACCESS_DELETE;
1365 io.smb2.in.alloc_size = 0;
1366 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1367 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1368 io.smb2.in.security_flags = 0;
1369 io.smb2.in.fname = sname1;
1371 /* Open/create new stream. */
1372 status = smb2_create(tree, mem_ctx, &(io.smb2));
1373 CHECK_STATUS(status, NT_STATUS_OK);
1375 smb2_util_close(tree, io.smb2.out.file.handle);
1378 * Reopen the stream for SMB2 renames.
1380 io.smb2.in.fname = sname1;
1381 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1382 status = smb2_create(tree, mem_ctx, &(io.smb2));
1383 CHECK_STATUS(status, NT_STATUS_OK);
1384 h1 = io.smb2.out.file.handle;
1387 * Check SMB2 rename of a stream using :<stream>.
1389 torture_comment(tctx, "(%s) Checking SMB2 rename of a stream using "
1390 ":<stream>\n", __location__);
1392 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION_SMB2;
1393 sinfo.rename_information.in.file.handle = h1;
1394 sinfo.rename_information.in.overwrite = 1;
1395 sinfo.rename_information.in.root_fid = 0;
1396 sinfo.rename_information.in.new_name = stream_name1;
1397 status = smb2_setinfo_file(tree, &sinfo);
1398 CHECK_STATUS(status, NT_STATUS_OK);
1401 * Check SMB2 rename of an overwriting stream using :<stream>.
1403 torture_comment(tctx, "(%s) Checking SMB2 rename of an overwriting "
1404 "stream using :<stream>\n", __location__);
1406 /* Create second stream. */
1407 io.smb2.in.fname = sname2;
1408 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1409 status = smb2_create(tree, mem_ctx, &(io.smb2));
1410 CHECK_STATUS(status, NT_STATUS_OK);
1411 smb2_util_close(tree, io.smb2.out.file.handle);
1413 /* Rename the first stream onto the second. */
1414 sinfo.rename_information.in.file.handle = h1;
1415 sinfo.rename_information.in.new_name = stream_name2;
1416 status = smb2_setinfo_file(tree, &sinfo);
1417 CHECK_STATUS(status, NT_STATUS_OK);
1419 smb2_util_close(tree, h1);
1422 * Reopen the stream with the new name.
1424 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1425 io.smb2.in.fname = sname2;
1426 status = smb2_create(tree, mem_ctx, &(io.smb2));
1427 CHECK_STATUS(status, NT_STATUS_OK);
1428 h1 = io.smb2.out.file.handle;
1431 * Check SMB2 rename of a stream using <base>:<stream>.
1433 torture_comment(tctx, "(%s) Checking SMB2 rename of a stream using "
1434 "<base>:<stream>\n", __location__);
1435 sinfo.rename_information.in.file.handle = h1;
1436 sinfo.rename_information.in.new_name = sname1;
1437 status = smb2_setinfo_file(tree, &sinfo);
1438 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
1440 if (!torture_setting_bool(tctx, "samba4", false)) {
1442 * Check SMB2 rename to the default stream using :<stream>.
1444 torture_comment(tctx, "(%s) Checking SMB2 rename to default stream "
1445 "using :<stream>\n", __location__);
1446 sinfo.rename_information.in.file.handle = h1;
1447 sinfo.rename_information.in.new_name = stream_name_default;
1448 status = smb2_setinfo_file(tree, &sinfo);
1449 CHECK_STATUS(status, NT_STATUS_OK);
1452 smb2_util_close(tree, h1);
1455 smb2_util_close(tree, h1);
1456 status = smb2_util_unlink(tree, fname1);
1457 status = smb2_util_unlink(tree, fname2);
1458 smb2_deltree(tree, DNAME);
1459 talloc_free(mem_ctx);
1464 static bool create_file_with_stream(struct torture_context *tctx,
1465 struct smb2_tree *tree,
1466 TALLOC_CTX *mem_ctx,
1467 const char *base_fname,
1474 /* Create a file with a stream */
1475 ZERO_STRUCT(io.smb2);
1476 io.smb2.in.create_flags = 0;
1477 io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1478 SEC_FILE_WRITE_DATA |
1479 SEC_FILE_APPEND_DATA |
1480 SEC_STD_READ_CONTROL;
1481 io.smb2.in.create_options = 0;
1482 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1483 io.smb2.in.share_access = 0;
1484 io.smb2.in.alloc_size = 0;
1485 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1486 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1487 io.smb2.in.security_flags = 0;
1488 io.smb2.in.fname = stream;
1490 status = smb2_create(tree, mem_ctx, &(io.smb2));
1491 CHECK_STATUS(status, NT_STATUS_OK);
1494 smb2_util_close(tree, io.smb2.out.file.handle);
1499 /* Test how streams interact with create dispositions */
1500 static bool test_stream_create_disposition(struct torture_context *tctx,
1501 struct smb2_tree *tree)
1503 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1506 const char *fname = DNAME "\\stream_create_disp.txt";
1507 const char *stream = "Stream One:$DATA";
1508 const char *fname_stream;
1509 const char *default_stream_name = "::$DATA";
1510 const char *stream_list[2];
1512 struct smb2_handle h = {{0}};
1513 struct smb2_handle h1 = {{0}};
1515 /* clean slate .. */
1516 smb2_util_unlink(tree, fname);
1517 smb2_deltree(tree, fname);
1518 smb2_deltree(tree, DNAME);
1520 status = torture_smb2_testdir(tree, DNAME, &h);
1521 CHECK_STATUS(status, NT_STATUS_OK);
1523 fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream);
1525 stream_list[0] = talloc_asprintf(mem_ctx, ":%s", stream);
1526 stream_list[1] = default_stream_name;
1528 if (!create_file_with_stream(tctx, tree, mem_ctx, fname,
1533 /* Open the base file with OPEN */
1534 ZERO_STRUCT(io.smb2);
1535 io.smb2.in.create_flags = 0;
1536 io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1537 SEC_FILE_WRITE_DATA |
1538 SEC_FILE_APPEND_DATA |
1539 SEC_STD_READ_CONTROL;
1540 io.smb2.in.create_options = 0;
1541 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1542 io.smb2.in.share_access = 0;
1543 io.smb2.in.alloc_size = 0;
1544 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1545 io.smb2.in.security_flags = 0;
1546 io.smb2.in.fname = fname;
1549 * check create open: sanity check
1551 torture_comment(tctx, "(%s) Checking create disp: open\n",
1553 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1554 status = smb2_create(tree, mem_ctx, &(io.smb2));
1555 CHECK_STATUS(status, NT_STATUS_OK);
1556 if (!check_stream_list(tree, tctx, fname, 2, stream_list,
1557 io.smb2.out.file.handle)) {
1560 smb2_util_close(tree, io.smb2.out.file.handle);
1563 * check create overwrite
1565 torture_comment(tctx, "(%s) Checking create disp: overwrite\n",
1567 io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
1568 status = smb2_create(tree, mem_ctx, &(io.smb2));
1569 CHECK_STATUS(status, NT_STATUS_OK);
1570 if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name,
1571 io.smb2.out.file.handle)) {
1574 smb2_util_close(tree, io.smb2.out.file.handle);
1577 * check create overwrite_if
1579 torture_comment(tctx, "(%s) Checking create disp: overwrite_if\n",
1581 smb2_util_unlink(tree, fname);
1582 if (!create_file_with_stream(tctx, tree, mem_ctx, fname, fname_stream))
1585 io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1586 status = smb2_create(tree, mem_ctx, &(io.smb2));
1587 CHECK_STATUS(status, NT_STATUS_OK);
1588 if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name,
1589 io.smb2.out.file.handle)) {
1592 smb2_util_close(tree, io.smb2.out.file.handle);
1595 * check create supersede
1597 torture_comment(tctx, "(%s) Checking create disp: supersede\n",
1599 smb2_util_unlink(tree, fname);
1600 if (!create_file_with_stream(tctx, tree, mem_ctx, fname,
1605 io.smb2.in.create_disposition = NTCREATEX_DISP_SUPERSEDE;
1606 status = smb2_create(tree, mem_ctx, &(io.smb2));
1607 CHECK_STATUS(status, NT_STATUS_OK);
1608 if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name,
1609 io.smb2.out.file.handle)) {
1612 smb2_util_close(tree, io.smb2.out.file.handle);
1615 * check create overwrite_if on a stream.
1617 torture_comment(tctx, "(%s) Checking create disp: overwrite_if on "
1618 "stream\n", __location__);
1619 smb2_util_unlink(tree, fname);
1620 if (!create_file_with_stream(tctx, tree, mem_ctx, fname,
1625 io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1626 io.smb2.in.fname = fname_stream;
1627 status = smb2_create(tree, mem_ctx, &(io.smb2));
1628 CHECK_STATUS(status, NT_STATUS_OK);
1629 if (!check_stream_list(tree, tctx, fname, 2, stream_list,
1630 io.smb2.out.file.handle)) {
1633 smb2_util_close(tree, io.smb2.out.file.handle);
1635 smb2_util_close(tree, h1);
1636 smb2_util_unlink(tree, fname);
1637 smb2_deltree(tree, DNAME);
1638 talloc_free(mem_ctx);
1643 static bool open_stream(struct smb2_tree *tree,
1644 struct torture_context *mem_ctx,
1646 struct smb2_handle *h_out)
1651 ZERO_STRUCT(io.smb2);
1652 io.smb2.in.create_flags = 0;
1653 io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1654 SEC_FILE_WRITE_DATA |
1655 SEC_FILE_APPEND_DATA |
1656 SEC_STD_READ_CONTROL |
1657 SEC_FILE_WRITE_ATTRIBUTE;
1658 io.smb2.in.create_options = 0;
1659 io.smb2.in.file_attributes = 0;
1660 io.smb2.in.share_access = 0;
1661 io.smb2.in.alloc_size = 0;
1662 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1663 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1664 io.smb2.in.security_flags = 0;
1665 io.smb2.in.fname = fname;
1667 status = smb2_create(tree, mem_ctx, &(io.smb2));
1668 if (!NT_STATUS_IS_OK(status)) {
1671 *h_out = io.smb2.out.file.handle;
1676 /* Test the effect of setting attributes on a stream. */
1677 static bool test_stream_attributes(struct torture_context *tctx,
1678 struct smb2_tree *tree)
1680 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1684 const char *fname = DNAME "\\stream_attr.txt";
1685 const char *stream = "Stream One:$DATA";
1686 const char *fname_stream;
1687 struct smb2_handle h, h1;
1688 union smb_fileinfo finfo;
1689 union smb_setfileinfo sfinfo;
1690 time_t basetime = (time(NULL) - 86400) & ~1;
1695 torture_comment(tctx, "(%s) testing attribute setting on stream\n",
1698 /* clean slate .. */
1699 smb2_util_unlink(tree, fname);
1700 smb2_deltree(tree, fname);
1701 smb2_deltree(tree, DNAME);
1703 status = torture_smb2_testdir(tree, DNAME, &h);
1704 CHECK_STATUS(status, NT_STATUS_OK);
1706 fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream);
1708 /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */
1709 ret = create_file_with_stream(tctx, tree, mem_ctx, fname,
1715 ZERO_STRUCT(io.smb2);
1716 io.smb2.in.fname = fname;
1717 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1718 io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
1719 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1720 NTCREATEX_SHARE_ACCESS_WRITE |
1721 NTCREATEX_SHARE_ACCESS_DELETE;
1722 status = smb2_create(tree, mem_ctx, &(io.smb2));
1723 CHECK_STATUS(status, NT_STATUS_OK);
1726 finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1727 finfo.generic.in.file.handle = io.smb2.out.file.handle;
1728 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1729 CHECK_STATUS(status, NT_STATUS_OK);
1731 if (finfo.basic_info.out.attrib != FILE_ATTRIBUTE_ARCHIVE) {
1732 torture_comment(tctx, "(%s) Incorrect attrib %x - should be "
1733 "%x\n", __location__,
1734 (unsigned int)finfo.basic_info.out.attrib,
1735 (unsigned int)FILE_ATTRIBUTE_ARCHIVE);
1740 smb2_util_close(tree, io.smb2.out.file.handle);
1741 /* Now open the stream name. */
1743 if (!open_stream(tree, tctx, fname_stream, &h1)) {
1747 /* Change the time on the stream. */
1748 ZERO_STRUCT(sfinfo);
1749 unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime);
1750 sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1751 sfinfo.generic.in.file.handle = h1;
1752 status = smb2_setinfo_file(tree, &sfinfo);
1753 if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
1754 torture_comment(tctx, "(%s) %s - %s (should be %s)\n",
1755 __location__, "SETATTR",
1756 nt_errstr(status), nt_errstr(NT_STATUS_OK));
1761 smb2_util_close(tree, h1);
1763 ZERO_STRUCT(io.smb2);
1764 io.smb2.in.fname = fname;
1765 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1766 io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
1767 status = smb2_create(tree, mem_ctx, &(io.smb2));
1768 CHECK_STATUS(status, NT_STATUS_OK);
1769 h1 = io.smb2.out.file.handle;
1772 finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1773 finfo.generic.in.file.handle = h1;
1774 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1775 if (!NT_STATUS_IS_OK(status)) {
1776 torture_comment(tctx, "(%s) %s pathinfo - %s\n",
1777 __location__, "SETATTRE", nt_errstr(status));
1782 if (nt_time_to_unix(finfo.basic_info.out.write_time) != basetime) {
1783 torture_comment(tctx, "(%s) time incorrect.\n", __location__);
1787 smb2_util_close(tree, h1);
1789 if (!open_stream(tree, tctx, fname_stream, &h1)) {
1793 /* Changing attributes on stream */
1794 ZERO_STRUCT(sfinfo);
1795 sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY;
1797 sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1798 sfinfo.generic.in.file.handle = h1;
1799 status = smb2_setinfo_file(tree, &sfinfo);
1800 if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
1801 torture_comment(tctx, "(%s) %s - %s (should be %s)\n",
1802 __location__, "SETATTR",
1803 nt_errstr(status), nt_errstr(NT_STATUS_OK));
1808 smb2_util_close(tree, h1);
1810 ZERO_STRUCT(io.smb2);
1811 io.smb2.in.fname = fname;
1812 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1813 io.smb2.in.desired_access = SEC_FILE_READ_DATA;
1814 status = smb2_create(tree, mem_ctx, &(io.smb2));
1815 CHECK_STATUS(status, NT_STATUS_OK);
1816 h1 = io.smb2.out.file.handle;
1819 finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1820 finfo.generic.in.file.handle = h1;
1821 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1822 CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
1825 smb2_util_close(tree, h1);
1826 smb2_util_unlink(tree, fname);
1827 smb2_deltree(tree, DNAME);
1828 talloc_free(mem_ctx);
1835 basic testing of streams calls SMB2
1837 struct torture_suite *torture_smb2_streams_init(void)
1839 struct torture_suite *suite =
1840 torture_suite_create(talloc_autofree_context(), "streams");
1842 torture_suite_add_1smb2_test(suite, "dir", test_stream_dir);
1843 torture_suite_add_1smb2_test(suite, "io", test_stream_io);
1844 torture_suite_add_1smb2_test(suite, "sharemodes", test_stream_sharemodes);
1845 torture_suite_add_1smb2_test(suite, "names", test_stream_names);
1846 torture_suite_add_1smb2_test(suite, "names2", test_stream_names2);
1847 torture_suite_add_1smb2_test(suite, "rename", test_stream_rename);
1848 torture_suite_add_1smb2_test(suite, "rename2", test_stream_rename2);
1849 torture_suite_add_1smb2_test(suite, "create-disposition", test_stream_create_disposition);
1850 torture_suite_add_1smb2_test(suite, "attributes", test_stream_attributes);
1851 torture_suite_add_1smb2_test(suite, "delete", test_stream_delete);
1852 torture_suite_add_1smb2_test(suite, "zero-byte", test_zero_byte_stream);
1854 suite->description = talloc_strdup(suite, "SMB2-STREAM tests");