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); \
47 #define CHECK_NTTIME(v, correct) do { \
48 if ((v) != (correct)) { \
49 printf("(%s) Incorrect value %s=%llu - should be %llu\n", \
50 __location__, #v, (unsigned long long)v, \
51 (unsigned long long)correct); \
55 #define CHECK_STR(v, correct) do { \
57 if ((v) && !(correct)) { \
59 } else if (!(v) && (correct)) { \
61 } else if (!(v) && !(correct)) { \
63 } else if (strcmp((v), (correct)) == 0) { \
69 printf("(%s) Incorrect value %s='%s' - should be '%s'\n", \
70 __location__, #v, (v)?(v):"NULL", \
71 (correct)?(correct):"NULL"); \
76 check that a stream has the right contents
78 static bool check_stream(struct smbcli_state *cli, const char *location,
80 const char *fname, const char *sname,
84 const char *full_name;
88 full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname);
90 fnum = smbcli_open(cli->tree, full_name, O_RDONLY, DENY_NONE);
94 printf("(%s) should have failed stream open of %s\n",
102 printf("(%s) Failed to open stream '%s' - %s\n",
103 location, full_name, smbcli_errstr(cli->tree));
107 buf = talloc_array(mem_ctx, uint8_t, strlen(value)+11);
109 ret = smbcli_read(cli->tree, fnum, buf, 0, strlen(value)+11);
110 if (ret != strlen(value)) {
111 printf("(%s) Failed to read %lu bytes from stream '%s' - got %d\n",
112 location, (long)strlen(value), full_name, (int)ret);
116 if (memcmp(buf, value, strlen(value)) != 0) {
117 printf("(%s) Bad data in stream\n", location);
121 smbcli_close(cli->tree, fnum);
125 static int qsort_string(const void *v1, const void *v2)
127 char * const *s1 = v1;
128 char * const *s2 = v2;
129 return strcmp(*s1, *s2);
132 static int qsort_stream(const void *v1, const void *v2)
134 const struct stream_struct * s1 = v1;
135 const struct stream_struct * s2 = v2;
136 return strcmp(s1->stream_name.s, s2->stream_name.s);
139 static bool check_stream_list(struct smbcli_state *cli, const char *fname,
140 int num_exp, const char **exp)
142 union smb_fileinfo finfo;
145 TALLOC_CTX *tmp_ctx = talloc_new(cli);
147 struct stream_struct *stream_sort;
150 finfo.generic.level = RAW_FILEINFO_STREAM_INFO;
151 finfo.generic.in.file.path = fname;
153 status = smb_raw_pathinfo(cli->tree, tmp_ctx, &finfo);
154 if (!NT_STATUS_IS_OK(status)) {
155 d_fprintf(stderr, "(%s) smb_raw_pathinfo failed: %s\n",
156 __location__, nt_errstr(status));
160 if (finfo.stream_info.out.num_streams != num_exp) {
161 d_fprintf(stderr, "(%s) expected %d streams, got %d\n",
162 __location__, num_exp,
163 finfo.stream_info.out.num_streams);
172 exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
174 if (exp_sort == NULL) {
178 qsort(exp_sort, num_exp, sizeof(*exp_sort), qsort_string);
180 stream_sort = talloc_memdup(tmp_ctx, finfo.stream_info.out.streams,
181 finfo.stream_info.out.num_streams *
182 sizeof(*stream_sort));
184 if (stream_sort == NULL) {
188 qsort(stream_sort, finfo.stream_info.out.num_streams,
189 sizeof(*stream_sort), qsort_stream);
191 for (i=0; i<num_exp; i++) {
192 if (strcmp(exp_sort[i], stream_sort[i].stream_name.s) != 0) {
193 d_fprintf(stderr, "(%s) expected stream name %s, got "
194 "%s\n", __location__, exp_sort[i],
195 stream_sort[i].stream_name.s);
202 talloc_free(tmp_ctx);
207 test bahavior of streams on directories
209 static bool test_stream_dir(struct torture_context *tctx,
210 struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
214 const char *fname = BASEDIR "\\stream.txt";
217 const char *basedir_data;
219 basedir_data = talloc_asprintf(mem_ctx, "%s::$DATA", BASEDIR);
220 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
222 printf("(%s) opening non-existant directory stream\n", __location__);
223 io.generic.level = RAW_OPEN_NTCREATEX;
224 io.ntcreatex.in.root_fid = 0;
225 io.ntcreatex.in.flags = 0;
226 io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
227 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
228 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
229 io.ntcreatex.in.share_access = 0;
230 io.ntcreatex.in.alloc_size = 0;
231 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
232 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
233 io.ntcreatex.in.security_flags = 0;
234 io.ntcreatex.in.fname = sname1;
235 status = smb_raw_open(cli->tree, mem_ctx, &io);
236 CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
238 printf("(%s) opening basedir stream\n", __location__);
239 io.generic.level = RAW_OPEN_NTCREATEX;
240 io.ntcreatex.in.root_fid = 0;
241 io.ntcreatex.in.flags = 0;
242 io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
243 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
244 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY;
245 io.ntcreatex.in.share_access = 0;
246 io.ntcreatex.in.alloc_size = 0;
247 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
248 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
249 io.ntcreatex.in.security_flags = 0;
250 io.ntcreatex.in.fname = basedir_data;
251 status = smb_raw_open(cli->tree, mem_ctx, &io);
252 CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
254 printf("(%s) opening basedir ::$DATA stream\n", __location__);
255 io.generic.level = RAW_OPEN_NTCREATEX;
256 io.ntcreatex.in.root_fid = 0;
257 io.ntcreatex.in.flags = 0x10;
258 io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
259 io.ntcreatex.in.create_options = 0;
260 io.ntcreatex.in.file_attr = 0;
261 io.ntcreatex.in.share_access = 0;
262 io.ntcreatex.in.alloc_size = 0;
263 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
264 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
265 io.ntcreatex.in.security_flags = 0;
266 io.ntcreatex.in.fname = basedir_data;
267 status = smb_raw_open(cli->tree, mem_ctx, &io);
268 CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
270 printf("(%s) list the streams on the basedir\n", __location__);
271 ret &= check_stream_list(cli, BASEDIR, 0, NULL);
277 test basic behavior of streams on directories
279 static bool test_stream_io(struct torture_context *tctx,
280 struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
284 const char *fname = BASEDIR "\\stream.txt";
285 const char *sname1, *sname2;
290 const char *one[] = { "::$DATA" };
291 const char *two[] = { "::$DATA", ":Second Stream:$DATA" };
292 const char *three[] = { "::$DATA", ":Stream One:$DATA",
293 ":Second Stream:$DATA" };
295 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
296 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "Second Stream");
298 printf("(%s) creating a stream on a non-existant file\n", __location__);
299 io.generic.level = RAW_OPEN_NTCREATEX;
300 io.ntcreatex.in.root_fid = 0;
301 io.ntcreatex.in.flags = 0;
302 io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
303 io.ntcreatex.in.create_options = 0;
304 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
305 io.ntcreatex.in.share_access = 0;
306 io.ntcreatex.in.alloc_size = 0;
307 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
308 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
309 io.ntcreatex.in.security_flags = 0;
310 io.ntcreatex.in.fname = sname1;
311 status = smb_raw_open(cli->tree, mem_ctx, &io);
312 CHECK_STATUS(status, NT_STATUS_OK);
313 fnum = io.ntcreatex.out.file.fnum;
315 ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One", NULL);
317 printf("(%s) check that open of base file is allowed\n", __location__);
318 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
319 io.ntcreatex.in.fname = fname;
320 status = smb_raw_open(cli->tree, mem_ctx, &io);
321 CHECK_STATUS(status, NT_STATUS_OK);
322 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
324 printf("(%s) writing to stream\n", __location__);
325 retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9);
326 CHECK_VALUE(retsize, 9);
328 smbcli_close(cli->tree, fnum);
330 ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One", "test data");
332 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
333 io.ntcreatex.in.fname = sname1;
334 status = smb_raw_open(cli->tree, mem_ctx, &io);
335 CHECK_STATUS(status, NT_STATUS_OK);
336 fnum = io.ntcreatex.out.file.fnum;
338 printf("(%s) modifying stream\n", __location__);
339 retsize = smbcli_write(cli->tree, fnum, 0, "MORE DATA ", 5, 10);
340 CHECK_VALUE(retsize, 10);
342 smbcli_close(cli->tree, fnum);
344 ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:$FOO", NULL);
346 printf("(%s) creating a stream2 on a existing file\n", __location__);
347 io.ntcreatex.in.fname = sname2;
348 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
349 status = smb_raw_open(cli->tree, mem_ctx, &io);
350 CHECK_STATUS(status, NT_STATUS_OK);
351 fnum = io.ntcreatex.out.file.fnum;
353 printf("(%s) modifying stream\n", __location__);
354 retsize = smbcli_write(cli->tree, fnum, 0, "SECOND STREAM", 0, 13);
355 CHECK_VALUE(retsize, 13);
357 smbcli_close(cli->tree, fnum);
359 ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One", "test MORE DATA ");
360 ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:$DATA", "test MORE DATA ");
361 ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:", NULL);
362 ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream", "SECOND STREAM");
363 if (!torture_setting_bool(tctx, "samba4", false)) {
364 ret &= check_stream(cli, __location__, mem_ctx, fname,
365 "SECOND STREAM:$DATA", "SECOND STREAM");
367 ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:$DATA", "SECOND STREAM");
368 ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:", NULL);
369 ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:$FOO", NULL);
371 check_stream_list(cli, fname, 3, three);
373 printf("(%s) deleting stream\n", __location__);
374 status = smbcli_unlink(cli->tree, sname1);
375 CHECK_STATUS(status, NT_STATUS_OK);
377 check_stream_list(cli, fname, 2, two);
379 printf("(%s) delete a stream via delete-on-close\n", __location__);
380 io.ntcreatex.in.fname = sname2;
381 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
382 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
383 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
384 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
386 status = smb_raw_open(cli->tree, mem_ctx, &io);
387 CHECK_STATUS(status, NT_STATUS_OK);
388 fnum = io.ntcreatex.out.file.fnum;
390 smbcli_close(cli->tree, fnum);
391 status = smbcli_unlink(cli->tree, sname2);
392 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
394 check_stream_list(cli, fname, 1, one);
396 if (!torture_setting_bool(tctx, "samba4", false)) {
397 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
398 io.ntcreatex.in.fname = sname1;
399 status = smb_raw_open(cli->tree, mem_ctx, &io);
400 CHECK_STATUS(status, NT_STATUS_OK);
401 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
402 io.ntcreatex.in.fname = sname2;
403 status = smb_raw_open(cli->tree, mem_ctx, &io);
404 CHECK_STATUS(status, NT_STATUS_OK);
405 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
408 printf("(%s) deleting file\n", __location__);
409 status = smbcli_unlink(cli->tree, fname);
410 CHECK_STATUS(status, NT_STATUS_OK);
413 smbcli_close(cli->tree, fnum);
418 test stream sharemodes
420 static bool test_stream_sharemodes(struct torture_context *tctx,
421 struct smbcli_state *cli,
426 const char *fname = BASEDIR "\\stream.txt";
427 const char *sname1, *sname2;
432 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
433 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "Second Stream");
435 printf("(%s) testing stream share mode conflicts\n", __location__);
436 io.generic.level = RAW_OPEN_NTCREATEX;
437 io.ntcreatex.in.root_fid = 0;
438 io.ntcreatex.in.flags = 0;
439 io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
440 io.ntcreatex.in.create_options = 0;
441 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
442 io.ntcreatex.in.share_access = 0;
443 io.ntcreatex.in.alloc_size = 0;
444 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
445 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
446 io.ntcreatex.in.security_flags = 0;
447 io.ntcreatex.in.fname = sname1;
449 status = smb_raw_open(cli->tree, mem_ctx, &io);
450 CHECK_STATUS(status, NT_STATUS_OK);
451 fnum1 = io.ntcreatex.out.file.fnum;
454 * A different stream does not give a sharing violation
457 io.ntcreatex.in.fname = sname2;
458 status = smb_raw_open(cli->tree, mem_ctx, &io);
459 CHECK_STATUS(status, NT_STATUS_OK);
460 fnum2 = io.ntcreatex.out.file.fnum;
463 * ... whereas the same stream does with unchanged access/share_access
467 io.ntcreatex.in.fname = sname1;
468 io.ntcreatex.in.open_disposition = 0;
469 status = smb_raw_open(cli->tree, mem_ctx, &io);
470 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
472 io.ntcreatex.in.fname = sname2;
473 status = smb_raw_open(cli->tree, mem_ctx, &io);
474 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
477 if (fnum1 != -1) smbcli_close(cli->tree, fnum1);
478 if (fnum2 != -1) smbcli_close(cli->tree, fnum2);
479 status = smbcli_unlink(cli->tree, fname);
484 * Test FILE_SHARE_DELETE on streams
486 * A stream opened with !FILE_SHARE_DELETE prevents the main file to be opened
487 * with SEC_STD_DELETE.
489 * The main file opened with !FILE_SHARE_DELETE does *not* prevent a stream to
490 * be opened with SEC_STD_DELETE.
492 * A stream held open with FILE_SHARE_DELETE allows the file to be
493 * deleted. After the main file is deleted, access to the open file descriptor
494 * still works, but all name-based access to both the main file as well as the
495 * stream is denied with DELETE ending.
497 * This means, an open of the main file with SEC_STD_DELETE should walk all
498 * streams and also open them with SEC_STD_DELETE. If any of these opens gives
499 * SHARING_VIOLATION, the main open fails.
501 * Closing the main file after delete_on_close has been set does not really
502 * unlink it but leaves the corresponding share mode entry with
503 * delete_on_close being set around until all streams are closed.
505 * Opening a stream must also look at the main file's share mode entry, look
506 * at the delete_on_close bit and potentially return DELETE_PENDING.
509 static bool test_stream_delete(struct torture_context *tctx,
510 struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
514 const char *fname = BASEDIR "\\stream.txt";
520 union smb_fileinfo finfo;
522 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
524 printf("(%s) opening non-existant file stream\n", __location__);
525 io.generic.level = RAW_OPEN_NTCREATEX;
526 io.ntcreatex.in.root_fid = 0;
527 io.ntcreatex.in.flags = 0;
528 io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
529 io.ntcreatex.in.create_options = 0;
530 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
531 io.ntcreatex.in.share_access = 0;
532 io.ntcreatex.in.alloc_size = 0;
533 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
534 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
535 io.ntcreatex.in.security_flags = 0;
536 io.ntcreatex.in.fname = sname1;
538 status = smb_raw_open(cli->tree, mem_ctx, &io);
539 CHECK_STATUS(status, NT_STATUS_OK);
540 fnum = io.ntcreatex.out.file.fnum;
542 retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9);
543 CHECK_VALUE(retsize, 9);
546 * One stream opened without FILE_SHARE_DELETE prevents the main file
547 * to be deleted or even opened with DELETE access
550 status = smbcli_unlink(cli->tree, fname);
551 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
553 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
554 io.ntcreatex.in.fname = fname;
555 io.ntcreatex.in.access_mask = SEC_STD_DELETE;
556 status = smb_raw_open(cli->tree, mem_ctx, &io);
557 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
559 smbcli_close(cli->tree, fnum);
562 * ... but unlink works if a stream is opened with FILE_SHARE_DELETE
565 io.ntcreatex.in.fname = sname1;
566 io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
567 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
568 status = smb_raw_open(cli->tree, mem_ctx, &io);
569 CHECK_STATUS(status, NT_STATUS_OK);
570 fnum = io.ntcreatex.out.file.fnum;
572 status = smbcli_unlink(cli->tree, fname);
573 CHECK_STATUS(status, NT_STATUS_OK);
576 * file access still works on the stream while the main file is closed
579 retsize = smbcli_read(cli->tree, fnum, buf, 0, 9);
580 CHECK_VALUE(retsize, 9);
582 finfo.generic.level = RAW_FILEINFO_STANDARD;
583 finfo.generic.in.file.path = fname;
586 * name-based access to both the main file and the stream does not
587 * work anymore but gives DELETE_PENDING
590 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
591 CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
594 * older S3 doesn't do this
596 finfo.generic.in.file.path = sname1;
597 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
598 CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
601 * fd-based qfileinfo on the stream still works, the stream does not
602 * have the delete-on-close bit set. This could mean that open on the
603 * stream first opens the main file
606 finfo.all_info.level = RAW_FILEINFO_ALL_INFO;
607 finfo.all_info.in.file.fnum = fnum;
609 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo);
610 CHECK_STATUS(status, NT_STATUS_OK);
611 /* w2k and w2k3 return 0 and w2k8 returns 1
612 CHECK_VALUE(finfo.all_info.out.delete_pending, 0);
615 smbcli_close(cli->tree, fnum);
618 * After closing the stream the file is really gone.
621 finfo.generic.in.file.path = fname;
622 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
623 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
625 io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA
627 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
628 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
629 status = smb_raw_open(cli->tree, mem_ctx, &io);
630 CHECK_STATUS(status, NT_STATUS_OK);
631 fnum = io.ntcreatex.out.file.fnum;
633 finfo.generic.in.file.path = fname;
634 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
635 CHECK_STATUS(status, NT_STATUS_OK);
637 smbcli_close(cli->tree, fnum);
639 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
640 CHECK_STATUS(status, NT_STATUS_OK);
642 smbcli_close(cli->tree, fnum);
649 static bool test_stream_names(struct torture_context *tctx,
650 struct smbcli_state *cli,
655 union smb_fileinfo finfo;
656 union smb_fileinfo stinfo;
657 union smb_setfileinfo sinfo;
658 const char *fname = BASEDIR "\\stream_names.txt";
659 const char *sname1, *sname1b, *sname1c, *sname1d;
660 const char *sname2, *snamew, *snamew2;
661 const char *snamer1, *snamer2;
667 const char *four[4] = {
669 ":\x05Stream\n One:$DATA",
670 ":MStream Two:$DATA",
673 const char *five1[5] = {
675 ":\x05Stream\n One:$DATA",
676 ":BeforeRename:$DATA",
677 ":MStream Two:$DATA",
680 const char *five2[5] = {
682 ":\x05Stream\n One:$DATA",
683 ":AfterRename:$DATA",
684 ":MStream Two:$DATA",
688 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "\x05Stream\n One");
689 sname1b = talloc_asprintf(mem_ctx, "%s:", sname1);
690 sname1c = talloc_asprintf(mem_ctx, "%s:$FOO", sname1);
691 sname1d = talloc_asprintf(mem_ctx, "%s:?D*a", sname1);
692 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "MStream Two");
693 snamew = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "?Stream*");
694 snamew2 = talloc_asprintf(mem_ctx, "%s\\stream*:%s:$DATA", BASEDIR, "?Stream*");
695 snamer1 = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "BeforeRename");
696 snamer2 = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "AfterRename");
698 printf("(%s) testing stream names\n", __location__);
699 io.generic.level = RAW_OPEN_NTCREATEX;
700 io.ntcreatex.in.root_fid = 0;
701 io.ntcreatex.in.flags = 0;
702 io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
703 io.ntcreatex.in.create_options = 0;
704 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
705 io.ntcreatex.in.share_access = 0;
706 io.ntcreatex.in.alloc_size = 0;
707 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
708 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
709 io.ntcreatex.in.security_flags = 0;
710 io.ntcreatex.in.fname = sname1;
712 status = smb_raw_open(cli->tree, mem_ctx, &io);
713 CHECK_STATUS(status, NT_STATUS_OK);
714 fnum1 = io.ntcreatex.out.file.fnum;
717 * A different stream does not give a sharing violation
720 io.ntcreatex.in.fname = sname2;
721 status = smb_raw_open(cli->tree, mem_ctx, &io);
722 CHECK_STATUS(status, NT_STATUS_OK);
723 fnum2 = io.ntcreatex.out.file.fnum;
726 * ... whereas the same stream does with unchanged access/share_access
730 io.ntcreatex.in.fname = sname1;
731 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_SUPERSEDE;
732 status = smb_raw_open(cli->tree, mem_ctx, &io);
733 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
735 io.ntcreatex.in.fname = sname1b;
736 status = smb_raw_open(cli->tree, mem_ctx, &io);
737 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
739 io.ntcreatex.in.fname = sname1c;
740 status = smb_raw_open(cli->tree, mem_ctx, &io);
741 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
742 /* w2k returns INVALID_PARAMETER */
743 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
745 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
748 io.ntcreatex.in.fname = sname1d;
749 status = smb_raw_open(cli->tree, mem_ctx, &io);
750 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
751 /* w2k returns INVALID_PARAMETER */
752 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
754 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
757 io.ntcreatex.in.fname = sname2;
758 status = smb_raw_open(cli->tree, mem_ctx, &io);
759 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
761 io.ntcreatex.in.fname = snamew;
762 status = smb_raw_open(cli->tree, mem_ctx, &io);
763 CHECK_STATUS(status, NT_STATUS_OK);
764 fnum3 = io.ntcreatex.out.file.fnum;
766 io.ntcreatex.in.fname = snamew2;
767 status = smb_raw_open(cli->tree, mem_ctx, &io);
768 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
770 ret &= check_stream_list(cli, fname, 4, four);
772 smbcli_close(cli->tree, fnum1);
773 smbcli_close(cli->tree, fnum2);
774 smbcli_close(cli->tree, fnum3);
776 if (torture_setting_bool(tctx, "samba4", true)) {
780 finfo.generic.level = RAW_FILEINFO_ALL_INFO;
781 finfo.generic.in.file.path = fname;
782 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
783 CHECK_STATUS(status, NT_STATUS_OK);
785 ret &= check_stream_list(cli, fname, 4, four);
787 for (i=0; i < 4; i++) {
789 uint64_t stream_size;
790 char *path = talloc_asprintf(tctx, "%s%s",
793 char *rpath = talloc_strdup(path, path);
794 char *p = strrchr(rpath, ':');
802 printf("(%s): i[%u][%s]\n", __location__, i, path);
803 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
804 io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE |
805 SEC_FILE_WRITE_ATTRIBUTE |
807 io.ntcreatex.in.fname = path;
808 status = smb_raw_open(cli->tree, mem_ctx, &io);
809 CHECK_STATUS(status, NT_STATUS_OK);
810 fnum1 = io.ntcreatex.out.file.fnum;
812 finfo.generic.level = RAW_FILEINFO_ALL_INFO;
813 finfo.generic.in.file.path = fname;
814 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
815 CHECK_STATUS(status, NT_STATUS_OK);
817 stinfo.generic.level = RAW_FILEINFO_ALL_INFO;
818 stinfo.generic.in.file.fnum = fnum1;
819 status = smb_raw_fileinfo(cli->tree, mem_ctx, &stinfo);
820 CHECK_STATUS(status, NT_STATUS_OK);
821 if (!torture_setting_bool(tctx, "samba3", false)) {
822 CHECK_NTTIME(stinfo.all_info.out.create_time,
823 finfo.all_info.out.create_time);
824 CHECK_NTTIME(stinfo.all_info.out.access_time,
825 finfo.all_info.out.access_time);
826 CHECK_NTTIME(stinfo.all_info.out.write_time,
827 finfo.all_info.out.write_time);
828 CHECK_NTTIME(stinfo.all_info.out.change_time,
829 finfo.all_info.out.change_time);
831 CHECK_VALUE(stinfo.all_info.out.attrib,
832 finfo.all_info.out.attrib);
833 CHECK_VALUE(stinfo.all_info.out.size,
834 finfo.all_info.out.size);
835 CHECK_VALUE(stinfo.all_info.out.delete_pending,
836 finfo.all_info.out.delete_pending);
837 CHECK_VALUE(stinfo.all_info.out.directory,
838 finfo.all_info.out.directory);
839 CHECK_VALUE(stinfo.all_info.out.ea_size,
840 finfo.all_info.out.ea_size);
842 stinfo.generic.level = RAW_FILEINFO_NAME_INFO;
843 stinfo.generic.in.file.fnum = fnum1;
844 status = smb_raw_fileinfo(cli->tree, mem_ctx, &stinfo);
845 CHECK_STATUS(status, NT_STATUS_OK);
846 if (!torture_setting_bool(tctx, "samba3", false)) {
847 CHECK_STR(rpath, stinfo.name_info.out.fname.s);
850 write_time = finfo.all_info.out.write_time;
851 write_time += i*1000000;
852 write_time /= 1000000;
853 write_time *= 1000000;
856 sinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
857 sinfo.basic_info.in.file.fnum = fnum1;
858 sinfo.basic_info.in.write_time = write_time;
859 sinfo.basic_info.in.attrib = stinfo.all_info.out.attrib;
860 status = smb_raw_setfileinfo(cli->tree, &sinfo);
861 CHECK_STATUS(status, NT_STATUS_OK);
863 stream_size = i*8192;
866 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFO;
867 sinfo.end_of_file_info.in.file.fnum = fnum1;
868 sinfo.end_of_file_info.in.size = stream_size;
869 status = smb_raw_setfileinfo(cli->tree, &sinfo);
870 CHECK_STATUS(status, NT_STATUS_OK);
872 stinfo.generic.level = RAW_FILEINFO_ALL_INFO;
873 stinfo.generic.in.file.fnum = fnum1;
874 status = smb_raw_fileinfo(cli->tree, mem_ctx, &stinfo);
875 CHECK_STATUS(status, NT_STATUS_OK);
876 if (!torture_setting_bool(tctx, "samba3", false)) {
877 CHECK_NTTIME(stinfo.all_info.out.write_time,
879 CHECK_VALUE(stinfo.all_info.out.attrib,
880 finfo.all_info.out.attrib);
882 CHECK_VALUE(stinfo.all_info.out.size,
884 CHECK_VALUE(stinfo.all_info.out.delete_pending,
885 finfo.all_info.out.delete_pending);
886 CHECK_VALUE(stinfo.all_info.out.directory,
887 finfo.all_info.out.directory);
888 CHECK_VALUE(stinfo.all_info.out.ea_size,
889 finfo.all_info.out.ea_size);
891 ret &= check_stream_list(cli, fname, 4, four);
893 smbcli_close(cli->tree, fnum1);
897 printf("(%s): testing stream renames\n", __location__);
898 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
899 io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE |
900 SEC_FILE_WRITE_ATTRIBUTE |
902 io.ntcreatex.in.fname = snamer1;
903 status = smb_raw_open(cli->tree, mem_ctx, &io);
904 CHECK_STATUS(status, NT_STATUS_OK);
905 fnum1 = io.ntcreatex.out.file.fnum;
907 ret &= check_stream_list(cli, fname, 5, five1);
910 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
911 sinfo.rename_information.in.file.fnum = fnum1;
912 sinfo.rename_information.in.overwrite = true;
913 sinfo.rename_information.in.root_fid = 0;
914 sinfo.rename_information.in.new_name = ":AfterRename:$DATA";
915 status = smb_raw_setfileinfo(cli->tree, &sinfo);
916 CHECK_STATUS(status, NT_STATUS_OK);
918 ret &= check_stream_list(cli, fname, 5, five2);
921 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
922 sinfo.rename_information.in.file.fnum = fnum1;
923 sinfo.rename_information.in.overwrite = false;
924 sinfo.rename_information.in.root_fid = 0;
925 sinfo.rename_information.in.new_name = ":MStream Two:$DATA";
926 status = smb_raw_setfileinfo(cli->tree, &sinfo);
927 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
929 ret &= check_stream_list(cli, fname, 5, five2);
932 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
933 sinfo.rename_information.in.file.fnum = fnum1;
934 sinfo.rename_information.in.overwrite = true;
935 sinfo.rename_information.in.root_fid = 0;
936 sinfo.rename_information.in.new_name = ":MStream Two:$DATA";
937 status = smb_raw_setfileinfo(cli->tree, &sinfo);
938 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
940 ret &= check_stream_list(cli, fname, 5, five2);
942 /* TODO: we need to test more rename combinations */
945 if (fnum1 != -1) smbcli_close(cli->tree, fnum1);
946 if (fnum2 != -1) smbcli_close(cli->tree, fnum2);
947 if (fnum3 != -1) smbcli_close(cli->tree, fnum3);
948 status = smbcli_unlink(cli->tree, fname);
955 static bool test_stream_names2(struct torture_context *tctx,
956 struct smbcli_state *cli,
961 const char *fname = BASEDIR "\\stream_names2.txt";
966 printf("(%s) testing stream names\n", __location__);
967 io.generic.level = RAW_OPEN_NTCREATEX;
968 io.ntcreatex.in.root_fid = 0;
969 io.ntcreatex.in.flags = 0;
970 io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
971 io.ntcreatex.in.create_options = 0;
972 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
973 io.ntcreatex.in.share_access = 0;
974 io.ntcreatex.in.alloc_size = 0;
975 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
976 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
977 io.ntcreatex.in.security_flags = 0;
978 io.ntcreatex.in.fname = fname;
979 status = smb_raw_open(cli->tree, mem_ctx, &io);
980 CHECK_STATUS(status, NT_STATUS_OK);
981 fnum1 = io.ntcreatex.out.file.fnum;
983 for (i=0x01; i < 0x7F; i++) {
984 char *path = talloc_asprintf(tctx, "%s:Stream%c0x%02X:$DATA",
992 expected = NT_STATUS_OBJECT_NAME_INVALID;
995 expected = NT_STATUS_OBJECT_NAME_NOT_FOUND;
999 printf("(%s) %s:Stream%c0x%02X:$DATA%s => expected[%s]\n",
1000 __location__, fname, isprint(i)?(char)i:' ', i,
1001 isprint(i)?"":" (not printable)",
1002 nt_errstr(expected));
1004 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1005 io.ntcreatex.in.fname = path;
1006 status = smb_raw_open(cli->tree, mem_ctx, &io);
1007 CHECK_STATUS(status, expected);
1013 if (fnum1 != -1) smbcli_close(cli->tree, fnum1);
1014 status = smbcli_unlink(cli->tree, fname);
1018 #define CHECK_CALL_FNUM(call, rightstatus) do { \
1019 check_fnum = true; \
1020 call_name = #call; \
1021 sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
1022 sfinfo.generic.in.file.fnum = fnum; \
1023 status = smb_raw_setfileinfo(cli->tree, &sfinfo); \
1024 if (!NT_STATUS_EQUAL(status, rightstatus)) { \
1025 printf("(%s) %s - %s (should be %s)\n", __location__, #call, \
1026 nt_errstr(status), nt_errstr(rightstatus)); \
1029 finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \
1030 finfo1.generic.in.file.fnum = fnum; \
1031 status2 = smb_raw_fileinfo(cli->tree, tctx, &finfo1); \
1032 if (!NT_STATUS_IS_OK(status2)) { \
1033 printf("(%s) %s pathinfo - %s\n", __location__, #call, nt_errstr(status)); \
1040 static bool test_stream_rename(struct torture_context *tctx,
1041 struct smbcli_state *cli,
1042 TALLOC_CTX *mem_ctx)
1044 NTSTATUS status, status2;
1046 const char *fname = BASEDIR "\\stream_rename.txt";
1047 const char *sname1, *sname2;
1048 union smb_fileinfo finfo1;
1049 union smb_setfileinfo sfinfo;
1053 const char *call_name;
1055 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
1056 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "Second Stream");
1058 printf("(%s) testing stream renames\n", __location__);
1059 io.generic.level = RAW_OPEN_NTCREATEX;
1060 io.ntcreatex.in.root_fid = 0;
1061 io.ntcreatex.in.flags = 0;
1062 io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE |
1063 SEC_FILE_WRITE_ATTRIBUTE |
1064 SEC_RIGHTS_FILE_ALL;
1065 io.ntcreatex.in.create_options = 0;
1066 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1067 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE;
1068 io.ntcreatex.in.alloc_size = 0;
1069 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
1070 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1071 io.ntcreatex.in.security_flags = 0;
1072 io.ntcreatex.in.fname = sname1;
1074 /* Create two streams. */
1075 status = smb_raw_open(cli->tree, mem_ctx, &io);
1076 CHECK_STATUS(status, NT_STATUS_OK);
1077 fnum = io.ntcreatex.out.file.fnum;
1078 if (fnum != -1) smbcli_close(cli->tree, fnum);
1080 io.ntcreatex.in.fname = sname2;
1081 status = smb_raw_open(cli->tree, mem_ctx, &io);
1082 CHECK_STATUS(status, NT_STATUS_OK);
1083 fnum = io.ntcreatex.out.file.fnum;
1085 if (fnum != -1) smbcli_close(cli->tree, fnum);
1088 * Open the second stream.
1091 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
1092 status = smb_raw_open(cli->tree, mem_ctx, &io);
1093 CHECK_STATUS(status, NT_STATUS_OK);
1094 fnum = io.ntcreatex.out.file.fnum;
1097 * Now rename the second stream onto the first.
1100 ZERO_STRUCT(sfinfo);
1102 sfinfo.rename_information.in.overwrite = 1;
1103 sfinfo.rename_information.in.root_fid = 0;
1104 sfinfo.rename_information.in.new_name = ":Stream One";
1105 CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK);
1108 if (fnum != -1) smbcli_close(cli->tree, fnum);
1109 status = smbcli_unlink(cli->tree, fname);
1115 basic testing of streams calls
1117 bool torture_raw_streams(struct torture_context *torture,
1118 struct smbcli_state *cli)
1122 if (!torture_setup_dir(cli, BASEDIR)) {
1126 ret &= test_stream_dir(torture, cli, torture);
1127 smb_raw_exit(cli->session);
1128 ret &= test_stream_io(torture, cli, torture);
1129 smb_raw_exit(cli->session);
1130 ret &= test_stream_sharemodes(torture, cli, torture);
1131 smb_raw_exit(cli->session);
1132 ret &= test_stream_names(torture, cli, torture);
1133 smb_raw_exit(cli->session);
1134 ret &= test_stream_names2(torture, cli, torture);
1135 smb_raw_exit(cli->session);
1136 ret &= test_stream_rename(torture, cli, torture);
1137 smb_raw_exit(cli->session);
1138 if (!torture_setting_bool(torture, "samba4", false)) {
1139 ret &= test_stream_delete(torture, cli, torture);
1142 smb_raw_exit(cli->session);
1143 smbcli_deltree(cli->tree, BASEDIR);