s3: Fix bug #9085.
[Samba.git] / source4 / torture / raw / streams.c
blob6c5dc85a10580f7a1c4f1488b5cc0e633140d548
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_STD_DELETE;
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.access_mask = SEC_STD_DELETE;
1095 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
1096 status = smb_raw_open(cli->tree, mem_ctx, &io);
1097 CHECK_STATUS(status, NT_STATUS_OK);
1098 fnum = io.ntcreatex.out.file.fnum;
1101 * Now rename the second stream onto the first.
1104 ZERO_STRUCT(sfinfo);
1106 sfinfo.rename_information.in.overwrite = 1;
1107 sfinfo.rename_information.in.root_fid = 0;
1108 sfinfo.rename_information.in.new_name = ":Stream One";
1109 CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK);
1111 done:
1112 if (fnum != -1) smbcli_close(cli->tree, fnum);
1113 status = smbcli_unlink(cli->tree, fname);
1114 return ret;
1117 static bool test_stream_rename2(struct torture_context *tctx,
1118 struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
1120 NTSTATUS status;
1121 union smb_open io;
1122 const char *fname1 = BASEDIR "\\stream.txt";
1123 const char *fname2 = BASEDIR "\\stream2.txt";
1124 const char *stream_name1 = ":Stream One:$DATA";
1125 const char *stream_name2 = ":Stream Two:$DATA";
1126 const char *stream_name_default = "::$DATA";
1127 const char *sname1;
1128 const char *sname2;
1129 bool ret = true;
1130 int fnum = -1;
1131 union smb_setfileinfo sinfo;
1132 union smb_rename rio;
1134 sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream One");
1135 sname2 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream Two");
1137 io.generic.level = RAW_OPEN_NTCREATEX;
1138 io.ntcreatex.in.root_fid = 0;
1139 io.ntcreatex.in.flags = 0;
1140 io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA|
1141 SEC_STD_DELETE|SEC_FILE_APPEND_DATA|SEC_STD_READ_CONTROL);
1142 io.ntcreatex.in.create_options = 0;
1143 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1144 io.ntcreatex.in.share_access = (NTCREATEX_SHARE_ACCESS_READ |
1145 NTCREATEX_SHARE_ACCESS_WRITE |
1146 NTCREATEX_SHARE_ACCESS_DELETE);
1147 io.ntcreatex.in.alloc_size = 0;
1148 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
1149 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1150 io.ntcreatex.in.security_flags = 0;
1151 io.ntcreatex.in.fname = sname1;
1153 /* Open/create new stream. */
1154 status = smb_raw_open(cli->tree, mem_ctx, &io);
1155 CHECK_STATUS(status, NT_STATUS_OK);
1157 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1160 * Check raw rename with <base>:<stream>.
1162 printf("(%s) Checking NTRENAME of a stream using <base>:<stream>\n",
1163 __location__);
1164 rio.generic.level = RAW_RENAME_NTRENAME;
1165 rio.ntrename.in.old_name = sname1;
1166 rio.ntrename.in.new_name = sname2;
1167 rio.ntrename.in.attrib = 0;
1168 rio.ntrename.in.cluster_size = 0;
1169 rio.ntrename.in.flags = RENAME_FLAG_RENAME;
1170 status = smb_raw_rename(cli->tree, &rio);
1171 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1174 * Check raw rename to the default stream using :<stream>.
1176 printf("(%s) Checking NTRENAME to default stream using :<stream>\n",
1177 __location__);
1178 rio.ntrename.in.new_name = stream_name_default;
1179 status = smb_raw_rename(cli->tree, &rio);
1180 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
1183 * Check raw rename using :<stream>.
1185 printf("(%s) Checking NTRENAME of a stream using :<stream>\n",
1186 __location__);
1187 rio.ntrename.in.new_name = stream_name2;
1188 status = smb_raw_rename(cli->tree, &rio);
1189 CHECK_STATUS(status, NT_STATUS_OK);
1192 * Check raw rename of a stream to a file.
1194 printf("(%s) Checking NTRENAME of a stream to a file\n",
1195 __location__);
1196 rio.ntrename.in.old_name = sname2;
1197 rio.ntrename.in.new_name = fname2;
1198 status = smb_raw_rename(cli->tree, &rio);
1199 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1202 * Check raw rename of a file to a stream.
1204 printf("(%s) Checking NTRENAME of a file to a stream\n",
1205 __location__);
1207 /* Create the file. */
1208 io.ntcreatex.in.fname = fname2;
1209 status = smb_raw_open(cli->tree, mem_ctx, &io);
1210 CHECK_STATUS(status, NT_STATUS_OK);
1211 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1213 /* Try the rename. */
1214 rio.ntrename.in.old_name = fname2;
1215 rio.ntrename.in.new_name = sname1;
1216 status = smb_raw_rename(cli->tree, &rio);
1217 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
1220 * Reopen the stream for trans2 renames.
1222 io.ntcreatex.in.fname = sname2;
1223 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1224 status = smb_raw_open(cli->tree, mem_ctx, &io);
1225 CHECK_STATUS(status, NT_STATUS_OK);
1226 fnum = io.ntcreatex.out.file.fnum;
1229 * Check trans2 rename of a stream using :<stream>.
1231 printf("(%s) Checking trans2 rename of a stream using :<stream>\n",
1232 __location__);
1233 ZERO_STRUCT(sinfo);
1234 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
1235 sinfo.rename_information.in.file.fnum = fnum;
1236 sinfo.rename_information.in.overwrite = 1;
1237 sinfo.rename_information.in.root_fid = 0;
1238 sinfo.rename_information.in.new_name = stream_name1;
1239 status = smb_raw_setfileinfo(cli->tree, &sinfo);
1240 CHECK_STATUS(status, NT_STATUS_OK);
1243 * Check trans2 rename of an overwriting stream using :<stream>.
1245 printf("(%s) Checking trans2 rename of an overwriting stream using "
1246 ":<stream>\n", __location__);
1248 /* Create second stream. */
1249 io.ntcreatex.in.fname = sname2;
1250 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
1251 status = smb_raw_open(cli->tree, mem_ctx, &io);
1252 CHECK_STATUS(status, NT_STATUS_OK);
1253 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1255 /* Rename the first stream onto the second. */
1256 sinfo.rename_information.in.file.fnum = fnum;
1257 sinfo.rename_information.in.new_name = stream_name2;
1258 status = smb_raw_setfileinfo(cli->tree, &sinfo);
1259 CHECK_STATUS(status, NT_STATUS_OK);
1261 smbcli_close(cli->tree, fnum);
1264 * Reopen the stream with the new name.
1266 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1267 io.ntcreatex.in.fname = sname2;
1268 status = smb_raw_open(cli->tree, mem_ctx, &io);
1269 CHECK_STATUS(status, NT_STATUS_OK);
1270 fnum = io.ntcreatex.out.file.fnum;
1273 * Check trans2 rename of a stream using <base>:<stream>.
1275 printf("(%s) Checking trans2 rename of a stream using "
1276 "<base>:<stream>\n", __location__);
1277 sinfo.rename_information.in.file.fnum = fnum;
1278 sinfo.rename_information.in.new_name = sname1;
1279 status = smb_raw_setfileinfo(cli->tree, &sinfo);
1280 CHECK_STATUS(status, NT_STATUS_NOT_SUPPORTED);
1283 * Samba3 doesn't currently support renaming a stream to the default
1284 * stream. This test does pass on windows.
1286 if (torture_setting_bool(tctx, "samba3", false) ||
1287 torture_setting_bool(tctx, "samba4", false)) {
1288 goto done;
1292 * Check trans2 rename to the default stream using :<stream>.
1294 printf("(%s) Checking trans2 rename to defaualt stream using "
1295 ":<stream>\n", __location__);
1296 sinfo.rename_information.in.file.fnum = fnum;
1297 sinfo.rename_information.in.new_name = stream_name_default;
1298 status = smb_raw_setfileinfo(cli->tree, &sinfo);
1299 CHECK_STATUS(status, NT_STATUS_OK);
1301 smbcli_close(cli->tree, fnum);
1303 done:
1304 smbcli_close(cli->tree, fnum);
1305 status = smbcli_unlink(cli->tree, fname1);
1306 status = smbcli_unlink(cli->tree, fname2);
1307 return ret;
1310 static bool create_file_with_stream(struct torture_context *tctx,
1311 struct smbcli_state *cli,
1312 TALLOC_CTX *mem_ctx,
1313 const char *stream)
1315 NTSTATUS status;
1316 bool ret = true;
1317 union smb_open io;
1319 /* Create a file with a stream */
1320 io.generic.level = RAW_OPEN_NTCREATEX;
1321 io.ntcreatex.in.root_fid = 0;
1322 io.ntcreatex.in.flags = 0;
1323 io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA|
1324 SEC_FILE_APPEND_DATA|SEC_STD_READ_CONTROL);
1325 io.ntcreatex.in.create_options = 0;
1326 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1327 io.ntcreatex.in.share_access = 0;
1328 io.ntcreatex.in.alloc_size = 0;
1329 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
1330 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1331 io.ntcreatex.in.security_flags = 0;
1332 io.ntcreatex.in.fname = stream;
1334 status = smb_raw_open(cli->tree, mem_ctx, &io);
1335 CHECK_STATUS(status, NT_STATUS_OK);
1337 done:
1338 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1339 return ret;
1342 /* Test how streams interact with create dispositions */
1343 static bool test_stream_create_disposition(struct torture_context *tctx,
1344 struct smbcli_state *cli,
1345 TALLOC_CTX *mem_ctx)
1347 NTSTATUS status;
1348 union smb_open io;
1349 const char *fname = BASEDIR "\\stream.txt";
1350 const char *stream = "Stream One:$DATA";
1351 const char *fname_stream;
1352 const char *default_stream_name = "::$DATA";
1353 const char *stream_list[2];
1354 bool ret = true;
1355 int fnum = -1;
1357 fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream);
1359 stream_list[0] = talloc_asprintf(mem_ctx, ":%s", stream);
1360 stream_list[1] = default_stream_name;
1362 if (!create_file_with_stream(tctx, cli, mem_ctx, fname_stream)) {
1363 goto done;
1366 /* Open the base file with OPEN */
1367 io.generic.level = RAW_OPEN_NTCREATEX;
1368 io.ntcreatex.in.root_fid = 0;
1369 io.ntcreatex.in.flags = 0;
1370 io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA|
1371 SEC_FILE_APPEND_DATA|SEC_STD_READ_CONTROL);
1372 io.ntcreatex.in.create_options = 0;
1373 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
1374 io.ntcreatex.in.share_access = 0;
1375 io.ntcreatex.in.alloc_size = 0;
1376 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1377 io.ntcreatex.in.security_flags = 0;
1378 io.ntcreatex.in.fname = fname;
1381 * check ntcreatex open: sanity check
1383 printf("(%s) Checking ntcreatex disp: open\n", __location__);
1384 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
1385 status = smb_raw_open(cli->tree, mem_ctx, &io);
1386 CHECK_STATUS(status, NT_STATUS_OK);
1387 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1388 if (!check_stream_list(cli, fname, 2, stream_list)) {
1389 goto done;
1393 * check ntcreatex overwrite
1395 printf("(%s) Checking ntcreatex disp: overwrite\n", __location__);
1396 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE;
1397 status = smb_raw_open(cli->tree, mem_ctx, &io);
1398 CHECK_STATUS(status, NT_STATUS_OK);
1399 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1400 if (!check_stream_list(cli, fname, 1, &default_stream_name)) {
1401 goto done;
1405 * check ntcreatex overwrite_if
1407 printf("(%s) Checking ntcreatex disp: overwrite_if\n", __location__);
1408 smbcli_unlink(cli->tree, fname);
1409 if (!create_file_with_stream(tctx, cli, mem_ctx, fname_stream)) {
1410 goto done;
1413 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1414 status = smb_raw_open(cli->tree, mem_ctx, &io);
1415 CHECK_STATUS(status, NT_STATUS_OK);
1416 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1417 if (!check_stream_list(cli, fname, 1, &default_stream_name)) {
1418 goto done;
1422 * check ntcreatex supersede
1424 printf("(%s) Checking ntcreatex disp: supersede\n", __location__);
1425 smbcli_unlink(cli->tree, fname);
1426 if (!create_file_with_stream(tctx, cli, mem_ctx, fname_stream)) {
1427 goto done;
1430 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_SUPERSEDE;
1431 status = smb_raw_open(cli->tree, mem_ctx, &io);
1432 CHECK_STATUS(status, NT_STATUS_OK);
1433 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1434 if (!check_stream_list(cli, fname, 1, &default_stream_name)) {
1435 goto done;
1439 * check ntcreatex overwrite_if on a stream.
1441 printf("(%s) Checking ntcreatex disp: overwrite_if on stream\n",
1442 __location__);
1443 smbcli_unlink(cli->tree, fname);
1444 if (!create_file_with_stream(tctx, cli, mem_ctx, fname_stream)) {
1445 goto done;
1448 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1449 io.ntcreatex.in.fname = fname_stream;
1450 status = smb_raw_open(cli->tree, mem_ctx, &io);
1451 CHECK_STATUS(status, NT_STATUS_OK);
1452 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1453 if (!check_stream_list(cli, fname, 1, &default_stream_name)) {
1454 goto done;
1458 * check openx overwrite_if
1460 printf("(%s) Checking openx disp: overwrite_if\n", __location__);
1461 smbcli_unlink(cli->tree, fname);
1462 if (!create_file_with_stream(tctx, cli, mem_ctx, fname_stream)) {
1463 goto done;
1466 io.openx.level = RAW_OPEN_OPENX;
1467 io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO;
1468 io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR;
1469 io.openx.in.search_attrs = 0;
1470 io.openx.in.file_attrs = 0;
1471 io.openx.in.write_time = 0;
1472 io.openx.in.size = 1024*1024;
1473 io.openx.in.timeout = 0;
1474 io.openx.in.fname = fname;
1476 io.openx.in.open_func = OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE;
1477 status = smb_raw_open(cli->tree, mem_ctx, &io);
1478 CHECK_STATUS(status, NT_STATUS_OK);
1479 smbcli_close(cli->tree, io.openx.out.file.fnum);
1480 if (!check_stream_list(cli, fname, 1, &default_stream_name)) {
1481 goto done;
1484 done:
1485 smbcli_close(cli->tree, fnum);
1486 smbcli_unlink(cli->tree, fname);
1487 return ret;
1490 /* Test streaminfo with enough streams on a file to fill up the buffer. */
1491 static bool test_stream_large_streaminfo(struct torture_context *tctx,
1492 struct smbcli_state *cli,
1493 TALLOC_CTX *mem_ctx)
1495 #define LONG_STREAM_SIZE 2
1496 char *lstream_name;
1497 const char *fname = BASEDIR "\\stream.txt";
1498 const char *fname_stream;
1499 NTSTATUS status;
1500 bool ret = true;
1501 int i;
1502 union smb_fileinfo finfo;
1504 lstream_name = talloc_array(mem_ctx, char, LONG_STREAM_SIZE);
1506 for (i = 0; i < LONG_STREAM_SIZE - 1; i++) {
1507 lstream_name[i] = (char)('a' + i%26);
1509 lstream_name[LONG_STREAM_SIZE - 1] = '\0';
1511 printf("(%s) Creating a file with a lot of streams\n", __location__);
1512 for (i = 0; i < 10000; i++) {
1513 fname_stream = talloc_asprintf(mem_ctx, "%s:%s%d", fname,
1514 lstream_name, i);
1515 ret = create_file_with_stream(tctx, cli, mem_ctx,
1516 fname_stream);
1517 if (!ret) {
1518 goto done;
1522 finfo.generic.level = RAW_FILEINFO_STREAM_INFO;
1523 finfo.generic.in.file.path = fname;
1525 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
1526 CHECK_STATUS(status, STATUS_BUFFER_OVERFLOW);
1528 done:
1529 smbcli_unlink(cli->tree, fname);
1530 return ret;
1533 /* Test the effect of setting attributes on a stream. */
1534 static bool test_stream_attributes(struct torture_context *tctx,
1535 struct smbcli_state *cli,
1536 TALLOC_CTX *mem_ctx)
1538 bool ret = true;
1539 NTSTATUS status;
1540 union smb_open io;
1541 const char *fname = BASEDIR "\\stream_attr.txt";
1542 const char *stream = "Stream One:$DATA";
1543 const char *fname_stream;
1544 int fnum = -1;
1545 union smb_fileinfo finfo;
1546 union smb_setfileinfo sfinfo;
1547 time_t basetime = (time(NULL) - 86400) & ~1;
1549 printf ("(%s) testing attribute setting on stream\n", __location__);
1551 fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream);
1553 /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */
1554 ret = create_file_with_stream(tctx, cli, mem_ctx, fname_stream);
1555 if (!ret) {
1556 goto done;
1559 ZERO_STRUCT(finfo);
1560 finfo.generic.level = RAW_FILEINFO_BASIC_INFO;
1561 finfo.generic.in.file.path = fname;
1562 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
1563 CHECK_STATUS(status, NT_STATUS_OK);
1565 if (finfo.basic_info.out.attrib != FILE_ATTRIBUTE_ARCHIVE) {
1566 printf("(%s) Incorrect attrib %x - should be %x\n", \
1567 __location__, (unsigned int)finfo.basic_info.out.attrib,
1568 (unsigned int)FILE_ATTRIBUTE_ARCHIVE);
1569 ret = false;
1570 goto done;
1573 /* Now open the stream name. */
1575 io.generic.level = RAW_OPEN_NTCREATEX;
1576 io.ntcreatex.in.root_fid = 0;
1577 io.ntcreatex.in.flags = 0;
1578 io.ntcreatex.in.access_mask = (SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA|
1579 SEC_FILE_APPEND_DATA|SEC_STD_READ_CONTROL|SEC_FILE_WRITE_ATTRIBUTE);
1580 io.ntcreatex.in.create_options = 0;
1581 io.ntcreatex.in.file_attr = 0;
1582 io.ntcreatex.in.share_access = 0;
1583 io.ntcreatex.in.alloc_size = 0;
1584 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
1585 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
1586 io.ntcreatex.in.security_flags = 0;
1587 io.ntcreatex.in.fname = fname_stream;
1589 status = smb_raw_open(cli->tree, mem_ctx, &io);
1590 CHECK_STATUS(status, NT_STATUS_OK);
1592 fnum = io.ntcreatex.out.file.fnum;
1594 /* Change the attributes + time on the stream fnum. */
1595 ZERO_STRUCT(sfinfo);
1596 sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY;
1597 unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime);
1599 sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
1600 sfinfo.generic.in.file.fnum = fnum;
1601 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
1602 if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
1603 printf("(%s) %s - %s (should be %s)\n", __location__, "SETATTR",
1604 nt_errstr(status), nt_errstr(NT_STATUS_OK));
1605 ret = false;
1606 goto done;
1609 smbcli_close(cli->tree, fnum);
1610 fnum = -1;
1612 ZERO_STRUCT(finfo);
1613 finfo.generic.level = RAW_FILEINFO_ALL_INFO;
1614 finfo.generic.in.file.path = fname;
1615 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
1616 if (!NT_STATUS_IS_OK(status)) {
1617 printf("(%s) %s pathinfo - %s\n", __location__, "SETATTRE", nt_errstr(status));
1618 ret = false;
1619 goto done;
1622 if (finfo.all_info.out.attrib != FILE_ATTRIBUTE_READONLY) {
1623 printf("(%s) attrib incorrect. Was 0x%x, should be 0x%x\n",
1624 __location__,
1625 (unsigned int)finfo.all_info.out.attrib,
1626 (unsigned int)FILE_ATTRIBUTE_READONLY);
1627 ret = false;
1628 goto done;
1631 if (nt_time_to_unix(finfo.all_info.out.write_time) != basetime) {
1632 printf("(%s) time incorrect.\n",
1633 __location__);
1634 ret = false;
1635 goto done;
1638 done:
1640 if (fnum != -1) {
1641 smbcli_close(cli->tree, fnum);
1643 smbcli_unlink(cli->tree, fname);
1644 return ret;
1648 basic testing of streams calls
1650 bool torture_raw_streams(struct torture_context *torture,
1651 struct smbcli_state *cli)
1653 bool ret = true;
1655 if (!torture_setup_dir(cli, BASEDIR)) {
1656 return false;
1659 ret &= test_stream_dir(torture, cli, torture);
1660 smb_raw_exit(cli->session);
1661 ret &= test_stream_io(torture, cli, torture);
1662 smb_raw_exit(cli->session);
1663 ret &= test_stream_sharemodes(torture, cli, torture);
1664 smb_raw_exit(cli->session);
1665 if (!torture_setting_bool(torture, "samba4", false)) {
1666 ret &= test_stream_delete(torture, cli, torture);
1668 ret &= test_stream_names(torture, cli, torture);
1669 smb_raw_exit(cli->session);
1670 ret &= test_stream_names2(torture, cli, torture);
1671 smb_raw_exit(cli->session);
1672 ret &= test_stream_rename(torture, cli, torture);
1673 smb_raw_exit(cli->session);
1674 ret &= test_stream_rename2(torture, cli, torture);
1675 smb_raw_exit(cli->session);
1676 ret &= test_stream_create_disposition(torture, cli, torture);
1677 smb_raw_exit(cli->session);
1679 ret &= test_stream_attributes(torture, cli, torture);
1680 smb_raw_exit(cli->session);
1682 /* ret &= test_stream_large_streaminfo(torture, cli, torture); */
1683 /* smb_raw_exit(cli->session); */
1685 smbcli_deltree(cli->tree, BASEDIR);
1687 return ret;