s3/docs: Raise version number up to 3.5.
[Samba/gebeck_regimport.git] / source4 / torture / raw / streams.c
blob818eb402dda442edcfc2ad00e70bec6866cc8e50
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 "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)); \
36 ret = false; \
37 goto done; \
38 }} while (0)
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); \
44 ret = false; \
45 }} while (0)
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); \
52 ret = false; \
53 }} while (0)
55 #define CHECK_STR(v, correct) do { \
56 bool ok; \
57 if ((v) && !(correct)) { \
58 ok = false; \
59 } else if (!(v) && (correct)) { \
60 ok = false; \
61 } else if (!(v) && !(correct)) { \
62 ok = true; \
63 } else if (strcmp((v), (correct)) == 0) { \
64 ok = true; \
65 } else { \
66 ok = false; \
67 } \
68 if (!ok) { \
69 printf("(%s) Incorrect value %s='%s' - should be '%s'\n", \
70 __location__, #v, (v)?(v):"NULL", \
71 (correct)?(correct):"NULL"); \
72 ret = false; \
73 }} while (0)
76 check that a stream has the right contents
78 static bool check_stream(struct smbcli_state *cli, const char *location,
79 TALLOC_CTX *mem_ctx,
80 const char *fname, const char *sname,
81 const char *value)
83 int fnum;
84 const char *full_name;
85 uint8_t *buf;
86 ssize_t ret;
88 full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname);
90 fnum = smbcli_open(cli->tree, full_name, O_RDONLY, DENY_NONE);
92 if (value == NULL) {
93 if (fnum != -1) {
94 printf("(%s) should have failed stream open of %s\n",
95 location, full_name);
96 return false;
98 return true;
101 if (fnum == -1) {
102 printf("(%s) Failed to open stream '%s' - %s\n",
103 location, full_name, smbcli_errstr(cli->tree));
104 return false;
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);
113 return false;
116 if (memcmp(buf, value, strlen(value)) != 0) {
117 printf("(%s) Bad data in stream\n", location);
118 return false;
121 smbcli_close(cli->tree, fnum);
122 return true;
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;
143 NTSTATUS status;
144 int i;
145 TALLOC_CTX *tmp_ctx = talloc_new(cli);
146 char **exp_sort;
147 struct stream_struct *stream_sort;
148 bool ret = false;
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));
157 goto fail;
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);
164 goto fail;
167 if (num_exp == 0) {
168 ret = true;
169 goto fail;
172 exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
174 if (exp_sort == NULL) {
175 goto fail;
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) {
185 goto fail;
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);
196 goto fail;
200 ret = true;
201 fail:
202 talloc_free(tmp_ctx);
203 return ret;
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)
212 NTSTATUS status;
213 union smb_open io;
214 const char *fname = BASEDIR "\\stream.txt";
215 const char *sname1;
216 bool ret = true;
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);
272 done:
273 return ret;
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)
282 NTSTATUS status;
283 union smb_open io;
284 const char *fname = BASEDIR "\\stream.txt";
285 const char *sname1, *sname2;
286 bool ret = true;
287 int fnum = -1;
288 ssize_t retsize;
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);
412 done:
413 smbcli_close(cli->tree, fnum);
414 return ret;
418 test stream sharemodes
420 static bool test_stream_sharemodes(struct torture_context *tctx,
421 struct smbcli_state *cli,
422 TALLOC_CTX *mem_ctx)
424 NTSTATUS status;
425 union smb_open io;
426 const char *fname = BASEDIR "\\stream.txt";
427 const char *sname1, *sname2;
428 bool ret = true;
429 int fnum1 = -1;
430 int fnum2 = -1;
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
464 * flags
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);
476 done:
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);
480 return ret;
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)
512 NTSTATUS status;
513 union smb_open io;
514 const char *fname = BASEDIR "\\stream.txt";
515 const char *sname1;
516 bool ret = true;
517 int fnum = -1;
518 uint8_t buf[9];
519 ssize_t retsize;
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
626 |SEC_STD_DELETE;
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);
641 done:
642 smbcli_close(cli->tree, fnum);
643 smbcli_unlink(cli->tree, fname);
644 return ret;
648 test stream names
650 static bool test_stream_names(struct torture_context *tctx,
651 struct smbcli_state *cli,
652 TALLOC_CTX *mem_ctx)
654 NTSTATUS status;
655 union smb_open io;
656 union smb_fileinfo finfo;
657 union smb_fileinfo stinfo;
658 union smb_setfileinfo sinfo;
659 const char *fname = BASEDIR "\\stream_names.txt";
660 const char *sname1, *sname1b, *sname1c, *sname1d;
661 const char *sname2, *snamew, *snamew2;
662 const char *snamer1, *snamer2;
663 bool ret = true;
664 int fnum1 = -1;
665 int fnum2 = -1;
666 int fnum3 = -1;
667 int i;
668 const char *four[4] = {
669 "::$DATA",
670 ":\x05Stream\n One:$DATA",
671 ":MStream Two:$DATA",
672 ":?Stream*:$DATA"
674 const char *five1[5] = {
675 "::$DATA",
676 ":\x05Stream\n One:$DATA",
677 ":BeforeRename:$DATA",
678 ":MStream Two:$DATA",
679 ":?Stream*:$DATA"
681 const char *five2[5] = {
682 "::$DATA",
683 ":\x05Stream\n One:$DATA",
684 ":AfterRename:$DATA",
685 ":MStream Two:$DATA",
686 ":?Stream*:$DATA"
689 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "\x05Stream\n One");
690 sname1b = talloc_asprintf(mem_ctx, "%s:", sname1);
691 sname1c = talloc_asprintf(mem_ctx, "%s:$FOO", sname1);
692 sname1d = talloc_asprintf(mem_ctx, "%s:?D*a", sname1);
693 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "MStream Two");
694 snamew = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "?Stream*");
695 snamew2 = talloc_asprintf(mem_ctx, "%s\\stream*:%s:$DATA", BASEDIR, "?Stream*");
696 snamer1 = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "BeforeRename");
697 snamer2 = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "AfterRename");
699 printf("(%s) testing stream names\n", __location__);
700 io.generic.level = RAW_OPEN_NTCREATEX;
701 io.ntcreatex.in.root_fid = 0;
702 io.ntcreatex.in.flags = 0;
703 io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
704 io.ntcreatex.in.create_options = 0;
705 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
706 io.ntcreatex.in.share_access = 0;
707 io.ntcreatex.in.alloc_size = 0;
708 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
709 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
710 io.ntcreatex.in.security_flags = 0;
711 io.ntcreatex.in.fname = sname1;
713 status = smb_raw_open(cli->tree, mem_ctx, &io);
714 CHECK_STATUS(status, NT_STATUS_OK);
715 fnum1 = io.ntcreatex.out.file.fnum;
718 * A different stream does not give a sharing violation
721 io.ntcreatex.in.fname = sname2;
722 status = smb_raw_open(cli->tree, mem_ctx, &io);
723 CHECK_STATUS(status, NT_STATUS_OK);
724 fnum2 = io.ntcreatex.out.file.fnum;
727 * ... whereas the same stream does with unchanged access/share_access
728 * flags
731 io.ntcreatex.in.fname = sname1;
732 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_SUPERSEDE;
733 status = smb_raw_open(cli->tree, mem_ctx, &io);
734 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
736 io.ntcreatex.in.fname = sname1b;
737 status = smb_raw_open(cli->tree, mem_ctx, &io);
738 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
740 io.ntcreatex.in.fname = sname1c;
741 status = smb_raw_open(cli->tree, mem_ctx, &io);
742 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
743 /* w2k returns INVALID_PARAMETER */
744 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
745 } else {
746 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
749 io.ntcreatex.in.fname = sname1d;
750 status = smb_raw_open(cli->tree, mem_ctx, &io);
751 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
752 /* w2k returns INVALID_PARAMETER */
753 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
754 } else {
755 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
758 io.ntcreatex.in.fname = sname2;
759 status = smb_raw_open(cli->tree, mem_ctx, &io);
760 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
762 io.ntcreatex.in.fname = snamew;
763 status = smb_raw_open(cli->tree, mem_ctx, &io);
764 CHECK_STATUS(status, NT_STATUS_OK);
765 fnum3 = io.ntcreatex.out.file.fnum;
767 io.ntcreatex.in.fname = snamew2;
768 status = smb_raw_open(cli->tree, mem_ctx, &io);
769 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
771 ret &= check_stream_list(cli, fname, 4, four);
773 smbcli_close(cli->tree, fnum1);
774 smbcli_close(cli->tree, fnum2);
775 smbcli_close(cli->tree, fnum3);
777 if (torture_setting_bool(tctx, "samba4", true)) {
778 goto done;
781 finfo.generic.level = RAW_FILEINFO_ALL_INFO;
782 finfo.generic.in.file.path = fname;
783 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
784 CHECK_STATUS(status, NT_STATUS_OK);
786 ret &= check_stream_list(cli, fname, 4, four);
788 for (i=0; i < 4; i++) {
789 NTTIME write_time;
790 uint64_t stream_size;
791 char *path = talloc_asprintf(tctx, "%s%s",
792 fname, four[i]);
794 char *rpath = talloc_strdup(path, path);
795 char *p = strrchr(rpath, ':');
796 /* eat :$DATA */
797 *p = 0;
798 p--;
799 if (*p == ':') {
800 /* eat ::$DATA */
801 *p = 0;
803 printf("(%s): i[%u][%s]\n", __location__, i, path);
804 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
805 io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE |
806 SEC_FILE_WRITE_ATTRIBUTE |
807 SEC_RIGHTS_FILE_ALL;
808 io.ntcreatex.in.fname = path;
809 status = smb_raw_open(cli->tree, mem_ctx, &io);
810 CHECK_STATUS(status, NT_STATUS_OK);
811 fnum1 = io.ntcreatex.out.file.fnum;
813 finfo.generic.level = RAW_FILEINFO_ALL_INFO;
814 finfo.generic.in.file.path = fname;
815 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
816 CHECK_STATUS(status, NT_STATUS_OK);
818 stinfo.generic.level = RAW_FILEINFO_ALL_INFO;
819 stinfo.generic.in.file.fnum = fnum1;
820 status = smb_raw_fileinfo(cli->tree, mem_ctx, &stinfo);
821 CHECK_STATUS(status, NT_STATUS_OK);
822 if (!torture_setting_bool(tctx, "samba3", false)) {
823 CHECK_NTTIME(stinfo.all_info.out.create_time,
824 finfo.all_info.out.create_time);
825 CHECK_NTTIME(stinfo.all_info.out.access_time,
826 finfo.all_info.out.access_time);
827 CHECK_NTTIME(stinfo.all_info.out.write_time,
828 finfo.all_info.out.write_time);
829 CHECK_NTTIME(stinfo.all_info.out.change_time,
830 finfo.all_info.out.change_time);
832 CHECK_VALUE(stinfo.all_info.out.attrib,
833 finfo.all_info.out.attrib);
834 CHECK_VALUE(stinfo.all_info.out.size,
835 finfo.all_info.out.size);
836 CHECK_VALUE(stinfo.all_info.out.delete_pending,
837 finfo.all_info.out.delete_pending);
838 CHECK_VALUE(stinfo.all_info.out.directory,
839 finfo.all_info.out.directory);
840 CHECK_VALUE(stinfo.all_info.out.ea_size,
841 finfo.all_info.out.ea_size);
843 stinfo.generic.level = RAW_FILEINFO_NAME_INFO;
844 stinfo.generic.in.file.fnum = fnum1;
845 status = smb_raw_fileinfo(cli->tree, mem_ctx, &stinfo);
846 CHECK_STATUS(status, NT_STATUS_OK);
847 if (!torture_setting_bool(tctx, "samba3", false)) {
848 CHECK_STR(rpath, stinfo.name_info.out.fname.s);
851 write_time = finfo.all_info.out.write_time;
852 write_time += i*1000000;
853 write_time /= 1000000;
854 write_time *= 1000000;
856 ZERO_STRUCT(sinfo);
857 sinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
858 sinfo.basic_info.in.file.fnum = fnum1;
859 sinfo.basic_info.in.write_time = write_time;
860 sinfo.basic_info.in.attrib = stinfo.all_info.out.attrib;
861 status = smb_raw_setfileinfo(cli->tree, &sinfo);
862 CHECK_STATUS(status, NT_STATUS_OK);
864 stream_size = i*8192;
866 ZERO_STRUCT(sinfo);
867 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFO;
868 sinfo.end_of_file_info.in.file.fnum = fnum1;
869 sinfo.end_of_file_info.in.size = stream_size;
870 status = smb_raw_setfileinfo(cli->tree, &sinfo);
871 CHECK_STATUS(status, NT_STATUS_OK);
873 stinfo.generic.level = RAW_FILEINFO_ALL_INFO;
874 stinfo.generic.in.file.fnum = fnum1;
875 status = smb_raw_fileinfo(cli->tree, mem_ctx, &stinfo);
876 CHECK_STATUS(status, NT_STATUS_OK);
877 if (!torture_setting_bool(tctx, "samba3", false)) {
878 CHECK_NTTIME(stinfo.all_info.out.write_time,
879 write_time);
880 CHECK_VALUE(stinfo.all_info.out.attrib,
881 finfo.all_info.out.attrib);
883 CHECK_VALUE(stinfo.all_info.out.size,
884 stream_size);
885 CHECK_VALUE(stinfo.all_info.out.delete_pending,
886 finfo.all_info.out.delete_pending);
887 CHECK_VALUE(stinfo.all_info.out.directory,
888 finfo.all_info.out.directory);
889 CHECK_VALUE(stinfo.all_info.out.ea_size,
890 finfo.all_info.out.ea_size);
892 ret &= check_stream_list(cli, fname, 4, four);
894 smbcli_close(cli->tree, fnum1);
895 talloc_free(path);
898 printf("(%s): testing stream renames\n", __location__);
899 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
900 io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE |
901 SEC_FILE_WRITE_ATTRIBUTE |
902 SEC_RIGHTS_FILE_ALL;
903 io.ntcreatex.in.fname = snamer1;
904 status = smb_raw_open(cli->tree, mem_ctx, &io);
905 CHECK_STATUS(status, NT_STATUS_OK);
906 fnum1 = io.ntcreatex.out.file.fnum;
908 ret &= check_stream_list(cli, fname, 5, five1);
910 ZERO_STRUCT(sinfo);
911 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
912 sinfo.rename_information.in.file.fnum = fnum1;
913 sinfo.rename_information.in.overwrite = true;
914 sinfo.rename_information.in.root_fid = 0;
915 sinfo.rename_information.in.new_name = ":AfterRename:$DATA";
916 status = smb_raw_setfileinfo(cli->tree, &sinfo);
917 CHECK_STATUS(status, NT_STATUS_OK);
919 ret &= check_stream_list(cli, fname, 5, five2);
921 ZERO_STRUCT(sinfo);
922 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
923 sinfo.rename_information.in.file.fnum = fnum1;
924 sinfo.rename_information.in.overwrite = false;
925 sinfo.rename_information.in.root_fid = 0;
926 sinfo.rename_information.in.new_name = ":MStream Two:$DATA";
927 status = smb_raw_setfileinfo(cli->tree, &sinfo);
928 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
930 ret &= check_stream_list(cli, fname, 5, five2);
932 ZERO_STRUCT(sinfo);
933 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
934 sinfo.rename_information.in.file.fnum = fnum1;
935 sinfo.rename_information.in.overwrite = true;
936 sinfo.rename_information.in.root_fid = 0;
937 sinfo.rename_information.in.new_name = ":MStream Two:$DATA";
938 status = smb_raw_setfileinfo(cli->tree, &sinfo);
939 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
941 ret &= check_stream_list(cli, fname, 5, five2);
943 /* TODO: we need to test more rename combinations */
945 done:
946 if (fnum1 != -1) smbcli_close(cli->tree, fnum1);
947 if (fnum2 != -1) smbcli_close(cli->tree, fnum2);
948 if (fnum3 != -1) smbcli_close(cli->tree, fnum3);
949 status = smbcli_unlink(cli->tree, fname);
950 return ret;
954 test stream names
956 static bool test_stream_names2(struct torture_context *tctx,
957 struct smbcli_state *cli,
958 TALLOC_CTX *mem_ctx)
960 NTSTATUS status;
961 union smb_open io;
962 const char *fname = BASEDIR "\\stream_names2.txt";
963 bool ret = true;
964 int fnum1 = -1;
965 uint8_t i;
967 printf("(%s) testing stream names\n", __location__);
968 io.generic.level = RAW_OPEN_NTCREATEX;
969 io.ntcreatex.in.root_fid = 0;
970 io.ntcreatex.in.flags = 0;
971 io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
972 io.ntcreatex.in.create_options = 0;
973 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
974 io.ntcreatex.in.share_access = 0;
975 io.ntcreatex.in.alloc_size = 0;
976 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
977 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
978 io.ntcreatex.in.security_flags = 0;
979 io.ntcreatex.in.fname = fname;
980 status = smb_raw_open(cli->tree, mem_ctx, &io);
981 CHECK_STATUS(status, NT_STATUS_OK);
982 fnum1 = io.ntcreatex.out.file.fnum;
984 for (i=0x01; i < 0x7F; i++) {
985 char *path = talloc_asprintf(tctx, "%s:Stream%c0x%02X:$DATA",
986 fname, i, i);
987 NTSTATUS expected;
989 switch (i) {
990 case '/':/*0x2F*/
991 case ':':/*0x3A*/
992 case '\\':/*0x5C*/
993 expected = NT_STATUS_OBJECT_NAME_INVALID;
994 break;
995 default:
996 expected = NT_STATUS_OBJECT_NAME_NOT_FOUND;
997 break;
1001 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1002 io.ntcreatex.in.fname = path;
1003 status = smb_raw_open(cli->tree, mem_ctx, &io);
1004 if (!NT_STATUS_EQUAL(status, expected)) {
1005 printf("(%s) %s:Stream%c0x%02X:$DATA%s => expected[%s]\n",
1006 __location__, fname, isprint(i)?(char)i:' ', i,
1007 isprint(i)?"":" (not printable)",
1008 nt_errstr(expected));
1010 CHECK_STATUS(status, expected);
1012 talloc_free(path);
1015 done:
1016 if (fnum1 != -1) smbcli_close(cli->tree, fnum1);
1017 status = smbcli_unlink(cli->tree, fname);
1018 return ret;
1021 #define CHECK_CALL_FNUM(call, rightstatus) do { \
1022 check_fnum = true; \
1023 call_name = #call; \
1024 sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
1025 sfinfo.generic.in.file.fnum = fnum; \
1026 status = smb_raw_setfileinfo(cli->tree, &sfinfo); \
1027 if (!NT_STATUS_EQUAL(status, rightstatus)) { \
1028 printf("(%s) %s - %s (should be %s)\n", __location__, #call, \
1029 nt_errstr(status), nt_errstr(rightstatus)); \
1030 ret = false; \
1032 finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \
1033 finfo1.generic.in.file.fnum = fnum; \
1034 status2 = smb_raw_fileinfo(cli->tree, tctx, &finfo1); \
1035 if (!NT_STATUS_IS_OK(status2)) { \
1036 printf("(%s) %s pathinfo - %s\n", __location__, #call, nt_errstr(status)); \
1037 ret = false; \
1038 }} while (0)
1041 test stream renames
1043 static bool test_stream_rename(struct torture_context *tctx,
1044 struct smbcli_state *cli,
1045 TALLOC_CTX *mem_ctx)
1047 NTSTATUS status, status2;
1048 union smb_open io;
1049 const char *fname = BASEDIR "\\stream_rename.txt";
1050 const char *sname1, *sname2;
1051 union smb_fileinfo finfo1;
1052 union smb_setfileinfo sfinfo;
1053 bool ret = true;
1054 int fnum = -1;
1055 bool check_fnum;
1056 const char *call_name;
1058 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
1059 sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "Second Stream");
1061 printf("(%s) testing stream renames\n", __location__);
1062 io.generic.level = RAW_OPEN_NTCREATEX;
1063 io.ntcreatex.in.root_fid = 0;
1064 io.ntcreatex.in.flags = 0;
1065 io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE |
1066 SEC_FILE_WRITE_ATTRIBUTE |
1067 SEC_RIGHTS_FILE_ALL;
1068 io.ntcreatex.in.create_options = 0;
1069 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1070 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE;
1071 io.ntcreatex.in.alloc_size = 0;
1072 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
1073 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1074 io.ntcreatex.in.security_flags = 0;
1075 io.ntcreatex.in.fname = sname1;
1077 /* Create two streams. */
1078 status = smb_raw_open(cli->tree, mem_ctx, &io);
1079 CHECK_STATUS(status, NT_STATUS_OK);
1080 fnum = io.ntcreatex.out.file.fnum;
1081 if (fnum != -1) smbcli_close(cli->tree, fnum);
1083 io.ntcreatex.in.fname = sname2;
1084 status = smb_raw_open(cli->tree, mem_ctx, &io);
1085 CHECK_STATUS(status, NT_STATUS_OK);
1086 fnum = io.ntcreatex.out.file.fnum;
1088 if (fnum != -1) smbcli_close(cli->tree, fnum);
1091 * Open the second stream.
1094 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
1095 status = smb_raw_open(cli->tree, mem_ctx, &io);
1096 CHECK_STATUS(status, NT_STATUS_OK);
1097 fnum = io.ntcreatex.out.file.fnum;
1100 * Now rename the second stream onto the first.
1103 ZERO_STRUCT(sfinfo);
1105 sfinfo.rename_information.in.overwrite = 1;
1106 sfinfo.rename_information.in.root_fid = 0;
1107 sfinfo.rename_information.in.new_name = ":Stream One";
1108 CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK);
1110 done:
1111 if (fnum != -1) smbcli_close(cli->tree, fnum);
1112 status = smbcli_unlink(cli->tree, fname);
1113 return ret;
1116 static bool test_stream_rename2(struct torture_context *tctx,
1117 struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
1119 NTSTATUS status;
1120 union smb_open io;
1121 const char *fname1 = BASEDIR "\\stream.txt";
1122 const char *fname2 = BASEDIR "\\stream2.txt";
1123 const char *stream_name1 = ":Stream One:$DATA";
1124 const char *stream_name2 = ":Stream Two:$DATA";
1125 const char *stream_name_default = "::$DATA";
1126 const char *sname1;
1127 const char *sname2;
1128 bool ret = true;
1129 int fnum = -1;
1130 union smb_setfileinfo sinfo;
1131 union smb_rename rio;
1133 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream One");
1134 sname2 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream Two");
1136 io.generic.level = RAW_OPEN_NTCREATEX;
1137 io.ntcreatex.in.root_fid = 0;
1138 io.ntcreatex.in.flags = 0;
1139 io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA|
1140 SEC_STD_DELETE|SEC_FILE_APPEND_DATA|SEC_STD_READ_CONTROL);
1141 io.ntcreatex.in.create_options = 0;
1142 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1143 io.ntcreatex.in.share_access = (NTCREATEX_SHARE_ACCESS_READ |
1144 NTCREATEX_SHARE_ACCESS_WRITE |
1145 NTCREATEX_SHARE_ACCESS_DELETE);
1146 io.ntcreatex.in.alloc_size = 0;
1147 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
1148 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1149 io.ntcreatex.in.security_flags = 0;
1150 io.ntcreatex.in.fname = sname1;
1152 /* Open/create new stream. */
1153 status = smb_raw_open(cli->tree, mem_ctx, &io);
1154 CHECK_STATUS(status, NT_STATUS_OK);
1156 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1159 * Check raw rename with <base>:<stream>.
1161 printf("(%s) Checking NTRENAME of a stream using <base>:<stream>\n",
1162 __location__);
1163 rio.generic.level = RAW_RENAME_NTRENAME;
1164 rio.ntrename.in.old_name = sname1;
1165 rio.ntrename.in.new_name = sname2;
1166 rio.ntrename.in.attrib = 0;
1167 rio.ntrename.in.cluster_size = 0;
1168 rio.ntrename.in.flags = RENAME_FLAG_RENAME;
1169 status = smb_raw_rename(cli->tree, &rio);
1170 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1173 * Check raw rename to the default stream using :<stream>.
1175 printf("(%s) Checking NTRENAME to default stream using :<stream>\n",
1176 __location__);
1177 rio.ntrename.in.new_name = stream_name_default;
1178 status = smb_raw_rename(cli->tree, &rio);
1179 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
1182 * Check raw rename using :<stream>.
1184 printf("(%s) Checking NTRENAME of a stream using :<stream>\n",
1185 __location__);
1186 rio.ntrename.in.new_name = stream_name2;
1187 status = smb_raw_rename(cli->tree, &rio);
1188 CHECK_STATUS(status, NT_STATUS_OK);
1191 * Check raw rename of a stream to a file.
1193 printf("(%s) Checking NTRENAME of a stream to a file\n",
1194 __location__);
1195 rio.ntrename.in.old_name = sname2;
1196 rio.ntrename.in.new_name = fname2;
1197 status = smb_raw_rename(cli->tree, &rio);
1198 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1201 * Check raw rename of a file to a stream.
1203 printf("(%s) Checking NTRENAME of a file to a stream\n",
1204 __location__);
1206 /* Create the file. */
1207 io.ntcreatex.in.fname = fname2;
1208 status = smb_raw_open(cli->tree, mem_ctx, &io);
1209 CHECK_STATUS(status, NT_STATUS_OK);
1210 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1212 /* Try the rename. */
1213 rio.ntrename.in.old_name = fname2;
1214 rio.ntrename.in.new_name = sname1;
1215 status = smb_raw_rename(cli->tree, &rio);
1216 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
1219 * Reopen the stream for trans2 renames.
1221 io.ntcreatex.in.fname = sname2;
1222 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1223 status = smb_raw_open(cli->tree, mem_ctx, &io);
1224 CHECK_STATUS(status, NT_STATUS_OK);
1225 fnum = io.ntcreatex.out.file.fnum;
1228 * Check trans2 rename of a stream using :<stream>.
1230 printf("(%s) Checking trans2 rename of a stream using :<stream>\n",
1231 __location__);
1232 ZERO_STRUCT(sinfo);
1233 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1234 sinfo.rename_information.in.file.fnum = fnum;
1235 sinfo.rename_information.in.overwrite = 1;
1236 sinfo.rename_information.in.root_fid = 0;
1237 sinfo.rename_information.in.new_name = stream_name1;
1238 status = smb_raw_setfileinfo(cli->tree, &sinfo);
1239 CHECK_STATUS(status, NT_STATUS_OK);
1242 * Check trans2 rename of an overwriting stream using :<stream>.
1244 printf("(%s) Checking trans2 rename of an overwriting stream using "
1245 ":<stream>\n", __location__);
1247 /* Create second stream. */
1248 io.ntcreatex.in.fname = sname2;
1249 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
1250 status = smb_raw_open(cli->tree, mem_ctx, &io);
1251 CHECK_STATUS(status, NT_STATUS_OK);
1252 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1254 /* Rename the first stream onto the second. */
1255 sinfo.rename_information.in.file.fnum = fnum;
1256 sinfo.rename_information.in.new_name = stream_name2;
1257 status = smb_raw_setfileinfo(cli->tree, &sinfo);
1258 CHECK_STATUS(status, NT_STATUS_OK);
1260 smbcli_close(cli->tree, fnum);
1263 * Reopen the stream with the new name.
1265 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1266 io.ntcreatex.in.fname = sname2;
1267 status = smb_raw_open(cli->tree, mem_ctx, &io);
1268 CHECK_STATUS(status, NT_STATUS_OK);
1269 fnum = io.ntcreatex.out.file.fnum;
1272 * Check trans2 rename of a stream using <base>:<stream>.
1274 printf("(%s) Checking trans2 rename of a stream using "
1275 "<base>:<stream>\n", __location__);
1276 sinfo.rename_information.in.file.fnum = fnum;
1277 sinfo.rename_information.in.new_name = sname1;
1278 status = smb_raw_setfileinfo(cli->tree, &sinfo);
1279 CHECK_STATUS(status, NT_STATUS_NOT_SUPPORTED);
1282 * Samba3 doesn't currently support renaming a stream to the default
1283 * stream. This test does pass on windows.
1285 if (torture_setting_bool(tctx, "samba3", false) ||
1286 torture_setting_bool(tctx, "samba4", false)) {
1287 goto done;
1291 * Check trans2 rename to the default stream using :<stream>.
1293 printf("(%s) Checking trans2 rename to defaualt stream using "
1294 ":<stream>\n", __location__);
1295 sinfo.rename_information.in.file.fnum = fnum;
1296 sinfo.rename_information.in.new_name = stream_name_default;
1297 status = smb_raw_setfileinfo(cli->tree, &sinfo);
1298 CHECK_STATUS(status, NT_STATUS_OK);
1300 smbcli_close(cli->tree, fnum);
1302 done:
1303 smbcli_close(cli->tree, fnum);
1304 status = smbcli_unlink(cli->tree, fname1);
1305 status = smbcli_unlink(cli->tree, fname2);
1306 return ret;
1309 static bool create_file_with_stream(struct torture_context *tctx,
1310 struct smbcli_state *cli,
1311 TALLOC_CTX *mem_ctx,
1312 const char *stream)
1314 NTSTATUS status;
1315 bool ret = true;
1316 union smb_open io;
1318 /* Create a file with a stream */
1319 io.generic.level = RAW_OPEN_NTCREATEX;
1320 io.ntcreatex.in.root_fid = 0;
1321 io.ntcreatex.in.flags = 0;
1322 io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA|
1323 SEC_FILE_APPEND_DATA|SEC_STD_READ_CONTROL);
1324 io.ntcreatex.in.create_options = 0;
1325 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1326 io.ntcreatex.in.share_access = 0;
1327 io.ntcreatex.in.alloc_size = 0;
1328 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
1329 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1330 io.ntcreatex.in.security_flags = 0;
1331 io.ntcreatex.in.fname = stream;
1333 status = smb_raw_open(cli->tree, mem_ctx, &io);
1334 CHECK_STATUS(status, NT_STATUS_OK);
1336 done:
1337 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1338 return ret;
1341 /* Test how streams interact with create dispositions */
1342 static bool test_stream_create_disposition(struct torture_context *tctx,
1343 struct smbcli_state *cli,
1344 TALLOC_CTX *mem_ctx)
1346 NTSTATUS status;
1347 union smb_open io;
1348 const char *fname = BASEDIR "\\stream.txt";
1349 const char *stream = "Stream One:$DATA";
1350 const char *fname_stream;
1351 const char *default_stream_name = "::$DATA";
1352 const char *stream_list[2];
1353 bool ret = true;
1354 int fnum = -1;
1356 fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream);
1358 stream_list[0] = talloc_asprintf(mem_ctx, ":%s", stream);
1359 stream_list[1] = default_stream_name;
1361 if (!create_file_with_stream(tctx, cli, mem_ctx, fname_stream)) {
1362 goto done;
1365 /* Open the base file with OPEN */
1366 io.generic.level = RAW_OPEN_NTCREATEX;
1367 io.ntcreatex.in.root_fid = 0;
1368 io.ntcreatex.in.flags = 0;
1369 io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA|
1370 SEC_FILE_APPEND_DATA|SEC_STD_READ_CONTROL);
1371 io.ntcreatex.in.create_options = 0;
1372 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1373 io.ntcreatex.in.share_access = 0;
1374 io.ntcreatex.in.alloc_size = 0;
1375 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1376 io.ntcreatex.in.security_flags = 0;
1377 io.ntcreatex.in.fname = fname;
1380 * check ntcreatex open: sanity check
1382 printf("(%s) Checking ntcreatex disp: open\n", __location__);
1383 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1384 status = smb_raw_open(cli->tree, mem_ctx, &io);
1385 CHECK_STATUS(status, NT_STATUS_OK);
1386 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1387 if (!check_stream_list(cli, fname, 2, stream_list)) {
1388 goto done;
1392 * check ntcreatex overwrite
1394 printf("(%s) Checking ntcreatex disp: overwrite\n", __location__);
1395 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE;
1396 status = smb_raw_open(cli->tree, mem_ctx, &io);
1397 CHECK_STATUS(status, NT_STATUS_OK);
1398 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1399 if (!check_stream_list(cli, fname, 1, &default_stream_name)) {
1400 goto done;
1404 * check ntcreatex overwrite_if
1406 printf("(%s) Checking ntcreatex disp: overwrite_if\n", __location__);
1407 smbcli_unlink(cli->tree, fname);
1408 if (!create_file_with_stream(tctx, cli, mem_ctx, fname_stream)) {
1409 goto done;
1412 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1413 status = smb_raw_open(cli->tree, mem_ctx, &io);
1414 CHECK_STATUS(status, NT_STATUS_OK);
1415 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1416 if (!check_stream_list(cli, fname, 1, &default_stream_name)) {
1417 goto done;
1421 * check ntcreatex supersede
1423 printf("(%s) Checking ntcreatex disp: supersede\n", __location__);
1424 smbcli_unlink(cli->tree, fname);
1425 if (!create_file_with_stream(tctx, cli, mem_ctx, fname_stream)) {
1426 goto done;
1429 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_SUPERSEDE;
1430 status = smb_raw_open(cli->tree, mem_ctx, &io);
1431 CHECK_STATUS(status, NT_STATUS_OK);
1432 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1433 if (!check_stream_list(cli, fname, 1, &default_stream_name)) {
1434 goto done;
1438 * check ntcreatex overwrite_if on a stream.
1440 printf("(%s) Checking ntcreatex disp: overwrite_if on stream\n",
1441 __location__);
1442 smbcli_unlink(cli->tree, fname);
1443 if (!create_file_with_stream(tctx, cli, mem_ctx, fname_stream)) {
1444 goto done;
1447 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1448 io.ntcreatex.in.fname = fname_stream;
1449 status = smb_raw_open(cli->tree, mem_ctx, &io);
1450 CHECK_STATUS(status, NT_STATUS_OK);
1451 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1452 if (!check_stream_list(cli, fname, 1, &default_stream_name)) {
1453 goto done;
1457 * check openx overwrite_if
1459 printf("(%s) Checking openx disp: overwrite_if\n", __location__);
1460 smbcli_unlink(cli->tree, fname);
1461 if (!create_file_with_stream(tctx, cli, mem_ctx, fname_stream)) {
1462 goto done;
1465 io.openx.level = RAW_OPEN_OPENX;
1466 io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO;
1467 io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR;
1468 io.openx.in.search_attrs = 0;
1469 io.openx.in.file_attrs = 0;
1470 io.openx.in.write_time = 0;
1471 io.openx.in.size = 1024*1024;
1472 io.openx.in.timeout = 0;
1473 io.openx.in.fname = fname;
1475 io.openx.in.open_func = OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE;
1476 status = smb_raw_open(cli->tree, mem_ctx, &io);
1477 CHECK_STATUS(status, NT_STATUS_OK);
1478 smbcli_close(cli->tree, io.openx.out.file.fnum);
1479 if (!check_stream_list(cli, fname, 1, &default_stream_name)) {
1480 goto done;
1483 done:
1484 smbcli_close(cli->tree, fnum);
1485 smbcli_unlink(cli->tree, fname);
1486 return ret;
1489 /* Test streaminfo with enough streams on a file to fill up the buffer. */
1490 static bool test_stream_large_streaminfo(struct torture_context *tctx,
1491 struct smbcli_state *cli,
1492 TALLOC_CTX *mem_ctx)
1494 #define LONG_STREAM_SIZE 2
1495 char *lstream_name;
1496 const char *fname = BASEDIR "\\stream.txt";
1497 const char *fname_stream;
1498 NTSTATUS status;
1499 bool ret = true;
1500 int i;
1501 union smb_fileinfo finfo;
1503 lstream_name = talloc_array(mem_ctx, char, LONG_STREAM_SIZE);
1505 for (i = 0; i < LONG_STREAM_SIZE - 1; i++) {
1506 lstream_name[i] = (char)('a' + i%26);
1508 lstream_name[LONG_STREAM_SIZE - 1] = '\0';
1510 printf("(%s) Creating a file with a lot of streams\n", __location__);
1511 for (i = 0; i < 10000; i++) {
1512 fname_stream = talloc_asprintf(mem_ctx, "%s:%s%d", fname,
1513 lstream_name, i);
1514 ret = create_file_with_stream(tctx, cli, mem_ctx,
1515 fname_stream);
1516 if (!ret) {
1517 goto done;
1521 finfo.generic.level = RAW_FILEINFO_STREAM_INFO;
1522 finfo.generic.in.file.path = fname;
1524 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
1525 CHECK_STATUS(status, STATUS_BUFFER_OVERFLOW);
1527 done:
1528 smbcli_unlink(cli->tree, fname);
1529 return ret;
1532 /* Test the effect of setting attributes on a stream. */
1533 static bool test_stream_attributes(struct torture_context *tctx,
1534 struct smbcli_state *cli,
1535 TALLOC_CTX *mem_ctx)
1537 bool ret = true;
1538 NTSTATUS status;
1539 union smb_open io;
1540 const char *fname = BASEDIR "\\stream_attr.txt";
1541 const char *stream = "Stream One:$DATA";
1542 const char *fname_stream;
1543 int fnum = -1;
1544 union smb_fileinfo finfo;
1545 union smb_setfileinfo sfinfo;
1546 time_t basetime = (time(NULL) - 86400) & ~1;
1548 printf ("(%s) testing attribute setting on stream\n", __location__);
1550 fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream);
1552 /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */
1553 ret = create_file_with_stream(tctx, cli, mem_ctx, fname_stream);
1554 if (!ret) {
1555 goto done;
1558 ZERO_STRUCT(finfo);
1559 finfo.generic.level = RAW_FILEINFO_BASIC_INFO;
1560 finfo.generic.in.file.path = fname;
1561 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
1562 CHECK_STATUS(status, NT_STATUS_OK);
1564 if (finfo.basic_info.out.attrib != FILE_ATTRIBUTE_ARCHIVE) {
1565 printf("(%s) Incorrect attrib %x - should be %x\n", \
1566 __location__, (unsigned int)finfo.basic_info.out.attrib,
1567 (unsigned int)FILE_ATTRIBUTE_ARCHIVE);
1568 ret = false;
1569 goto done;
1572 /* Now open the stream name. */
1574 io.generic.level = RAW_OPEN_NTCREATEX;
1575 io.ntcreatex.in.root_fid = 0;
1576 io.ntcreatex.in.flags = 0;
1577 io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA|
1578 SEC_FILE_APPEND_DATA|SEC_STD_READ_CONTROL|SEC_FILE_WRITE_ATTRIBUTE);
1579 io.ntcreatex.in.create_options = 0;
1580 io.ntcreatex.in.file_attr = 0;
1581 io.ntcreatex.in.share_access = 0;
1582 io.ntcreatex.in.alloc_size = 0;
1583 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
1584 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1585 io.ntcreatex.in.security_flags = 0;
1586 io.ntcreatex.in.fname = fname_stream;
1588 status = smb_raw_open(cli->tree, mem_ctx, &io);
1589 CHECK_STATUS(status, NT_STATUS_OK);
1591 fnum = io.ntcreatex.out.file.fnum;
1593 /* Change the attributes + time on the stream fnum. */
1594 ZERO_STRUCT(sfinfo);
1595 sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY;
1596 unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime);
1598 sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1599 sfinfo.generic.in.file.fnum = fnum;
1600 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
1601 if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
1602 printf("(%s) %s - %s (should be %s)\n", __location__, "SETATTR",
1603 nt_errstr(status), nt_errstr(NT_STATUS_OK));
1604 ret = false;
1605 goto done;
1608 smbcli_close(cli->tree, fnum);
1609 fnum = -1;
1611 ZERO_STRUCT(finfo);
1612 finfo.generic.level = RAW_FILEINFO_ALL_INFO;
1613 finfo.generic.in.file.path = fname;
1614 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
1615 if (!NT_STATUS_IS_OK(status)) {
1616 printf("(%s) %s pathinfo - %s\n", __location__, "SETATTRE", nt_errstr(status));
1617 ret = false;
1618 goto done;
1621 if (finfo.all_info.out.attrib != FILE_ATTRIBUTE_READONLY) {
1622 printf("(%s) attrib incorrect. Was 0x%x, should be 0x%x\n",
1623 __location__,
1624 (unsigned int)finfo.all_info.out.attrib,
1625 (unsigned int)FILE_ATTRIBUTE_READONLY);
1626 ret = false;
1627 goto done;
1630 if (nt_time_to_unix(finfo.all_info.out.write_time) != basetime) {
1631 printf("(%s) time incorrect.\n",
1632 __location__);
1633 ret = false;
1634 goto done;
1637 done:
1639 if (fnum != -1) {
1640 smbcli_close(cli->tree, fnum);
1642 smbcli_unlink(cli->tree, fname);
1643 return ret;
1647 basic testing of streams calls
1649 bool torture_raw_streams(struct torture_context *torture,
1650 struct smbcli_state *cli)
1652 bool ret = true;
1654 if (!torture_setup_dir(cli, BASEDIR)) {
1655 return false;
1658 ret &= test_stream_dir(torture, cli, torture);
1659 smb_raw_exit(cli->session);
1660 ret &= test_stream_io(torture, cli, torture);
1661 smb_raw_exit(cli->session);
1662 ret &= test_stream_sharemodes(torture, cli, torture);
1663 smb_raw_exit(cli->session);
1664 if (!torture_setting_bool(torture, "samba4", false)) {
1665 ret &= test_stream_delete(torture, cli, torture);
1667 ret &= test_stream_names(torture, cli, torture);
1668 smb_raw_exit(cli->session);
1669 ret &= test_stream_names2(torture, cli, torture);
1670 smb_raw_exit(cli->session);
1671 ret &= test_stream_rename(torture, cli, torture);
1672 smb_raw_exit(cli->session);
1673 ret &= test_stream_rename2(torture, cli, torture);
1674 smb_raw_exit(cli->session);
1675 ret &= test_stream_create_disposition(torture, cli, torture);
1676 smb_raw_exit(cli->session);
1678 ret &= test_stream_attributes(torture, cli, torture);
1679 smb_raw_exit(cli->session);
1681 /* ret &= test_stream_large_streaminfo(torture, cli, torture); */
1682 /* smb_raw_exit(cli->session); */
1684 smbcli_deltree(cli->tree, BASEDIR);
1686 return ret;