2 Unix SMB/CIFS implementation.
4 test alternate data streams
6 Copyright (C) Andrew Tridgell 2004
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "system/locale.h"
24 #include "torture/torture.h"
25 #include "libcli/raw/libcliraw.h"
26 #include "system/filesys.h"
27 #include "libcli/libcli.h"
28 #include "torture/util.h"
30 #define BASEDIR "\\teststreams"
32 #define CHECK_STATUS(status, correct) do { \
33 if (!NT_STATUS_EQUAL(status, correct)) { \
34 printf("(%s) Incorrect status %s - should be %s\n", \
35 __location__, nt_errstr(status), nt_errstr(correct)); \
40 #define CHECK_VALUE(v, correct) do { \
41 if ((v) != (correct)) { \
42 printf("(%s) Incorrect value %s=%d - should be %d\n", \
43 __location__, #v, (int)v, (int)correct); \
48 check that a stream has the right contents
50 static bool check_stream(struct smbcli_state *cli, const char *location,
52 const char *fname, const char *sname,
56 const char *full_name;
60 full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname);
62 fnum = smbcli_open(cli->tree, full_name, O_RDONLY, DENY_NONE);
66 printf("(%s) should have failed stream open of %s\n",
74 printf("(%s) Failed to open stream '%s' - %s\n",
75 location, full_name, smbcli_errstr(cli->tree));
79 buf = talloc_array(mem_ctx, uint8_t, strlen(value)+11);
81 ret = smbcli_read(cli->tree, fnum, buf, 0, strlen(value)+11);
82 if (ret != strlen(value)) {
83 printf("(%s) Failed to read %lu bytes from stream '%s' - got %d\n",
84 location, (long)strlen(value), full_name, (int)ret);
88 if (memcmp(buf, value, strlen(value)) != 0) {
89 printf("(%s) Bad data in stream\n", location);
93 smbcli_close(cli->tree, fnum);
97 static int qsort_string(const void *v1, const void *v2)
99 char * const *s1 = v1;
100 char * const *s2 = v2;
101 return strcmp(*s1, *s2);
104 static int qsort_stream(const void *v1, const void *v2)
106 const struct stream_struct * s1 = v1;
107 const struct stream_struct * s2 = v2;
108 return strcmp(s1->stream_name.s, s2->stream_name.s);
111 static bool check_stream_list(struct smbcli_state *cli, const char *fname,
112 int num_exp, const char **exp)
114 union smb_fileinfo finfo;
117 TALLOC_CTX *tmp_ctx = talloc_new(cli);
119 struct stream_struct *stream_sort;
122 finfo.generic.level = RAW_FILEINFO_STREAM_INFO;
123 finfo.generic.in.file.path = fname;
125 status = smb_raw_pathinfo(cli->tree, tmp_ctx, &finfo);
126 if (!NT_STATUS_IS_OK(status)) {
127 d_fprintf(stderr, "(%s) smb_raw_pathinfo failed: %s\n",
128 __location__, nt_errstr(status));
132 if (finfo.stream_info.out.num_streams != num_exp) {
133 d_fprintf(stderr, "(%s) expected %d streams, got %d\n",
134 __location__, num_exp,
135 finfo.stream_info.out.num_streams);
144 exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
146 if (exp_sort == NULL) {
150 qsort(exp_sort, num_exp, sizeof(*exp_sort), qsort_string);
152 stream_sort = talloc_memdup(tmp_ctx, finfo.stream_info.out.streams,
153 finfo.stream_info.out.num_streams *
154 sizeof(*stream_sort));
156 if (stream_sort == NULL) {
160 qsort(stream_sort, finfo.stream_info.out.num_streams,
161 sizeof(*stream_sort), qsort_stream);
163 for (i=0; i<num_exp; i++) {
164 if (strcmp(exp_sort[i], stream_sort[i].stream_name.s) != 0) {
165 d_fprintf(stderr, "(%s) expected stream name %s, got "
166 "%s\n", __location__, exp_sort[i],
167 stream_sort[i].stream_name.s);
174 talloc_free(tmp_ctx);
179 test bahavior of streams on directories
181 static bool test_stream_dir(struct torture_context *tctx,
182 struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
186 const char *fname = BASEDIR "\\stream.txt";
189 const char *basedir_data;
191 basedir_data = talloc_asprintf(mem_ctx, "%s::$DATA", BASEDIR);
192 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
194 printf("(%s) opening non-existant directory stream\n", __location__);
195 io.generic.level = RAW_OPEN_NTCREATEX;
196 io.ntcreatex.in.root_fid = 0;
197 io.ntcreatex.in.flags = 0;
198 io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
199 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
200 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
201 io.ntcreatex.in.share_access = 0;
202 io.ntcreatex.in.alloc_size = 0;
203 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
204 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
205 io.ntcreatex.in.security_flags = 0;
206 io.ntcreatex.in.fname = sname1;
207 status = smb_raw_open(cli->tree, mem_ctx, &io);
208 CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
210 printf("(%s) opening basedir stream\n", __location__);
211 io.generic.level = RAW_OPEN_NTCREATEX;
212 io.ntcreatex.in.root_fid = 0;
213 io.ntcreatex.in.flags = 0;
214 io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
215 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
216 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY;
217 io.ntcreatex.in.share_access = 0;
218 io.ntcreatex.in.alloc_size = 0;
219 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
220 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
221 io.ntcreatex.in.security_flags = 0;
222 io.ntcreatex.in.fname = basedir_data;
223 status = smb_raw_open(cli->tree, mem_ctx, &io);
224 CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
226 printf("(%s) opening basedir ::$DATA stream\n", __location__);
227 io.generic.level = RAW_OPEN_NTCREATEX;
228 io.ntcreatex.in.root_fid = 0;
229 io.ntcreatex.in.flags = 0x10;
230 io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
231 io.ntcreatex.in.create_options = 0;
232 io.ntcreatex.in.file_attr = 0;
233 io.ntcreatex.in.share_access = 0;
234 io.ntcreatex.in.alloc_size = 0;
235 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
236 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
237 io.ntcreatex.in.security_flags = 0;
238 io.ntcreatex.in.fname = basedir_data;
239 status = smb_raw_open(cli->tree, mem_ctx, &io);
240 if (torture_setting_bool(tctx, "samba3", false)) {
241 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
243 CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
246 printf("(%s) list the streams on the basedir\n", __location__);
247 ret &= check_stream_list(cli, BASEDIR, 0, NULL);
253 test basic behavior of streams on directories
255 static bool test_stream_io(struct torture_context *tctx,
256 struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
260 const char *fname = BASEDIR "\\stream.txt";
261 const char *sname1, *sname2;
266 const char *one[] = { "::$DATA" };
267 const char *two[] = { "::$DATA", ":Second Stream:$DATA" };
268 const char *three[] = { "::$DATA", ":Stream One:$DATA",
269 ":Second Stream:$DATA" };
271 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
272 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "Second Stream");
274 printf("(%s) creating a stream on a non-existant file\n", __location__);
275 io.generic.level = RAW_OPEN_NTCREATEX;
276 io.ntcreatex.in.root_fid = 0;
277 io.ntcreatex.in.flags = 0;
278 io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
279 io.ntcreatex.in.create_options = 0;
280 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
281 io.ntcreatex.in.share_access = 0;
282 io.ntcreatex.in.alloc_size = 0;
283 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
284 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
285 io.ntcreatex.in.security_flags = 0;
286 io.ntcreatex.in.fname = sname1;
287 status = smb_raw_open(cli->tree, mem_ctx, &io);
288 CHECK_STATUS(status, NT_STATUS_OK);
289 fnum = io.ntcreatex.out.file.fnum;
291 ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One", NULL);
293 printf("(%s) check that open of base file is allowed\n", __location__);
294 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
295 io.ntcreatex.in.fname = fname;
296 status = smb_raw_open(cli->tree, mem_ctx, &io);
297 CHECK_STATUS(status, NT_STATUS_OK);
298 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
300 printf("(%s) writing to stream\n", __location__);
301 retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9);
302 CHECK_VALUE(retsize, 9);
304 smbcli_close(cli->tree, fnum);
306 ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One", "test data");
308 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
309 io.ntcreatex.in.fname = sname1;
310 status = smb_raw_open(cli->tree, mem_ctx, &io);
311 CHECK_STATUS(status, NT_STATUS_OK);
312 fnum = io.ntcreatex.out.file.fnum;
314 printf("(%s) modifying stream\n", __location__);
315 retsize = smbcli_write(cli->tree, fnum, 0, "MORE DATA ", 5, 10);
316 CHECK_VALUE(retsize, 10);
318 smbcli_close(cli->tree, fnum);
320 ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:$FOO", NULL);
322 printf("(%s) creating a stream2 on a existing file\n", __location__);
323 io.ntcreatex.in.fname = sname2;
324 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
325 status = smb_raw_open(cli->tree, mem_ctx, &io);
326 CHECK_STATUS(status, NT_STATUS_OK);
327 fnum = io.ntcreatex.out.file.fnum;
329 printf("(%s) modifying stream\n", __location__);
330 retsize = smbcli_write(cli->tree, fnum, 0, "SECOND STREAM", 0, 13);
331 CHECK_VALUE(retsize, 13);
333 smbcli_close(cli->tree, fnum);
335 ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One", "test MORE DATA ");
336 ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:$DATA", "test MORE DATA ");
337 ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:", NULL);
338 ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream", "SECOND STREAM");
339 if (!torture_setting_bool(tctx, "samba4", false)) {
340 ret &= check_stream(cli, __location__, mem_ctx, fname,
341 "SECOND STREAM:$DATA", "SECOND STREAM");
343 ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:$DATA", "SECOND STREAM");
344 ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:", NULL);
345 ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:$FOO", NULL);
347 check_stream_list(cli, fname, 3, three);
349 printf("(%s) deleting stream\n", __location__);
350 status = smbcli_unlink(cli->tree, sname1);
351 CHECK_STATUS(status, NT_STATUS_OK);
353 check_stream_list(cli, fname, 2, two);
355 printf("(%s) delete a stream via delete-on-close\n", __location__);
356 io.ntcreatex.in.fname = sname2;
357 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
358 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
359 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
360 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
362 status = smb_raw_open(cli->tree, mem_ctx, &io);
363 CHECK_STATUS(status, NT_STATUS_OK);
364 fnum = io.ntcreatex.out.file.fnum;
366 smbcli_close(cli->tree, fnum);
367 status = smbcli_unlink(cli->tree, sname2);
368 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
370 check_stream_list(cli, fname, 1, one);
372 if (!torture_setting_bool(tctx, "samba4", false)) {
373 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
374 io.ntcreatex.in.fname = sname1;
375 status = smb_raw_open(cli->tree, mem_ctx, &io);
376 CHECK_STATUS(status, NT_STATUS_OK);
377 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
378 io.ntcreatex.in.fname = sname2;
379 status = smb_raw_open(cli->tree, mem_ctx, &io);
380 CHECK_STATUS(status, NT_STATUS_OK);
381 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
384 printf("(%s) deleting file\n", __location__);
385 status = smbcli_unlink(cli->tree, fname);
386 CHECK_STATUS(status, NT_STATUS_OK);
389 smbcli_close(cli->tree, fnum);
394 test stream sharemodes
396 static bool test_stream_sharemodes(struct torture_context *tctx,
397 struct smbcli_state *cli,
402 const char *fname = BASEDIR "\\stream.txt";
403 const char *sname1, *sname2;
408 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
409 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "Second Stream");
411 printf("(%s) testing stream share mode conflicts\n", __location__);
412 io.generic.level = RAW_OPEN_NTCREATEX;
413 io.ntcreatex.in.root_fid = 0;
414 io.ntcreatex.in.flags = 0;
415 io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
416 io.ntcreatex.in.create_options = 0;
417 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
418 io.ntcreatex.in.share_access = 0;
419 io.ntcreatex.in.alloc_size = 0;
420 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
421 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
422 io.ntcreatex.in.security_flags = 0;
423 io.ntcreatex.in.fname = sname1;
425 status = smb_raw_open(cli->tree, mem_ctx, &io);
426 CHECK_STATUS(status, NT_STATUS_OK);
427 fnum1 = io.ntcreatex.out.file.fnum;
430 * A different stream does not give a sharing violation
433 io.ntcreatex.in.fname = sname2;
434 status = smb_raw_open(cli->tree, mem_ctx, &io);
435 CHECK_STATUS(status, NT_STATUS_OK);
436 fnum2 = io.ntcreatex.out.file.fnum;
439 * ... whereas the same stream does with unchanged access/share_access
443 io.ntcreatex.in.fname = sname1;
444 io.ntcreatex.in.open_disposition = 0;
445 status = smb_raw_open(cli->tree, mem_ctx, &io);
446 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
448 io.ntcreatex.in.fname = sname2;
449 status = smb_raw_open(cli->tree, mem_ctx, &io);
450 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
453 if (fnum1 != -1) smbcli_close(cli->tree, fnum1);
454 if (fnum2 != -1) smbcli_close(cli->tree, fnum2);
455 status = smbcli_unlink(cli->tree, fname);
460 * Test FILE_SHARE_DELETE on streams
462 * A stream opened with !FILE_SHARE_DELETE prevents the main file to be opened
463 * with SEC_STD_DELETE.
465 * The main file opened with !FILE_SHARE_DELETE does *not* prevent a stream to
466 * be opened with SEC_STD_DELETE.
468 * A stream held open with FILE_SHARE_DELETE allows the file to be
469 * deleted. After the main file is deleted, access to the open file descriptor
470 * still works, but all name-based access to both the main file as well as the
471 * stream is denied with DELETE ending.
473 * This means, an open of the main file with SEC_STD_DELETE should walk all
474 * streams and also open them with SEC_STD_DELETE. If any of these opens gives
475 * SHARING_VIOLATION, the main open fails.
477 * Closing the main file after delete_on_close has been set does not really
478 * unlink it but leaves the corresponding share mode entry with
479 * delete_on_close being set around until all streams are closed.
481 * Opening a stream must also look at the main file's share mode entry, look
482 * at the delete_on_close bit and potentially return DELETE_PENDING.
485 static bool test_stream_delete(struct torture_context *tctx,
486 struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
490 const char *fname = BASEDIR "\\stream.txt";
496 union smb_fileinfo finfo;
498 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
500 printf("(%s) opening non-existant file stream\n", __location__);
501 io.generic.level = RAW_OPEN_NTCREATEX;
502 io.ntcreatex.in.root_fid = 0;
503 io.ntcreatex.in.flags = 0;
504 io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
505 io.ntcreatex.in.create_options = 0;
506 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
507 io.ntcreatex.in.share_access = 0;
508 io.ntcreatex.in.alloc_size = 0;
509 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
510 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
511 io.ntcreatex.in.security_flags = 0;
512 io.ntcreatex.in.fname = sname1;
514 status = smb_raw_open(cli->tree, mem_ctx, &io);
515 CHECK_STATUS(status, NT_STATUS_OK);
516 fnum = io.ntcreatex.out.file.fnum;
518 retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9);
519 CHECK_VALUE(retsize, 9);
522 * One stream opened without FILE_SHARE_DELETE prevents the main file
523 * to be deleted or even opened with DELETE access
526 status = smbcli_unlink(cli->tree, fname);
527 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
529 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
530 io.ntcreatex.in.fname = fname;
531 io.ntcreatex.in.access_mask = SEC_STD_DELETE;
532 status = smb_raw_open(cli->tree, mem_ctx, &io);
533 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
535 smbcli_close(cli->tree, fnum);
538 * ... but unlink works if a stream is opened with FILE_SHARE_DELETE
541 io.ntcreatex.in.fname = sname1;
542 io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
543 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
544 status = smb_raw_open(cli->tree, mem_ctx, &io);
545 CHECK_STATUS(status, NT_STATUS_OK);
546 fnum = io.ntcreatex.out.file.fnum;
548 status = smbcli_unlink(cli->tree, fname);
549 CHECK_STATUS(status, NT_STATUS_OK);
552 * file access still works on the stream while the main file is closed
555 retsize = smbcli_read(cli->tree, fnum, buf, 0, 9);
556 CHECK_VALUE(retsize, 9);
558 finfo.generic.level = RAW_FILEINFO_STANDARD;
559 finfo.generic.in.file.path = fname;
562 * name-based access to both the main file and the stream does not
563 * work anymore but gives DELETE_PENDING
566 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
567 CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
569 if (!torture_setting_bool(tctx, "samba3", false)) {
572 * S3 doesn't do this yet
575 finfo.generic.in.file.path = sname1;
576 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
577 CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
581 * fd-based qfileinfo on the stream still works, the stream does not
582 * have the delete-on-close bit set. This could mean that open on the
583 * stream first opens the main file
586 finfo.all_info.level = RAW_FILEINFO_ALL_INFO;
587 finfo.all_info.in.file.fnum = fnum;
589 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo);
590 CHECK_STATUS(status, NT_STATUS_OK);
591 CHECK_VALUE(finfo.all_info.out.delete_pending, 0);
593 smbcli_close(cli->tree, fnum);
596 * After closing the stream the file is really gone.
599 finfo.generic.in.file.path = fname;
600 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
601 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
603 io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA
605 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
606 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
607 status = smb_raw_open(cli->tree, mem_ctx, &io);
608 CHECK_STATUS(status, NT_STATUS_OK);
609 fnum = io.ntcreatex.out.file.fnum;
611 finfo.generic.in.file.path = fname;
612 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
613 CHECK_STATUS(status, NT_STATUS_OK);
615 smbcli_close(cli->tree, fnum);
617 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
618 CHECK_STATUS(status, NT_STATUS_OK);
620 smbcli_close(cli->tree, fnum);
627 static bool test_stream_names(struct torture_context *tctx,
628 struct smbcli_state *cli,
633 const char *fname = BASEDIR "\\stream_names.txt";
634 const char *sname1, *sname1b, *sname1c, *sname1d;
635 const char *sname2, *snamew, *snamew2;
639 const char *four[4] = {
641 ":\x05Stream\n One:$DATA",
642 ":MStream Two:$DATA",
646 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "\x05Stream\n One");
647 sname1b = talloc_asprintf(mem_ctx, "%s:", sname1);
648 sname1c = talloc_asprintf(mem_ctx, "%s:$FOO", sname1);
649 sname1d = talloc_asprintf(mem_ctx, "%s:?D*a", sname1);
650 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "MStream Two");
651 snamew = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "?Stream*");
652 snamew2 = talloc_asprintf(mem_ctx, "%s\\stream*:%s:$DATA", BASEDIR, "?Stream*");
654 printf("(%s) testing stream names\n", __location__);
655 io.generic.level = RAW_OPEN_NTCREATEX;
656 io.ntcreatex.in.root_fid = 0;
657 io.ntcreatex.in.flags = 0;
658 io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
659 io.ntcreatex.in.create_options = 0;
660 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
661 io.ntcreatex.in.share_access = 0;
662 io.ntcreatex.in.alloc_size = 0;
663 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
664 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
665 io.ntcreatex.in.security_flags = 0;
666 io.ntcreatex.in.fname = sname1;
668 status = smb_raw_open(cli->tree, mem_ctx, &io);
669 CHECK_STATUS(status, NT_STATUS_OK);
670 fnum1 = io.ntcreatex.out.file.fnum;
673 * A different stream does not give a sharing violation
676 io.ntcreatex.in.fname = sname2;
677 status = smb_raw_open(cli->tree, mem_ctx, &io);
678 CHECK_STATUS(status, NT_STATUS_OK);
679 fnum2 = io.ntcreatex.out.file.fnum;
682 * ... whereas the same stream does with unchanged access/share_access
686 io.ntcreatex.in.fname = sname1;
687 io.ntcreatex.in.open_disposition = 0;
688 status = smb_raw_open(cli->tree, mem_ctx, &io);
689 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
691 io.ntcreatex.in.fname = sname1b;
692 status = smb_raw_open(cli->tree, mem_ctx, &io);
693 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
695 io.ntcreatex.in.fname = sname1c;
696 status = smb_raw_open(cli->tree, mem_ctx, &io);
697 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
699 io.ntcreatex.in.fname = sname1d;
700 status = smb_raw_open(cli->tree, mem_ctx, &io);
701 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
703 io.ntcreatex.in.fname = sname2;
704 status = smb_raw_open(cli->tree, mem_ctx, &io);
705 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
707 io.ntcreatex.in.fname = snamew;
708 status = smb_raw_open(cli->tree, mem_ctx, &io);
709 CHECK_STATUS(status, NT_STATUS_OK);
711 io.ntcreatex.in.fname = snamew2;
712 status = smb_raw_open(cli->tree, mem_ctx, &io);
713 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
715 ret &= check_stream_list(cli, fname, 4, four);
718 if (fnum1 != -1) smbcli_close(cli->tree, fnum1);
719 if (fnum2 != -1) smbcli_close(cli->tree, fnum2);
720 status = smbcli_unlink(cli->tree, fname);
727 static bool test_stream_names2(struct torture_context *tctx,
728 struct smbcli_state *cli,
733 const char *fname = BASEDIR "\\stream_names2.txt";
738 printf("(%s) testing stream names\n", __location__);
739 io.generic.level = RAW_OPEN_NTCREATEX;
740 io.ntcreatex.in.root_fid = 0;
741 io.ntcreatex.in.flags = 0;
742 io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
743 io.ntcreatex.in.create_options = 0;
744 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
745 io.ntcreatex.in.share_access = 0;
746 io.ntcreatex.in.alloc_size = 0;
747 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
748 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
749 io.ntcreatex.in.security_flags = 0;
750 io.ntcreatex.in.fname = fname;
751 status = smb_raw_open(cli->tree, mem_ctx, &io);
752 CHECK_STATUS(status, NT_STATUS_OK);
753 fnum1 = io.ntcreatex.out.file.fnum;
755 for (i=0x01; i < 0x7F; i++) {
756 char *path = talloc_asprintf(tctx, "%s:Stream%c0x%02X:$DATA",
764 expected = NT_STATUS_OBJECT_NAME_INVALID;
767 expected = NT_STATUS_OBJECT_NAME_NOT_FOUND;
771 printf("(%s) %s:Stream%c0x%02X:$DATA%s => expected[%s]\n",
772 __location__, fname, isprint(i)?(char)i:' ', i,
773 isprint(i)?"":" (not printable)",
774 nt_errstr(expected));
776 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
777 io.ntcreatex.in.fname = path;
778 status = smb_raw_open(cli->tree, mem_ctx, &io);
779 CHECK_STATUS(status, expected);
785 if (fnum1 != -1) smbcli_close(cli->tree, fnum1);
786 status = smbcli_unlink(cli->tree, fname);
791 basic testing of streams calls
793 bool torture_raw_streams(struct torture_context *torture,
794 struct smbcli_state *cli)
798 if (!torture_setup_dir(cli, BASEDIR)) {
802 ret &= test_stream_dir(torture, cli, torture);
803 smb_raw_exit(cli->session);
804 ret &= test_stream_io(torture, cli, torture);
805 smb_raw_exit(cli->session);
806 ret &= test_stream_sharemodes(torture, cli, torture);
807 smb_raw_exit(cli->session);
808 ret &= test_stream_names(torture, cli, torture);
809 smb_raw_exit(cli->session);
810 ret &= test_stream_names2(torture, cli, torture);
811 smb_raw_exit(cli->session);
812 if (!torture_setting_bool(torture, "samba4", false)) {
813 ret &= test_stream_delete(torture, cli, torture);
816 smb_raw_exit(cli->session);
817 smbcli_deltree(cli->tree, BASEDIR);