2 Unix SMB/CIFS implementation.
4 test alternate data streams
6 Copyright (C) Andrew Tridgell 2004
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "system/locale.h"
24 #include "torture/torture.h"
25 #include "libcli/raw/libcliraw.h"
26 #include "system/filesys.h"
27 #include "libcli/libcli.h"
28 #include "torture/util.h"
29 #include "lib/util/tsort.h"
30 #include "torture/raw/proto.h"
32 #define BASEDIR "\\teststreams"
34 #define CHECK_STATUS(status, correct) \
35 torture_assert_ntstatus_equal_goto(tctx,status,correct,ret,done,"CHECK_STATUS")
37 #define CHECK_VALUE(v, correct) \
38 torture_assert_int_equal(tctx,v,correct,"CHECK_VALUE")
40 #define CHECK_NTTIME(v, correct) \
41 torture_assert_u64_equal(tctx,v,correct,"CHECK_NTTIME")
43 #define CHECK_STR(v, correct) do { \
45 if ((v) && !(correct)) { \
47 } else if (!(v) && (correct)) { \
49 } else if (!(v) && !(correct)) { \
51 } else if (strcmp((v), (correct)) == 0) { \
56 torture_assert(tctx,ok,\
57 talloc_asprintf(tctx, "got '%s', expected '%s'",\
58 (v)?(v):"NULL", (correct)?(correct):"NULL")); \
62 check that a stream has the right contents
64 static bool check_stream(struct smbcli_state
*cli
, const char *location
,
66 const char *fname
, const char *sname
,
70 const char *full_name
;
74 full_name
= talloc_asprintf(mem_ctx
, "%s:%s", fname
, sname
);
76 fnum
= smbcli_open(cli
->tree
, full_name
, O_RDONLY
, DENY_NONE
);
80 printf("(%s) should have failed stream open of %s\n",
88 printf("(%s) Failed to open stream '%s' - %s\n",
89 location
, full_name
, smbcli_errstr(cli
->tree
));
93 buf
= talloc_array(mem_ctx
, uint8_t, strlen(value
)+11);
95 ret
= smbcli_read(cli
->tree
, fnum
, buf
, 0, strlen(value
)+11);
96 if (ret
!= strlen(value
)) {
97 printf("(%s) Failed to read %lu bytes from stream '%s' - got %d\n",
98 location
, (long)strlen(value
), full_name
, (int)ret
);
102 if (memcmp(buf
, value
, strlen(value
)) != 0) {
103 printf("(%s) Bad data in stream\n", location
);
107 smbcli_close(cli
->tree
, fnum
);
111 static int qsort_string(char * const *s1
, char * const *s2
)
113 return strcmp(*s1
, *s2
);
116 static int qsort_stream(const struct stream_struct
*s1
, const struct stream_struct
*s2
)
118 return strcmp(s1
->stream_name
.s
, s2
->stream_name
.s
);
121 static bool check_stream_list(struct torture_context
*tctx
,
122 struct smbcli_state
*cli
, const char *fname
,
123 int num_exp
, const char **exp
)
125 union smb_fileinfo finfo
;
128 TALLOC_CTX
*tmp_ctx
= talloc_new(cli
);
130 struct stream_struct
*stream_sort
;
134 finfo
.generic
.level
= RAW_FILEINFO_STREAM_INFO
;
135 finfo
.generic
.in
.file
.path
= fname
;
137 status
= smb_raw_pathinfo(cli
->tree
, tmp_ctx
, &finfo
);
138 CHECK_STATUS(status
, NT_STATUS_OK
);
140 CHECK_VALUE(finfo
.stream_info
.out
.num_streams
, num_exp
);
147 exp_sort
= (char **)talloc_memdup(tmp_ctx
, exp
, num_exp
* sizeof(*exp
));
149 if (exp_sort
== NULL
) {
153 TYPESAFE_QSORT(exp_sort
, num_exp
, qsort_string
);
155 stream_sort
= (struct stream_struct
*)talloc_memdup(tmp_ctx
,
156 finfo
.stream_info
.out
.streams
,
157 finfo
.stream_info
.out
.num_streams
*
158 sizeof(*stream_sort
));
160 if (stream_sort
== NULL
) {
164 TYPESAFE_QSORT(stream_sort
, finfo
.stream_info
.out
.num_streams
, qsort_stream
);
166 for (i
=0; i
<num_exp
; i
++) {
167 if (strcmp(exp_sort
[i
], stream_sort
[i
].stream_name
.s
) != 0) {
175 talloc_free(tmp_ctx
);
179 for (i
=0; i
<num_exp
; i
++) {
180 torture_comment(tctx
, "stream names '%s' '%s'\n",
181 exp_sort
[i
], stream_sort
[i
].stream_name
.s
);
183 CHECK_STR(stream_sort
[fail
].stream_name
.s
, exp_sort
[fail
]);
184 talloc_free(tmp_ctx
);
189 test bahavior of streams on directories
191 static bool test_stream_dir(struct torture_context
*tctx
,
192 struct smbcli_state
*cli
)
196 const char *fname
= BASEDIR
"\\stream.txt";
199 const char *basedir_data
;
201 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
203 basedir_data
= talloc_asprintf(tctx
, "%s::$DATA", BASEDIR
);
204 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "Stream One");
206 printf("(%s) opening non-existent directory stream\n", __location__
);
207 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
208 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
209 io
.ntcreatex
.in
.flags
= 0;
210 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
211 io
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
;
212 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
213 io
.ntcreatex
.in
.share_access
= 0;
214 io
.ntcreatex
.in
.alloc_size
= 0;
215 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
216 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
217 io
.ntcreatex
.in
.security_flags
= 0;
218 io
.ntcreatex
.in
.fname
= sname1
;
219 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
220 CHECK_STATUS(status
, NT_STATUS_NOT_A_DIRECTORY
);
222 printf("(%s) opening basedir stream\n", __location__
);
223 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
224 io
.ntcreatex
.in
.root_fid
.fnum
= 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_DIRECTORY
;
229 io
.ntcreatex
.in
.share_access
= 0;
230 io
.ntcreatex
.in
.alloc_size
= 0;
231 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
232 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
233 io
.ntcreatex
.in
.security_flags
= 0;
234 io
.ntcreatex
.in
.fname
= basedir_data
;
235 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
236 CHECK_STATUS(status
, NT_STATUS_NOT_A_DIRECTORY
);
238 printf("(%s) opening basedir ::$DATA stream\n", __location__
);
239 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
240 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
241 io
.ntcreatex
.in
.flags
= 0x10;
242 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
243 io
.ntcreatex
.in
.create_options
= 0;
244 io
.ntcreatex
.in
.file_attr
= 0;
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
, tctx
, &io
);
252 CHECK_STATUS(status
, NT_STATUS_FILE_IS_A_DIRECTORY
);
254 printf("(%s) list the streams on the basedir\n", __location__
);
255 ret
&= check_stream_list(tctx
, cli
, BASEDIR
, 0, NULL
);
257 smbcli_deltree(cli
->tree
, BASEDIR
);
262 test basic behavior of streams on directories
264 static bool test_stream_io(struct torture_context
*tctx
,
265 struct smbcli_state
*cli
)
269 const char *fname
= BASEDIR
"\\stream.txt";
270 const char *sname1
, *sname2
;
275 const char *one
[] = { "::$DATA" };
276 const char *two
[] = { "::$DATA", ":Second Stream:$DATA" };
277 const char *three
[] = { "::$DATA", ":Stream One:$DATA",
278 ":Second Stream:$DATA" };
280 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
282 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "Stream One");
283 sname2
= talloc_asprintf(tctx
, "%s:%s:$DaTa", fname
, "Second Stream");
285 printf("(%s) creating a stream on a non-existent file\n", __location__
);
286 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
287 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
288 io
.ntcreatex
.in
.flags
= 0;
289 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
290 io
.ntcreatex
.in
.create_options
= 0;
291 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
292 io
.ntcreatex
.in
.share_access
= 0;
293 io
.ntcreatex
.in
.alloc_size
= 0;
294 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
295 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
296 io
.ntcreatex
.in
.security_flags
= 0;
297 io
.ntcreatex
.in
.fname
= sname1
;
298 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
299 CHECK_STATUS(status
, NT_STATUS_OK
);
300 fnum
= io
.ntcreatex
.out
.file
.fnum
;
302 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Stream One", NULL
);
304 printf("(%s) check that open of base file is allowed\n", __location__
);
305 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
306 io
.ntcreatex
.in
.fname
= fname
;
307 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
308 CHECK_STATUS(status
, NT_STATUS_OK
);
309 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
311 printf("(%s) writing to stream\n", __location__
);
312 retsize
= smbcli_write(cli
->tree
, fnum
, 0, "test data", 0, 9);
313 CHECK_VALUE(retsize
, 9);
315 smbcli_close(cli
->tree
, fnum
);
317 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Stream One", "test data");
319 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
320 io
.ntcreatex
.in
.fname
= sname1
;
321 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
322 CHECK_STATUS(status
, NT_STATUS_OK
);
323 fnum
= io
.ntcreatex
.out
.file
.fnum
;
325 printf("(%s) modifying stream\n", __location__
);
326 retsize
= smbcli_write(cli
->tree
, fnum
, 0, "MORE DATA ", 5, 10);
327 CHECK_VALUE(retsize
, 10);
329 smbcli_close(cli
->tree
, fnum
);
331 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Stream One:$FOO", NULL
);
333 printf("(%s) creating a stream2 on a existing file\n", __location__
);
334 io
.ntcreatex
.in
.fname
= sname2
;
335 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
336 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
337 CHECK_STATUS(status
, NT_STATUS_OK
);
338 fnum
= io
.ntcreatex
.out
.file
.fnum
;
340 printf("(%s) modifying stream\n", __location__
);
341 retsize
= smbcli_write(cli
->tree
, fnum
, 0, "SECOND STREAM", 0, 13);
342 CHECK_VALUE(retsize
, 13);
344 smbcli_close(cli
->tree
, fnum
);
346 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Stream One", "test MORE DATA ");
347 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Stream One:$DATA", "test MORE DATA ");
348 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Stream One:", NULL
);
349 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Second Stream", "SECOND STREAM");
350 ret
&= check_stream(cli
, __location__
, tctx
, fname
,
351 "SECOND STREAM:$DATA", "SECOND STREAM");
352 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Second Stream:$DATA", "SECOND STREAM");
353 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Second Stream:", NULL
);
354 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Second Stream:$FOO", NULL
);
356 check_stream_list(tctx
, cli
, fname
, 3, three
);
358 printf("(%s) deleting stream\n", __location__
);
359 status
= smbcli_unlink(cli
->tree
, sname1
);
360 CHECK_STATUS(status
, NT_STATUS_OK
);
362 check_stream_list(tctx
, cli
, fname
, 2, two
);
364 printf("(%s) delete a stream via delete-on-close\n", __location__
);
365 io
.ntcreatex
.in
.fname
= sname2
;
366 io
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
367 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_DELETE
;
368 io
.ntcreatex
.in
.access_mask
= SEC_STD_DELETE
;
369 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
371 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
372 CHECK_STATUS(status
, NT_STATUS_OK
);
373 fnum
= io
.ntcreatex
.out
.file
.fnum
;
375 smbcli_close(cli
->tree
, fnum
);
376 status
= smbcli_unlink(cli
->tree
, sname2
);
377 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
379 check_stream_list(tctx
, cli
, fname
, 1, one
);
381 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
382 io
.ntcreatex
.in
.fname
= sname1
;
383 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
384 CHECK_STATUS(status
, NT_STATUS_OK
);
385 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
386 io
.ntcreatex
.in
.fname
= sname2
;
387 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
388 CHECK_STATUS(status
, NT_STATUS_OK
);
389 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
391 printf("(%s) deleting file\n", __location__
);
392 status
= smbcli_unlink(cli
->tree
, fname
);
393 CHECK_STATUS(status
, NT_STATUS_OK
);
396 smbcli_close(cli
->tree
, fnum
);
397 smbcli_deltree(cli
->tree
, BASEDIR
);
402 test stream sharemodes
404 static bool test_stream_sharemodes(struct torture_context
*tctx
,
405 struct smbcli_state
*cli
)
409 const char *fname
= BASEDIR
"\\stream.txt";
410 const char *sname1
, *sname2
;
415 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
417 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "Stream One");
418 sname2
= talloc_asprintf(tctx
, "%s:%s:$DaTa", fname
, "Second Stream");
420 printf("(%s) testing stream share mode conflicts\n", __location__
);
421 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
422 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
423 io
.ntcreatex
.in
.flags
= 0;
424 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
425 io
.ntcreatex
.in
.create_options
= 0;
426 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
427 io
.ntcreatex
.in
.share_access
= 0;
428 io
.ntcreatex
.in
.alloc_size
= 0;
429 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
430 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
431 io
.ntcreatex
.in
.security_flags
= 0;
432 io
.ntcreatex
.in
.fname
= sname1
;
434 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
435 CHECK_STATUS(status
, NT_STATUS_OK
);
436 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
439 * A different stream does not give a sharing violation
442 io
.ntcreatex
.in
.fname
= sname2
;
443 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
444 CHECK_STATUS(status
, NT_STATUS_OK
);
445 fnum2
= io
.ntcreatex
.out
.file
.fnum
;
448 * ... whereas the same stream does with unchanged access/share_access
452 io
.ntcreatex
.in
.fname
= sname1
;
453 io
.ntcreatex
.in
.open_disposition
= 0;
454 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
455 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
457 io
.ntcreatex
.in
.fname
= sname2
;
458 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
459 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
462 if (fnum1
!= -1) smbcli_close(cli
->tree
, fnum1
);
463 if (fnum2
!= -1) smbcli_close(cli
->tree
, fnum2
);
464 status
= smbcli_unlink(cli
->tree
, fname
);
465 smbcli_deltree(cli
->tree
, BASEDIR
);
470 * Test FILE_SHARE_DELETE on streams
472 * A stream opened with !FILE_SHARE_DELETE prevents the main file to be opened
473 * with SEC_STD_DELETE.
475 * The main file opened with !FILE_SHARE_DELETE does *not* prevent a stream to
476 * be opened with SEC_STD_DELETE.
478 * A stream held open with FILE_SHARE_DELETE allows the file to be
479 * deleted. After the main file is deleted, access to the open file descriptor
480 * still works, but all name-based access to both the main file as well as the
481 * stream is denied with DELETE ending.
483 * This means, an open of the main file with SEC_STD_DELETE should walk all
484 * streams and also open them with SEC_STD_DELETE. If any of these opens gives
485 * SHARING_VIOLATION, the main open fails.
487 * Closing the main file after delete_on_close has been set does not really
488 * unlink it but leaves the corresponding share mode entry with
489 * delete_on_close being set around until all streams are closed.
491 * Opening a stream must also look at the main file's share mode entry, look
492 * at the delete_on_close bit and potentially return DELETE_PENDING.
495 static bool test_stream_delete(struct torture_context
*tctx
,
496 struct smbcli_state
*cli
)
500 const char *fname
= BASEDIR
"\\stream.txt";
506 union smb_fileinfo finfo
;
508 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
510 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "Stream One");
512 printf("(%s) opening non-existent file stream\n", __location__
);
513 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
514 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
515 io
.ntcreatex
.in
.flags
= 0;
516 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
;
517 io
.ntcreatex
.in
.create_options
= 0;
518 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
519 io
.ntcreatex
.in
.share_access
= 0;
520 io
.ntcreatex
.in
.alloc_size
= 0;
521 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
522 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
523 io
.ntcreatex
.in
.security_flags
= 0;
524 io
.ntcreatex
.in
.fname
= sname1
;
526 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
527 CHECK_STATUS(status
, NT_STATUS_OK
);
528 fnum
= io
.ntcreatex
.out
.file
.fnum
;
530 retsize
= smbcli_write(cli
->tree
, fnum
, 0, "test data", 0, 9);
531 CHECK_VALUE(retsize
, 9);
534 * One stream opened without FILE_SHARE_DELETE prevents the main file
535 * to be deleted or even opened with DELETE access
538 status
= smbcli_unlink(cli
->tree
, fname
);
539 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
541 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
542 io
.ntcreatex
.in
.fname
= fname
;
543 io
.ntcreatex
.in
.access_mask
= SEC_STD_DELETE
;
544 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
545 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
547 smbcli_close(cli
->tree
, fnum
);
550 * ... but unlink works if a stream is opened with FILE_SHARE_DELETE
553 io
.ntcreatex
.in
.fname
= sname1
;
554 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
;
555 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_DELETE
;
556 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
557 CHECK_STATUS(status
, NT_STATUS_OK
);
558 fnum
= io
.ntcreatex
.out
.file
.fnum
;
560 status
= smbcli_unlink(cli
->tree
, fname
);
561 CHECK_STATUS(status
, NT_STATUS_OK
);
564 * file access still works on the stream while the main file is closed
567 retsize
= smbcli_read(cli
->tree
, fnum
, buf
, 0, 9);
568 CHECK_VALUE(retsize
, 9);
570 finfo
.generic
.level
= RAW_FILEINFO_STANDARD
;
571 finfo
.generic
.in
.file
.path
= fname
;
574 * name-based access to both the main file and the stream does not
575 * work anymore but gives DELETE_PENDING
578 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
579 CHECK_STATUS(status
, NT_STATUS_DELETE_PENDING
);
582 * older S3 doesn't do this
584 finfo
.generic
.in
.file
.path
= sname1
;
585 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
586 CHECK_STATUS(status
, NT_STATUS_DELETE_PENDING
);
589 * fd-based qfileinfo on the stream still works, the stream does not
590 * have the delete-on-close bit set. This could mean that open on the
591 * stream first opens the main file
594 finfo
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
595 finfo
.all_info
.in
.file
.fnum
= fnum
;
597 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo
);
598 CHECK_STATUS(status
, NT_STATUS_OK
);
600 /* w2k and w2k3 return 0 and w2k8 returns 1 */
601 if (TARGET_IS_WINXP(tctx
) || TARGET_IS_W2K3(tctx
) ||
602 TARGET_IS_SAMBA3(tctx
)) {
603 CHECK_VALUE(finfo
.all_info
.out
.delete_pending
, 0);
605 CHECK_VALUE(finfo
.all_info
.out
.delete_pending
, 1);
608 smbcli_close(cli
->tree
, fnum
);
611 * After closing the stream the file is really gone.
614 finfo
.generic
.in
.file
.path
= fname
;
615 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
616 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
618 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
620 io
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
621 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
622 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
623 CHECK_STATUS(status
, NT_STATUS_OK
);
624 fnum
= io
.ntcreatex
.out
.file
.fnum
;
626 finfo
.generic
.in
.file
.path
= fname
;
627 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
628 CHECK_STATUS(status
, NT_STATUS_OK
);
630 smbcli_close(cli
->tree
, fnum
);
632 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
633 CHECK_STATUS(status
, NT_STATUS_OK
);
635 smbcli_close(cli
->tree
, fnum
);
636 smbcli_unlink(cli
->tree
, fname
);
637 smbcli_deltree(cli
->tree
, BASEDIR
);
644 static bool test_stream_names(struct torture_context
*tctx
,
645 struct smbcli_state
*cli
)
649 union smb_fileinfo info
;
650 union smb_fileinfo finfo
;
651 union smb_fileinfo stinfo
;
652 union smb_setfileinfo sinfo
;
653 const char *fname
= BASEDIR
"\\stream_names.txt";
654 const char *sname1
, *sname1b
, *sname1c
, *sname1d
;
655 const char *sname2
, *snamew
, *snamew2
;
656 const char *snamer1
, *snamer2
;
662 const char *four
[4] = {
664 ":\x05Stream\n One:$DATA",
665 ":MStream Two:$DATA",
668 const char *five1
[5] = {
670 ":\x05Stream\n One:$DATA",
671 ":BeforeRename:$DATA",
672 ":MStream Two:$DATA",
675 const char *five2
[5] = {
677 ":\x05Stream\n One:$DATA",
678 ":AfterRename:$DATA",
679 ":MStream Two:$DATA",
683 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
685 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "\x05Stream\n One");
686 sname1b
= talloc_asprintf(tctx
, "%s:", sname1
);
687 sname1c
= talloc_asprintf(tctx
, "%s:$FOO", sname1
);
688 sname1d
= talloc_asprintf(tctx
, "%s:?D*a", sname1
);
689 sname2
= talloc_asprintf(tctx
, "%s:%s:$DaTa", fname
, "MStream Two");
690 snamew
= talloc_asprintf(tctx
, "%s:%s:$DATA", fname
, "?Stream*");
691 snamew2
= talloc_asprintf(tctx
, "%s\\stream*:%s:$DATA", BASEDIR
, "?Stream*");
692 snamer1
= talloc_asprintf(tctx
, "%s:%s:$DATA", fname
, "BeforeRename");
693 snamer2
= talloc_asprintf(tctx
, "%s:%s:$DATA", fname
, "AfterRename");
695 printf("(%s) testing stream names\n", __location__
);
696 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
697 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
698 io
.ntcreatex
.in
.flags
= 0;
699 io
.ntcreatex
.in
.access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
700 io
.ntcreatex
.in
.create_options
= 0;
701 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
702 io
.ntcreatex
.in
.share_access
=
703 NTCREATEX_SHARE_ACCESS_READ
|
704 NTCREATEX_SHARE_ACCESS_WRITE
;
705 io
.ntcreatex
.in
.alloc_size
= 0;
706 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
707 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
708 io
.ntcreatex
.in
.security_flags
= 0;
709 io
.ntcreatex
.in
.fname
= fname
;
711 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
712 CHECK_STATUS(status
, NT_STATUS_OK
);
713 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
715 torture_comment(tctx
, "Adding two EAs to base file\n");
717 sinfo
.generic
.level
= RAW_SFILEINFO_EA_SET
;
718 sinfo
.generic
.in
.file
.fnum
= fnum1
;
719 sinfo
.ea_set
.in
.num_eas
= 2;
720 sinfo
.ea_set
.in
.eas
= talloc_array(tctx
, struct ea_struct
, 2);
721 sinfo
.ea_set
.in
.eas
[0].flags
= 0;
722 sinfo
.ea_set
.in
.eas
[0].name
.s
= "EAONE";
723 sinfo
.ea_set
.in
.eas
[0].value
= data_blob_string_const("VALUE1");
724 sinfo
.ea_set
.in
.eas
[1].flags
= 0;
725 sinfo
.ea_set
.in
.eas
[1].name
.s
= "SECONDEA";
726 sinfo
.ea_set
.in
.eas
[1].value
= data_blob_string_const("ValueTwo");
728 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
729 CHECK_STATUS(status
, NT_STATUS_OK
);
732 * Make sure the create time of the streams are different from the
736 smbcli_close(cli
->tree
, fnum1
);
738 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
739 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
740 io
.ntcreatex
.in
.flags
= 0;
741 io
.ntcreatex
.in
.access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
742 io
.ntcreatex
.in
.create_options
= 0;
743 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
744 io
.ntcreatex
.in
.share_access
=
745 NTCREATEX_SHARE_ACCESS_READ
|
746 NTCREATEX_SHARE_ACCESS_WRITE
;
747 io
.ntcreatex
.in
.alloc_size
= 0;
748 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
749 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
750 io
.ntcreatex
.in
.security_flags
= 0;
751 io
.ntcreatex
.in
.fname
= sname1
;
753 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
754 CHECK_STATUS(status
, NT_STATUS_OK
);
755 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
757 torture_comment(tctx
, "Adding one EAs to first stream file\n");
759 sinfo
.generic
.level
= RAW_SFILEINFO_EA_SET
;
760 sinfo
.generic
.in
.file
.fnum
= fnum1
;
761 sinfo
.ea_set
.in
.num_eas
= 1;
762 sinfo
.ea_set
.in
.eas
= talloc_array(tctx
, struct ea_struct
, 1);
763 sinfo
.ea_set
.in
.eas
[0].flags
= 0;
764 sinfo
.ea_set
.in
.eas
[0].name
.s
= "STREAMEA";
765 sinfo
.ea_set
.in
.eas
[0].value
= data_blob_string_const("EA_VALUE1");
767 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
768 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
770 status
= torture_check_ea(cli
, sname1
, "STREAMEA", "EA_VALUE1");
771 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
774 info
.generic
.level
= RAW_FILEINFO_ALL_EAS
;
775 info
.all_eas
.in
.file
.path
= sname1
;
777 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &info
);
778 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
781 * A different stream does not give a sharing violation
784 io
.ntcreatex
.in
.fname
= sname2
;
785 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
786 CHECK_STATUS(status
, NT_STATUS_OK
);
787 fnum2
= io
.ntcreatex
.out
.file
.fnum
;
790 * ... whereas the same stream does with unchanged access/share_access
794 io
.ntcreatex
.in
.fname
= sname1
;
795 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_SUPERSEDE
;
796 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
797 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
799 io
.ntcreatex
.in
.fname
= sname1b
;
800 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
801 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
803 io
.ntcreatex
.in
.fname
= sname1c
;
804 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
805 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
)) {
806 /* w2k returns INVALID_PARAMETER */
807 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
809 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
812 io
.ntcreatex
.in
.fname
= sname1d
;
813 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
814 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
)) {
815 /* w2k returns INVALID_PARAMETER */
816 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
818 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
821 io
.ntcreatex
.in
.fname
= sname2
;
822 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
823 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
825 io
.ntcreatex
.in
.fname
= snamew
;
826 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
827 CHECK_STATUS(status
, NT_STATUS_OK
);
828 fnum3
= io
.ntcreatex
.out
.file
.fnum
;
830 io
.ntcreatex
.in
.fname
= snamew2
;
831 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
832 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
834 ret
&= check_stream_list(tctx
, cli
, fname
, 4, four
);
836 smbcli_close(cli
->tree
, fnum1
);
837 smbcli_close(cli
->tree
, fnum2
);
838 smbcli_close(cli
->tree
, fnum3
);
840 finfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
841 finfo
.generic
.in
.file
.path
= fname
;
842 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
843 CHECK_STATUS(status
, NT_STATUS_OK
);
845 ret
&= check_stream_list(tctx
, cli
, fname
, 4, four
);
847 for (i
=0; i
< 4; i
++) {
849 uint64_t stream_size
;
850 char *path
= talloc_asprintf(tctx
, "%s%s",
853 char *rpath
= talloc_strdup(path
, path
);
854 char *p
= strrchr(rpath
, ':');
862 printf("(%s): i[%u][%s]\n", __location__
, i
, path
);
863 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
864 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_ATTRIBUTE
|
865 SEC_FILE_WRITE_ATTRIBUTE
|
867 io
.ntcreatex
.in
.fname
= path
;
868 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
869 CHECK_STATUS(status
, NT_STATUS_OK
);
870 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
872 finfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
873 finfo
.generic
.in
.file
.path
= fname
;
874 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
875 CHECK_STATUS(status
, NT_STATUS_OK
);
877 stinfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
878 stinfo
.generic
.in
.file
.fnum
= fnum1
;
879 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &stinfo
);
880 CHECK_STATUS(status
, NT_STATUS_OK
);
881 if (!torture_setting_bool(tctx
, "samba3", false)) {
882 CHECK_NTTIME(stinfo
.all_info
.out
.create_time
,
883 finfo
.all_info
.out
.create_time
);
884 CHECK_NTTIME(stinfo
.all_info
.out
.access_time
,
885 finfo
.all_info
.out
.access_time
);
886 CHECK_NTTIME(stinfo
.all_info
.out
.write_time
,
887 finfo
.all_info
.out
.write_time
);
888 CHECK_NTTIME(stinfo
.all_info
.out
.change_time
,
889 finfo
.all_info
.out
.change_time
);
891 CHECK_VALUE(stinfo
.all_info
.out
.attrib
,
892 finfo
.all_info
.out
.attrib
);
893 CHECK_VALUE(stinfo
.all_info
.out
.size
,
894 finfo
.all_info
.out
.size
);
895 CHECK_VALUE(stinfo
.all_info
.out
.delete_pending
,
896 finfo
.all_info
.out
.delete_pending
);
897 CHECK_VALUE(stinfo
.all_info
.out
.directory
,
898 finfo
.all_info
.out
.directory
);
899 CHECK_VALUE(stinfo
.all_info
.out
.ea_size
,
900 finfo
.all_info
.out
.ea_size
);
902 stinfo
.generic
.level
= RAW_FILEINFO_NAME_INFO
;
903 stinfo
.generic
.in
.file
.fnum
= fnum1
;
904 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &stinfo
);
905 CHECK_STATUS(status
, NT_STATUS_OK
);
906 if (!torture_setting_bool(tctx
, "samba3", false)) {
907 CHECK_STR(stinfo
.name_info
.out
.fname
.s
, rpath
);
910 write_time
= finfo
.all_info
.out
.write_time
;
911 write_time
+= i
*1000000;
912 write_time
/= 1000000;
913 write_time
*= 1000000;
916 sinfo
.basic_info
.level
= RAW_SFILEINFO_BASIC_INFO
;
917 sinfo
.basic_info
.in
.file
.fnum
= fnum1
;
918 sinfo
.basic_info
.in
.write_time
= write_time
;
919 sinfo
.basic_info
.in
.attrib
= stinfo
.all_info
.out
.attrib
;
920 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
921 CHECK_STATUS(status
, NT_STATUS_OK
);
923 stream_size
= i
*8192;
926 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFO
;
927 sinfo
.end_of_file_info
.in
.file
.fnum
= fnum1
;
928 sinfo
.end_of_file_info
.in
.size
= stream_size
;
929 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
930 CHECK_STATUS(status
, NT_STATUS_OK
);
932 stinfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
933 stinfo
.generic
.in
.file
.fnum
= fnum1
;
934 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &stinfo
);
935 CHECK_STATUS(status
, NT_STATUS_OK
);
936 if (!torture_setting_bool(tctx
, "samba3", false)) {
937 CHECK_NTTIME(stinfo
.all_info
.out
.write_time
,
939 CHECK_VALUE(stinfo
.all_info
.out
.attrib
,
940 finfo
.all_info
.out
.attrib
);
942 CHECK_VALUE(stinfo
.all_info
.out
.size
,
944 CHECK_VALUE(stinfo
.all_info
.out
.delete_pending
,
945 finfo
.all_info
.out
.delete_pending
);
946 CHECK_VALUE(stinfo
.all_info
.out
.directory
,
947 finfo
.all_info
.out
.directory
);
948 CHECK_VALUE(stinfo
.all_info
.out
.ea_size
,
949 finfo
.all_info
.out
.ea_size
);
951 ret
&= check_stream_list(tctx
, cli
, fname
, 4, four
);
953 smbcli_close(cli
->tree
, fnum1
);
957 printf("(%s): testing stream renames\n", __location__
);
958 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
959 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_ATTRIBUTE
|
960 SEC_FILE_WRITE_ATTRIBUTE
|
962 io
.ntcreatex
.in
.fname
= snamer1
;
963 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
964 CHECK_STATUS(status
, NT_STATUS_OK
);
965 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
967 ret
&= check_stream_list(tctx
, cli
, fname
, 5, five1
);
970 sinfo
.rename_information
.level
= RAW_SFILEINFO_RENAME_INFORMATION
;
971 sinfo
.rename_information
.in
.file
.fnum
= fnum1
;
972 sinfo
.rename_information
.in
.overwrite
= true;
973 sinfo
.rename_information
.in
.root_fid
= 0;
974 sinfo
.rename_information
.in
.new_name
= ":AfterRename:$DATA";
975 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
976 CHECK_STATUS(status
, NT_STATUS_OK
);
978 ret
&= check_stream_list(tctx
, cli
, fname
, 5, five2
);
981 sinfo
.rename_information
.level
= RAW_SFILEINFO_RENAME_INFORMATION
;
982 sinfo
.rename_information
.in
.file
.fnum
= fnum1
;
983 sinfo
.rename_information
.in
.overwrite
= false;
984 sinfo
.rename_information
.in
.root_fid
= 0;
985 sinfo
.rename_information
.in
.new_name
= ":MStream Two:$DATA";
986 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
987 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_COLLISION
);
989 ret
&= check_stream_list(tctx
, cli
, fname
, 5, five2
);
992 sinfo
.rename_information
.level
= RAW_SFILEINFO_RENAME_INFORMATION
;
993 sinfo
.rename_information
.in
.file
.fnum
= fnum1
;
994 sinfo
.rename_information
.in
.overwrite
= true;
995 sinfo
.rename_information
.in
.root_fid
= 0;
996 sinfo
.rename_information
.in
.new_name
= ":MStream Two:$DATA";
997 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
998 if (torture_setting_bool(tctx
, "samba4", false) ||
999 torture_setting_bool(tctx
, "samba3", false)) {
1000 /* why should this rename be considered invalid?? */
1001 CHECK_STATUS(status
, NT_STATUS_OK
);
1002 ret
&= check_stream_list(tctx
, cli
, fname
, 4, four
);
1004 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1005 ret
&= check_stream_list(tctx
, cli
, fname
, 5, five2
);
1009 /* TODO: we need to test more rename combinations */
1012 if (fnum1
!= -1) smbcli_close(cli
->tree
, fnum1
);
1013 if (fnum2
!= -1) smbcli_close(cli
->tree
, fnum2
);
1014 if (fnum3
!= -1) smbcli_close(cli
->tree
, fnum3
);
1015 status
= smbcli_unlink(cli
->tree
, fname
);
1016 smbcli_deltree(cli
->tree
, BASEDIR
);
1023 static bool test_stream_names2(struct torture_context
*tctx
,
1024 struct smbcli_state
*cli
)
1028 const char *fname
= BASEDIR
"\\stream_names2.txt";
1033 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1035 printf("(%s) testing stream names\n", __location__
);
1036 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1037 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1038 io
.ntcreatex
.in
.flags
= 0;
1039 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
1040 io
.ntcreatex
.in
.create_options
= 0;
1041 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1042 io
.ntcreatex
.in
.share_access
= 0;
1043 io
.ntcreatex
.in
.alloc_size
= 0;
1044 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1045 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1046 io
.ntcreatex
.in
.security_flags
= 0;
1047 io
.ntcreatex
.in
.fname
= fname
;
1048 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1049 CHECK_STATUS(status
, NT_STATUS_OK
);
1050 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
1052 for (i
=0x01; i
< 0x7F; i
++) {
1053 char *path
= talloc_asprintf(tctx
, "%s:Stream%c0x%02X:$DATA",
1061 expected
= NT_STATUS_OBJECT_NAME_INVALID
;
1064 expected
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1069 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1070 io
.ntcreatex
.in
.fname
= path
;
1071 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1072 if (!NT_STATUS_EQUAL(status
, expected
)) {
1073 printf("(%s) %s:Stream%c0x%02X:$DATA%s => expected[%s]\n",
1074 __location__
, fname
, isprint(i
)?(char)i
:' ', i
,
1075 isprint(i
)?"":" (not printable)",
1076 nt_errstr(expected
));
1078 CHECK_STATUS(status
, expected
);
1084 if (fnum1
!= -1) smbcli_close(cli
->tree
, fnum1
);
1085 status
= smbcli_unlink(cli
->tree
, fname
);
1086 smbcli_deltree(cli
->tree
, BASEDIR
);
1090 #define CHECK_CALL_FNUM(call, rightstatus) do { \
1091 check_fnum = true; \
1092 call_name = #call; \
1093 sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
1094 sfinfo.generic.in.file.fnum = fnum; \
1095 status = smb_raw_setfileinfo(cli->tree, &sfinfo); \
1096 if (!NT_STATUS_EQUAL(status, rightstatus)) { \
1097 printf("(%s) %s - %s (should be %s)\n", __location__, #call, \
1098 nt_errstr(status), nt_errstr(rightstatus)); \
1101 finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \
1102 finfo1.generic.in.file.fnum = fnum; \
1103 status2 = smb_raw_fileinfo(cli->tree, tctx, &finfo1); \
1104 if (!NT_STATUS_IS_OK(status2)) { \
1105 printf("(%s) %s pathinfo - %s\n", __location__, #call, nt_errstr(status)); \
1112 static bool test_stream_rename(struct torture_context
*tctx
,
1113 struct smbcli_state
*cli
)
1115 NTSTATUS status
, status2
;
1117 const char *fname
= BASEDIR
"\\stream_rename.txt";
1118 const char *sname1
, *sname2
;
1119 union smb_fileinfo finfo1
;
1120 union smb_setfileinfo sfinfo
;
1124 const char *call_name
;
1126 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1128 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "Stream One");
1129 sname2
= talloc_asprintf(tctx
, "%s:%s:$DaTa", fname
, "Second Stream");
1131 printf("(%s) testing stream renames\n", __location__
);
1132 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1133 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1134 io
.ntcreatex
.in
.flags
= 0;
1135 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_ATTRIBUTE
|
1136 SEC_FILE_WRITE_ATTRIBUTE
|
1137 SEC_RIGHTS_FILE_ALL
;
1138 io
.ntcreatex
.in
.create_options
= 0;
1139 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1140 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
| NTCREATEX_SHARE_ACCESS_WRITE
| NTCREATEX_SHARE_ACCESS_DELETE
;
1141 io
.ntcreatex
.in
.alloc_size
= 0;
1142 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1143 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1144 io
.ntcreatex
.in
.security_flags
= 0;
1145 io
.ntcreatex
.in
.fname
= sname1
;
1147 /* Create two streams. */
1148 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1149 CHECK_STATUS(status
, NT_STATUS_OK
);
1150 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1151 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1153 io
.ntcreatex
.in
.fname
= sname2
;
1154 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1155 CHECK_STATUS(status
, NT_STATUS_OK
);
1156 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1158 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1161 * Open the second stream.
1164 io
.ntcreatex
.in
.access_mask
= SEC_STD_DELETE
;
1165 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
1166 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1167 CHECK_STATUS(status
, NT_STATUS_OK
);
1168 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1171 * Now rename the second stream onto the first.
1174 ZERO_STRUCT(sfinfo
);
1176 sfinfo
.rename_information
.in
.overwrite
= 1;
1177 sfinfo
.rename_information
.in
.root_fid
= 0;
1178 sfinfo
.rename_information
.in
.new_name
= ":Stream One";
1179 CHECK_CALL_FNUM(RENAME_INFORMATION
, NT_STATUS_OK
);
1182 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1183 status
= smbcli_unlink(cli
->tree
, fname
);
1184 smbcli_deltree(cli
->tree
, BASEDIR
);
1188 static bool test_stream_rename2(struct torture_context
*tctx
,
1189 struct smbcli_state
*cli
)
1193 const char *fname1
= BASEDIR
"\\stream.txt";
1194 const char *fname2
= BASEDIR
"\\stream2.txt";
1195 const char *stream_name1
= ":Stream One:$DATA";
1196 const char *stream_name2
= ":Stream Two:$DATA";
1197 const char *stream_name_default
= "::$DATA";
1202 union smb_setfileinfo sinfo
;
1203 union smb_rename rio
;
1205 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1207 sname1
= talloc_asprintf(tctx
, "%s:%s", fname1
, "Stream One");
1208 sname2
= talloc_asprintf(tctx
, "%s:%s", fname1
, "Stream Two");
1210 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1211 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1212 io
.ntcreatex
.in
.flags
= 0;
1213 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1214 SEC_STD_DELETE
|SEC_FILE_APPEND_DATA
|SEC_STD_READ_CONTROL
);
1215 io
.ntcreatex
.in
.create_options
= 0;
1216 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1217 io
.ntcreatex
.in
.share_access
= (NTCREATEX_SHARE_ACCESS_READ
|
1218 NTCREATEX_SHARE_ACCESS_WRITE
|
1219 NTCREATEX_SHARE_ACCESS_DELETE
);
1220 io
.ntcreatex
.in
.alloc_size
= 0;
1221 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1222 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1223 io
.ntcreatex
.in
.security_flags
= 0;
1224 io
.ntcreatex
.in
.fname
= sname1
;
1226 /* Open/create new stream. */
1227 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1228 CHECK_STATUS(status
, NT_STATUS_OK
);
1230 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1233 * Check raw rename with <base>:<stream>.
1235 printf("(%s) Checking NTRENAME of a stream using <base>:<stream>\n",
1237 rio
.generic
.level
= RAW_RENAME_NTRENAME
;
1238 rio
.ntrename
.in
.old_name
= sname1
;
1239 rio
.ntrename
.in
.new_name
= sname2
;
1240 rio
.ntrename
.in
.attrib
= 0;
1241 rio
.ntrename
.in
.cluster_size
= 0;
1242 rio
.ntrename
.in
.flags
= RENAME_FLAG_RENAME
;
1243 status
= smb_raw_rename(cli
->tree
, &rio
);
1244 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1247 * Check raw rename to the default stream using :<stream>.
1249 printf("(%s) Checking NTRENAME to default stream using :<stream>\n",
1251 rio
.ntrename
.in
.new_name
= stream_name_default
;
1252 status
= smb_raw_rename(cli
->tree
, &rio
);
1253 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_COLLISION
);
1256 * Check raw rename using :<stream>.
1258 printf("(%s) Checking NTRENAME of a stream using :<stream>\n",
1260 rio
.ntrename
.in
.new_name
= stream_name2
;
1261 status
= smb_raw_rename(cli
->tree
, &rio
);
1262 CHECK_STATUS(status
, NT_STATUS_OK
);
1265 * Check raw rename of a stream to a file.
1267 printf("(%s) Checking NTRENAME of a stream to a file\n",
1269 rio
.ntrename
.in
.old_name
= sname2
;
1270 rio
.ntrename
.in
.new_name
= fname2
;
1271 status
= smb_raw_rename(cli
->tree
, &rio
);
1272 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1275 * Check raw rename of a file to a stream.
1277 printf("(%s) Checking NTRENAME of a file to a stream\n",
1280 /* Create the file. */
1281 io
.ntcreatex
.in
.fname
= fname2
;
1282 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1283 CHECK_STATUS(status
, NT_STATUS_OK
);
1284 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1286 /* Try the rename. */
1287 rio
.ntrename
.in
.old_name
= fname2
;
1288 rio
.ntrename
.in
.new_name
= sname1
;
1289 status
= smb_raw_rename(cli
->tree
, &rio
);
1290 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
1293 * Reopen the stream for trans2 renames.
1295 io
.ntcreatex
.in
.fname
= sname2
;
1296 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1297 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1298 CHECK_STATUS(status
, NT_STATUS_OK
);
1299 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1302 * Check trans2 rename of a stream using :<stream>.
1304 printf("(%s) Checking trans2 rename of a stream using :<stream>\n",
1307 sinfo
.rename_information
.level
= RAW_SFILEINFO_RENAME_INFORMATION
;
1308 sinfo
.rename_information
.in
.file
.fnum
= fnum
;
1309 sinfo
.rename_information
.in
.overwrite
= 1;
1310 sinfo
.rename_information
.in
.root_fid
= 0;
1311 sinfo
.rename_information
.in
.new_name
= stream_name1
;
1312 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
1313 CHECK_STATUS(status
, NT_STATUS_OK
);
1316 * Check trans2 rename of an overwriting stream using :<stream>.
1318 printf("(%s) Checking trans2 rename of an overwriting stream using "
1319 ":<stream>\n", __location__
);
1321 /* Create second stream. */
1322 io
.ntcreatex
.in
.fname
= sname2
;
1323 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1324 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1325 CHECK_STATUS(status
, NT_STATUS_OK
);
1326 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1328 /* Rename the first stream onto the second. */
1329 sinfo
.rename_information
.in
.file
.fnum
= fnum
;
1330 sinfo
.rename_information
.in
.new_name
= stream_name2
;
1331 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
1332 CHECK_STATUS(status
, NT_STATUS_OK
);
1334 smbcli_close(cli
->tree
, fnum
);
1337 * Reopen the stream with the new name.
1339 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1340 io
.ntcreatex
.in
.fname
= sname2
;
1341 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1342 CHECK_STATUS(status
, NT_STATUS_OK
);
1343 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1346 * Check trans2 rename of a stream using <base>:<stream>.
1348 printf("(%s) Checking trans2 rename of a stream using "
1349 "<base>:<stream>\n", __location__
);
1350 sinfo
.rename_information
.in
.file
.fnum
= fnum
;
1351 sinfo
.rename_information
.in
.new_name
= sname1
;
1352 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
1353 CHECK_STATUS(status
, NT_STATUS_NOT_SUPPORTED
);
1356 * Samba3 doesn't currently support renaming a stream to the default
1357 * stream. This test does pass on windows.
1359 if (torture_setting_bool(tctx
, "samba3", false) ||
1360 torture_setting_bool(tctx
, "samba4", false)) {
1365 * Check trans2 rename to the default stream using :<stream>.
1367 printf("(%s) Checking trans2 rename to defaualt stream using "
1368 ":<stream>\n", __location__
);
1369 sinfo
.rename_information
.in
.file
.fnum
= fnum
;
1370 sinfo
.rename_information
.in
.new_name
= stream_name_default
;
1371 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
1372 CHECK_STATUS(status
, NT_STATUS_OK
);
1374 smbcli_close(cli
->tree
, fnum
);
1377 smbcli_close(cli
->tree
, fnum
);
1378 status
= smbcli_unlink(cli
->tree
, fname1
);
1379 status
= smbcli_unlink(cli
->tree
, fname2
);
1380 smbcli_deltree(cli
->tree
, BASEDIR
);
1387 static bool test_stream_rename3(struct torture_context
*tctx
,
1388 struct smbcli_state
*cli
)
1390 NTSTATUS status
, status2
;
1392 const char *fname
= BASEDIR
"\\stream_rename.txt";
1393 const char *sname1
, *sname2
;
1394 union smb_fileinfo finfo1
;
1395 union smb_setfileinfo sfinfo
;
1400 const char *call_name
;
1402 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1404 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "MStream Two:$DATA");
1405 sname2
= talloc_asprintf(tctx
, "%s:%s:$DaTa", fname
, "Second Stream");
1407 printf("(%s) testing stream renames\n", __location__
);
1408 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1409 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1410 io
.ntcreatex
.in
.flags
= 0;
1411 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_ATTRIBUTE
|
1412 SEC_FILE_WRITE_ATTRIBUTE
|
1413 SEC_RIGHTS_FILE_ALL
;
1414 io
.ntcreatex
.in
.create_options
= 0;
1415 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1416 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
1417 NTCREATEX_SHARE_ACCESS_WRITE
| NTCREATEX_SHARE_ACCESS_DELETE
;
1418 io
.ntcreatex
.in
.alloc_size
= 0;
1419 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1420 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1421 io
.ntcreatex
.in
.security_flags
= 0;
1422 io
.ntcreatex
.in
.fname
= sname1
;
1424 /* Create two streams. */
1425 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1426 CHECK_STATUS(status
, NT_STATUS_OK
);
1427 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1428 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1430 io
.ntcreatex
.in
.fname
= sname2
;
1431 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1432 CHECK_STATUS(status
, NT_STATUS_OK
);
1433 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1435 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1437 /* open the second stream. */
1438 io
.ntcreatex
.in
.access_mask
= SEC_STD_DELETE
;
1439 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
1440 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1441 CHECK_STATUS(status
, NT_STATUS_OK
);
1442 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1444 /* Keep a handle to the first stream open. */
1445 io
.ntcreatex
.in
.fname
= sname1
;
1446 io
.ntcreatex
.in
.access_mask
= SEC_STD_DELETE
;
1447 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
1448 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1449 CHECK_STATUS(status
, NT_STATUS_OK
);
1450 fnum2
= io
.ntcreatex
.out
.file
.fnum
;
1452 ZERO_STRUCT(sfinfo
);
1453 sfinfo
.rename_information
.in
.overwrite
= 1;
1454 sfinfo
.rename_information
.in
.root_fid
= 0;
1455 sfinfo
.rename_information
.in
.new_name
= ":MStream Two:$DATA";
1456 if (torture_setting_bool(tctx
, "samba4", false) ||
1457 torture_setting_bool(tctx
, "samba3", false)) {
1458 CHECK_CALL_FNUM(RENAME_INFORMATION
, NT_STATUS_OK
);
1460 CHECK_CALL_FNUM(RENAME_INFORMATION
,
1461 NT_STATUS_INVALID_PARAMETER
);
1466 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1467 if (fnum2
!= -1) smbcli_close(cli
->tree
, fnum2
);
1468 status
= smbcli_unlink(cli
->tree
, fname
);
1469 smbcli_deltree(cli
->tree
, BASEDIR
);
1473 static bool create_file_with_stream(struct torture_context
*tctx
,
1474 struct smbcli_state
*cli
,
1481 /* Create a file with a stream */
1482 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1483 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1484 io
.ntcreatex
.in
.flags
= 0;
1485 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1486 SEC_FILE_APPEND_DATA
|SEC_STD_READ_CONTROL
);
1487 io
.ntcreatex
.in
.create_options
= 0;
1488 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1489 io
.ntcreatex
.in
.share_access
= 0;
1490 io
.ntcreatex
.in
.alloc_size
= 0;
1491 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1492 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1493 io
.ntcreatex
.in
.security_flags
= 0;
1494 io
.ntcreatex
.in
.fname
= stream
;
1496 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1497 CHECK_STATUS(status
, NT_STATUS_OK
);
1500 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1504 /* Test how streams interact with create dispositions */
1505 static bool test_stream_create_disposition(struct torture_context
*tctx
,
1506 struct smbcli_state
*cli
)
1510 const char *fname
= BASEDIR
"\\stream.txt";
1511 const char *stream
= "Stream One:$DATA";
1512 const char *fname_stream
;
1513 const char *default_stream_name
= "::$DATA";
1514 const char *stream_list
[2];
1518 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1520 fname_stream
= talloc_asprintf(tctx
, "%s:%s", fname
, stream
);
1522 stream_list
[0] = talloc_asprintf(tctx
, ":%s", stream
);
1523 stream_list
[1] = default_stream_name
;
1525 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1529 /* Open the base file with OPEN */
1530 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1531 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1532 io
.ntcreatex
.in
.flags
= 0;
1533 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1534 SEC_FILE_APPEND_DATA
|SEC_STD_READ_CONTROL
);
1535 io
.ntcreatex
.in
.create_options
= 0;
1536 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1537 io
.ntcreatex
.in
.share_access
= 0;
1538 io
.ntcreatex
.in
.alloc_size
= 0;
1539 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1540 io
.ntcreatex
.in
.security_flags
= 0;
1541 io
.ntcreatex
.in
.fname
= fname
;
1544 * check ntcreatex open: sanity check
1546 printf("(%s) Checking ntcreatex disp: open\n", __location__
);
1547 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1548 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1549 CHECK_STATUS(status
, NT_STATUS_OK
);
1550 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1551 if (!check_stream_list(tctx
, cli
, fname
, 2, stream_list
)) {
1556 * check ntcreatex overwrite
1558 printf("(%s) Checking ntcreatex disp: overwrite\n", __location__
);
1559 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OVERWRITE
;
1560 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1561 CHECK_STATUS(status
, NT_STATUS_OK
);
1562 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1563 if (!check_stream_list(tctx
, cli
, fname
, 1, &default_stream_name
)) {
1568 * check ntcreatex overwrite_if
1570 printf("(%s) Checking ntcreatex disp: overwrite_if\n", __location__
);
1571 smbcli_unlink(cli
->tree
, fname
);
1572 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1576 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OVERWRITE_IF
;
1577 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1578 CHECK_STATUS(status
, NT_STATUS_OK
);
1579 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1580 if (!check_stream_list(tctx
, cli
, fname
, 1, &default_stream_name
)) {
1585 * check ntcreatex supersede
1587 printf("(%s) Checking ntcreatex disp: supersede\n", __location__
);
1588 smbcli_unlink(cli
->tree
, fname
);
1589 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1593 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_SUPERSEDE
;
1594 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1595 CHECK_STATUS(status
, NT_STATUS_OK
);
1596 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1597 if (!check_stream_list(tctx
, cli
, fname
, 1, &default_stream_name
)) {
1602 * check ntcreatex overwrite_if on a stream.
1604 printf("(%s) Checking ntcreatex disp: overwrite_if on stream\n",
1606 smbcli_unlink(cli
->tree
, fname
);
1607 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1611 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OVERWRITE_IF
;
1612 io
.ntcreatex
.in
.fname
= fname_stream
;
1613 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1614 CHECK_STATUS(status
, NT_STATUS_OK
);
1615 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1616 if (!check_stream_list(tctx
, cli
, fname
, 2, stream_list
)) {
1621 * check openx overwrite_if
1623 printf("(%s) Checking openx disp: overwrite_if\n", __location__
);
1624 smbcli_unlink(cli
->tree
, fname
);
1625 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1629 io
.openx
.level
= RAW_OPEN_OPENX
;
1630 io
.openx
.in
.flags
= OPENX_FLAGS_ADDITIONAL_INFO
;
1631 io
.openx
.in
.open_mode
= OPENX_MODE_ACCESS_RDWR
| OPEN_FLAGS_DENY_NONE
;
1632 io
.openx
.in
.search_attrs
= 0;
1633 io
.openx
.in
.file_attrs
= 0;
1634 io
.openx
.in
.write_time
= 0;
1635 io
.openx
.in
.size
= 1024*1024;
1636 io
.openx
.in
.timeout
= 0;
1637 io
.openx
.in
.fname
= fname
;
1639 io
.openx
.in
.open_func
= OPENX_OPEN_FUNC_TRUNC
| OPENX_OPEN_FUNC_CREATE
;
1640 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1641 CHECK_STATUS(status
, NT_STATUS_OK
);
1642 smbcli_close(cli
->tree
, io
.openx
.out
.file
.fnum
);
1643 if (!check_stream_list(tctx
, cli
, fname
, 1, &default_stream_name
)) {
1650 smbcli_close(cli
->tree
, fnum
);
1651 smbcli_unlink(cli
->tree
, fname
);
1652 smbcli_deltree(cli
->tree
, BASEDIR
);
1657 /* Test streaminfo with enough streams on a file to fill up the buffer. */
1658 static bool test_stream_large_streaminfo(struct torture_context
*tctx
,
1659 struct smbcli_state
*cli
)
1661 #define LONG_STREAM_SIZE 2
1663 const char *fname
= BASEDIR
"\\stream.txt";
1664 const char *fname_stream
;
1668 union smb_fileinfo finfo
;
1670 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1672 lstream_name
= talloc_array(tctx
, char, LONG_STREAM_SIZE
);
1674 for (i
= 0; i
< LONG_STREAM_SIZE
- 1; i
++) {
1675 lstream_name
[i
] = (char)('a' + i
%26);
1677 lstream_name
[LONG_STREAM_SIZE
- 1] = '\0';
1679 torture_comment(tctx
, "(%s) Creating a file with a lot of streams\n", __location__
);
1680 for (i
= 0; i
< 10000; i
++) {
1681 fname_stream
= talloc_asprintf(tctx
, "%s:%s%d", fname
,
1683 ret
= create_file_with_stream(tctx
, cli
, fname_stream
);
1689 finfo
.generic
.level
= RAW_FILEINFO_STREAM_INFO
;
1690 finfo
.generic
.in
.file
.path
= fname
;
1692 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1693 CHECK_STATUS(status
, STATUS_BUFFER_OVERFLOW
);
1696 smbcli_unlink(cli
->tree
, fname
);
1697 smbcli_deltree(cli
->tree
, BASEDIR
);
1702 /* Test the effect of setting attributes on a stream. */
1703 static bool test_stream_attributes(struct torture_context
*tctx
,
1704 struct smbcli_state
*cli
)
1709 const char *fname
= BASEDIR
"\\stream_attr.txt";
1710 const char *stream
= "Stream One:$DATA";
1711 const char *fname_stream
;
1713 union smb_fileinfo finfo
;
1714 union smb_setfileinfo sfinfo
;
1715 time_t basetime
= (time(NULL
) - 86400) & ~1;
1717 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1719 torture_comment(tctx
, "(%s) testing attribute setting on stream\n", __location__
);
1721 fname_stream
= talloc_asprintf(tctx
, "%s:%s", fname
, stream
);
1723 /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */
1724 ret
= create_file_with_stream(tctx
, cli
, fname_stream
);
1730 finfo
.generic
.level
= RAW_FILEINFO_BASIC_INFO
;
1731 finfo
.generic
.in
.file
.path
= fname
;
1732 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1733 CHECK_STATUS(status
, NT_STATUS_OK
);
1735 torture_assert_int_equal_goto(tctx
, finfo
.all_info
.out
.attrib
& ~FILE_ATTRIBUTE_NONINDEXED
, FILE_ATTRIBUTE_ARCHIVE
, ret
, done
, "attrib incorrect");
1737 /* Now open the stream name. */
1739 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1740 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1741 io
.ntcreatex
.in
.flags
= 0;
1742 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1743 SEC_FILE_APPEND_DATA
|SEC_STD_READ_CONTROL
|SEC_FILE_WRITE_ATTRIBUTE
);
1744 io
.ntcreatex
.in
.create_options
= 0;
1745 io
.ntcreatex
.in
.file_attr
= 0;
1746 io
.ntcreatex
.in
.share_access
= 0;
1747 io
.ntcreatex
.in
.alloc_size
= 0;
1748 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
1749 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1750 io
.ntcreatex
.in
.security_flags
= 0;
1751 io
.ntcreatex
.in
.fname
= fname_stream
;
1753 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1754 CHECK_STATUS(status
, NT_STATUS_OK
);
1756 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1758 /* Change the attributes + time on the stream fnum. */
1759 ZERO_STRUCT(sfinfo
);
1760 sfinfo
.basic_info
.in
.attrib
= FILE_ATTRIBUTE_READONLY
;
1761 unix_to_nt_time(&sfinfo
.basic_info
.in
.write_time
, basetime
);
1763 sfinfo
.generic
.level
= RAW_SFILEINFO_BASIC_INFORMATION
;
1764 sfinfo
.generic
.in
.file
.fnum
= fnum
;
1765 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
1766 torture_assert_ntstatus_equal_goto(tctx
, status
, NT_STATUS_OK
, ret
, done
, "smb_raw_setfileinfo failed");
1768 smbcli_close(cli
->tree
, fnum
);
1772 finfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
1773 finfo
.generic
.in
.file
.path
= fname
;
1774 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1775 torture_assert_ntstatus_equal_goto(tctx
, status
, NT_STATUS_OK
, ret
, done
, "smb_raw_pathinfo failed");
1777 torture_assert_int_equal_goto(tctx
, finfo
.all_info
.out
.attrib
& ~FILE_ATTRIBUTE_NONINDEXED
, FILE_ATTRIBUTE_READONLY
, ret
, done
, "attrib incorrect");
1779 torture_assert_int_equal_goto(tctx
, nt_time_to_unix(finfo
.all_info
.out
.write_time
), basetime
, ret
, done
, "time incorrect");
1784 smbcli_close(cli
->tree
, fnum
);
1786 smbcli_unlink(cli
->tree
, fname
);
1787 smbcli_deltree(cli
->tree
, BASEDIR
);
1792 * A rough approximation of how a windows client creates the streams for use
1793 * in the summary tab.
1795 static bool test_stream_summary_tab(struct torture_context
*tctx
,
1796 struct smbcli_state
*cli
)
1801 const char *fname
= BASEDIR
"\\stream_summary.txt";
1802 const char *stream
= ":\005SummaryInformation:$DATA";
1803 const char *fname_stream
= NULL
;
1804 const char *tmp_stream
= ":Updt_\005SummaryInformation:$DATA";
1805 const char *fname_tmp_stream
= NULL
;
1807 union smb_fileinfo finfo
;
1808 union smb_rename rio
;
1811 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1813 fname_stream
= talloc_asprintf(tctx
, "%s%s", fname
, stream
);
1814 fname_tmp_stream
= talloc_asprintf(tctx
, "%s%s", fname
,
1817 /* Create summary info stream */
1818 ret
= create_file_with_stream(tctx
, cli
, fname_stream
);
1823 /* Create summary info tmp update stream */
1824 ret
= create_file_with_stream(tctx
, cli
, fname_tmp_stream
);
1829 /* Open tmp stream and write to it */
1830 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1831 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1832 io
.ntcreatex
.in
.flags
= 0;
1833 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
;
1834 io
.ntcreatex
.in
.create_options
= 0;
1835 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1836 io
.ntcreatex
.in
.share_access
= 0;
1837 io
.ntcreatex
.in
.alloc_size
= 0;
1838 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1839 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1840 io
.ntcreatex
.in
.security_flags
= 0;
1841 io
.ntcreatex
.in
.fname
= fname_tmp_stream
;
1843 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1844 CHECK_STATUS(status
, NT_STATUS_OK
);
1845 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1847 retsize
= smbcli_write(cli
->tree
, fnum
, 0, "test data", 0, 9);
1848 CHECK_VALUE(retsize
, 9);
1850 /* close the tmp stream. */
1851 smbcli_close(cli
->tree
, fnum
);
1854 /* Delete the current stream */
1855 smbcli_unlink(cli
->tree
, fname_stream
);
1857 /* Do the rename. */
1858 rio
.generic
.level
= RAW_RENAME_RENAME
;
1859 rio
.rename
.in
.pattern1
= fname_tmp_stream
;
1860 rio
.rename
.in
.pattern2
= stream
;
1861 rio
.rename
.in
.attrib
= FILE_ATTRIBUTE_SYSTEM
|
1862 FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_DIRECTORY
;
1863 status
= smb_raw_rename(cli
->tree
, &rio
);
1864 CHECK_STATUS(status
, NT_STATUS_OK
);
1866 /* Try to open the tmp stream that we just renamed away. */
1867 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1868 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1870 /* Query the base file to make sure it's still there. */
1871 finfo
.generic
.level
= RAW_FILEINFO_BASIC_INFO
;
1872 finfo
.generic
.in
.file
.path
= fname
;
1874 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1875 CHECK_STATUS(status
, NT_STATUS_OK
);
1880 smbcli_close(cli
->tree
, fnum
);
1882 smbcli_unlink(cli
->tree
, fname
);
1884 smbcli_deltree(cli
->tree
, BASEDIR
);
1889 basic testing of streams calls
1891 struct torture_suite
*torture_raw_streams(TALLOC_CTX
*tctx
)
1893 struct torture_suite
*suite
= torture_suite_create(tctx
, "streams");
1895 torture_suite_add_1smb_test(suite
, "dir", test_stream_dir
);
1896 torture_suite_add_1smb_test(suite
, "io", test_stream_io
);
1897 torture_suite_add_1smb_test(suite
, "sharemodes", test_stream_sharemodes
);
1898 torture_suite_add_1smb_test(suite
, "delete", test_stream_delete
);
1899 torture_suite_add_1smb_test(suite
, "names", test_stream_names
);
1900 torture_suite_add_1smb_test(suite
, "names2", test_stream_names2
);
1901 torture_suite_add_1smb_test(suite
, "rename", test_stream_rename
);
1902 torture_suite_add_1smb_test(suite
, "rename2", test_stream_rename2
);
1903 torture_suite_add_1smb_test(suite
, "rename3", test_stream_rename3
);
1904 torture_suite_add_1smb_test(suite
, "createdisp",
1905 test_stream_create_disposition
);
1906 torture_suite_add_1smb_test(suite
, "attr", test_stream_attributes
);
1907 torture_suite_add_1smb_test(suite
, "sumtab", test_stream_summary_tab
);
1910 torture_suite_add_1smb_test(suite
, "LARGESTREAMINFO",
1911 test_stream_large_streaminfo
);