s4:torture: avoid reporting error on failure of smb2.streams tests
[Samba/gebeck_regimport.git] / source4 / torture / smb2 / streams.c
blob8e7fb220c811eab97ac2d5b9cd3d5b76ce81e587
1 /*
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/>.
22 #include "includes.h"
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)); \
40 ret = false; \
41 goto done; \
42 }} while (0)
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); \
49 ret = false; \
50 }} while (0)
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); \
58 ret = false; \
59 }} while (0)
61 #define CHECK_STR(v, correct) do { \
62 bool ok; \
63 if ((v) && !(correct)) { \
64 ok = false; \
65 } else if (!(v) && (correct)) { \
66 ok = false; \
67 } else if (!(v) && !(correct)) { \
68 ok = true; \
69 } else if (strcmp((v), (correct)) == 0) { \
70 ok = true; \
71 } else { \
72 ok = false; \
73 } \
74 if (!ok) { \
75 torture_result(tctx, TORTURE_FAIL, \
76 "(%s) Incorrect value %s='%s' - " \
77 "should be '%s'\n", \
78 __location__, #v, (v)?(v):"NULL", \
79 (correct)?(correct):"NULL"); \
80 ret = false; \
81 }} while (0)
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,
95 const char *location,
96 TALLOC_CTX *mem_ctx,
97 const char *fname,
98 const char *sname,
99 const char *value)
101 struct smb2_handle handle;
102 struct smb2_create create;
103 struct smb2_read r;
104 NTSTATUS status;
105 const char *full_name;
107 full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname);
109 ZERO_STRUCT(create);
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)) {
117 if (value == NULL) {
118 return true;
119 } else {
120 torture_comment(mem_ctx, "Unable to open stream %s\n",
121 full_name);
122 return false;
126 handle = create.out.file.handle;
127 if (value == NULL) {
128 return true;
132 ZERO_STRUCT(r);
133 r.in.file.handle = handle;
134 r.in.length = strlen(value)+11;
135 r.in.offset = 0;
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);
142 return false;
145 if (memcmp(r.out.data.data, value, strlen(value)) != 0) {
146 torture_comment(mem_ctx, "(%s) Bad data in stream\n", location);
147 return false;
150 smb2_util_close(tree, handle);
151 return true;
154 static bool check_stream_list(struct smb2_tree *tree,
155 struct torture_context *tctx,
156 const char *fname,
157 int num_exp,
158 const char **exp,
159 struct smb2_handle h)
161 union smb_fileinfo finfo;
162 NTSTATUS status;
163 int i;
164 TALLOC_CTX *tmp_ctx = talloc_new(tctx);
165 char **exp_sort;
166 struct stream_struct *stream_sort;
167 bool ret = false;
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));
176 goto fail;
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);
182 goto fail;
185 if (num_exp == 0) {
186 ret = true;
187 goto fail;
190 exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
192 if (exp_sort == NULL) {
193 goto fail;
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) {
203 goto fail;
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);
214 goto fail;
218 ret = true;
219 fail:
220 talloc_free(tmp_ctx);
221 return ret;
225 static bool test_stream_dir(struct torture_context *tctx,
226 struct smb2_tree *tree)
228 TALLOC_CTX *mem_ctx = talloc_new(tctx);
229 NTSTATUS status;
230 union smb_open io;
231 const char *fname = DNAME "\\stream.txt";
232 const char *sname1;
233 bool ret = true;
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-existant directory stream\n",
248 __location__);
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",
279 __location__);
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",
295 __location__);
296 ret &= check_stream_list(tree, mem_ctx, DNAME, 0, NULL, h);
297 done:
298 smb2_util_unlink(tree, fname);
299 smb2_deltree(tree, DNAME);
300 talloc_free(mem_ctx);
302 return ret;
305 static bool test_stream_io(struct torture_context *tctx,
306 struct smb2_tree *tree)
308 TALLOC_CTX *mem_ctx = talloc_new(tctx);
309 NTSTATUS status;
310 union smb_open io;
311 const char *fname = DNAME "\\stream.txt";
312 const char *sname1, *sname2;
313 bool ret = true;
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" };
321 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
322 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname,
323 "Second Stream");
325 smb2_util_unlink(tree, fname);
326 smb2_deltree(tree, DNAME);
328 status = torture_smb2_testdir(tree, DNAME, &h);
329 CHECK_STATUS(status, NT_STATUS_OK);
331 torture_comment(tctx, "(%s) creating a stream on a non-existant file\n",
332 __location__);
334 ZERO_STRUCT(io.smb2);
335 io.smb2.in.create_flags = 0;
336 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
337 io.smb2.in.create_options = 0;
338 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
339 io.smb2.in.share_access = 0;
340 io.smb2.in.alloc_size = 0;
341 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
342 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
343 io.smb2.in.security_flags = 0;
344 io.smb2.in.fname = sname1;
345 status = smb2_create(tree, mem_ctx, &(io.smb2));
346 CHECK_STATUS(status, NT_STATUS_OK);
347 h2 = io.smb2.out.file.handle;
349 ret &= check_stream(tree, __location__, mem_ctx, fname,
350 "Stream One", NULL);
352 torture_comment(tctx, "(%s) check that open of base file is allowed\n", __location__);
353 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
354 io.smb2.in.fname = fname;
355 status = smb2_create(tree, mem_ctx, &(io.smb2));
356 CHECK_STATUS(status, NT_STATUS_OK);
357 smb2_util_close(tree, io.smb2.out.file.handle);
359 torture_comment(tctx, "(%s) writing to stream\n", __location__);
360 status = smb2_util_write(tree, h2, "test data", 0, 9);
361 CHECK_STATUS(status, NT_STATUS_OK);
363 smb2_util_close(tree, h2);
365 ret &= check_stream(tree, __location__, mem_ctx, fname,
366 "Stream One", "test data");
368 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
369 io.smb2.in.fname = sname1;
370 status = smb2_create(tree, mem_ctx, &(io.smb2));
371 CHECK_STATUS(status, NT_STATUS_OK);
372 h2 = io.smb2.out.file.handle;
374 torture_comment(tctx, "(%s) modifying stream\n", __location__);
375 status = smb2_util_write(tree, h2, "MORE DATA ", 5, 10);
376 CHECK_STATUS(status, NT_STATUS_OK);
378 smb2_util_close(tree, h2);
380 ret &= check_stream(tree, __location__, mem_ctx, fname,
381 "Stream One:$FOO", NULL);
383 torture_comment(tctx, "(%s) creating a stream2 on a existing file\n",
384 __location__);
385 io.smb2.in.fname = sname2;
386 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
387 status = smb2_create(tree, mem_ctx, &(io.smb2));
388 CHECK_STATUS(status, NT_STATUS_OK);
389 h2 = io.smb2.out.file.handle;
391 torture_comment(tctx, "(%s) modifying stream\n", __location__);
392 status= smb2_util_write(tree, h2, "SECOND STREAM", 0, 13);
393 CHECK_STATUS(status, NT_STATUS_OK);
394 smb2_util_close(tree, h2);
396 ret &= check_stream(tree, __location__, mem_ctx, fname,
397 "Stream One", "test MORE DATA ");
399 ret &= check_stream(tree, __location__, mem_ctx, fname,
400 "Stream One:$DATA", "test MORE DATA ");
402 ret &= check_stream(tree, __location__, mem_ctx, fname,
403 "Stream One:", NULL);
405 ret &= check_stream(tree, __location__, mem_ctx, fname,
406 "Second Stream", "SECOND STREAM");
408 ret &= check_stream(tree, __location__, mem_ctx, fname,
409 "SECOND STREAM:$DATA", "SECOND STREAM");
410 ret &= check_stream(tree, __location__, mem_ctx, fname,
411 "Second Stream:$DATA", "SECOND STREAM");
413 ret &= check_stream(tree, __location__, mem_ctx, fname,
414 "Second Stream:", NULL);
416 ret &= check_stream(tree, __location__, mem_ctx, fname,
417 "Second Stream:$FOO", NULL);
419 io.smb2.in.fname = sname2;
420 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
421 status = smb2_create(tree, mem_ctx, &(io.smb2));
422 CHECK_STATUS(status, NT_STATUS_OK);
423 h2 = io.smb2.out.file.handle;
424 check_stream_list(tree, tctx, fname, 3, three, h2);
426 smb2_util_close(tree, h2);
428 torture_comment(tctx, "(%s) deleting stream\n", __location__);
429 status = smb2_util_unlink(tree, sname1);
430 CHECK_STATUS(status, NT_STATUS_OK);
432 io.smb2.in.fname = sname2;
433 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
434 status = smb2_create(tree, mem_ctx, &(io.smb2));
435 CHECK_STATUS(status, NT_STATUS_OK);
436 h2 = io.smb2.out.file.handle;
437 check_stream_list(tree, tctx, fname, 2, two, h2);
438 smb2_util_close(tree, h2);
440 torture_comment(tctx, "(%s) delete a stream via delete-on-close\n",
441 __location__);
442 io.smb2.in.fname = sname2;
443 io.smb2.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
444 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
445 io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
446 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
448 status = smb2_create(tree, mem_ctx, &(io.smb2));
449 CHECK_STATUS(status, NT_STATUS_OK);
450 h2 = io.smb2.out.file.handle;
452 smb2_util_close(tree, h2);
453 status = smb2_util_unlink(tree, sname2);
454 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
456 io.smb2.in.fname = fname;
457 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
458 status = smb2_create(tree, mem_ctx, &(io.smb2));
459 h2 = io.smb2.out.file.handle;
460 check_stream_list(tree,tctx, fname, 1, one, h2);
461 smb2_util_close(tree, h2);
463 if (!torture_setting_bool(tctx, "samba4", false)) {
464 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
465 io.smb2.in.fname = sname1;
466 status = smb2_create(tree, mem_ctx, &(io.smb2));
467 CHECK_STATUS(status, NT_STATUS_OK);
468 smb2_util_close(tree, io.ntcreatex.out.file.handle);
469 io.smb2.in.fname = sname2;
470 status = smb2_create(tree, mem_ctx, &(io.smb2));
471 CHECK_STATUS(status, NT_STATUS_OK);
472 smb2_util_close(tree, io.ntcreatex.out.file.handle);
473 torture_comment(tctx, "(%s) deleting file\n", __location__);
474 status = smb2_util_unlink(tree, fname);
475 CHECK_STATUS(status, NT_STATUS_OK);
479 done:
480 smb2_util_close(tree, h2);
481 smb2_deltree(tree, DNAME);
482 talloc_free(mem_ctx);
484 return ret;
488 test stream sharemodes
490 static bool test_stream_sharemodes(struct torture_context *tctx,
491 struct smb2_tree *tree)
493 TALLOC_CTX *mem_ctx = talloc_new(tctx);
494 NTSTATUS status;
495 union smb_open io;
496 const char *fname = DNAME "\\stream_share.txt";
497 const char *sname1, *sname2;
498 bool ret = true;
499 struct smb2_handle h, h1, h2;
501 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
502 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname,
503 "Second Stream");
505 smb2_util_unlink(tree, fname);
506 smb2_deltree(tree, DNAME);
508 status = torture_smb2_testdir(tree, DNAME, &h);
509 CHECK_STATUS(status, NT_STATUS_OK);
511 torture_comment(tctx, "(%s) Testing stream share mode conflicts\n",
512 __location__);
513 ZERO_STRUCT(io.smb2);
514 io.generic.level = RAW_OPEN_SMB2;
515 io.smb2.in.create_flags = 0;
516 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
517 io.smb2.in.create_options = 0;
518 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
519 io.smb2.in.share_access = 0;
520 io.smb2.in.alloc_size = 0;
521 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
522 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
523 io.smb2.in.security_flags = 0;
524 io.smb2.in.fname = sname1;
526 status = smb2_create(tree, mem_ctx, &(io.smb2));
527 CHECK_STATUS(status, NT_STATUS_OK);
528 h1 = io.smb2.out.file.handle;
531 * A different stream does not give a sharing violation
534 io.smb2.in.fname = sname2;
535 status = smb2_create(tree, mem_ctx, &(io.smb2));
536 CHECK_STATUS(status, NT_STATUS_OK);
537 h2 = io.smb2.out.file.handle;
540 * ... whereas the same stream does with unchanged access/share_access
541 * flags
544 io.smb2.in.fname = sname1;
545 io.smb2.in.create_disposition = 0;
546 status = smb2_create(tree, mem_ctx, &(io.smb2));
547 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
549 io.smb2.in.fname = sname2;
550 status = smb2_create(tree, mem_ctx, &(io.smb2));
551 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
553 done:
554 smb2_util_close(tree, h1);
555 smb2_util_close(tree, h2);
556 status = smb2_util_unlink(tree, fname);
557 smb2_deltree(tree, DNAME);
558 talloc_free(mem_ctx);
560 return ret;
564 * Test FILE_SHARE_DELETE on streams
566 * A stream opened with !FILE_SHARE_DELETE prevents the main file to be opened
567 * with SEC_STD_DELETE.
569 * The main file opened with !FILE_SHARE_DELETE does *not* prevent a stream to
570 * be opened with SEC_STD_DELETE.
572 * A stream held open with FILE_SHARE_DELETE allows the file to be
573 * deleted. After the main file is deleted, access to the open file descriptor
574 * still works, but all name-based access to both the main file as well as the
575 * stream is denied with DELETE pending.
577 * This means, an open of the main file with SEC_STD_DELETE should walk all
578 * streams and also open them with SEC_STD_DELETE. If any of these opens gives
579 * SHARING_VIOLATION, the main open fails.
581 * Closing the main file after delete_on_close has been set does not really
582 * unlink it but leaves the corresponding share mode entry with
583 * delete_on_close being set around until all streams are closed.
585 * Opening a stream must also look at the main file's share mode entry, look
586 * at the delete_on_close bit and potentially return DELETE_PENDING.
589 static bool test_stream_delete(struct torture_context *tctx,
590 struct smb2_tree *tree)
592 TALLOC_CTX *mem_ctx = talloc_new(tctx);
593 NTSTATUS status;
594 union smb_open io;
595 const char *fname = DNAME "\\stream_delete.txt";
596 const char *sname1;
597 bool ret = true;
598 struct smb2_handle h, h1;
599 struct smb2_read r;
601 if (torture_setting_bool(tctx, "samba4", false)) {
602 torture_comment(tctx, "Skipping test as samba4 is enabled\n");
603 goto done;
606 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
608 /* clean slate .. */
609 smb2_util_unlink(tree, fname);
610 smb2_deltree(tree, fname);
611 smb2_deltree(tree, DNAME);
613 status = torture_smb2_testdir(tree, DNAME, &h);
614 CHECK_STATUS(status, NT_STATUS_OK);
616 torture_comment(tctx, "(%s) opening non-existant file stream\n",
617 __location__);
618 ZERO_STRUCT(io.smb2);
619 io.smb2.in.create_flags = 0;
620 io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
621 io.smb2.in.create_options = 0;
622 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
623 io.smb2.in.share_access = 0;
624 io.smb2.in.alloc_size = 0;
625 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
626 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
627 io.smb2.in.security_flags = 0;
628 io.smb2.in.fname = sname1;
630 status = smb2_create(tree, mem_ctx, &(io.smb2));
631 CHECK_STATUS(status, NT_STATUS_OK);
632 h1 = io.smb2.out.file.handle;
634 status = smb2_util_write(tree, h1, "test data", 0, 9);
635 CHECK_STATUS(status, NT_STATUS_OK);
638 * One stream opened without FILE_SHARE_DELETE prevents the main file
639 * to be deleted or even opened with DELETE access
642 status = smb2_util_unlink(tree, fname);
643 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
645 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
646 io.smb2.in.fname = fname;
647 io.smb2.in.desired_access = SEC_STD_DELETE;
648 status = smb2_create(tree, mem_ctx, &(io.smb2));
649 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
651 smb2_util_close(tree, h1);
654 * ... but unlink works if a stream is opened with FILE_SHARE_DELETE
657 io.smb2.in.fname = sname1;
658 io.smb2.in.desired_access = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
659 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
660 NTCREATEX_SHARE_ACCESS_READ |
661 NTCREATEX_SHARE_ACCESS_WRITE;
662 status = smb2_create(tree, mem_ctx, &(io.smb2));
663 CHECK_STATUS(status, NT_STATUS_OK);
664 h1 = io.smb2.out.file.handle;
666 status = smb2_util_unlink(tree, fname);
667 CHECK_STATUS(status, NT_STATUS_OK);
670 * file access still works on the stream while the main file is closed
672 ZERO_STRUCT(r);
673 r.in.file.handle = h1;
674 r.in.length = 9;
675 r.in.offset = 0;
677 status = smb2_read(tree, tree, &r);
678 CHECK_STATUS(status, NT_STATUS_OK);
681 * name-based access to both the main file and the stream does not
682 * work anymore but gives DELETE_PENDING
685 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
686 io.smb2.in.fname = fname;
687 status = smb2_create(tree, mem_ctx, &(io.smb2));
688 CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
691 * older S3 doesn't do this
694 io.smb2.in.fname = sname1;
695 status = smb2_create(tree, mem_ctx, &(io.smb2));
696 CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
698 smb2_util_close(tree, h1);
701 * After closing the stream the file is really gone.
704 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
705 io.smb2.in.fname = fname;
706 status = smb2_create(tree, mem_ctx, &(io.smb2));
707 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
709 done:
710 smb2_util_close(tree, h1);
711 smb2_util_unlink(tree, fname);
712 smb2_deltree(tree, DNAME);
713 talloc_free(mem_ctx);
715 return ret;
719 test stream names
721 static bool test_stream_names(struct torture_context *tctx,
722 struct smb2_tree *tree)
724 TALLOC_CTX *mem_ctx = talloc_new(tctx);
725 NTSTATUS status;
726 union smb_open io;
727 union smb_fileinfo finfo;
728 union smb_fileinfo stinfo;
729 union smb_setfileinfo sinfo;
730 const char *fname = DNAME "\\stream_names.txt";
731 const char *sname1, *sname1b, *sname1c, *sname1d;
732 const char *sname2, *snamew, *snamew2;
733 const char *snamer1, *snamer2;
734 bool ret = true;
735 struct smb2_handle h, h1, h2, h3;
736 int i;
737 const char *four[4] = {
738 "::$DATA",
739 ":\x05Stream\n One:$DATA",
740 ":MStream Two:$DATA",
741 ":?Stream*:$DATA"
743 const char *five1[5] = {
744 "::$DATA",
745 ":\x05Stream\n One:$DATA",
746 ":BeforeRename:$DATA",
747 ":MStream Two:$DATA",
748 ":?Stream*:$DATA"
750 const char *five2[5] = {
751 "::$DATA",
752 ":\x05Stream\n One:$DATA",
753 ":AfterRename:$DATA",
754 ":MStream Two:$DATA",
755 ":?Stream*:$DATA"
758 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "\x05Stream\n One");
759 sname1b = talloc_asprintf(mem_ctx, "%s:", sname1);
760 sname1c = talloc_asprintf(mem_ctx, "%s:$FOO", sname1);
761 sname1d = talloc_asprintf(mem_ctx, "%s:?D*a", sname1);
762 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "MStream Two");
763 snamew = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "?Stream*");
764 snamew2 = talloc_asprintf(mem_ctx, "%s\\stream*:%s:$DATA", DNAME,
765 "?Stream*");
766 snamer1 = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname,
767 "BeforeRename");
768 snamer2 = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "AfterRename");
770 /* clean slate ...*/
771 smb2_util_unlink(tree, fname);
772 smb2_deltree(tree, fname);
773 smb2_deltree(tree, DNAME);
775 status = torture_smb2_testdir(tree, DNAME, &h);
776 CHECK_STATUS(status, NT_STATUS_OK);
778 torture_comment(tctx, "(%s) testing stream names\n", __location__);
779 ZERO_STRUCT(io.smb2);
780 io.smb2.in.create_flags = 0;
781 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
782 io.smb2.in.create_options = 0;
783 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
784 io.smb2.in.share_access = 0;
785 io.smb2.in.alloc_size = 0;
786 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
787 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
788 io.smb2.in.security_flags = 0;
789 io.smb2.in.fname = sname1;
791 status = smb2_create(tree, mem_ctx, &(io.smb2));
792 CHECK_STATUS(status, NT_STATUS_OK);
793 h1 = io.smb2.out.file.handle;
796 * A different stream does not give a sharing violation
799 io.smb2.in.fname = sname2;
800 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
801 status = smb2_create(tree, mem_ctx, &(io.smb2));
802 CHECK_STATUS(status, NT_STATUS_OK);
803 h2 = io.smb2.out.file.handle;
806 * ... whereas the same stream does with unchanged access/share_access
807 * flags
810 io.smb2.in.fname = sname1;
811 io.smb2.in.create_disposition = NTCREATEX_DISP_SUPERSEDE;
812 status = smb2_create(tree, mem_ctx, &(io.smb2));
813 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
815 io.smb2.in.fname = sname1b;
816 status = smb2_create(tree, mem_ctx, &(io.smb2));
817 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
819 io.smb2.in.fname = sname1c;
820 status = smb2_create(tree, mem_ctx, &(io.smb2));
821 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
822 /* w2k returns INVALID_PARAMETER */
823 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
824 } else {
825 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
828 io.smb2.in.fname = sname1d;
829 status = smb2_create(tree, mem_ctx, &(io.smb2));
830 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
831 /* w2k returns INVALID_PARAMETER */
832 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
833 } else {
834 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
837 io.smb2.in.fname = sname2;
838 status = smb2_create(tree, mem_ctx, &(io.smb2));
839 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
841 io.smb2.in.fname = snamew;
842 status = smb2_create(tree, mem_ctx, &(io.smb2));
843 CHECK_STATUS(status, NT_STATUS_OK);
844 h3 = io.smb2.out.file.handle;
846 io.smb2.in.fname = snamew2;
847 status = smb2_create(tree, mem_ctx, &(io.smb2));
848 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
850 io.smb2.in.fname = fname;
851 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
852 status = smb2_create(tree, mem_ctx, &(io.smb2));
853 CHECK_STATUS(status, NT_STATUS_OK);
854 ret &= check_stream_list(tree, tctx, fname, 4, four,
855 io.smb2.out.file.handle);
857 smb2_util_close(tree, h1);
858 smb2_util_close(tree, h2);
859 smb2_util_close(tree, h3);
861 if (torture_setting_bool(tctx, "samba4", true)) {
862 goto done;
865 finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
866 finfo.generic.in.file.handle = io.smb2.out.file.handle;
867 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
868 CHECK_STATUS(status, NT_STATUS_OK);
869 ret &= check_stream_list(tree, tctx, fname, 4, four,
870 io.smb2.out.file.handle);
872 for (i=0; i < 4; i++) {
873 NTTIME write_time;
874 uint64_t stream_size;
875 char *path = talloc_asprintf(tctx, "%s%s",
876 fname, four[i]);
878 char *rpath = talloc_strdup(path, path);
879 char *p = strrchr(rpath, ':');
880 /* eat :$DATA */
881 *p = 0;
882 p--;
883 if (*p == ':') {
884 /* eat ::$DATA */
885 *p = 0;
887 torture_comment(tctx, "(%s): i[%u][%s]\n",
888 __location__, i,path);
889 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
890 io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
891 SEC_FILE_WRITE_ATTRIBUTE |
892 SEC_RIGHTS_FILE_ALL;
893 io.smb2.in.fname = path;
894 status = smb2_create(tree, mem_ctx, &(io.smb2));
895 CHECK_STATUS(status, NT_STATUS_OK);
896 h1 = io.smb2.out.file.handle;
898 finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
899 finfo.generic.in.file.path = fname;
900 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
901 CHECK_STATUS(status, NT_STATUS_OK);
903 stinfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
904 stinfo.generic.in.file.handle = h1;
905 status = smb2_getinfo_file(tree, mem_ctx, &stinfo);
906 CHECK_STATUS(status, NT_STATUS_OK);
907 if (!torture_setting_bool(tctx, "samba3", false)) {
908 CHECK_NTTIME(stinfo.all_info.out.create_time,
909 finfo.all_info.out.create_time);
910 CHECK_NTTIME(stinfo.all_info.out.access_time,
911 finfo.all_info.out.access_time);
912 CHECK_NTTIME(stinfo.all_info.out.write_time,
913 finfo.all_info.out.write_time);
914 CHECK_NTTIME(stinfo.all_info.out.change_time,
915 finfo.all_info.out.change_time);
917 CHECK_VALUE(stinfo.all_info.out.attrib,
918 finfo.all_info.out.attrib);
919 CHECK_VALUE(stinfo.all_info.out.size,
920 finfo.all_info.out.size);
921 CHECK_VALUE(stinfo.all_info.out.delete_pending,
922 finfo.all_info.out.delete_pending);
923 CHECK_VALUE(stinfo.all_info.out.directory,
924 finfo.all_info.out.directory);
925 CHECK_VALUE(stinfo.all_info.out.ea_size,
926 finfo.all_info.out.ea_size);
928 stinfo.generic.level = RAW_FILEINFO_NAME_INFORMATION;
929 stinfo.generic.in.file.handle = h1;
930 status = smb2_getinfo_file(tree, mem_ctx, &stinfo);
931 CHECK_STATUS(status, NT_STATUS_OK);
932 if (!torture_setting_bool(tctx, "samba3", false)) {
933 CHECK_STR(rpath, stinfo.name_info.out.fname.s);
936 write_time = finfo.all_info.out.write_time;
937 write_time += i*1000000;
938 write_time /= 1000000;
939 write_time *= 1000000;
941 ZERO_STRUCT(sinfo);
942 sinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
943 sinfo.basic_info.in.file.handle = h1;
944 sinfo.basic_info.in.write_time = write_time;
945 sinfo.basic_info.in.attrib = stinfo.all_info.out.attrib;
946 status = smb2_setinfo_file(tree, &sinfo);
947 CHECK_STATUS(status, NT_STATUS_OK);
949 stream_size = i*8192;
951 ZERO_STRUCT(sinfo);
952 sinfo.end_of_file_info.level =
953 RAW_SFILEINFO_END_OF_FILE_INFORMATION;
954 sinfo.end_of_file_info.in.file.handle = h1;
955 sinfo.end_of_file_info.in.size = stream_size;
956 status = smb2_setinfo_file(tree, &sinfo);
957 CHECK_STATUS(status, NT_STATUS_OK);
959 stinfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
960 stinfo.generic.in.file.handle = h1;
961 status = smb2_getinfo_file(tree, mem_ctx, &stinfo);
962 CHECK_STATUS(status, NT_STATUS_OK);
963 if (!torture_setting_bool(tctx, "samba3", false)) {
964 CHECK_NTTIME(stinfo.all_info.out.write_time,
965 write_time);
966 CHECK_VALUE(stinfo.all_info.out.attrib,
967 finfo.all_info.out.attrib);
969 CHECK_VALUE(stinfo.all_info.out.size,
970 stream_size);
971 CHECK_VALUE(stinfo.all_info.out.delete_pending,
972 finfo.all_info.out.delete_pending);
973 CHECK_VALUE(stinfo.all_info.out.directory,
974 finfo.all_info.out.directory);
975 CHECK_VALUE(stinfo.all_info.out.ea_size,
976 finfo.all_info.out.ea_size);
978 io.smb2.in.fname = fname;
979 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
980 status = smb2_create(tree, mem_ctx, &(io.smb2));
981 CHECK_STATUS(status, NT_STATUS_OK);
982 ret &= check_stream_list(tree, tctx, fname, 4, four,
983 io.smb2.out.file.handle);
985 smb2_util_close(tree, h1);
986 talloc_free(path);
989 torture_comment(tctx, "(%s): testing stream renames\n", __location__);
990 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
991 io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
992 SEC_FILE_WRITE_ATTRIBUTE |
993 SEC_RIGHTS_FILE_ALL;
994 io.smb2.in.fname = snamer1;
995 status = smb2_create(tree, mem_ctx, &(io.smb2));
996 CHECK_STATUS(status, NT_STATUS_OK);
997 h1 = io.smb2.out.file.handle;
998 ret &= check_stream_list(tree,tctx, fname, 5, five1,
999 io.smb2.out.file.handle);
1001 ZERO_STRUCT(sinfo);
1002 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1003 sinfo.rename_information.in.file.handle = h1;
1004 sinfo.rename_information.in.overwrite = true;
1005 sinfo.rename_information.in.root_fid = 0;
1006 sinfo.rename_information.in.new_name = ":AfterRename:$DATA";
1007 status = smb2_setinfo_file(tree, &sinfo);
1008 CHECK_STATUS(status, NT_STATUS_OK);
1010 ret &= check_stream_list(tree,tctx, fname, 5, five2,
1011 io.smb2.out.file.handle);
1013 ZERO_STRUCT(sinfo);
1014 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1015 sinfo.rename_information.in.file.handle = h1;
1016 sinfo.rename_information.in.overwrite = false;
1017 sinfo.rename_information.in.root_fid = 0;
1018 sinfo.rename_information.in.new_name = ":MStream Two:$DATA";
1019 status = smb2_setinfo_file(tree, &sinfo);
1020 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
1022 ret &= check_stream_list(tree,tctx, fname, 5, five2,
1023 io.smb2.out.file.handle);
1025 ZERO_STRUCT(sinfo);
1026 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1027 sinfo.rename_information.in.file.handle = h1;
1028 sinfo.rename_information.in.overwrite = true;
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_INVALID_PARAMETER);
1034 ret &= check_stream_list(tree,tctx, fname, 5, five2,
1035 io.smb2.out.file.handle);
1037 /* TODO: we need to test more rename combinations */
1039 done:
1040 smb2_util_close(tree, h1);
1041 status = smb2_util_unlink(tree, fname);
1042 smb2_deltree(tree, DNAME);
1043 talloc_free(mem_ctx);
1045 return ret;
1049 test stream names
1051 static bool test_stream_names2(struct torture_context *tctx,
1052 struct smb2_tree *tree)
1054 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1055 NTSTATUS status;
1056 union smb_open io;
1057 const char *fname = DNAME "\\stream_names2.txt";
1058 bool ret = true;
1059 struct smb2_handle h, h1;
1060 uint8_t i;
1062 smb2_util_unlink(tree, fname);
1063 smb2_deltree(tree, DNAME);
1065 status = torture_smb2_testdir(tree, DNAME, &h);
1066 CHECK_STATUS(status, NT_STATUS_OK);
1068 torture_comment(tctx, "(%s) testing stream names\n", __location__);
1069 ZERO_STRUCT(io.smb2);
1070 io.smb2.in.create_flags = 0;
1071 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
1072 io.smb2.in.create_options = 0;
1073 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1074 io.smb2.in.share_access = 0;
1075 io.smb2.in.alloc_size = 0;
1076 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1077 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1078 io.smb2.in.security_flags = 0;
1079 io.smb2.in.fname = fname;
1080 status = smb2_create(tree, mem_ctx, &(io.smb2));
1081 CHECK_STATUS(status, NT_STATUS_OK);
1082 h1 = io.smb2.out.file.handle;
1084 for (i=0x01; i < 0x7F; i++) {
1085 char *path = talloc_asprintf(mem_ctx, "%s:Stream%c0x%02X:$DATA",
1086 fname, i, i);
1087 NTSTATUS expected;
1089 switch (i) {
1090 case '/':/*0x2F*/
1091 case ':':/*0x3A*/
1092 case '\\':/*0x5C*/
1093 expected = NT_STATUS_OBJECT_NAME_INVALID;
1094 break;
1095 default:
1096 expected = NT_STATUS_OBJECT_NAME_NOT_FOUND;
1097 break;
1101 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1102 io.smb2.in.fname = path;
1103 status = smb2_create(tree, mem_ctx, &(io.smb2));
1104 if (!NT_STATUS_EQUAL(status, expected)) {
1105 torture_comment(tctx,
1106 "(%s) %s:Stream%c0x%02X:$DATA%s => expected[%s]\n",
1107 __location__, fname, isprint(i)?(char)i:' ', i,
1108 isprint(i)?"":" (not printable)",
1109 nt_errstr(expected));
1111 CHECK_STATUS(status, expected);
1113 talloc_free(path);
1116 done:
1117 smb2_util_close(tree, h1);
1118 status = smb2_util_unlink(tree, fname);
1119 smb2_deltree(tree, DNAME);
1120 talloc_free(mem_ctx);
1122 return ret;
1125 #define CHECK_CALL_HANDLE(call, rightstatus) do { \
1126 check_handle = true; \
1127 call_name = #call; \
1128 sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
1129 sfinfo.generic.in.file.handle = h1; \
1130 status = smb2_setinfo_file(tree, &sfinfo); \
1131 if (!NT_STATUS_EQUAL(status, rightstatus)) { \
1132 torture_result(tctx, TORTURE_FAIL, \
1133 "(%s) %s - %s (should be %s)\n", \
1134 __location__, #call, \
1135 nt_errstr(status), nt_errstr(rightstatus)); \
1136 ret = false; \
1138 finfo1.generic.level = RAW_FILEINFO_ALL_INFORMATION; \
1139 finfo1.generic.in.file.handle = h1; \
1140 status2 = smb2_getinfo_file(tree, tctx, &finfo1); \
1141 if (!NT_STATUS_IS_OK(status2)) { \
1142 torture_result(tctx, TORTURE_FAIL, \
1143 "(%s) %s pathinfo - %s\n", \
1144 __location__, #call, nt_errstr(status)); \
1145 ret = false; \
1146 }} while (0)
1149 test stream renames
1151 static bool test_stream_rename(struct torture_context *tctx,
1152 struct smb2_tree *tree)
1154 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1155 NTSTATUS status, status2;
1156 union smb_open io;
1157 const char *fname = DNAME "\\stream_rename.txt";
1158 const char *sname1, *sname2;
1159 union smb_fileinfo finfo1;
1160 union smb_setfileinfo sfinfo;
1161 bool ret = true;
1162 struct smb2_handle h, h1;
1163 bool check_handle;
1164 const char *call_name;
1166 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
1167 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname,
1168 "Second Stream");
1170 smb2_util_unlink(tree, fname);
1171 smb2_deltree(tree, DNAME);
1173 status = torture_smb2_testdir(tree, DNAME, &h);
1174 CHECK_STATUS(status, NT_STATUS_OK);
1176 torture_comment(tctx, "(%s) testing stream renames\n", __location__);
1177 ZERO_STRUCT(io.smb2);
1178 io.smb2.in.create_flags = 0;
1179 io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
1180 SEC_FILE_WRITE_ATTRIBUTE |
1181 SEC_RIGHTS_FILE_ALL;
1182 io.smb2.in.create_options = 0;
1183 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1184 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1185 NTCREATEX_SHARE_ACCESS_WRITE |
1186 NTCREATEX_SHARE_ACCESS_DELETE;
1187 io.smb2.in.alloc_size = 0;
1188 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1189 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1190 io.smb2.in.security_flags = 0;
1191 io.smb2.in.fname = sname1;
1193 /* Create two streams. */
1194 status = smb2_create(tree, mem_ctx, &(io.smb2));
1195 CHECK_STATUS(status, NT_STATUS_OK);
1196 h1 = io.smb2.out.file.handle;
1197 smb2_util_close(tree, h1);
1199 io.smb2.in.fname = sname2;
1200 status = smb2_create(tree, mem_ctx, &(io.smb2));
1201 CHECK_STATUS(status, NT_STATUS_OK);
1202 h1 = io.smb2.out.file.handle;
1204 smb2_util_close(tree, h1);
1207 * Open the second stream.
1210 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1211 status = smb2_create(tree, mem_ctx, &(io.smb2));
1212 CHECK_STATUS(status, NT_STATUS_OK);
1213 h1 = io.smb2.out.file.handle;
1216 * Now rename the second stream onto the first.
1219 ZERO_STRUCT(sfinfo);
1221 sfinfo.rename_information.in.overwrite = 1;
1222 sfinfo.rename_information.in.root_fid = 0;
1223 sfinfo.rename_information.in.new_name = ":Stream One";
1224 CHECK_CALL_HANDLE(RENAME_INFORMATION, NT_STATUS_OK);
1225 done:
1226 smb2_util_close(tree, h1);
1227 status = smb2_util_unlink(tree, fname);
1228 smb2_deltree(tree, DNAME);
1229 talloc_free(mem_ctx);
1231 return ret;
1234 static bool test_stream_rename2(struct torture_context *tctx,
1235 struct smb2_tree *tree)
1237 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1238 NTSTATUS status;
1239 union smb_open io;
1240 const char *fname1 = DNAME "\\stream_rename2.txt";
1241 const char *fname2 = DNAME "\\stream2_rename2.txt";
1242 const char *stream_name1 = ":Stream One:$DATA";
1243 const char *stream_name2 = ":Stream Two:$DATA";
1244 const char *stream_name_default = "::$DATA";
1245 const char *sname1;
1246 const char *sname2;
1247 bool ret = true;
1248 struct smb2_handle h, h1;
1249 union smb_setfileinfo sinfo;
1251 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream One");
1252 sname2 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream Two");
1254 smb2_util_unlink(tree, fname1);
1255 smb2_util_unlink(tree, fname2);
1256 smb2_deltree(tree, DNAME);
1258 status = torture_smb2_testdir(tree, DNAME, &h);
1259 CHECK_STATUS(status, NT_STATUS_OK);
1261 ZERO_STRUCT(io.smb2);
1262 io.smb2.in.create_flags = 0;
1263 io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1264 SEC_FILE_WRITE_DATA |
1265 SEC_STD_DELETE |
1266 SEC_FILE_APPEND_DATA |
1267 SEC_STD_READ_CONTROL;
1268 io.smb2.in.create_options = 0;
1269 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1270 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1271 NTCREATEX_SHARE_ACCESS_WRITE |
1272 NTCREATEX_SHARE_ACCESS_DELETE;
1273 io.smb2.in.alloc_size = 0;
1274 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1275 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1276 io.smb2.in.security_flags = 0;
1277 io.smb2.in.fname = sname1;
1279 /* Open/create new stream. */
1280 status = smb2_create(tree, mem_ctx, &(io.smb2));
1281 CHECK_STATUS(status, NT_STATUS_OK);
1283 smb2_util_close(tree, io.smb2.out.file.handle);
1286 * Reopen the stream for SMB2 renames.
1288 io.smb2.in.fname = sname1;
1289 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1290 status = smb2_create(tree, mem_ctx, &(io.smb2));
1291 CHECK_STATUS(status, NT_STATUS_OK);
1292 h1 = io.smb2.out.file.handle;
1295 * Check SMB2 rename of a stream using :<stream>.
1297 torture_comment(tctx, "(%s) Checking SMB2 rename of a stream using "
1298 ":<stream>\n", __location__);
1299 ZERO_STRUCT(sinfo);
1300 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION_SMB2;
1301 sinfo.rename_information.in.file.handle = h1;
1302 sinfo.rename_information.in.overwrite = 1;
1303 sinfo.rename_information.in.root_fid = 0;
1304 sinfo.rename_information.in.new_name = stream_name1;
1305 status = smb2_setinfo_file(tree, &sinfo);
1306 CHECK_STATUS(status, NT_STATUS_OK);
1309 * Check SMB2 rename of an overwriting stream using :<stream>.
1311 torture_comment(tctx, "(%s) Checking SMB2 rename of an overwriting "
1312 "stream using :<stream>\n", __location__);
1314 /* Create second stream. */
1315 io.smb2.in.fname = sname2;
1316 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1317 status = smb2_create(tree, mem_ctx, &(io.smb2));
1318 CHECK_STATUS(status, NT_STATUS_OK);
1319 smb2_util_close(tree, io.smb2.out.file.handle);
1321 /* Rename the first stream onto the second. */
1322 sinfo.rename_information.in.file.handle = h1;
1323 sinfo.rename_information.in.new_name = stream_name2;
1324 status = smb2_setinfo_file(tree, &sinfo);
1325 CHECK_STATUS(status, NT_STATUS_OK);
1327 smb2_util_close(tree, h1);
1330 * Reopen the stream with the new name.
1332 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1333 io.smb2.in.fname = sname2;
1334 status = smb2_create(tree, mem_ctx, &(io.smb2));
1335 CHECK_STATUS(status, NT_STATUS_OK);
1336 h1 = io.smb2.out.file.handle;
1339 * Check SMB2 rename of a stream using <base>:<stream>.
1341 torture_comment(tctx, "(%s) Checking SMB2 rename of a stream using "
1342 "<base>:<stream>\n", __location__);
1343 sinfo.rename_information.in.file.handle = h1;
1344 sinfo.rename_information.in.new_name = sname1;
1345 status = smb2_setinfo_file(tree, &sinfo);
1346 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
1348 if (!torture_setting_bool(tctx, "samba4", false)) {
1350 * Check SMB2 rename to the default stream using :<stream>.
1352 torture_comment(tctx, "(%s) Checking SMB2 rename to default stream "
1353 "using :<stream>\n", __location__);
1354 sinfo.rename_information.in.file.handle = h1;
1355 sinfo.rename_information.in.new_name = stream_name_default;
1356 status = smb2_setinfo_file(tree, &sinfo);
1357 CHECK_STATUS(status, NT_STATUS_OK);
1360 smb2_util_close(tree, h1);
1362 done:
1363 smb2_util_close(tree, h1);
1364 status = smb2_util_unlink(tree, fname1);
1365 status = smb2_util_unlink(tree, fname2);
1366 smb2_deltree(tree, DNAME);
1367 talloc_free(mem_ctx);
1369 return ret;
1372 static bool create_file_with_stream(struct torture_context *tctx,
1373 struct smb2_tree *tree,
1374 TALLOC_CTX *mem_ctx,
1375 const char *base_fname,
1376 const char *stream)
1378 NTSTATUS status;
1379 bool ret = true;
1380 union smb_open io;
1382 /* Create a file with a stream */
1383 ZERO_STRUCT(io.smb2);
1384 io.smb2.in.create_flags = 0;
1385 io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1386 SEC_FILE_WRITE_DATA |
1387 SEC_FILE_APPEND_DATA |
1388 SEC_STD_READ_CONTROL;
1389 io.smb2.in.create_options = 0;
1390 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1391 io.smb2.in.share_access = 0;
1392 io.smb2.in.alloc_size = 0;
1393 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1394 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1395 io.smb2.in.security_flags = 0;
1396 io.smb2.in.fname = stream;
1398 status = smb2_create(tree, mem_ctx, &(io.smb2));
1399 CHECK_STATUS(status, NT_STATUS_OK);
1401 done:
1402 smb2_util_close(tree, io.smb2.out.file.handle);
1403 return ret;
1407 /* Test how streams interact with create dispositions */
1408 static bool test_stream_create_disposition(struct torture_context *tctx,
1409 struct smb2_tree *tree)
1411 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1412 NTSTATUS status;
1413 union smb_open io;
1414 const char *fname = DNAME "\\stream_create_disp.txt";
1415 const char *stream = "Stream One:$DATA";
1416 const char *fname_stream;
1417 const char *default_stream_name = "::$DATA";
1418 const char *stream_list[2];
1419 bool ret = true;
1420 struct smb2_handle h, h1;
1422 /* clean slate .. */
1423 smb2_util_unlink(tree, fname);
1424 smb2_deltree(tree, fname);
1425 smb2_deltree(tree, DNAME);
1427 status = torture_smb2_testdir(tree, DNAME, &h);
1428 CHECK_STATUS(status, NT_STATUS_OK);
1430 fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream);
1432 stream_list[0] = talloc_asprintf(mem_ctx, ":%s", stream);
1433 stream_list[1] = default_stream_name;
1435 if (!create_file_with_stream(tctx, tree, mem_ctx, fname,
1436 fname_stream)) {
1437 goto done;
1440 /* Open the base file with OPEN */
1441 ZERO_STRUCT(io.smb2);
1442 io.smb2.in.create_flags = 0;
1443 io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1444 SEC_FILE_WRITE_DATA |
1445 SEC_FILE_APPEND_DATA |
1446 SEC_STD_READ_CONTROL;
1447 io.smb2.in.create_options = 0;
1448 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1449 io.smb2.in.share_access = 0;
1450 io.smb2.in.alloc_size = 0;
1451 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1452 io.smb2.in.security_flags = 0;
1453 io.smb2.in.fname = fname;
1456 * check create open: sanity check
1458 torture_comment(tctx, "(%s) Checking create disp: open\n",
1459 __location__);
1460 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1461 status = smb2_create(tree, mem_ctx, &(io.smb2));
1462 CHECK_STATUS(status, NT_STATUS_OK);
1463 if (!check_stream_list(tree, tctx, fname, 2, stream_list,
1464 io.smb2.out.file.handle)) {
1465 goto done;
1467 smb2_util_close(tree, io.smb2.out.file.handle);
1470 * check create overwrite
1472 torture_comment(tctx, "(%s) Checking create disp: overwrite\n",
1473 __location__);
1474 io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
1475 status = smb2_create(tree, mem_ctx, &(io.smb2));
1476 CHECK_STATUS(status, NT_STATUS_OK);
1477 if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name,
1478 io.smb2.out.file.handle)) {
1479 goto done;
1481 smb2_util_close(tree, io.smb2.out.file.handle);
1484 * check create overwrite_if
1486 torture_comment(tctx, "(%s) Checking create disp: overwrite_if\n",
1487 __location__);
1488 smb2_util_unlink(tree, fname);
1489 if (!create_file_with_stream(tctx, tree, mem_ctx, fname, fname_stream))
1490 goto done;
1492 io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1493 status = smb2_create(tree, mem_ctx, &(io.smb2));
1494 CHECK_STATUS(status, NT_STATUS_OK);
1495 if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name,
1496 io.smb2.out.file.handle)) {
1497 goto done;
1499 smb2_util_close(tree, io.smb2.out.file.handle);
1502 * check create supersede
1504 torture_comment(tctx, "(%s) Checking create disp: supersede\n",
1505 __location__);
1506 smb2_util_unlink(tree, fname);
1507 if (!create_file_with_stream(tctx, tree, mem_ctx, fname,
1508 fname_stream)) {
1509 goto done;
1512 io.smb2.in.create_disposition = NTCREATEX_DISP_SUPERSEDE;
1513 status = smb2_create(tree, mem_ctx, &(io.smb2));
1514 CHECK_STATUS(status, NT_STATUS_OK);
1515 if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name,
1516 io.smb2.out.file.handle)) {
1517 goto done;
1519 smb2_util_close(tree, io.smb2.out.file.handle);
1522 * check create overwrite_if on a stream.
1524 torture_comment(tctx, "(%s) Checking create disp: overwrite_if on "
1525 "stream\n", __location__);
1526 smb2_util_unlink(tree, fname);
1527 if (!create_file_with_stream(tctx, tree, mem_ctx, fname,
1528 fname_stream)) {
1529 goto done;
1532 io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1533 io.smb2.in.fname = fname_stream;
1534 status = smb2_create(tree, mem_ctx, &(io.smb2));
1535 CHECK_STATUS(status, NT_STATUS_OK);
1536 if (!check_stream_list(tree, tctx, fname, 2, stream_list,
1537 io.smb2.out.file.handle)) {
1538 goto done;
1540 smb2_util_close(tree, io.smb2.out.file.handle);
1541 done:
1542 smb2_util_close(tree, h1);
1543 smb2_util_unlink(tree, fname);
1544 smb2_deltree(tree, DNAME);
1545 talloc_free(mem_ctx);
1547 return ret;
1550 static bool open_stream(struct smb2_tree *tree,
1551 struct torture_context *mem_ctx,
1552 const char *fname,
1553 struct smb2_handle *h_out)
1555 NTSTATUS status;
1556 union smb_open io;
1558 ZERO_STRUCT(io.smb2);
1559 io.smb2.in.create_flags = 0;
1560 io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1561 SEC_FILE_WRITE_DATA |
1562 SEC_FILE_APPEND_DATA |
1563 SEC_STD_READ_CONTROL |
1564 SEC_FILE_WRITE_ATTRIBUTE;
1565 io.smb2.in.create_options = 0;
1566 io.smb2.in.file_attributes = 0;
1567 io.smb2.in.share_access = 0;
1568 io.smb2.in.alloc_size = 0;
1569 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1570 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1571 io.smb2.in.security_flags = 0;
1572 io.smb2.in.fname = fname;
1574 status = smb2_create(tree, mem_ctx, &(io.smb2));
1575 if (!NT_STATUS_IS_OK(status)) {
1576 return false;
1578 *h_out = io.smb2.out.file.handle;
1579 return true;
1583 /* Test the effect of setting attributes on a stream. */
1584 static bool test_stream_attributes(struct torture_context *tctx,
1585 struct smb2_tree *tree)
1587 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1588 bool ret = true;
1589 NTSTATUS status;
1590 union smb_open io;
1591 const char *fname = DNAME "\\stream_attr.txt";
1592 const char *stream = "Stream One:$DATA";
1593 const char *fname_stream;
1594 struct smb2_handle h, h1;
1595 union smb_fileinfo finfo;
1596 union smb_setfileinfo sfinfo;
1597 time_t basetime = (time(NULL) - 86400) & ~1;
1599 torture_comment(tctx, "(%s) testing attribute setting on stream\n",
1600 __location__);
1602 /* clean slate .. */
1603 smb2_util_unlink(tree, fname);
1604 smb2_deltree(tree, fname);
1605 smb2_deltree(tree, DNAME);
1607 status = torture_smb2_testdir(tree, DNAME, &h);
1608 CHECK_STATUS(status, NT_STATUS_OK);
1610 fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream);
1612 /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */
1613 ret = create_file_with_stream(tctx, tree, mem_ctx, fname,
1614 fname_stream);
1615 if (!ret) {
1616 goto done;
1619 ZERO_STRUCT(io.smb2);
1620 io.smb2.in.fname = fname;
1621 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1622 io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
1623 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1624 NTCREATEX_SHARE_ACCESS_WRITE |
1625 NTCREATEX_SHARE_ACCESS_DELETE;
1626 status = smb2_create(tree, mem_ctx, &(io.smb2));
1627 CHECK_STATUS(status, NT_STATUS_OK);
1629 ZERO_STRUCT(finfo);
1630 finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1631 finfo.generic.in.file.handle = io.smb2.out.file.handle;
1632 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1633 CHECK_STATUS(status, NT_STATUS_OK);
1635 if (finfo.basic_info.out.attrib != FILE_ATTRIBUTE_ARCHIVE) {
1636 torture_comment(tctx, "(%s) Incorrect attrib %x - should be "
1637 "%x\n", __location__,
1638 (unsigned int)finfo.basic_info.out.attrib,
1639 (unsigned int)FILE_ATTRIBUTE_ARCHIVE);
1640 ret = false;
1641 goto done;
1644 smb2_util_close(tree, io.smb2.out.file.handle);
1645 /* Now open the stream name. */
1647 if (!open_stream(tree, tctx, fname_stream, &h1)) {
1648 goto done;
1651 /* Change the time on the stream. */
1652 ZERO_STRUCT(sfinfo);
1653 unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime);
1654 sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1655 sfinfo.generic.in.file.handle = h1;
1656 status = smb2_setinfo_file(tree, &sfinfo);
1657 if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
1658 torture_comment(tctx, "(%s) %s - %s (should be %s)\n",
1659 __location__, "SETATTR",
1660 nt_errstr(status), nt_errstr(NT_STATUS_OK));
1661 ret = false;
1662 goto done;
1665 smb2_util_close(tree, h1);
1667 ZERO_STRUCT(io.smb2);
1668 io.smb2.in.fname = fname;
1669 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1670 io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
1671 status = smb2_create(tree, mem_ctx, &(io.smb2));
1672 CHECK_STATUS(status, NT_STATUS_OK);
1673 h1 = io.smb2.out.file.handle;
1675 ZERO_STRUCT(finfo);
1676 finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1677 finfo.generic.in.file.handle = h1;
1678 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1679 if (!NT_STATUS_IS_OK(status)) {
1680 torture_comment(tctx, "(%s) %s pathinfo - %s\n",
1681 __location__, "SETATTRE", nt_errstr(status));
1682 ret = false;
1683 goto done;
1686 if (nt_time_to_unix(finfo.basic_info.out.write_time) != basetime) {
1687 torture_comment(tctx, "(%s) time incorrect.\n", __location__);
1688 ret = false;
1689 goto done;
1691 smb2_util_close(tree, h1);
1693 if (!open_stream(tree, tctx, fname_stream, &h1)) {
1694 goto done;
1697 /* Changing attributes on stream */
1698 ZERO_STRUCT(sfinfo);
1699 sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY;
1701 sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1702 sfinfo.generic.in.file.handle = h1;
1703 status = smb2_setinfo_file(tree, &sfinfo);
1704 if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
1705 torture_comment(tctx, "(%s) %s - %s (should be %s)\n",
1706 __location__, "SETATTR",
1707 nt_errstr(status), nt_errstr(NT_STATUS_OK));
1708 ret = false;
1709 goto done;
1712 smb2_util_close(tree, h1);
1714 ZERO_STRUCT(io.smb2);
1715 io.smb2.in.fname = fname;
1716 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1717 io.smb2.in.desired_access = SEC_FILE_READ_DATA;
1718 status = smb2_create(tree, mem_ctx, &(io.smb2));
1719 CHECK_STATUS(status, NT_STATUS_OK);
1720 h1 = io.smb2.out.file.handle;
1722 ZERO_STRUCT(finfo);
1723 finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1724 finfo.generic.in.file.handle = h1;
1725 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1726 CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
1728 done:
1729 smb2_util_close(tree, h1);
1730 smb2_util_unlink(tree, fname);
1731 smb2_deltree(tree, DNAME);
1732 talloc_free(mem_ctx);
1734 return ret;
1739 basic testing of streams calls SMB2
1741 struct torture_suite *torture_smb2_streams_init(void)
1743 struct torture_suite *suite =
1744 torture_suite_create(talloc_autofree_context(), "streams");
1746 torture_suite_add_1smb2_test(suite, "dir", test_stream_dir);
1747 torture_suite_add_1smb2_test(suite, "io", test_stream_io);
1748 torture_suite_add_1smb2_test(suite, "sharemodes", test_stream_sharemodes);
1749 torture_suite_add_1smb2_test(suite, "names", test_stream_names);
1750 torture_suite_add_1smb2_test(suite, "names2", test_stream_names2);
1751 torture_suite_add_1smb2_test(suite, "rename", test_stream_rename);
1752 torture_suite_add_1smb2_test(suite, "rename2", test_stream_rename2);
1753 torture_suite_add_1smb2_test(suite, "create-disposition", test_stream_create_disposition);
1754 torture_suite_add_1smb2_test(suite, "attributes", test_stream_attributes);
1755 torture_suite_add_1smb2_test(suite, "delete", test_stream_delete);
1757 suite->description = talloc_strdup(suite, "SMB2-STREAM tests");
1759 return suite;