s3-spoolss: fix _spoolss_GetPrinterDataEx after IDL change.
[Samba/nascimento.git] / source4 / torture / smb2 / streams.c
blobf4e83c9ad313be2965edb83a495df4dcb80b5e59
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_comment(tctx,"(%s) Incorrect value %s='%s' - " \
76 "should be '%s'\n", \
77 __location__, #v, (v)?(v):"NULL", \
78 (correct)?(correct):"NULL"); \
79 ret = false; \
80 }} while (0)
83 static int qsort_string(char * const *s1, char * const *s2)
85 return strcmp(*s1, *s2);
88 static int qsort_stream(const struct stream_struct * s1, const struct stream_struct *s2)
90 return strcmp(s1->stream_name.s, s2->stream_name.s);
93 static bool check_stream(struct smb2_tree *tree,
94 const char *location,
95 TALLOC_CTX *mem_ctx,
96 const char *fname,
97 const char *sname,
98 const char *value)
100 struct smb2_handle handle;
101 struct smb2_create create;
102 struct smb2_read r;
103 NTSTATUS status;
104 const char *full_name;
106 full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname);
108 ZERO_STRUCT(create);
109 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
110 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
111 create.in.create_disposition = NTCREATEX_DISP_OPEN;
112 create.in.fname = full_name;
114 status = smb2_create(tree, mem_ctx, &create);
115 if (!NT_STATUS_IS_OK(status)) {
116 if (value == NULL) {
117 return true;
118 } else {
119 torture_comment(mem_ctx, "Unable to open stream %s\n",
120 full_name);
121 return false;
125 handle = create.out.file.handle;
126 if (value == NULL) {
127 return true;
131 ZERO_STRUCT(r);
132 r.in.file.handle = handle;
133 r.in.length = strlen(value)+11;
134 r.in.offset = 0;
136 status = smb2_read(tree, tree, &r);
138 if (!NT_STATUS_IS_OK(status)) {
139 torture_comment(mem_ctx, "(%s) Failed to read %lu bytes from "
140 "stream '%s'\n", location, (long)strlen(value), full_name);
141 return false;
144 if (memcmp(r.out.data.data, value, strlen(value)) != 0) {
145 torture_comment(mem_ctx, "(%s) Bad data in stream\n", location);
146 return false;
149 smb2_util_close(tree, handle);
150 return true;
153 static bool check_stream_list(struct smb2_tree *tree,
154 struct torture_context *tctx,
155 const char *fname,
156 int num_exp,
157 const char **exp,
158 struct smb2_handle h)
160 union smb_fileinfo finfo;
161 NTSTATUS status;
162 int i;
163 TALLOC_CTX *tmp_ctx = talloc_new(tctx);
164 char **exp_sort;
165 struct stream_struct *stream_sort;
166 bool ret = false;
168 finfo.generic.level = RAW_FILEINFO_STREAM_INFORMATION;
169 finfo.generic.in.file.handle = h;
171 status = smb2_getinfo_file(tree, tctx, &finfo);
172 if (!NT_STATUS_IS_OK(status)) {
173 torture_comment(tctx, "(%s) smb_raw_pathinfo failed: %s\n",
174 __location__, nt_errstr(status));
175 goto fail;
178 if (finfo.stream_info.out.num_streams != num_exp) {
179 torture_comment(tctx, "(%s) expected %d streams, got %d\n",
180 __location__, num_exp, finfo.stream_info.out.num_streams);
181 goto fail;
184 if (num_exp == 0) {
185 ret = true;
186 goto fail;
189 exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
191 if (exp_sort == NULL) {
192 goto fail;
195 TYPESAFE_QSORT(exp_sort, num_exp, qsort_string);
197 stream_sort = talloc_memdup(tmp_ctx, finfo.stream_info.out.streams,
198 finfo.stream_info.out.num_streams *
199 sizeof(*stream_sort));
201 if (stream_sort == NULL) {
202 goto fail;
205 TYPESAFE_QSORT(stream_sort, finfo.stream_info.out.num_streams, qsort_stream);
207 for (i=0; i<num_exp; i++) {
208 if (strcmp(exp_sort[i], stream_sort[i].stream_name.s) != 0) {
209 torture_comment(tctx,
210 "(%s) expected stream name %s, got %s\n",
211 __location__, exp_sort[i],
212 stream_sort[i].stream_name.s);
213 goto fail;
217 ret = true;
218 fail:
219 talloc_free(tmp_ctx);
220 return ret;
224 static bool test_stream_dir(struct torture_context *tctx,
225 struct smb2_tree *tree)
227 TALLOC_CTX *mem_ctx = talloc_new(tctx);
228 NTSTATUS status;
229 union smb_open io;
230 const char *fname = DNAME "\\stream.txt";
231 const char *sname1;
232 bool ret = true;
233 const char *basedir_data;
234 struct smb2_handle h;
236 smb2_util_unlink(tree, fname);
237 smb2_deltree(tree, DNAME);
239 status = torture_smb2_testdir(tree, DNAME, &h);
240 CHECK_STATUS(status, NT_STATUS_OK);
242 basedir_data = talloc_asprintf(mem_ctx, "%s::$DATA", DNAME);
243 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
244 torture_comment(tctx, "%s\n", sname1);
246 torture_comment(tctx, "(%s) opening non-existant directory stream\n",
247 __location__);
248 ZERO_STRUCT(io.smb2);
249 io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
250 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
251 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
252 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
253 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
254 io.smb2.in.share_access = 0;
255 io.smb2.in.alloc_size = 0;
256 io.smb2.in.security_flags = 0;
257 io.smb2.in.fname = sname1;
258 io.smb2.in.create_flags = 0;
259 status = smb2_create(tree, mem_ctx, &(io.smb2));
260 CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
262 torture_comment(tctx, "(%s) opening basedir stream\n", __location__);
263 ZERO_STRUCT(io.smb2);
264 io.smb2.in.create_flags = 0;
265 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
266 io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
267 io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
268 io.smb2.in.share_access = 0;
269 io.smb2.in.alloc_size = 0;
270 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
271 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
272 io.smb2.in.security_flags = 0;
273 io.smb2.in.fname = basedir_data;
274 status = smb2_create(tree, mem_ctx, &(io.smb2));
275 CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
277 torture_comment(tctx, "(%s) opening basedir ::$DATA stream\n",
278 __location__);
279 ZERO_STRUCT(io.smb2);
280 io.smb2.in.create_flags = 0x10;
281 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
282 io.smb2.in.create_options = 0;
283 io.smb2.in.file_attributes = 0;
284 io.smb2.in.share_access = 0;
285 io.smb2.in.alloc_size = 0;
286 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
287 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
288 io.smb2.in.security_flags = 0;
289 io.smb2.in.fname = basedir_data;
290 status = smb2_create(tree, mem_ctx, &(io.smb2));
291 CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
293 torture_comment(tctx, "(%s) list the streams on the basedir\n",
294 __location__);
295 ret &= check_stream_list(tree, mem_ctx, DNAME, 0, NULL, h);
296 done:
297 smb2_util_unlink(tree, fname);
298 smb2_deltree(tree, DNAME);
299 talloc_free(mem_ctx);
301 return ret;
304 static bool test_stream_io(struct torture_context *tctx,
305 struct smb2_tree *tree)
307 TALLOC_CTX *mem_ctx = talloc_new(tctx);
308 NTSTATUS status;
309 union smb_open io;
310 const char *fname = DNAME "\\stream.txt";
311 const char *sname1, *sname2;
312 bool ret = true;
313 struct smb2_handle h, h2;
315 const char *one[] = { "::$DATA" };
316 const char *two[] = { "::$DATA", ":Second Stream:$DATA" };
317 const char *three[] = { "::$DATA", ":Stream One:$DATA",
318 ":Second Stream:$DATA" };
320 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
321 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname,
322 "Second Stream");
324 smb2_util_unlink(tree, fname);
325 smb2_deltree(tree, DNAME);
327 status = torture_smb2_testdir(tree, DNAME, &h);
328 CHECK_STATUS(status, NT_STATUS_OK);
330 torture_comment(tctx, "(%s) creating a stream on a non-existant file\n",
331 __location__);
333 ZERO_STRUCT(io.smb2);
334 io.smb2.in.create_flags = 0;
335 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
336 io.smb2.in.create_options = 0;
337 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
338 io.smb2.in.share_access = 0;
339 io.smb2.in.alloc_size = 0;
340 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
341 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
342 io.smb2.in.security_flags = 0;
343 io.smb2.in.fname = sname1;
344 status = smb2_create(tree, mem_ctx, &(io.smb2));
345 CHECK_STATUS(status, NT_STATUS_OK);
346 h2 = io.smb2.out.file.handle;
348 ret &= check_stream(tree, __location__, mem_ctx, fname,
349 "Stream One", NULL);
351 torture_comment(tctx, "(%s) check that open of base file is allowed\n", __location__);
352 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
353 io.smb2.in.fname = fname;
354 status = smb2_create(tree, mem_ctx, &(io.smb2));
355 CHECK_STATUS(status, NT_STATUS_OK);
356 smb2_util_close(tree, io.smb2.out.file.handle);
358 torture_comment(tctx, "(%s) writing to stream\n", __location__);
359 status = smb2_util_write(tree, h2, "test data", 0, 9);
360 CHECK_STATUS(status, NT_STATUS_OK);
362 smb2_util_close(tree, h2);
364 ret &= check_stream(tree, __location__, mem_ctx, fname,
365 "Stream One", "test data");
367 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
368 io.smb2.in.fname = sname1;
369 status = smb2_create(tree, mem_ctx, &(io.smb2));
370 CHECK_STATUS(status, NT_STATUS_OK);
371 h2 = io.smb2.out.file.handle;
373 torture_comment(tctx, "(%s) modifying stream\n", __location__);
374 status = smb2_util_write(tree, h2, "MORE DATA ", 5, 10);
375 CHECK_STATUS(status, NT_STATUS_OK);
377 smb2_util_close(tree, h2);
379 ret &= check_stream(tree, __location__, mem_ctx, fname,
380 "Stream One:$FOO", NULL);
382 torture_comment(tctx, "(%s) creating a stream2 on a existing file\n",
383 __location__);
384 io.smb2.in.fname = sname2;
385 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
386 status = smb2_create(tree, mem_ctx, &(io.smb2));
387 CHECK_STATUS(status, NT_STATUS_OK);
388 h2 = io.smb2.out.file.handle;
390 torture_comment(tctx, "(%s) modifying stream\n", __location__);
391 status= smb2_util_write(tree, h2, "SECOND STREAM", 0, 13);
392 CHECK_STATUS(status, NT_STATUS_OK);
393 smb2_util_close(tree, h2);
395 ret &= check_stream(tree, __location__, mem_ctx, fname,
396 "Stream One", "test MORE DATA ");
398 ret &= check_stream(tree, __location__, mem_ctx, fname,
399 "Stream One:$DATA", "test MORE DATA ");
401 ret &= check_stream(tree, __location__, mem_ctx, fname,
402 "Stream One:", NULL);
404 ret &= check_stream(tree, __location__, mem_ctx, fname,
405 "Second Stream", "SECOND STREAM");
407 ret &= check_stream(tree, __location__, mem_ctx, fname,
408 "SECOND STREAM:$DATA", "SECOND STREAM");
409 ret &= check_stream(tree, __location__, mem_ctx, fname,
410 "Second Stream:$DATA", "SECOND STREAM");
412 ret &= check_stream(tree, __location__, mem_ctx, fname,
413 "Second Stream:", NULL);
415 ret &= check_stream(tree, __location__, mem_ctx, fname,
416 "Second Stream:$FOO", NULL);
418 io.smb2.in.fname = sname2;
419 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
420 status = smb2_create(tree, mem_ctx, &(io.smb2));
421 CHECK_STATUS(status, NT_STATUS_OK);
422 h2 = io.smb2.out.file.handle;
423 check_stream_list(tree, tctx, fname, 3, three, h2);
425 smb2_util_close(tree, h2);
427 torture_comment(tctx, "(%s) deleting stream\n", __location__);
428 status = smb2_util_unlink(tree, sname1);
429 CHECK_STATUS(status, NT_STATUS_OK);
431 io.smb2.in.fname = sname2;
432 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
433 status = smb2_create(tree, mem_ctx, &(io.smb2));
434 CHECK_STATUS(status, NT_STATUS_OK);
435 h2 = io.smb2.out.file.handle;
436 check_stream_list(tree, tctx, fname, 2, two, h2);
437 smb2_util_close(tree, h2);
439 torture_comment(tctx, "(%s) delete a stream via delete-on-close\n",
440 __location__);
441 io.smb2.in.fname = sname2;
442 io.smb2.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
443 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
444 io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
445 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
447 status = smb2_create(tree, mem_ctx, &(io.smb2));
448 CHECK_STATUS(status, NT_STATUS_OK);
449 h2 = io.smb2.out.file.handle;
451 smb2_util_close(tree, h2);
452 status = smb2_util_unlink(tree, sname2);
453 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
455 io.smb2.in.fname = fname;
456 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
457 status = smb2_create(tree, mem_ctx, &(io.smb2));
458 h2 = io.smb2.out.file.handle;
459 check_stream_list(tree,tctx, fname, 1, one, h2);
460 smb2_util_close(tree, h2);
462 if (!torture_setting_bool(tctx, "samba4", false)) {
463 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
464 io.smb2.in.fname = sname1;
465 status = smb2_create(tree, mem_ctx, &(io.smb2));
466 CHECK_STATUS(status, NT_STATUS_OK);
467 smb2_util_close(tree, io.ntcreatex.out.file.handle);
468 io.smb2.in.fname = sname2;
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 torture_comment(tctx, "(%s) deleting file\n", __location__);
473 status = smb2_util_unlink(tree, fname);
474 CHECK_STATUS(status, NT_STATUS_OK);
478 done:
479 smb2_util_close(tree, h2);
480 smb2_deltree(tree, DNAME);
481 talloc_free(mem_ctx);
483 return ret;
487 test stream sharemodes
489 static bool test_stream_sharemodes(struct torture_context *tctx,
490 struct smb2_tree *tree)
492 TALLOC_CTX *mem_ctx = talloc_new(tctx);
493 NTSTATUS status;
494 union smb_open io;
495 const char *fname = DNAME "\\stream_share.txt";
496 const char *sname1, *sname2;
497 bool ret = true;
498 struct smb2_handle h, h1, h2;
500 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
501 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname,
502 "Second Stream");
504 smb2_util_unlink(tree, fname);
505 smb2_deltree(tree, DNAME);
507 status = torture_smb2_testdir(tree, DNAME, &h);
508 CHECK_STATUS(status, NT_STATUS_OK);
510 torture_comment(tctx, "(%s) Testing stream share mode conflicts\n",
511 __location__);
512 ZERO_STRUCT(io.smb2);
513 io.generic.level = RAW_OPEN_SMB2;
514 io.smb2.in.create_flags = 0;
515 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
516 io.smb2.in.create_options = 0;
517 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
518 io.smb2.in.share_access = 0;
519 io.smb2.in.alloc_size = 0;
520 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
521 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
522 io.smb2.in.security_flags = 0;
523 io.smb2.in.fname = sname1;
525 status = smb2_create(tree, mem_ctx, &(io.smb2));
526 CHECK_STATUS(status, NT_STATUS_OK);
527 h1 = io.smb2.out.file.handle;
530 * A different stream does not give a sharing violation
533 io.smb2.in.fname = sname2;
534 status = smb2_create(tree, mem_ctx, &(io.smb2));
535 CHECK_STATUS(status, NT_STATUS_OK);
536 h2 = io.smb2.out.file.handle;
539 * ... whereas the same stream does with unchanged access/share_access
540 * flags
543 io.smb2.in.fname = sname1;
544 io.smb2.in.create_disposition = 0;
545 status = smb2_create(tree, mem_ctx, &(io.smb2));
546 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
548 io.smb2.in.fname = sname2;
549 status = smb2_create(tree, mem_ctx, &(io.smb2));
550 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
552 done:
553 smb2_util_close(tree, h1);
554 smb2_util_close(tree, h2);
555 status = smb2_util_unlink(tree, fname);
556 smb2_deltree(tree, DNAME);
557 talloc_free(mem_ctx);
559 return ret;
563 * Test FILE_SHARE_DELETE on streams
565 * A stream opened with !FILE_SHARE_DELETE prevents the main file to be opened
566 * with SEC_STD_DELETE.
568 * The main file opened with !FILE_SHARE_DELETE does *not* prevent a stream to
569 * be opened with SEC_STD_DELETE.
571 * A stream held open with FILE_SHARE_DELETE allows the file to be
572 * deleted. After the main file is deleted, access to the open file descriptor
573 * still works, but all name-based access to both the main file as well as the
574 * stream is denied with DELETE pending.
576 * This means, an open of the main file with SEC_STD_DELETE should walk all
577 * streams and also open them with SEC_STD_DELETE. If any of these opens gives
578 * SHARING_VIOLATION, the main open fails.
580 * Closing the main file after delete_on_close has been set does not really
581 * unlink it but leaves the corresponding share mode entry with
582 * delete_on_close being set around until all streams are closed.
584 * Opening a stream must also look at the main file's share mode entry, look
585 * at the delete_on_close bit and potentially return DELETE_PENDING.
588 static bool test_stream_delete(struct torture_context *tctx,
589 struct smb2_tree *tree)
591 TALLOC_CTX *mem_ctx = talloc_new(tctx);
592 NTSTATUS status;
593 union smb_open io;
594 const char *fname = DNAME "\\stream_delete.txt";
595 const char *sname1;
596 bool ret = true;
597 struct smb2_handle h, h1;
598 struct smb2_read r;
600 if (torture_setting_bool(tctx, "samba4", false)) {
601 torture_comment(tctx, "Skipping test as samba4 is enabled\n");
602 goto done;
605 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
607 /* clean slate .. */
608 smb2_util_unlink(tree, fname);
609 smb2_deltree(tree, fname);
610 smb2_deltree(tree, DNAME);
612 status = torture_smb2_testdir(tree, DNAME, &h);
613 CHECK_STATUS(status, NT_STATUS_OK);
615 torture_comment(tctx, "(%s) opening non-existant file stream\n",
616 __location__);
617 ZERO_STRUCT(io.smb2);
618 io.smb2.in.create_flags = 0;
619 io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
620 io.smb2.in.create_options = 0;
621 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
622 io.smb2.in.share_access = 0;
623 io.smb2.in.alloc_size = 0;
624 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
625 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
626 io.smb2.in.security_flags = 0;
627 io.smb2.in.fname = sname1;
629 status = smb2_create(tree, mem_ctx, &(io.smb2));
630 CHECK_STATUS(status, NT_STATUS_OK);
631 h1 = io.smb2.out.file.handle;
633 status = smb2_util_write(tree, h1, "test data", 0, 9);
634 CHECK_STATUS(status, NT_STATUS_OK);
637 * One stream opened without FILE_SHARE_DELETE prevents the main file
638 * to be deleted or even opened with DELETE access
641 status = smb2_util_unlink(tree, fname);
642 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
644 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
645 io.smb2.in.fname = fname;
646 io.smb2.in.desired_access = SEC_STD_DELETE;
647 status = smb2_create(tree, mem_ctx, &(io.smb2));
648 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
650 smb2_util_close(tree, h1);
653 * ... but unlink works if a stream is opened with FILE_SHARE_DELETE
656 io.smb2.in.fname = sname1;
657 io.smb2.in.desired_access = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
658 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE |
659 NTCREATEX_SHARE_ACCESS_READ |
660 NTCREATEX_SHARE_ACCESS_WRITE;
661 status = smb2_create(tree, mem_ctx, &(io.smb2));
662 CHECK_STATUS(status, NT_STATUS_OK);
663 h1 = io.smb2.out.file.handle;
665 status = smb2_util_unlink(tree, fname);
666 CHECK_STATUS(status, NT_STATUS_OK);
669 * file access still works on the stream while the main file is closed
671 ZERO_STRUCT(r);
672 r.in.file.handle = h1;
673 r.in.length = 9;
674 r.in.offset = 0;
676 status = smb2_read(tree, tree, &r);
677 CHECK_STATUS(status, NT_STATUS_OK);
680 * name-based access to both the main file and the stream does not
681 * work anymore but gives DELETE_PENDING
684 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
685 io.smb2.in.fname = fname;
686 status = smb2_create(tree, mem_ctx, &(io.smb2));
687 CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
690 * older S3 doesn't do this
693 io.smb2.in.fname = sname1;
694 status = smb2_create(tree, mem_ctx, &(io.smb2));
695 CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
697 smb2_util_close(tree, h1);
700 * After closing the stream the file is really gone.
703 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
704 io.smb2.in.fname = fname;
705 status = smb2_create(tree, mem_ctx, &(io.smb2));
706 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
708 done:
709 smb2_util_close(tree, h1);
710 smb2_util_unlink(tree, fname);
711 smb2_deltree(tree, DNAME);
712 talloc_free(mem_ctx);
714 return ret;
718 test stream names
720 static bool test_stream_names(struct torture_context *tctx,
721 struct smb2_tree *tree)
723 TALLOC_CTX *mem_ctx = talloc_new(tctx);
724 NTSTATUS status;
725 union smb_open io;
726 union smb_fileinfo finfo;
727 union smb_fileinfo stinfo;
728 union smb_setfileinfo sinfo;
729 const char *fname = DNAME "\\stream_names.txt";
730 const char *sname1, *sname1b, *sname1c, *sname1d;
731 const char *sname2, *snamew, *snamew2;
732 const char *snamer1, *snamer2;
733 bool ret = true;
734 struct smb2_handle h, h1, h2, h3;
735 int i;
736 const char *four[4] = {
737 "::$DATA",
738 ":\x05Stream\n One:$DATA",
739 ":MStream Two:$DATA",
740 ":?Stream*:$DATA"
742 const char *five1[5] = {
743 "::$DATA",
744 ":\x05Stream\n One:$DATA",
745 ":BeforeRename:$DATA",
746 ":MStream Two:$DATA",
747 ":?Stream*:$DATA"
749 const char *five2[5] = {
750 "::$DATA",
751 ":\x05Stream\n One:$DATA",
752 ":AfterRename:$DATA",
753 ":MStream Two:$DATA",
754 ":?Stream*:$DATA"
757 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "\x05Stream\n One");
758 sname1b = talloc_asprintf(mem_ctx, "%s:", sname1);
759 sname1c = talloc_asprintf(mem_ctx, "%s:$FOO", sname1);
760 sname1d = talloc_asprintf(mem_ctx, "%s:?D*a", sname1);
761 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "MStream Two");
762 snamew = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "?Stream*");
763 snamew2 = talloc_asprintf(mem_ctx, "%s\\stream*:%s:$DATA", DNAME,
764 "?Stream*");
765 snamer1 = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname,
766 "BeforeRename");
767 snamer2 = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "AfterRename");
769 /* clean slate ...*/
770 smb2_util_unlink(tree, fname);
771 smb2_deltree(tree, fname);
772 smb2_deltree(tree, DNAME);
774 status = torture_smb2_testdir(tree, DNAME, &h);
775 CHECK_STATUS(status, NT_STATUS_OK);
777 torture_comment(tctx, "(%s) testing stream names\n", __location__);
778 ZERO_STRUCT(io.smb2);
779 io.smb2.in.create_flags = 0;
780 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
781 io.smb2.in.create_options = 0;
782 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
783 io.smb2.in.share_access = 0;
784 io.smb2.in.alloc_size = 0;
785 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
786 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
787 io.smb2.in.security_flags = 0;
788 io.smb2.in.fname = sname1;
790 status = smb2_create(tree, mem_ctx, &(io.smb2));
791 CHECK_STATUS(status, NT_STATUS_OK);
792 h1 = io.smb2.out.file.handle;
795 * A different stream does not give a sharing violation
798 io.smb2.in.fname = sname2;
799 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
800 status = smb2_create(tree, mem_ctx, &(io.smb2));
801 CHECK_STATUS(status, NT_STATUS_OK);
802 h2 = io.smb2.out.file.handle;
805 * ... whereas the same stream does with unchanged access/share_access
806 * flags
809 io.smb2.in.fname = sname1;
810 io.smb2.in.create_disposition = NTCREATEX_DISP_SUPERSEDE;
811 status = smb2_create(tree, mem_ctx, &(io.smb2));
812 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
814 io.smb2.in.fname = sname1b;
815 status = smb2_create(tree, mem_ctx, &(io.smb2));
816 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
818 io.smb2.in.fname = sname1c;
819 status = smb2_create(tree, mem_ctx, &(io.smb2));
820 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
821 /* w2k returns INVALID_PARAMETER */
822 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
823 } else {
824 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
827 io.smb2.in.fname = sname1d;
828 status = smb2_create(tree, mem_ctx, &(io.smb2));
829 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
830 /* w2k returns INVALID_PARAMETER */
831 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
832 } else {
833 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
836 io.smb2.in.fname = sname2;
837 status = smb2_create(tree, mem_ctx, &(io.smb2));
838 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
840 io.smb2.in.fname = snamew;
841 status = smb2_create(tree, mem_ctx, &(io.smb2));
842 CHECK_STATUS(status, NT_STATUS_OK);
843 h3 = io.smb2.out.file.handle;
845 io.smb2.in.fname = snamew2;
846 status = smb2_create(tree, mem_ctx, &(io.smb2));
847 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
849 io.smb2.in.fname = fname;
850 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
851 status = smb2_create(tree, mem_ctx, &(io.smb2));
852 CHECK_STATUS(status, NT_STATUS_OK);
853 ret &= check_stream_list(tree, tctx, fname, 4, four,
854 io.smb2.out.file.handle);
856 smb2_util_close(tree, h1);
857 smb2_util_close(tree, h2);
858 smb2_util_close(tree, h3);
860 if (torture_setting_bool(tctx, "samba4", true)) {
861 goto done;
864 finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
865 finfo.generic.in.file.handle = io.smb2.out.file.handle;
866 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
867 CHECK_STATUS(status, NT_STATUS_OK);
868 ret &= check_stream_list(tree, tctx, fname, 4, four,
869 io.smb2.out.file.handle);
871 for (i=0; i < 4; i++) {
872 NTTIME write_time;
873 uint64_t stream_size;
874 char *path = talloc_asprintf(tctx, "%s%s",
875 fname, four[i]);
877 char *rpath = talloc_strdup(path, path);
878 char *p = strrchr(rpath, ':');
879 /* eat :$DATA */
880 *p = 0;
881 p--;
882 if (*p == ':') {
883 /* eat ::$DATA */
884 *p = 0;
886 torture_comment(tctx, "(%s): i[%u][%s]\n",
887 __location__, i,path);
888 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
889 io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
890 SEC_FILE_WRITE_ATTRIBUTE |
891 SEC_RIGHTS_FILE_ALL;
892 io.smb2.in.fname = path;
893 status = smb2_create(tree, mem_ctx, &(io.smb2));
894 CHECK_STATUS(status, NT_STATUS_OK);
895 h1 = io.smb2.out.file.handle;
897 finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
898 finfo.generic.in.file.path = fname;
899 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
900 CHECK_STATUS(status, NT_STATUS_OK);
902 stinfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
903 stinfo.generic.in.file.handle = h1;
904 status = smb2_getinfo_file(tree, mem_ctx, &stinfo);
905 CHECK_STATUS(status, NT_STATUS_OK);
906 if (!torture_setting_bool(tctx, "samba3", false)) {
907 CHECK_NTTIME(stinfo.all_info.out.create_time,
908 finfo.all_info.out.create_time);
909 CHECK_NTTIME(stinfo.all_info.out.access_time,
910 finfo.all_info.out.access_time);
911 CHECK_NTTIME(stinfo.all_info.out.write_time,
912 finfo.all_info.out.write_time);
913 CHECK_NTTIME(stinfo.all_info.out.change_time,
914 finfo.all_info.out.change_time);
916 CHECK_VALUE(stinfo.all_info.out.attrib,
917 finfo.all_info.out.attrib);
918 CHECK_VALUE(stinfo.all_info.out.size,
919 finfo.all_info.out.size);
920 CHECK_VALUE(stinfo.all_info.out.delete_pending,
921 finfo.all_info.out.delete_pending);
922 CHECK_VALUE(stinfo.all_info.out.directory,
923 finfo.all_info.out.directory);
924 CHECK_VALUE(stinfo.all_info.out.ea_size,
925 finfo.all_info.out.ea_size);
927 stinfo.generic.level = RAW_FILEINFO_NAME_INFORMATION;
928 stinfo.generic.in.file.handle = h1;
929 status = smb2_getinfo_file(tree, mem_ctx, &stinfo);
930 CHECK_STATUS(status, NT_STATUS_OK);
931 if (!torture_setting_bool(tctx, "samba3", false)) {
932 CHECK_STR(rpath, stinfo.name_info.out.fname.s);
935 write_time = finfo.all_info.out.write_time;
936 write_time += i*1000000;
937 write_time /= 1000000;
938 write_time *= 1000000;
940 ZERO_STRUCT(sinfo);
941 sinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
942 sinfo.basic_info.in.file.handle = h1;
943 sinfo.basic_info.in.write_time = write_time;
944 sinfo.basic_info.in.attrib = stinfo.all_info.out.attrib;
945 status = smb2_setinfo_file(tree, &sinfo);
946 CHECK_STATUS(status, NT_STATUS_OK);
948 stream_size = i*8192;
950 ZERO_STRUCT(sinfo);
951 sinfo.end_of_file_info.level =
952 RAW_SFILEINFO_END_OF_FILE_INFORMATION;
953 sinfo.end_of_file_info.in.file.handle = h1;
954 sinfo.end_of_file_info.in.size = stream_size;
955 status = smb2_setinfo_file(tree, &sinfo);
956 CHECK_STATUS(status, NT_STATUS_OK);
958 stinfo.generic.level = RAW_FILEINFO_ALL_INFORMATION;
959 stinfo.generic.in.file.handle = h1;
960 status = smb2_getinfo_file(tree, mem_ctx, &stinfo);
961 CHECK_STATUS(status, NT_STATUS_OK);
962 if (!torture_setting_bool(tctx, "samba3", false)) {
963 CHECK_NTTIME(stinfo.all_info.out.write_time,
964 write_time);
965 CHECK_VALUE(stinfo.all_info.out.attrib,
966 finfo.all_info.out.attrib);
968 CHECK_VALUE(stinfo.all_info.out.size,
969 stream_size);
970 CHECK_VALUE(stinfo.all_info.out.delete_pending,
971 finfo.all_info.out.delete_pending);
972 CHECK_VALUE(stinfo.all_info.out.directory,
973 finfo.all_info.out.directory);
974 CHECK_VALUE(stinfo.all_info.out.ea_size,
975 finfo.all_info.out.ea_size);
977 io.smb2.in.fname = fname;
978 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
979 status = smb2_create(tree, mem_ctx, &(io.smb2));
980 CHECK_STATUS(status, NT_STATUS_OK);
981 ret &= check_stream_list(tree, tctx, fname, 4, four,
982 io.smb2.out.file.handle);
984 smb2_util_close(tree, h1);
985 talloc_free(path);
988 torture_comment(tctx, "(%s): testing stream renames\n", __location__);
989 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
990 io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
991 SEC_FILE_WRITE_ATTRIBUTE |
992 SEC_RIGHTS_FILE_ALL;
993 io.smb2.in.fname = snamer1;
994 status = smb2_create(tree, mem_ctx, &(io.smb2));
995 CHECK_STATUS(status, NT_STATUS_OK);
996 h1 = io.smb2.out.file.handle;
997 ret &= check_stream_list(tree,tctx, fname, 5, five1,
998 io.smb2.out.file.handle);
1000 ZERO_STRUCT(sinfo);
1001 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1002 sinfo.rename_information.in.file.handle = h1;
1003 sinfo.rename_information.in.overwrite = true;
1004 sinfo.rename_information.in.root_fid = 0;
1005 sinfo.rename_information.in.new_name = ":AfterRename:$DATA";
1006 status = smb2_setinfo_file(tree, &sinfo);
1007 CHECK_STATUS(status, NT_STATUS_OK);
1009 ret &= check_stream_list(tree,tctx, fname, 5, five2,
1010 io.smb2.out.file.handle);
1012 ZERO_STRUCT(sinfo);
1013 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1014 sinfo.rename_information.in.file.handle = h1;
1015 sinfo.rename_information.in.overwrite = false;
1016 sinfo.rename_information.in.root_fid = 0;
1017 sinfo.rename_information.in.new_name = ":MStream Two:$DATA";
1018 status = smb2_setinfo_file(tree, &sinfo);
1019 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
1021 ret &= check_stream_list(tree,tctx, fname, 5, five2,
1022 io.smb2.out.file.handle);
1024 ZERO_STRUCT(sinfo);
1025 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1026 sinfo.rename_information.in.file.handle = h1;
1027 sinfo.rename_information.in.overwrite = true;
1028 sinfo.rename_information.in.root_fid = 0;
1029 sinfo.rename_information.in.new_name = ":MStream Two:$DATA";
1030 status = smb2_setinfo_file(tree, &sinfo);
1031 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1033 ret &= check_stream_list(tree,tctx, fname, 5, five2,
1034 io.smb2.out.file.handle);
1036 /* TODO: we need to test more rename combinations */
1038 done:
1039 smb2_util_close(tree, h1);
1040 status = smb2_util_unlink(tree, fname);
1041 smb2_deltree(tree, DNAME);
1042 talloc_free(mem_ctx);
1044 return ret;
1048 test stream names
1050 static bool test_stream_names2(struct torture_context *tctx,
1051 struct smb2_tree *tree)
1053 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1054 NTSTATUS status;
1055 union smb_open io;
1056 const char *fname = DNAME "\\stream_names2.txt";
1057 bool ret = true;
1058 struct smb2_handle h, h1;
1059 uint8_t i;
1061 smb2_util_unlink(tree, fname);
1062 smb2_deltree(tree, DNAME);
1064 status = torture_smb2_testdir(tree, DNAME, &h);
1065 CHECK_STATUS(status, NT_STATUS_OK);
1067 torture_comment(tctx, "(%s) testing stream names\n", __location__);
1068 ZERO_STRUCT(io.smb2);
1069 io.smb2.in.create_flags = 0;
1070 io.smb2.in.desired_access = SEC_FILE_WRITE_DATA;
1071 io.smb2.in.create_options = 0;
1072 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1073 io.smb2.in.share_access = 0;
1074 io.smb2.in.alloc_size = 0;
1075 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1076 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1077 io.smb2.in.security_flags = 0;
1078 io.smb2.in.fname = fname;
1079 status = smb2_create(tree, mem_ctx, &(io.smb2));
1080 CHECK_STATUS(status, NT_STATUS_OK);
1081 h1 = io.smb2.out.file.handle;
1083 for (i=0x01; i < 0x7F; i++) {
1084 char *path = talloc_asprintf(mem_ctx, "%s:Stream%c0x%02X:$DATA",
1085 fname, i, i);
1086 NTSTATUS expected;
1088 switch (i) {
1089 case '/':/*0x2F*/
1090 case ':':/*0x3A*/
1091 case '\\':/*0x5C*/
1092 expected = NT_STATUS_OBJECT_NAME_INVALID;
1093 break;
1094 default:
1095 expected = NT_STATUS_OBJECT_NAME_NOT_FOUND;
1096 break;
1100 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1101 io.smb2.in.fname = path;
1102 status = smb2_create(tree, mem_ctx, &(io.smb2));
1103 if (!NT_STATUS_EQUAL(status, expected)) {
1104 torture_comment(tctx,
1105 "(%s) %s:Stream%c0x%02X:$DATA%s => expected[%s]\n",
1106 __location__, fname, isprint(i)?(char)i:' ', i,
1107 isprint(i)?"":" (not printable)",
1108 nt_errstr(expected));
1110 CHECK_STATUS(status, expected);
1112 talloc_free(path);
1115 done:
1116 smb2_util_close(tree, h1);
1117 status = smb2_util_unlink(tree, fname);
1118 smb2_deltree(tree, DNAME);
1119 talloc_free(mem_ctx);
1121 return ret;
1124 #define CHECK_CALL_HANDLE(call, rightstatus) do { \
1125 check_handle = true; \
1126 call_name = #call; \
1127 sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
1128 sfinfo.generic.in.file.handle = h1; \
1129 status = smb2_setinfo_file(tree, &sfinfo); \
1130 if (!NT_STATUS_EQUAL(status, rightstatus)) { \
1131 torture_comment(tctx,"(%s) %s - %s (should be %s)\n", \
1132 __location__, #call, \
1133 nt_errstr(status), nt_errstr(rightstatus)); \
1134 ret = false; \
1136 finfo1.generic.level = RAW_FILEINFO_ALL_INFORMATION; \
1137 finfo1.generic.in.file.handle = h1; \
1138 status2 = smb2_getinfo_file(tree, tctx, &finfo1); \
1139 if (!NT_STATUS_IS_OK(status2)) { \
1140 torture_comment(tctx,"(%s) %s pathinfo - %s\n", \
1141 __location__, #call, nt_errstr(status)); \
1142 ret = false; \
1143 }} while (0)
1146 test stream renames
1148 static bool test_stream_rename(struct torture_context *tctx,
1149 struct smb2_tree *tree)
1151 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1152 NTSTATUS status, status2;
1153 union smb_open io;
1154 const char *fname = DNAME "\\stream_rename.txt";
1155 const char *sname1, *sname2;
1156 union smb_fileinfo finfo1;
1157 union smb_setfileinfo sfinfo;
1158 bool ret = true;
1159 struct smb2_handle h, h1;
1160 bool check_handle;
1161 const char *call_name;
1163 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
1164 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname,
1165 "Second Stream");
1167 smb2_util_unlink(tree, fname);
1168 smb2_deltree(tree, DNAME);
1170 status = torture_smb2_testdir(tree, DNAME, &h);
1171 CHECK_STATUS(status, NT_STATUS_OK);
1173 torture_comment(tctx, "(%s) testing stream renames\n", __location__);
1174 ZERO_STRUCT(io.smb2);
1175 io.smb2.in.create_flags = 0;
1176 io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE |
1177 SEC_FILE_WRITE_ATTRIBUTE |
1178 SEC_RIGHTS_FILE_ALL;
1179 io.smb2.in.create_options = 0;
1180 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1181 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1182 NTCREATEX_SHARE_ACCESS_WRITE |
1183 NTCREATEX_SHARE_ACCESS_DELETE;
1184 io.smb2.in.alloc_size = 0;
1185 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1186 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1187 io.smb2.in.security_flags = 0;
1188 io.smb2.in.fname = sname1;
1190 /* Create two streams. */
1191 status = smb2_create(tree, mem_ctx, &(io.smb2));
1192 CHECK_STATUS(status, NT_STATUS_OK);
1193 h1 = io.smb2.out.file.handle;
1194 smb2_util_close(tree, h1);
1196 io.smb2.in.fname = sname2;
1197 status = smb2_create(tree, mem_ctx, &(io.smb2));
1198 CHECK_STATUS(status, NT_STATUS_OK);
1199 h1 = io.smb2.out.file.handle;
1201 smb2_util_close(tree, h1);
1204 * Open the second stream.
1207 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1208 status = smb2_create(tree, mem_ctx, &(io.smb2));
1209 CHECK_STATUS(status, NT_STATUS_OK);
1210 h1 = io.smb2.out.file.handle;
1213 * Now rename the second stream onto the first.
1216 ZERO_STRUCT(sfinfo);
1218 sfinfo.rename_information.in.overwrite = 1;
1219 sfinfo.rename_information.in.root_fid = 0;
1220 sfinfo.rename_information.in.new_name = ":Stream One";
1221 CHECK_CALL_HANDLE(RENAME_INFORMATION, NT_STATUS_OK);
1222 done:
1223 smb2_util_close(tree, h1);
1224 status = smb2_util_unlink(tree, fname);
1225 smb2_deltree(tree, DNAME);
1226 talloc_free(mem_ctx);
1228 return ret;
1231 static bool test_stream_rename2(struct torture_context *tctx,
1232 struct smb2_tree *tree)
1234 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1235 NTSTATUS status;
1236 union smb_open io;
1237 const char *fname1 = DNAME "\\stream_rename2.txt";
1238 const char *fname2 = DNAME "\\stream2_rename2.txt";
1239 const char *stream_name1 = ":Stream One:$DATA";
1240 const char *stream_name2 = ":Stream Two:$DATA";
1241 const char *stream_name_default = "::$DATA";
1242 const char *sname1;
1243 const char *sname2;
1244 bool ret = true;
1245 struct smb2_handle h, h1;
1246 union smb_setfileinfo sinfo;
1248 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream One");
1249 sname2 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream Two");
1251 smb2_util_unlink(tree, fname1);
1252 smb2_util_unlink(tree, fname2);
1253 smb2_deltree(tree, DNAME);
1255 status = torture_smb2_testdir(tree, DNAME, &h);
1256 CHECK_STATUS(status, NT_STATUS_OK);
1258 ZERO_STRUCT(io.smb2);
1259 io.smb2.in.create_flags = 0;
1260 io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1261 SEC_FILE_WRITE_DATA |
1262 SEC_STD_DELETE |
1263 SEC_FILE_APPEND_DATA |
1264 SEC_STD_READ_CONTROL;
1265 io.smb2.in.create_options = 0;
1266 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1267 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1268 NTCREATEX_SHARE_ACCESS_WRITE |
1269 NTCREATEX_SHARE_ACCESS_DELETE;
1270 io.smb2.in.alloc_size = 0;
1271 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1272 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1273 io.smb2.in.security_flags = 0;
1274 io.smb2.in.fname = sname1;
1276 /* Open/create new stream. */
1277 status = smb2_create(tree, mem_ctx, &(io.smb2));
1278 CHECK_STATUS(status, NT_STATUS_OK);
1280 smb2_util_close(tree, io.smb2.out.file.handle);
1283 * Reopen the stream for SMB2 renames.
1285 io.smb2.in.fname = sname1;
1286 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1287 status = smb2_create(tree, mem_ctx, &(io.smb2));
1288 CHECK_STATUS(status, NT_STATUS_OK);
1289 h1 = io.smb2.out.file.handle;
1292 * Check SMB2 rename of a stream using :<stream>.
1294 torture_comment(tctx, "(%s) Checking SMB2 rename of a stream using "
1295 ":<stream>\n", __location__);
1296 ZERO_STRUCT(sinfo);
1297 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION_SMB2;
1298 sinfo.rename_information.in.file.handle = h1;
1299 sinfo.rename_information.in.overwrite = 1;
1300 sinfo.rename_information.in.root_fid = 0;
1301 sinfo.rename_information.in.new_name = stream_name1;
1302 status = smb2_setinfo_file(tree, &sinfo);
1303 CHECK_STATUS(status, NT_STATUS_OK);
1306 * Check SMB2 rename of an overwriting stream using :<stream>.
1308 torture_comment(tctx, "(%s) Checking SMB2 rename of an overwriting "
1309 "stream using :<stream>\n", __location__);
1311 /* Create second stream. */
1312 io.smb2.in.fname = sname2;
1313 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1314 status = smb2_create(tree, mem_ctx, &(io.smb2));
1315 CHECK_STATUS(status, NT_STATUS_OK);
1316 smb2_util_close(tree, io.smb2.out.file.handle);
1318 /* Rename the first stream onto the second. */
1319 sinfo.rename_information.in.file.handle = h1;
1320 sinfo.rename_information.in.new_name = stream_name2;
1321 status = smb2_setinfo_file(tree, &sinfo);
1322 CHECK_STATUS(status, NT_STATUS_OK);
1324 smb2_util_close(tree, h1);
1327 * Reopen the stream with the new name.
1329 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1330 io.smb2.in.fname = sname2;
1331 status = smb2_create(tree, mem_ctx, &(io.smb2));
1332 CHECK_STATUS(status, NT_STATUS_OK);
1333 h1 = io.smb2.out.file.handle;
1336 * Check SMB2 rename of a stream using <base>:<stream>.
1338 torture_comment(tctx, "(%s) Checking SMB2 rename of a stream using "
1339 "<base>:<stream>\n", __location__);
1340 sinfo.rename_information.in.file.handle = h1;
1341 sinfo.rename_information.in.new_name = sname1;
1342 status = smb2_setinfo_file(tree, &sinfo);
1343 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
1345 if (!torture_setting_bool(tctx, "samba4", false)) {
1347 * Check SMB2 rename to the default stream using :<stream>.
1349 torture_comment(tctx, "(%s) Checking SMB2 rename to default stream "
1350 "using :<stream>\n", __location__);
1351 sinfo.rename_information.in.file.handle = h1;
1352 sinfo.rename_information.in.new_name = stream_name_default;
1353 status = smb2_setinfo_file(tree, &sinfo);
1354 CHECK_STATUS(status, NT_STATUS_OK);
1357 smb2_util_close(tree, h1);
1359 done:
1360 smb2_util_close(tree, h1);
1361 status = smb2_util_unlink(tree, fname1);
1362 status = smb2_util_unlink(tree, fname2);
1363 smb2_deltree(tree, DNAME);
1364 talloc_free(mem_ctx);
1366 return ret;
1369 static bool create_file_with_stream(struct torture_context *tctx,
1370 struct smb2_tree *tree,
1371 TALLOC_CTX *mem_ctx,
1372 const char *base_fname,
1373 const char *stream)
1375 NTSTATUS status;
1376 bool ret = true;
1377 union smb_open io;
1379 /* Create a file with a stream */
1380 ZERO_STRUCT(io.smb2);
1381 io.smb2.in.create_flags = 0;
1382 io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1383 SEC_FILE_WRITE_DATA |
1384 SEC_FILE_APPEND_DATA |
1385 SEC_STD_READ_CONTROL;
1386 io.smb2.in.create_options = 0;
1387 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1388 io.smb2.in.share_access = 0;
1389 io.smb2.in.alloc_size = 0;
1390 io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE;
1391 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1392 io.smb2.in.security_flags = 0;
1393 io.smb2.in.fname = stream;
1395 status = smb2_create(tree, mem_ctx, &(io.smb2));
1396 CHECK_STATUS(status, NT_STATUS_OK);
1398 done:
1399 smb2_util_close(tree, io.smb2.out.file.handle);
1400 return ret;
1404 /* Test how streams interact with create dispositions */
1405 static bool test_stream_create_disposition(struct torture_context *tctx,
1406 struct smb2_tree *tree)
1408 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1409 NTSTATUS status;
1410 union smb_open io;
1411 const char *fname = DNAME "\\stream_create_disp.txt";
1412 const char *stream = "Stream One:$DATA";
1413 const char *fname_stream;
1414 const char *default_stream_name = "::$DATA";
1415 const char *stream_list[2];
1416 bool ret = true;
1417 struct smb2_handle h, h1;
1419 /* clean slate .. */
1420 smb2_util_unlink(tree, fname);
1421 smb2_deltree(tree, fname);
1422 smb2_deltree(tree, DNAME);
1424 status = torture_smb2_testdir(tree, DNAME, &h);
1425 CHECK_STATUS(status, NT_STATUS_OK);
1427 fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream);
1429 stream_list[0] = talloc_asprintf(mem_ctx, ":%s", stream);
1430 stream_list[1] = default_stream_name;
1432 if (!create_file_with_stream(tctx, tree, mem_ctx, fname,
1433 fname_stream)) {
1434 goto done;
1437 /* Open the base file with OPEN */
1438 ZERO_STRUCT(io.smb2);
1439 io.smb2.in.create_flags = 0;
1440 io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1441 SEC_FILE_WRITE_DATA |
1442 SEC_FILE_APPEND_DATA |
1443 SEC_STD_READ_CONTROL;
1444 io.smb2.in.create_options = 0;
1445 io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1446 io.smb2.in.share_access = 0;
1447 io.smb2.in.alloc_size = 0;
1448 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1449 io.smb2.in.security_flags = 0;
1450 io.smb2.in.fname = fname;
1453 * check create open: sanity check
1455 torture_comment(tctx, "(%s) Checking create disp: open\n",
1456 __location__);
1457 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1458 status = smb2_create(tree, mem_ctx, &(io.smb2));
1459 CHECK_STATUS(status, NT_STATUS_OK);
1460 if (!check_stream_list(tree, tctx, fname, 2, stream_list,
1461 io.smb2.out.file.handle)) {
1462 goto done;
1464 smb2_util_close(tree, io.smb2.out.file.handle);
1467 * check create overwrite
1469 torture_comment(tctx, "(%s) Checking create disp: overwrite\n",
1470 __location__);
1471 io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
1472 status = smb2_create(tree, mem_ctx, &(io.smb2));
1473 CHECK_STATUS(status, NT_STATUS_OK);
1474 if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name,
1475 io.smb2.out.file.handle)) {
1476 goto done;
1478 smb2_util_close(tree, io.smb2.out.file.handle);
1481 * check create overwrite_if
1483 torture_comment(tctx, "(%s) Checking create disp: overwrite_if\n",
1484 __location__);
1485 smb2_util_unlink(tree, fname);
1486 if (!create_file_with_stream(tctx, tree, mem_ctx, fname, fname_stream))
1487 goto done;
1489 io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1490 status = smb2_create(tree, mem_ctx, &(io.smb2));
1491 CHECK_STATUS(status, NT_STATUS_OK);
1492 if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name,
1493 io.smb2.out.file.handle)) {
1494 goto done;
1496 smb2_util_close(tree, io.smb2.out.file.handle);
1499 * check create supersede
1501 torture_comment(tctx, "(%s) Checking create disp: supersede\n",
1502 __location__);
1503 smb2_util_unlink(tree, fname);
1504 if (!create_file_with_stream(tctx, tree, mem_ctx, fname,
1505 fname_stream)) {
1506 goto done;
1509 io.smb2.in.create_disposition = NTCREATEX_DISP_SUPERSEDE;
1510 status = smb2_create(tree, mem_ctx, &(io.smb2));
1511 CHECK_STATUS(status, NT_STATUS_OK);
1512 if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name,
1513 io.smb2.out.file.handle)) {
1514 goto done;
1516 smb2_util_close(tree, io.smb2.out.file.handle);
1519 * check create overwrite_if on a stream.
1521 torture_comment(tctx, "(%s) Checking create disp: overwrite_if on "
1522 "stream\n", __location__);
1523 smb2_util_unlink(tree, fname);
1524 if (!create_file_with_stream(tctx, tree, mem_ctx, fname,
1525 fname_stream)) {
1526 goto done;
1529 io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1530 io.smb2.in.fname = fname_stream;
1531 status = smb2_create(tree, mem_ctx, &(io.smb2));
1532 CHECK_STATUS(status, NT_STATUS_OK);
1533 if (!check_stream_list(tree, tctx, fname, 2, stream_list,
1534 io.smb2.out.file.handle)) {
1535 goto done;
1537 smb2_util_close(tree, io.smb2.out.file.handle);
1538 done:
1539 smb2_util_close(tree, h1);
1540 smb2_util_unlink(tree, fname);
1541 smb2_deltree(tree, DNAME);
1542 talloc_free(mem_ctx);
1544 return ret;
1547 static bool open_stream(struct smb2_tree *tree,
1548 struct torture_context *mem_ctx,
1549 const char *fname,
1550 struct smb2_handle *h_out)
1552 NTSTATUS status;
1553 union smb_open io;
1555 ZERO_STRUCT(io.smb2);
1556 io.smb2.in.create_flags = 0;
1557 io.smb2.in.desired_access = SEC_FILE_READ_DATA |
1558 SEC_FILE_WRITE_DATA |
1559 SEC_FILE_APPEND_DATA |
1560 SEC_STD_READ_CONTROL |
1561 SEC_FILE_WRITE_ATTRIBUTE;
1562 io.smb2.in.create_options = 0;
1563 io.smb2.in.file_attributes = 0;
1564 io.smb2.in.share_access = 0;
1565 io.smb2.in.alloc_size = 0;
1566 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
1567 io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
1568 io.smb2.in.security_flags = 0;
1569 io.smb2.in.fname = fname;
1571 status = smb2_create(tree, mem_ctx, &(io.smb2));
1572 if (!NT_STATUS_IS_OK(status)) {
1573 return false;
1575 *h_out = io.smb2.out.file.handle;
1576 return true;
1580 /* Test the effect of setting attributes on a stream. */
1581 static bool test_stream_attributes(struct torture_context *tctx,
1582 struct smb2_tree *tree)
1584 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1585 bool ret = true;
1586 NTSTATUS status;
1587 union smb_open io;
1588 const char *fname = DNAME "\\stream_attr.txt";
1589 const char *stream = "Stream One:$DATA";
1590 const char *fname_stream;
1591 struct smb2_handle h, h1;
1592 union smb_fileinfo finfo;
1593 union smb_setfileinfo sfinfo;
1594 time_t basetime = (time(NULL) - 86400) & ~1;
1596 torture_comment(tctx, "(%s) testing attribute setting on stream\n",
1597 __location__);
1599 /* clean slate .. */
1600 smb2_util_unlink(tree, fname);
1601 smb2_deltree(tree, fname);
1602 smb2_deltree(tree, DNAME);
1604 status = torture_smb2_testdir(tree, DNAME, &h);
1605 CHECK_STATUS(status, NT_STATUS_OK);
1607 fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream);
1609 /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */
1610 ret = create_file_with_stream(tctx, tree, mem_ctx, fname,
1611 fname_stream);
1612 if (!ret) {
1613 goto done;
1616 ZERO_STRUCT(io.smb2);
1617 io.smb2.in.fname = fname;
1618 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1619 io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
1620 io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1621 NTCREATEX_SHARE_ACCESS_WRITE |
1622 NTCREATEX_SHARE_ACCESS_DELETE;
1623 status = smb2_create(tree, mem_ctx, &(io.smb2));
1624 CHECK_STATUS(status, NT_STATUS_OK);
1626 ZERO_STRUCT(finfo);
1627 finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1628 finfo.generic.in.file.handle = io.smb2.out.file.handle;
1629 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1630 CHECK_STATUS(status, NT_STATUS_OK);
1632 if (finfo.basic_info.out.attrib != FILE_ATTRIBUTE_ARCHIVE) {
1633 torture_comment(tctx, "(%s) Incorrect attrib %x - should be "
1634 "%x\n", __location__,
1635 (unsigned int)finfo.basic_info.out.attrib,
1636 (unsigned int)FILE_ATTRIBUTE_ARCHIVE);
1637 ret = false;
1638 goto done;
1641 smb2_util_close(tree, io.smb2.out.file.handle);
1642 /* Now open the stream name. */
1644 if (!open_stream(tree, tctx, fname_stream, &h1)) {
1645 goto done;
1648 /* Change the time on the stream. */
1649 ZERO_STRUCT(sfinfo);
1650 unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime);
1651 sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1652 sfinfo.generic.in.file.handle = h1;
1653 status = smb2_setinfo_file(tree, &sfinfo);
1654 if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
1655 torture_comment(tctx, "(%s) %s - %s (should be %s)\n",
1656 __location__, "SETATTR",
1657 nt_errstr(status), nt_errstr(NT_STATUS_OK));
1658 ret = false;
1659 goto done;
1662 smb2_util_close(tree, h1);
1664 ZERO_STRUCT(io.smb2);
1665 io.smb2.in.fname = fname;
1666 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1667 io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
1668 status = smb2_create(tree, mem_ctx, &(io.smb2));
1669 CHECK_STATUS(status, NT_STATUS_OK);
1670 h1 = io.smb2.out.file.handle;
1672 ZERO_STRUCT(finfo);
1673 finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1674 finfo.generic.in.file.handle = h1;
1675 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1676 if (!NT_STATUS_IS_OK(status)) {
1677 torture_comment(tctx, "(%s) %s pathinfo - %s\n",
1678 __location__, "SETATTRE", nt_errstr(status));
1679 ret = false;
1680 goto done;
1683 if (nt_time_to_unix(finfo.basic_info.out.write_time) != basetime) {
1684 torture_comment(tctx, "(%s) time incorrect.\n", __location__);
1685 ret = false;
1686 goto done;
1688 smb2_util_close(tree, h1);
1690 if (!open_stream(tree, tctx, fname_stream, &h1)) {
1691 goto done;
1694 /* Changing attributes on stream */
1695 ZERO_STRUCT(sfinfo);
1696 sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY;
1698 sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1699 sfinfo.generic.in.file.handle = h1;
1700 status = smb2_setinfo_file(tree, &sfinfo);
1701 if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
1702 torture_comment(tctx, "(%s) %s - %s (should be %s)\n",
1703 __location__, "SETATTR",
1704 nt_errstr(status), nt_errstr(NT_STATUS_OK));
1705 ret = false;
1706 goto done;
1709 smb2_util_close(tree, h1);
1711 ZERO_STRUCT(io.smb2);
1712 io.smb2.in.fname = fname;
1713 io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN;
1714 io.smb2.in.desired_access = SEC_FILE_READ_DATA;
1715 status = smb2_create(tree, mem_ctx, &(io.smb2));
1716 CHECK_STATUS(status, NT_STATUS_OK);
1717 h1 = io.smb2.out.file.handle;
1719 ZERO_STRUCT(finfo);
1720 finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1721 finfo.generic.in.file.handle = h1;
1722 status = smb2_getinfo_file(tree, mem_ctx, &finfo);
1723 CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
1725 done:
1726 smb2_util_close(tree, h1);
1727 smb2_util_unlink(tree, fname);
1728 smb2_deltree(tree, DNAME);
1729 talloc_free(mem_ctx);
1731 return ret;
1736 basic testing of streams calls SMB2
1738 struct torture_suite *torture_smb2_streams_init(void)
1740 struct torture_suite *suite =
1741 torture_suite_create(talloc_autofree_context(), "STREAMS");
1743 torture_suite_add_1smb2_test(suite, "DIR", test_stream_dir);
1744 torture_suite_add_1smb2_test(suite, "IO", test_stream_io);
1745 torture_suite_add_1smb2_test(suite, "SHAREMODES", test_stream_sharemodes);
1746 torture_suite_add_1smb2_test(suite, "NAMES", test_stream_names);
1747 torture_suite_add_1smb2_test(suite, "NAMES2", test_stream_names2);
1748 torture_suite_add_1smb2_test(suite, "RENAME", test_stream_rename);
1749 torture_suite_add_1smb2_test(suite, "RENAME2", test_stream_rename2);
1750 torture_suite_add_1smb2_test(suite, "CREATE-DISPOSITION", test_stream_create_disposition);
1751 torture_suite_add_1smb2_test(suite, "ATTRIBUTES", test_stream_attributes);
1752 torture_suite_add_1smb2_test(suite, "DELETE", test_stream_delete);
1754 suite->description = talloc_strdup(suite, "SMB2-STREAM tests");
1756 return suite;