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 "libcli/security/dom_sid.h"
27 #include "libcli/security/security_descriptor.h"
28 #include "system/filesys.h"
29 #include "libcli/libcli.h"
30 #include "torture/util.h"
31 #include "lib/util/tsort.h"
32 #include "torture/raw/proto.h"
34 #define BASEDIR "\\teststreams"
36 #define CHECK_STATUS(status, correct) \
37 torture_assert_ntstatus_equal_goto(tctx,status,correct,ret,done,"CHECK_STATUS")
39 #define CHECK_VALUE(v, correct) \
40 torture_assert_int_equal(tctx,v,correct,"CHECK_VALUE")
42 #define CHECK_NTTIME(v, correct) \
43 torture_assert_u64_equal(tctx,v,correct,"CHECK_NTTIME")
45 #define CHECK_STR(v, correct) do { \
47 if ((v) && !(correct)) { \
49 } else if (!(v) && (correct)) { \
51 } else if (!(v) && !(correct)) { \
53 } else if (strcmp((v), (correct)) == 0) { \
58 torture_assert(tctx,ok,\
59 talloc_asprintf(tctx, "got '%s', expected '%s'",\
60 (v)?(v):"NULL", (correct)?(correct):"NULL")); \
64 check that a stream has the right contents
66 static bool check_stream(struct smbcli_state
*cli
, const char *location
,
68 const char *fname
, const char *sname
,
72 const char *full_name
;
76 full_name
= talloc_asprintf(mem_ctx
, "%s:%s", fname
, sname
);
78 fnum
= smbcli_open(cli
->tree
, full_name
, O_RDONLY
, DENY_NONE
);
82 printf("(%s) should have failed stream open of %s\n",
90 printf("(%s) Failed to open stream '%s' - %s\n",
91 location
, full_name
, smbcli_errstr(cli
->tree
));
95 buf
= talloc_array(mem_ctx
, uint8_t, strlen(value
)+11);
97 ret
= smbcli_read(cli
->tree
, fnum
, buf
, 0, strlen(value
)+11);
98 if (ret
!= strlen(value
)) {
99 printf("(%s) Failed to read %lu bytes from stream '%s' - got %d\n",
100 location
, (long)strlen(value
), full_name
, (int)ret
);
104 if (memcmp(buf
, value
, strlen(value
)) != 0) {
105 printf("(%s) Bad data in stream\n", location
);
109 smbcli_close(cli
->tree
, fnum
);
113 static int qsort_string(char * const *s1
, char * const *s2
)
115 return strcmp(*s1
, *s2
);
118 static int qsort_stream(const struct stream_struct
*s1
, const struct stream_struct
*s2
)
120 return strcmp(s1
->stream_name
.s
, s2
->stream_name
.s
);
123 static bool check_stream_list(struct torture_context
*tctx
,
124 struct smbcli_state
*cli
, const char *fname
,
125 int num_exp
, const char **exp
)
127 union smb_fileinfo finfo
;
130 TALLOC_CTX
*tmp_ctx
= talloc_new(cli
);
132 struct stream_struct
*stream_sort
;
136 finfo
.generic
.level
= RAW_FILEINFO_STREAM_INFO
;
137 finfo
.generic
.in
.file
.path
= fname
;
139 status
= smb_raw_pathinfo(cli
->tree
, tmp_ctx
, &finfo
);
140 CHECK_STATUS(status
, NT_STATUS_OK
);
142 CHECK_VALUE(finfo
.stream_info
.out
.num_streams
, num_exp
);
149 exp_sort
= (char **)talloc_memdup(tmp_ctx
, exp
, num_exp
* sizeof(*exp
));
151 if (exp_sort
== NULL
) {
155 TYPESAFE_QSORT(exp_sort
, num_exp
, qsort_string
);
157 stream_sort
= (struct stream_struct
*)talloc_memdup(tmp_ctx
,
158 finfo
.stream_info
.out
.streams
,
159 finfo
.stream_info
.out
.num_streams
*
160 sizeof(*stream_sort
));
162 if (stream_sort
== NULL
) {
166 TYPESAFE_QSORT(stream_sort
, finfo
.stream_info
.out
.num_streams
, qsort_stream
);
168 for (i
=0; i
<num_exp
; i
++) {
169 if (strcmp(exp_sort
[i
], stream_sort
[i
].stream_name
.s
) != 0) {
177 talloc_free(tmp_ctx
);
181 for (i
=0; i
<num_exp
; i
++) {
182 torture_comment(tctx
, "stream names '%s' '%s'\n",
183 exp_sort
[i
], stream_sort
[i
].stream_name
.s
);
185 CHECK_STR(stream_sort
[fail
].stream_name
.s
, exp_sort
[fail
]);
186 talloc_free(tmp_ctx
);
191 test bahavior of streams on directories
193 static bool test_stream_dir(struct torture_context
*tctx
,
194 struct smbcli_state
*cli
)
198 const char *fname
= BASEDIR
"\\stream.txt";
201 const char *basedir_data
;
203 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
205 basedir_data
= talloc_asprintf(tctx
, "%s::$DATA", BASEDIR
);
206 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "Stream One");
208 printf("(%s) opening non-existent directory stream\n", __location__
);
209 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
210 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
211 io
.ntcreatex
.in
.flags
= 0;
212 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
213 io
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
;
214 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
215 io
.ntcreatex
.in
.share_access
= 0;
216 io
.ntcreatex
.in
.alloc_size
= 0;
217 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
218 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
219 io
.ntcreatex
.in
.security_flags
= 0;
220 io
.ntcreatex
.in
.fname
= sname1
;
221 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
222 CHECK_STATUS(status
, NT_STATUS_NOT_A_DIRECTORY
);
224 printf("(%s) opening basedir stream\n", __location__
);
225 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
226 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
227 io
.ntcreatex
.in
.flags
= 0;
228 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
229 io
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
;
230 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_DIRECTORY
;
231 io
.ntcreatex
.in
.share_access
= 0;
232 io
.ntcreatex
.in
.alloc_size
= 0;
233 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
234 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
235 io
.ntcreatex
.in
.security_flags
= 0;
236 io
.ntcreatex
.in
.fname
= basedir_data
;
237 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
238 CHECK_STATUS(status
, NT_STATUS_NOT_A_DIRECTORY
);
240 printf("(%s) opening basedir ::$DATA stream\n", __location__
);
241 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
242 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
243 io
.ntcreatex
.in
.flags
= 0x10;
244 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
245 io
.ntcreatex
.in
.create_options
= 0;
246 io
.ntcreatex
.in
.file_attr
= 0;
247 io
.ntcreatex
.in
.share_access
= 0;
248 io
.ntcreatex
.in
.alloc_size
= 0;
249 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
250 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
251 io
.ntcreatex
.in
.security_flags
= 0;
252 io
.ntcreatex
.in
.fname
= basedir_data
;
253 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
254 CHECK_STATUS(status
, NT_STATUS_FILE_IS_A_DIRECTORY
);
256 printf("(%s) list the streams on the basedir\n", __location__
);
257 ret
&= check_stream_list(tctx
, cli
, BASEDIR
, 0, NULL
);
259 smbcli_deltree(cli
->tree
, BASEDIR
);
264 test basic behavior of streams on directories
266 static bool test_stream_io(struct torture_context
*tctx
,
267 struct smbcli_state
*cli
)
271 const char *fname
= BASEDIR
"\\stream.txt";
272 const char *sname1
, *sname2
;
277 const char *one
[] = { "::$DATA" };
278 const char *two
[] = { "::$DATA", ":Second Stream:$DATA" };
279 const char *three
[] = { "::$DATA", ":Stream One:$DATA",
280 ":Second Stream:$DATA" };
282 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
284 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "Stream One");
285 sname2
= talloc_asprintf(tctx
, "%s:%s:$DaTa", fname
, "Second Stream");
287 printf("(%s) creating a stream on a non-existent file\n", __location__
);
288 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
289 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
290 io
.ntcreatex
.in
.flags
= 0;
291 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
292 io
.ntcreatex
.in
.create_options
= 0;
293 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
294 io
.ntcreatex
.in
.share_access
= 0;
295 io
.ntcreatex
.in
.alloc_size
= 0;
296 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
297 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
298 io
.ntcreatex
.in
.security_flags
= 0;
299 io
.ntcreatex
.in
.fname
= sname1
;
300 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
301 CHECK_STATUS(status
, NT_STATUS_OK
);
302 fnum
= io
.ntcreatex
.out
.file
.fnum
;
304 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Stream One", NULL
);
306 printf("(%s) check that open of base file is allowed\n", __location__
);
307 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
308 io
.ntcreatex
.in
.fname
= fname
;
309 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
310 CHECK_STATUS(status
, NT_STATUS_OK
);
311 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
313 printf("(%s) writing to stream\n", __location__
);
314 retsize
= smbcli_write(cli
->tree
, fnum
, 0, "test data", 0, 9);
315 CHECK_VALUE(retsize
, 9);
317 smbcli_close(cli
->tree
, fnum
);
319 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Stream One", "test data");
321 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
322 io
.ntcreatex
.in
.fname
= sname1
;
323 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
324 CHECK_STATUS(status
, NT_STATUS_OK
);
325 fnum
= io
.ntcreatex
.out
.file
.fnum
;
327 printf("(%s) modifying stream\n", __location__
);
328 retsize
= smbcli_write(cli
->tree
, fnum
, 0, "MORE DATA ", 5, 10);
329 CHECK_VALUE(retsize
, 10);
331 smbcli_close(cli
->tree
, fnum
);
333 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Stream One:$FOO", NULL
);
335 printf("(%s) creating a stream2 on a existing file\n", __location__
);
336 io
.ntcreatex
.in
.fname
= sname2
;
337 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
338 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
339 CHECK_STATUS(status
, NT_STATUS_OK
);
340 fnum
= io
.ntcreatex
.out
.file
.fnum
;
342 printf("(%s) modifying stream\n", __location__
);
343 retsize
= smbcli_write(cli
->tree
, fnum
, 0, "SECOND STREAM", 0, 13);
344 CHECK_VALUE(retsize
, 13);
346 smbcli_close(cli
->tree
, fnum
);
348 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Stream One", "test MORE DATA ");
349 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Stream One:$DATA", "test MORE DATA ");
350 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Stream One:", NULL
);
351 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Second Stream", "SECOND STREAM");
352 ret
&= check_stream(cli
, __location__
, tctx
, fname
,
353 "SECOND STREAM:$DATA", "SECOND STREAM");
354 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Second Stream:$DATA", "SECOND STREAM");
355 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Second Stream:", NULL
);
356 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Second Stream:$FOO", NULL
);
358 check_stream_list(tctx
, cli
, fname
, 3, three
);
360 printf("(%s) deleting stream\n", __location__
);
361 status
= smbcli_unlink(cli
->tree
, sname1
);
362 CHECK_STATUS(status
, NT_STATUS_OK
);
364 check_stream_list(tctx
, cli
, fname
, 2, two
);
366 printf("(%s) delete a stream via delete-on-close\n", __location__
);
367 io
.ntcreatex
.in
.fname
= sname2
;
368 io
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
369 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_DELETE
;
370 io
.ntcreatex
.in
.access_mask
= SEC_STD_DELETE
;
371 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
373 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
374 CHECK_STATUS(status
, NT_STATUS_OK
);
375 fnum
= io
.ntcreatex
.out
.file
.fnum
;
377 smbcli_close(cli
->tree
, fnum
);
378 status
= smbcli_unlink(cli
->tree
, sname2
);
379 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
381 check_stream_list(tctx
, cli
, fname
, 1, one
);
383 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
384 io
.ntcreatex
.in
.fname
= sname1
;
385 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
386 CHECK_STATUS(status
, NT_STATUS_OK
);
387 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
388 io
.ntcreatex
.in
.fname
= sname2
;
389 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
390 CHECK_STATUS(status
, NT_STATUS_OK
);
391 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
393 printf("(%s) deleting file\n", __location__
);
394 status
= smbcli_unlink(cli
->tree
, fname
);
395 CHECK_STATUS(status
, NT_STATUS_OK
);
398 smbcli_close(cli
->tree
, fnum
);
399 smbcli_deltree(cli
->tree
, BASEDIR
);
404 test stream sharemodes
406 static bool test_stream_sharemodes(struct torture_context
*tctx
,
407 struct smbcli_state
*cli
)
411 const char *fname
= BASEDIR
"\\stream.txt";
412 const char *sname1
, *sname2
;
417 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
419 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "Stream One");
420 sname2
= talloc_asprintf(tctx
, "%s:%s:$DaTa", fname
, "Second Stream");
422 printf("(%s) testing stream share mode conflicts\n", __location__
);
423 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
424 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
425 io
.ntcreatex
.in
.flags
= 0;
426 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
427 io
.ntcreatex
.in
.create_options
= 0;
428 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
429 io
.ntcreatex
.in
.share_access
= 0;
430 io
.ntcreatex
.in
.alloc_size
= 0;
431 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
432 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
433 io
.ntcreatex
.in
.security_flags
= 0;
434 io
.ntcreatex
.in
.fname
= sname1
;
436 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
437 CHECK_STATUS(status
, NT_STATUS_OK
);
438 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
441 * A different stream does not give a sharing violation
444 io
.ntcreatex
.in
.fname
= sname2
;
445 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
446 CHECK_STATUS(status
, NT_STATUS_OK
);
447 fnum2
= io
.ntcreatex
.out
.file
.fnum
;
450 * ... whereas the same stream does with unchanged access/share_access
454 io
.ntcreatex
.in
.fname
= sname1
;
455 io
.ntcreatex
.in
.open_disposition
= 0;
456 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
457 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
459 io
.ntcreatex
.in
.fname
= sname2
;
460 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
461 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
464 if (fnum1
!= -1) smbcli_close(cli
->tree
, fnum1
);
465 if (fnum2
!= -1) smbcli_close(cli
->tree
, fnum2
);
466 status
= smbcli_unlink(cli
->tree
, fname
);
467 smbcli_deltree(cli
->tree
, BASEDIR
);
472 * Test FILE_SHARE_DELETE on streams
474 * A stream opened with !FILE_SHARE_DELETE prevents the main file to be opened
475 * with SEC_STD_DELETE.
477 * The main file opened with !FILE_SHARE_DELETE does *not* prevent a stream to
478 * be opened with SEC_STD_DELETE.
480 * A stream held open with FILE_SHARE_DELETE allows the file to be
481 * deleted. After the main file is deleted, access to the open file descriptor
482 * still works, but all name-based access to both the main file as well as the
483 * stream is denied with DELETE pending.
485 * This means, an open of the main file with SEC_STD_DELETE should walk all
486 * streams and also open them with SEC_STD_DELETE. If any of these opens gives
487 * SHARING_VIOLATION, the main open fails.
489 * Closing the main file after delete_on_close has been set does not really
490 * unlink it but leaves the corresponding share mode entry with
491 * delete_on_close being set around until all streams are closed.
493 * Opening a stream must also look at the main file's share mode entry, look
494 * at the delete_on_close bit and potentially return DELETE_PENDING.
497 static bool test_stream_delete(struct torture_context
*tctx
,
498 struct smbcli_state
*cli
)
502 const char *fname
= BASEDIR
"\\stream.txt";
508 union smb_fileinfo finfo
;
510 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
512 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "Stream One");
514 printf("(%s) opening non-existent file stream\n", __location__
);
515 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
516 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
517 io
.ntcreatex
.in
.flags
= 0;
518 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
;
519 io
.ntcreatex
.in
.create_options
= 0;
520 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
521 io
.ntcreatex
.in
.share_access
= 0;
522 io
.ntcreatex
.in
.alloc_size
= 0;
523 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
524 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
525 io
.ntcreatex
.in
.security_flags
= 0;
526 io
.ntcreatex
.in
.fname
= sname1
;
528 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
529 CHECK_STATUS(status
, NT_STATUS_OK
);
530 fnum
= io
.ntcreatex
.out
.file
.fnum
;
532 retsize
= smbcli_write(cli
->tree
, fnum
, 0, "test data", 0, 9);
533 CHECK_VALUE(retsize
, 9);
536 * One stream opened without FILE_SHARE_DELETE prevents the main file
537 * to be deleted or even opened with DELETE access
540 status
= smbcli_unlink(cli
->tree
, fname
);
541 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
543 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
544 io
.ntcreatex
.in
.fname
= fname
;
545 io
.ntcreatex
.in
.access_mask
= SEC_STD_DELETE
;
546 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
547 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
549 smbcli_close(cli
->tree
, fnum
);
552 * ... but unlink works if a stream is opened with FILE_SHARE_DELETE
555 io
.ntcreatex
.in
.fname
= sname1
;
556 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
;
557 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_DELETE
;
558 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
559 CHECK_STATUS(status
, NT_STATUS_OK
);
560 fnum
= io
.ntcreatex
.out
.file
.fnum
;
562 status
= smbcli_unlink(cli
->tree
, fname
);
563 CHECK_STATUS(status
, NT_STATUS_OK
);
566 * file access still works on the stream while the main file is closed
569 retsize
= smbcli_read(cli
->tree
, fnum
, buf
, 0, 9);
570 CHECK_VALUE(retsize
, 9);
572 finfo
.generic
.level
= RAW_FILEINFO_STANDARD
;
573 finfo
.generic
.in
.file
.path
= fname
;
576 * name-based access to both the main file and the stream does not
577 * work anymore but gives DELETE_PENDING
580 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
581 CHECK_STATUS(status
, NT_STATUS_DELETE_PENDING
);
584 * older S3 doesn't do this
586 finfo
.generic
.in
.file
.path
= sname1
;
587 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
588 CHECK_STATUS(status
, NT_STATUS_DELETE_PENDING
);
591 * fd-based qfileinfo on the stream still works, the stream does not
592 * have the delete-on-close bit set. This could mean that open on the
593 * stream first opens the main file
596 finfo
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
597 finfo
.all_info
.in
.file
.fnum
= fnum
;
599 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo
);
600 CHECK_STATUS(status
, NT_STATUS_OK
);
602 /* w2k and w2k3 return 0 and w2k8 returns 1 */
603 if (TARGET_IS_WINXP(tctx
) || TARGET_IS_W2K3(tctx
) ||
604 TARGET_IS_SAMBA3(tctx
)) {
605 CHECK_VALUE(finfo
.all_info
.out
.delete_pending
, 0);
607 CHECK_VALUE(finfo
.all_info
.out
.delete_pending
, 1);
610 smbcli_close(cli
->tree
, fnum
);
613 * After closing the stream the file is really gone.
616 finfo
.generic
.in
.file
.path
= fname
;
617 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
618 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
620 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
622 io
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
623 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
624 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
625 CHECK_STATUS(status
, NT_STATUS_OK
);
626 fnum
= io
.ntcreatex
.out
.file
.fnum
;
628 finfo
.generic
.in
.file
.path
= fname
;
629 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
630 CHECK_STATUS(status
, NT_STATUS_OK
);
632 smbcli_close(cli
->tree
, fnum
);
634 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
635 CHECK_STATUS(status
, NT_STATUS_OK
);
637 smbcli_close(cli
->tree
, fnum
);
638 smbcli_unlink(cli
->tree
, fname
);
639 smbcli_deltree(cli
->tree
, BASEDIR
);
646 static bool test_stream_names(struct torture_context
*tctx
,
647 struct smbcli_state
*cli
)
651 union smb_fileinfo info
;
652 union smb_fileinfo finfo
;
653 union smb_fileinfo stinfo
;
654 union smb_setfileinfo sinfo
;
655 const char *fname
= BASEDIR
"\\stream_names.txt";
656 const char *sname1
, *sname1b
, *sname1c
, *sname1d
;
657 const char *sname2
, *snamew
, *snamew2
;
658 const char *snamer1
, *snamer2
;
664 const char *four
[4] = {
666 ":\x05Stream\n One:$DATA",
667 ":MStream Two:$DATA",
670 const char *five1
[5] = {
672 ":\x05Stream\n One:$DATA",
673 ":BeforeRename:$DATA",
674 ":MStream Two:$DATA",
677 const char *five2
[5] = {
679 ":\x05Stream\n One:$DATA",
680 ":AfterRename:$DATA",
681 ":MStream Two:$DATA",
685 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
687 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "\x05Stream\n One");
688 sname1b
= talloc_asprintf(tctx
, "%s:", sname1
);
689 sname1c
= talloc_asprintf(tctx
, "%s:$FOO", sname1
);
690 sname1d
= talloc_asprintf(tctx
, "%s:?D*a", sname1
);
691 sname2
= talloc_asprintf(tctx
, "%s:%s:$DaTa", fname
, "MStream Two");
692 snamew
= talloc_asprintf(tctx
, "%s:%s:$DATA", fname
, "?Stream*");
693 snamew2
= talloc_asprintf(tctx
, "%s\\stream*:%s:$DATA", BASEDIR
, "?Stream*");
694 snamer1
= talloc_asprintf(tctx
, "%s:%s:$DATA", fname
, "BeforeRename");
695 snamer2
= talloc_asprintf(tctx
, "%s:%s:$DATA", fname
, "AfterRename");
697 printf("(%s) testing stream names\n", __location__
);
698 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
699 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
700 io
.ntcreatex
.in
.flags
= 0;
701 io
.ntcreatex
.in
.access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
702 io
.ntcreatex
.in
.create_options
= 0;
703 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
704 io
.ntcreatex
.in
.share_access
=
705 NTCREATEX_SHARE_ACCESS_READ
|
706 NTCREATEX_SHARE_ACCESS_WRITE
;
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
= fname
;
713 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
714 CHECK_STATUS(status
, NT_STATUS_OK
);
715 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
717 torture_comment(tctx
, "Adding two EAs to base file\n");
719 sinfo
.generic
.level
= RAW_SFILEINFO_EA_SET
;
720 sinfo
.generic
.in
.file
.fnum
= fnum1
;
721 sinfo
.ea_set
.in
.num_eas
= 2;
722 sinfo
.ea_set
.in
.eas
= talloc_array(tctx
, struct ea_struct
, 2);
723 sinfo
.ea_set
.in
.eas
[0].flags
= 0;
724 sinfo
.ea_set
.in
.eas
[0].name
.s
= "EAONE";
725 sinfo
.ea_set
.in
.eas
[0].value
= data_blob_string_const("VALUE1");
726 sinfo
.ea_set
.in
.eas
[1].flags
= 0;
727 sinfo
.ea_set
.in
.eas
[1].name
.s
= "SECONDEA";
728 sinfo
.ea_set
.in
.eas
[1].value
= data_blob_string_const("ValueTwo");
730 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
731 CHECK_STATUS(status
, NT_STATUS_OK
);
734 * Make sure the create time of the streams are different from the
738 smbcli_close(cli
->tree
, fnum1
);
740 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
741 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
742 io
.ntcreatex
.in
.flags
= 0;
743 io
.ntcreatex
.in
.access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
744 io
.ntcreatex
.in
.create_options
= 0;
745 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
746 io
.ntcreatex
.in
.share_access
=
747 NTCREATEX_SHARE_ACCESS_READ
|
748 NTCREATEX_SHARE_ACCESS_WRITE
;
749 io
.ntcreatex
.in
.alloc_size
= 0;
750 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
751 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
752 io
.ntcreatex
.in
.security_flags
= 0;
753 io
.ntcreatex
.in
.fname
= sname1
;
755 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
756 CHECK_STATUS(status
, NT_STATUS_OK
);
757 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
759 torture_comment(tctx
, "Adding one EAs to first stream file\n");
761 sinfo
.generic
.level
= RAW_SFILEINFO_EA_SET
;
762 sinfo
.generic
.in
.file
.fnum
= fnum1
;
763 sinfo
.ea_set
.in
.num_eas
= 1;
764 sinfo
.ea_set
.in
.eas
= talloc_array(tctx
, struct ea_struct
, 1);
765 sinfo
.ea_set
.in
.eas
[0].flags
= 0;
766 sinfo
.ea_set
.in
.eas
[0].name
.s
= "STREAMEA";
767 sinfo
.ea_set
.in
.eas
[0].value
= data_blob_string_const("EA_VALUE1");
769 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
770 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
772 status
= torture_check_ea(cli
, sname1
, "STREAMEA", "EA_VALUE1");
773 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
776 info
.generic
.level
= RAW_FILEINFO_ALL_EAS
;
777 info
.all_eas
.in
.file
.path
= sname1
;
779 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &info
);
780 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
783 * A different stream does not give a sharing violation
786 io
.ntcreatex
.in
.fname
= sname2
;
787 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
788 CHECK_STATUS(status
, NT_STATUS_OK
);
789 fnum2
= io
.ntcreatex
.out
.file
.fnum
;
792 * ... whereas the same stream does with unchanged access/share_access
796 io
.ntcreatex
.in
.fname
= sname1
;
797 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_SUPERSEDE
;
798 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
799 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
801 io
.ntcreatex
.in
.fname
= sname1b
;
802 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
803 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
805 io
.ntcreatex
.in
.fname
= sname1c
;
806 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
807 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
)) {
808 /* w2k returns INVALID_PARAMETER */
809 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
811 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
814 io
.ntcreatex
.in
.fname
= sname1d
;
815 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
816 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
)) {
817 /* w2k returns INVALID_PARAMETER */
818 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
820 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
823 io
.ntcreatex
.in
.fname
= sname2
;
824 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
825 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
827 io
.ntcreatex
.in
.fname
= snamew
;
828 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
829 CHECK_STATUS(status
, NT_STATUS_OK
);
830 fnum3
= io
.ntcreatex
.out
.file
.fnum
;
832 io
.ntcreatex
.in
.fname
= snamew2
;
833 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
834 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
836 ret
&= check_stream_list(tctx
, cli
, fname
, 4, four
);
838 smbcli_close(cli
->tree
, fnum1
);
839 smbcli_close(cli
->tree
, fnum2
);
840 smbcli_close(cli
->tree
, fnum3
);
842 finfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
843 finfo
.generic
.in
.file
.path
= fname
;
844 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
845 CHECK_STATUS(status
, NT_STATUS_OK
);
847 ret
&= check_stream_list(tctx
, cli
, fname
, 4, four
);
849 for (i
=0; i
< 4; i
++) {
851 uint64_t stream_size
;
852 char *path
= talloc_asprintf(tctx
, "%s%s",
855 char *rpath
= talloc_strdup(path
, path
);
856 char *p
= strrchr(rpath
, ':');
864 printf("(%s): i[%u][%s]\n", __location__
, i
, path
);
865 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
866 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_ATTRIBUTE
|
867 SEC_FILE_WRITE_ATTRIBUTE
|
869 io
.ntcreatex
.in
.fname
= path
;
870 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
871 CHECK_STATUS(status
, NT_STATUS_OK
);
872 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
874 finfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
875 finfo
.generic
.in
.file
.path
= fname
;
876 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
877 CHECK_STATUS(status
, NT_STATUS_OK
);
879 stinfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
880 stinfo
.generic
.in
.file
.fnum
= fnum1
;
881 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &stinfo
);
882 CHECK_STATUS(status
, NT_STATUS_OK
);
883 if (!torture_setting_bool(tctx
, "samba3", false)) {
884 CHECK_NTTIME(stinfo
.all_info
.out
.create_time
,
885 finfo
.all_info
.out
.create_time
);
886 CHECK_NTTIME(stinfo
.all_info
.out
.access_time
,
887 finfo
.all_info
.out
.access_time
);
888 CHECK_NTTIME(stinfo
.all_info
.out
.write_time
,
889 finfo
.all_info
.out
.write_time
);
890 CHECK_NTTIME(stinfo
.all_info
.out
.change_time
,
891 finfo
.all_info
.out
.change_time
);
893 CHECK_VALUE(stinfo
.all_info
.out
.attrib
,
894 finfo
.all_info
.out
.attrib
);
895 CHECK_VALUE(stinfo
.all_info
.out
.size
,
896 finfo
.all_info
.out
.size
);
897 CHECK_VALUE(stinfo
.all_info
.out
.delete_pending
,
898 finfo
.all_info
.out
.delete_pending
);
899 CHECK_VALUE(stinfo
.all_info
.out
.directory
,
900 finfo
.all_info
.out
.directory
);
901 CHECK_VALUE(stinfo
.all_info
.out
.ea_size
,
902 finfo
.all_info
.out
.ea_size
);
904 stinfo
.generic
.level
= RAW_FILEINFO_NAME_INFO
;
905 stinfo
.generic
.in
.file
.fnum
= fnum1
;
906 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &stinfo
);
907 CHECK_STATUS(status
, NT_STATUS_OK
);
908 if (!torture_setting_bool(tctx
, "samba3", false)) {
909 CHECK_STR(stinfo
.name_info
.out
.fname
.s
, rpath
);
912 write_time
= finfo
.all_info
.out
.write_time
;
913 write_time
+= i
*1000000;
914 write_time
/= 1000000;
915 write_time
*= 1000000;
918 sinfo
.basic_info
.level
= RAW_SFILEINFO_BASIC_INFO
;
919 sinfo
.basic_info
.in
.file
.fnum
= fnum1
;
920 sinfo
.basic_info
.in
.write_time
= write_time
;
921 sinfo
.basic_info
.in
.attrib
= stinfo
.all_info
.out
.attrib
;
922 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
923 CHECK_STATUS(status
, NT_STATUS_OK
);
925 stream_size
= i
*8192;
928 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFO
;
929 sinfo
.end_of_file_info
.in
.file
.fnum
= fnum1
;
930 sinfo
.end_of_file_info
.in
.size
= stream_size
;
931 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
932 CHECK_STATUS(status
, NT_STATUS_OK
);
934 stinfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
935 stinfo
.generic
.in
.file
.fnum
= fnum1
;
936 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &stinfo
);
937 CHECK_STATUS(status
, NT_STATUS_OK
);
938 if (!torture_setting_bool(tctx
, "samba3", false)) {
939 CHECK_NTTIME(stinfo
.all_info
.out
.write_time
,
941 CHECK_VALUE(stinfo
.all_info
.out
.attrib
,
942 finfo
.all_info
.out
.attrib
);
944 CHECK_VALUE(stinfo
.all_info
.out
.size
,
946 CHECK_VALUE(stinfo
.all_info
.out
.delete_pending
,
947 finfo
.all_info
.out
.delete_pending
);
948 CHECK_VALUE(stinfo
.all_info
.out
.directory
,
949 finfo
.all_info
.out
.directory
);
950 CHECK_VALUE(stinfo
.all_info
.out
.ea_size
,
951 finfo
.all_info
.out
.ea_size
);
953 ret
&= check_stream_list(tctx
, cli
, fname
, 4, four
);
955 smbcli_close(cli
->tree
, fnum1
);
959 printf("(%s): testing stream renames\n", __location__
);
960 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
961 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_ATTRIBUTE
|
962 SEC_FILE_WRITE_ATTRIBUTE
|
964 io
.ntcreatex
.in
.fname
= snamer1
;
965 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
966 CHECK_STATUS(status
, NT_STATUS_OK
);
967 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
969 ret
&= check_stream_list(tctx
, cli
, fname
, 5, five1
);
972 sinfo
.rename_information
.level
= RAW_SFILEINFO_RENAME_INFORMATION
;
973 sinfo
.rename_information
.in
.file
.fnum
= fnum1
;
974 sinfo
.rename_information
.in
.overwrite
= true;
975 sinfo
.rename_information
.in
.root_fid
= 0;
976 sinfo
.rename_information
.in
.new_name
= ":AfterRename:$DATA";
977 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
978 CHECK_STATUS(status
, NT_STATUS_OK
);
980 ret
&= check_stream_list(tctx
, cli
, fname
, 5, five2
);
983 sinfo
.rename_information
.level
= RAW_SFILEINFO_RENAME_INFORMATION
;
984 sinfo
.rename_information
.in
.file
.fnum
= fnum1
;
985 sinfo
.rename_information
.in
.overwrite
= false;
986 sinfo
.rename_information
.in
.root_fid
= 0;
987 sinfo
.rename_information
.in
.new_name
= ":MStream Two:$DATA";
988 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
989 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_COLLISION
);
991 ret
&= check_stream_list(tctx
, cli
, fname
, 5, five2
);
994 sinfo
.rename_information
.level
= RAW_SFILEINFO_RENAME_INFORMATION
;
995 sinfo
.rename_information
.in
.file
.fnum
= fnum1
;
996 sinfo
.rename_information
.in
.overwrite
= true;
997 sinfo
.rename_information
.in
.root_fid
= 0;
998 sinfo
.rename_information
.in
.new_name
= ":MStream Two:$DATA";
999 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
1000 if (torture_setting_bool(tctx
, "samba4", false) ||
1001 torture_setting_bool(tctx
, "samba3", false)) {
1002 /* why should this rename be considered invalid?? */
1003 CHECK_STATUS(status
, NT_STATUS_OK
);
1004 ret
&= check_stream_list(tctx
, cli
, fname
, 4, four
);
1006 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1007 ret
&= check_stream_list(tctx
, cli
, fname
, 5, five2
);
1011 /* TODO: we need to test more rename combinations */
1014 if (fnum1
!= -1) smbcli_close(cli
->tree
, fnum1
);
1015 if (fnum2
!= -1) smbcli_close(cli
->tree
, fnum2
);
1016 if (fnum3
!= -1) smbcli_close(cli
->tree
, fnum3
);
1017 status
= smbcli_unlink(cli
->tree
, fname
);
1018 smbcli_deltree(cli
->tree
, BASEDIR
);
1025 static bool test_stream_names2(struct torture_context
*tctx
,
1026 struct smbcli_state
*cli
)
1030 const char *fname
= BASEDIR
"\\stream_names2.txt";
1035 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1037 printf("(%s) testing stream names\n", __location__
);
1038 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1039 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1040 io
.ntcreatex
.in
.flags
= 0;
1041 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
1042 io
.ntcreatex
.in
.create_options
= 0;
1043 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1044 io
.ntcreatex
.in
.share_access
= 0;
1045 io
.ntcreatex
.in
.alloc_size
= 0;
1046 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1047 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1048 io
.ntcreatex
.in
.security_flags
= 0;
1049 io
.ntcreatex
.in
.fname
= fname
;
1050 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1051 CHECK_STATUS(status
, NT_STATUS_OK
);
1052 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
1054 for (i
=0x01; i
< 0x7F; i
++) {
1055 char *path
= talloc_asprintf(tctx
, "%s:Stream%c0x%02X:$DATA",
1063 expected
= NT_STATUS_OBJECT_NAME_INVALID
;
1066 expected
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1071 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1072 io
.ntcreatex
.in
.fname
= path
;
1073 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1074 if (!NT_STATUS_EQUAL(status
, expected
)) {
1075 printf("(%s) %s:Stream%c0x%02X:$DATA%s => expected[%s]\n",
1076 __location__
, fname
, isprint(i
)?(char)i
:' ', i
,
1077 isprint(i
)?"":" (not printable)",
1078 nt_errstr(expected
));
1080 CHECK_STATUS(status
, expected
);
1086 if (fnum1
!= -1) smbcli_close(cli
->tree
, fnum1
);
1087 status
= smbcli_unlink(cli
->tree
, fname
);
1088 smbcli_deltree(cli
->tree
, BASEDIR
);
1092 #define CHECK_CALL_FNUM(call, rightstatus) do { \
1093 check_fnum = true; \
1094 call_name = #call; \
1095 sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
1096 sfinfo.generic.in.file.fnum = fnum; \
1097 status = smb_raw_setfileinfo(cli->tree, &sfinfo); \
1098 if (!NT_STATUS_EQUAL(status, rightstatus)) { \
1099 printf("(%s) %s - %s (should be %s)\n", __location__, #call, \
1100 nt_errstr(status), nt_errstr(rightstatus)); \
1103 finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \
1104 finfo1.generic.in.file.fnum = fnum; \
1105 status2 = smb_raw_fileinfo(cli->tree, tctx, &finfo1); \
1106 if (!NT_STATUS_IS_OK(status2)) { \
1107 printf("(%s) %s pathinfo - %s\n", __location__, #call, nt_errstr(status)); \
1114 static bool test_stream_rename(struct torture_context
*tctx
,
1115 struct smbcli_state
*cli
)
1117 NTSTATUS status
, status2
;
1119 const char *fname
= BASEDIR
"\\stream_rename.txt";
1120 const char *sname1
, *sname2
;
1121 union smb_fileinfo finfo1
;
1122 union smb_setfileinfo sfinfo
;
1126 const char *call_name
;
1128 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1130 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "Stream One");
1131 sname2
= talloc_asprintf(tctx
, "%s:%s:$DaTa", fname
, "Second Stream");
1133 printf("(%s) testing stream renames\n", __location__
);
1134 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1135 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1136 io
.ntcreatex
.in
.flags
= 0;
1137 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_ATTRIBUTE
|
1138 SEC_FILE_WRITE_ATTRIBUTE
|
1139 SEC_RIGHTS_FILE_ALL
;
1140 io
.ntcreatex
.in
.create_options
= 0;
1141 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1142 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
| NTCREATEX_SHARE_ACCESS_WRITE
| NTCREATEX_SHARE_ACCESS_DELETE
;
1143 io
.ntcreatex
.in
.alloc_size
= 0;
1144 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1145 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1146 io
.ntcreatex
.in
.security_flags
= 0;
1147 io
.ntcreatex
.in
.fname
= sname1
;
1149 /* Create two streams. */
1150 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1151 CHECK_STATUS(status
, NT_STATUS_OK
);
1152 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1153 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1155 io
.ntcreatex
.in
.fname
= sname2
;
1156 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1157 CHECK_STATUS(status
, NT_STATUS_OK
);
1158 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1160 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1163 * Open the second stream.
1166 io
.ntcreatex
.in
.access_mask
= SEC_STD_DELETE
;
1167 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
1168 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1169 CHECK_STATUS(status
, NT_STATUS_OK
);
1170 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1173 * Now rename the second stream onto the first.
1176 ZERO_STRUCT(sfinfo
);
1178 sfinfo
.rename_information
.in
.overwrite
= 1;
1179 sfinfo
.rename_information
.in
.root_fid
= 0;
1180 sfinfo
.rename_information
.in
.new_name
= ":Stream One";
1181 CHECK_CALL_FNUM(RENAME_INFORMATION
, NT_STATUS_OK
);
1184 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1185 status
= smbcli_unlink(cli
->tree
, fname
);
1186 smbcli_deltree(cli
->tree
, BASEDIR
);
1190 static bool test_stream_rename2(struct torture_context
*tctx
,
1191 struct smbcli_state
*cli
)
1195 const char *fname1
= BASEDIR
"\\stream.txt";
1196 const char *fname2
= BASEDIR
"\\stream2.txt";
1197 const char *stream_name1
= ":Stream One:$DATA";
1198 const char *stream_name2
= ":Stream Two:$DATA";
1199 const char *stream_name_default
= "::$DATA";
1204 union smb_setfileinfo sinfo
;
1205 union smb_rename rio
;
1207 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1209 sname1
= talloc_asprintf(tctx
, "%s:%s", fname1
, "Stream One");
1210 sname2
= talloc_asprintf(tctx
, "%s:%s", fname1
, "Stream Two");
1212 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1213 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1214 io
.ntcreatex
.in
.flags
= 0;
1215 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1216 SEC_STD_DELETE
|SEC_FILE_APPEND_DATA
|SEC_STD_READ_CONTROL
);
1217 io
.ntcreatex
.in
.create_options
= 0;
1218 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1219 io
.ntcreatex
.in
.share_access
= (NTCREATEX_SHARE_ACCESS_READ
|
1220 NTCREATEX_SHARE_ACCESS_WRITE
|
1221 NTCREATEX_SHARE_ACCESS_DELETE
);
1222 io
.ntcreatex
.in
.alloc_size
= 0;
1223 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1224 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1225 io
.ntcreatex
.in
.security_flags
= 0;
1226 io
.ntcreatex
.in
.fname
= sname1
;
1228 /* Open/create new stream. */
1229 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1230 CHECK_STATUS(status
, NT_STATUS_OK
);
1232 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1235 * Check raw rename with <base>:<stream>.
1237 printf("(%s) Checking NTRENAME of a stream using <base>:<stream>\n",
1239 rio
.generic
.level
= RAW_RENAME_NTRENAME
;
1240 rio
.ntrename
.in
.old_name
= sname1
;
1241 rio
.ntrename
.in
.new_name
= sname2
;
1242 rio
.ntrename
.in
.attrib
= 0;
1243 rio
.ntrename
.in
.cluster_size
= 0;
1244 rio
.ntrename
.in
.flags
= RENAME_FLAG_RENAME
;
1245 status
= smb_raw_rename(cli
->tree
, &rio
);
1246 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1249 * Check raw rename to the default stream using :<stream>.
1251 printf("(%s) Checking NTRENAME to default stream using :<stream>\n",
1253 rio
.ntrename
.in
.new_name
= stream_name_default
;
1254 status
= smb_raw_rename(cli
->tree
, &rio
);
1255 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_COLLISION
);
1258 * Check raw rename using :<stream>.
1260 printf("(%s) Checking NTRENAME of a stream using :<stream>\n",
1262 rio
.ntrename
.in
.new_name
= stream_name2
;
1263 status
= smb_raw_rename(cli
->tree
, &rio
);
1264 CHECK_STATUS(status
, NT_STATUS_OK
);
1267 * Check raw rename of a stream to a file.
1269 printf("(%s) Checking NTRENAME of a stream to a file\n",
1271 rio
.ntrename
.in
.old_name
= sname2
;
1272 rio
.ntrename
.in
.new_name
= fname2
;
1273 status
= smb_raw_rename(cli
->tree
, &rio
);
1274 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1277 * Check raw rename of a file to a stream.
1279 printf("(%s) Checking NTRENAME of a file to a stream\n",
1282 /* Create the file. */
1283 io
.ntcreatex
.in
.fname
= fname2
;
1284 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1285 CHECK_STATUS(status
, NT_STATUS_OK
);
1286 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1288 /* Try the rename. */
1289 rio
.ntrename
.in
.old_name
= fname2
;
1290 rio
.ntrename
.in
.new_name
= sname1
;
1291 status
= smb_raw_rename(cli
->tree
, &rio
);
1292 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
1295 * Reopen the stream for trans2 renames.
1297 io
.ntcreatex
.in
.fname
= sname2
;
1298 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1299 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1300 CHECK_STATUS(status
, NT_STATUS_OK
);
1301 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1304 * Check trans2 rename of a stream using :<stream>.
1306 printf("(%s) Checking trans2 rename of a stream using :<stream>\n",
1309 sinfo
.rename_information
.level
= RAW_SFILEINFO_RENAME_INFORMATION
;
1310 sinfo
.rename_information
.in
.file
.fnum
= fnum
;
1311 sinfo
.rename_information
.in
.overwrite
= 1;
1312 sinfo
.rename_information
.in
.root_fid
= 0;
1313 sinfo
.rename_information
.in
.new_name
= stream_name1
;
1314 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
1315 CHECK_STATUS(status
, NT_STATUS_OK
);
1318 * Check trans2 rename of an overwriting stream using :<stream>.
1320 printf("(%s) Checking trans2 rename of an overwriting stream using "
1321 ":<stream>\n", __location__
);
1323 /* Create second stream. */
1324 io
.ntcreatex
.in
.fname
= sname2
;
1325 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1326 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1327 CHECK_STATUS(status
, NT_STATUS_OK
);
1328 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1330 /* Rename the first stream onto the second. */
1331 sinfo
.rename_information
.in
.file
.fnum
= fnum
;
1332 sinfo
.rename_information
.in
.new_name
= stream_name2
;
1333 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
1334 CHECK_STATUS(status
, NT_STATUS_OK
);
1336 smbcli_close(cli
->tree
, fnum
);
1339 * Reopen the stream with the new name.
1341 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1342 io
.ntcreatex
.in
.fname
= sname2
;
1343 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1344 CHECK_STATUS(status
, NT_STATUS_OK
);
1345 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1348 * Check trans2 rename of a stream using <base>:<stream>.
1350 printf("(%s) Checking trans2 rename of a stream using "
1351 "<base>:<stream>\n", __location__
);
1352 sinfo
.rename_information
.in
.file
.fnum
= fnum
;
1353 sinfo
.rename_information
.in
.new_name
= sname1
;
1354 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
1355 CHECK_STATUS(status
, NT_STATUS_NOT_SUPPORTED
);
1358 * Samba3 doesn't currently support renaming a stream to the default
1359 * stream. This test does pass on windows.
1361 if (torture_setting_bool(tctx
, "samba3", false) ||
1362 torture_setting_bool(tctx
, "samba4", false)) {
1367 * Check trans2 rename to the default stream using :<stream>.
1369 printf("(%s) Checking trans2 rename to defaualt stream using "
1370 ":<stream>\n", __location__
);
1371 sinfo
.rename_information
.in
.file
.fnum
= fnum
;
1372 sinfo
.rename_information
.in
.new_name
= stream_name_default
;
1373 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
1374 CHECK_STATUS(status
, NT_STATUS_OK
);
1376 smbcli_close(cli
->tree
, fnum
);
1379 smbcli_close(cli
->tree
, fnum
);
1380 status
= smbcli_unlink(cli
->tree
, fname1
);
1381 status
= smbcli_unlink(cli
->tree
, fname2
);
1382 smbcli_deltree(cli
->tree
, BASEDIR
);
1389 static bool test_stream_rename3(struct torture_context
*tctx
,
1390 struct smbcli_state
*cli
)
1392 NTSTATUS status
, status2
;
1394 const char *fname
= BASEDIR
"\\stream_rename.txt";
1395 const char *sname1
, *sname2
;
1396 union smb_fileinfo finfo1
;
1397 union smb_setfileinfo sfinfo
;
1402 const char *call_name
;
1404 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1406 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "MStream Two:$DATA");
1407 sname2
= talloc_asprintf(tctx
, "%s:%s:$DaTa", fname
, "Second Stream");
1409 printf("(%s) testing stream renames\n", __location__
);
1410 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1411 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1412 io
.ntcreatex
.in
.flags
= 0;
1413 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_ATTRIBUTE
|
1414 SEC_FILE_WRITE_ATTRIBUTE
|
1415 SEC_RIGHTS_FILE_ALL
;
1416 io
.ntcreatex
.in
.create_options
= 0;
1417 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1418 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
1419 NTCREATEX_SHARE_ACCESS_WRITE
| NTCREATEX_SHARE_ACCESS_DELETE
;
1420 io
.ntcreatex
.in
.alloc_size
= 0;
1421 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1422 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1423 io
.ntcreatex
.in
.security_flags
= 0;
1424 io
.ntcreatex
.in
.fname
= sname1
;
1426 /* Create two streams. */
1427 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1428 CHECK_STATUS(status
, NT_STATUS_OK
);
1429 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1430 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1432 io
.ntcreatex
.in
.fname
= sname2
;
1433 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1434 CHECK_STATUS(status
, NT_STATUS_OK
);
1435 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1437 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1439 /* open the second stream. */
1440 io
.ntcreatex
.in
.access_mask
= SEC_STD_DELETE
;
1441 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
1442 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1443 CHECK_STATUS(status
, NT_STATUS_OK
);
1444 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1446 /* Keep a handle to the first stream open. */
1447 io
.ntcreatex
.in
.fname
= sname1
;
1448 io
.ntcreatex
.in
.access_mask
= SEC_STD_DELETE
;
1449 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
1450 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1451 CHECK_STATUS(status
, NT_STATUS_OK
);
1452 fnum2
= io
.ntcreatex
.out
.file
.fnum
;
1454 ZERO_STRUCT(sfinfo
);
1455 sfinfo
.rename_information
.in
.overwrite
= 1;
1456 sfinfo
.rename_information
.in
.root_fid
= 0;
1457 sfinfo
.rename_information
.in
.new_name
= ":MStream Two:$DATA";
1458 if (torture_setting_bool(tctx
, "samba4", false) ||
1459 torture_setting_bool(tctx
, "samba3", false)) {
1460 CHECK_CALL_FNUM(RENAME_INFORMATION
, NT_STATUS_OK
);
1462 CHECK_CALL_FNUM(RENAME_INFORMATION
,
1463 NT_STATUS_INVALID_PARAMETER
);
1468 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1469 if (fnum2
!= -1) smbcli_close(cli
->tree
, fnum2
);
1470 status
= smbcli_unlink(cli
->tree
, fname
);
1471 smbcli_deltree(cli
->tree
, BASEDIR
);
1475 static bool create_file_with_stream(struct torture_context
*tctx
,
1476 struct smbcli_state
*cli
,
1483 /* Create a file with a stream */
1484 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1485 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1486 io
.ntcreatex
.in
.flags
= 0;
1487 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1488 SEC_FILE_APPEND_DATA
|SEC_STD_READ_CONTROL
);
1489 io
.ntcreatex
.in
.create_options
= 0;
1490 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1491 io
.ntcreatex
.in
.share_access
= 0;
1492 io
.ntcreatex
.in
.alloc_size
= 0;
1493 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1494 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1495 io
.ntcreatex
.in
.security_flags
= 0;
1496 io
.ntcreatex
.in
.fname
= stream
;
1498 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1499 CHECK_STATUS(status
, NT_STATUS_OK
);
1502 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1506 /* Test how streams interact with create dispositions */
1507 static bool test_stream_create_disposition(struct torture_context
*tctx
,
1508 struct smbcli_state
*cli
)
1512 const char *fname
= BASEDIR
"\\stream.txt";
1513 const char *stream
= "Stream One:$DATA";
1514 const char *fname_stream
;
1515 const char *default_stream_name
= "::$DATA";
1516 const char *stream_list
[2];
1520 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1522 fname_stream
= talloc_asprintf(tctx
, "%s:%s", fname
, stream
);
1524 stream_list
[0] = talloc_asprintf(tctx
, ":%s", stream
);
1525 stream_list
[1] = default_stream_name
;
1527 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1531 /* Open the base file with OPEN */
1532 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1533 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1534 io
.ntcreatex
.in
.flags
= 0;
1535 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1536 SEC_FILE_APPEND_DATA
|SEC_STD_READ_CONTROL
);
1537 io
.ntcreatex
.in
.create_options
= 0;
1538 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1539 io
.ntcreatex
.in
.share_access
= 0;
1540 io
.ntcreatex
.in
.alloc_size
= 0;
1541 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1542 io
.ntcreatex
.in
.security_flags
= 0;
1543 io
.ntcreatex
.in
.fname
= fname
;
1546 * check ntcreatex open: sanity check
1548 printf("(%s) Checking ntcreatex disp: open\n", __location__
);
1549 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1550 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1551 CHECK_STATUS(status
, NT_STATUS_OK
);
1552 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1553 if (!check_stream_list(tctx
, cli
, fname
, 2, stream_list
)) {
1558 * check ntcreatex overwrite
1560 printf("(%s) Checking ntcreatex disp: overwrite\n", __location__
);
1561 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OVERWRITE
;
1562 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1563 CHECK_STATUS(status
, NT_STATUS_OK
);
1564 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1565 if (!check_stream_list(tctx
, cli
, fname
, 1, &default_stream_name
)) {
1570 * check ntcreatex overwrite_if
1572 printf("(%s) Checking ntcreatex disp: overwrite_if\n", __location__
);
1573 smbcli_unlink(cli
->tree
, fname
);
1574 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1578 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OVERWRITE_IF
;
1579 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1580 CHECK_STATUS(status
, NT_STATUS_OK
);
1581 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1582 if (!check_stream_list(tctx
, cli
, fname
, 1, &default_stream_name
)) {
1587 * check ntcreatex supersede
1589 printf("(%s) Checking ntcreatex disp: supersede\n", __location__
);
1590 smbcli_unlink(cli
->tree
, fname
);
1591 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1595 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_SUPERSEDE
;
1596 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1597 CHECK_STATUS(status
, NT_STATUS_OK
);
1598 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1599 if (!check_stream_list(tctx
, cli
, fname
, 1, &default_stream_name
)) {
1604 * check ntcreatex overwrite_if on a stream.
1606 printf("(%s) Checking ntcreatex disp: overwrite_if on stream\n",
1608 smbcli_unlink(cli
->tree
, fname
);
1609 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1613 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OVERWRITE_IF
;
1614 io
.ntcreatex
.in
.fname
= fname_stream
;
1615 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1616 CHECK_STATUS(status
, NT_STATUS_OK
);
1617 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1618 if (!check_stream_list(tctx
, cli
, fname
, 2, stream_list
)) {
1623 * check openx overwrite_if
1625 printf("(%s) Checking openx disp: overwrite_if\n", __location__
);
1626 smbcli_unlink(cli
->tree
, fname
);
1627 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1631 io
.openx
.level
= RAW_OPEN_OPENX
;
1632 io
.openx
.in
.flags
= OPENX_FLAGS_ADDITIONAL_INFO
;
1633 io
.openx
.in
.open_mode
= OPENX_MODE_ACCESS_RDWR
| OPEN_FLAGS_DENY_NONE
;
1634 io
.openx
.in
.search_attrs
= 0;
1635 io
.openx
.in
.file_attrs
= 0;
1636 io
.openx
.in
.write_time
= 0;
1637 io
.openx
.in
.size
= 1024*1024;
1638 io
.openx
.in
.timeout
= 0;
1639 io
.openx
.in
.fname
= fname
;
1641 io
.openx
.in
.open_func
= OPENX_OPEN_FUNC_TRUNC
| OPENX_OPEN_FUNC_CREATE
;
1642 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1643 CHECK_STATUS(status
, NT_STATUS_OK
);
1644 smbcli_close(cli
->tree
, io
.openx
.out
.file
.fnum
);
1645 if (!check_stream_list(tctx
, cli
, fname
, 1, &default_stream_name
)) {
1652 smbcli_close(cli
->tree
, fnum
);
1653 smbcli_unlink(cli
->tree
, fname
);
1654 smbcli_deltree(cli
->tree
, BASEDIR
);
1659 /* Test streaminfo with enough streams on a file to fill up the buffer. */
1660 static bool test_stream_large_streaminfo(struct torture_context
*tctx
,
1661 struct smbcli_state
*cli
)
1663 #define LONG_STREAM_SIZE 2
1665 const char *fname
= BASEDIR
"\\stream.txt";
1666 const char *fname_stream
;
1670 union smb_fileinfo finfo
;
1672 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1674 lstream_name
= talloc_array(tctx
, char, LONG_STREAM_SIZE
);
1676 for (i
= 0; i
< LONG_STREAM_SIZE
- 1; i
++) {
1677 lstream_name
[i
] = (char)('a' + i
%26);
1679 lstream_name
[LONG_STREAM_SIZE
- 1] = '\0';
1681 torture_comment(tctx
, "(%s) Creating a file with a lot of streams\n", __location__
);
1682 for (i
= 0; i
< 10000; i
++) {
1683 fname_stream
= talloc_asprintf(tctx
, "%s:%s%d", fname
,
1685 ret
= create_file_with_stream(tctx
, cli
, fname_stream
);
1691 finfo
.generic
.level
= RAW_FILEINFO_STREAM_INFO
;
1692 finfo
.generic
.in
.file
.path
= fname
;
1694 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1695 CHECK_STATUS(status
, STATUS_BUFFER_OVERFLOW
);
1698 smbcli_unlink(cli
->tree
, fname
);
1699 smbcli_deltree(cli
->tree
, BASEDIR
);
1704 /* Test the effect of setting attributes on a stream. */
1705 static bool test_stream_attributes(struct torture_context
*tctx
,
1706 struct smbcli_state
*cli
)
1711 const char *fname
= BASEDIR
"\\stream_attr.txt";
1712 const char *stream
= "Stream One:$DATA";
1713 const char *fname_stream
;
1715 union smb_fileinfo finfo
;
1716 union smb_setfileinfo sfinfo
;
1717 time_t basetime
= (time(NULL
) - 86400) & ~1;
1719 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1721 torture_comment(tctx
, "(%s) testing attribute setting on stream\n", __location__
);
1723 fname_stream
= talloc_asprintf(tctx
, "%s:%s", fname
, stream
);
1725 /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */
1726 ret
= create_file_with_stream(tctx
, cli
, fname_stream
);
1732 finfo
.generic
.level
= RAW_FILEINFO_BASIC_INFO
;
1733 finfo
.generic
.in
.file
.path
= fname
;
1734 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1735 CHECK_STATUS(status
, NT_STATUS_OK
);
1737 torture_assert_int_equal_goto(tctx
, finfo
.all_info
.out
.attrib
& ~FILE_ATTRIBUTE_NONINDEXED
, FILE_ATTRIBUTE_ARCHIVE
, ret
, done
, "attrib incorrect");
1739 /* Now open the stream name. */
1741 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1742 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1743 io
.ntcreatex
.in
.flags
= 0;
1744 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1745 SEC_FILE_APPEND_DATA
|SEC_STD_READ_CONTROL
|SEC_FILE_WRITE_ATTRIBUTE
);
1746 io
.ntcreatex
.in
.create_options
= 0;
1747 io
.ntcreatex
.in
.file_attr
= 0;
1748 io
.ntcreatex
.in
.share_access
= 0;
1749 io
.ntcreatex
.in
.alloc_size
= 0;
1750 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
1751 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1752 io
.ntcreatex
.in
.security_flags
= 0;
1753 io
.ntcreatex
.in
.fname
= fname_stream
;
1755 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1756 CHECK_STATUS(status
, NT_STATUS_OK
);
1758 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1760 /* Change the attributes + time on the stream fnum. */
1761 ZERO_STRUCT(sfinfo
);
1762 sfinfo
.basic_info
.in
.attrib
= FILE_ATTRIBUTE_READONLY
;
1763 unix_to_nt_time(&sfinfo
.basic_info
.in
.write_time
, basetime
);
1765 sfinfo
.generic
.level
= RAW_SFILEINFO_BASIC_INFORMATION
;
1766 sfinfo
.generic
.in
.file
.fnum
= fnum
;
1767 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
1768 torture_assert_ntstatus_equal_goto(tctx
, status
, NT_STATUS_OK
, ret
, done
, "smb_raw_setfileinfo failed");
1770 smbcli_close(cli
->tree
, fnum
);
1774 finfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
1775 finfo
.generic
.in
.file
.path
= fname
;
1776 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1777 torture_assert_ntstatus_equal_goto(tctx
, status
, NT_STATUS_OK
, ret
, done
, "smb_raw_pathinfo failed");
1779 torture_assert_int_equal_goto(tctx
, finfo
.all_info
.out
.attrib
& ~FILE_ATTRIBUTE_NONINDEXED
, FILE_ATTRIBUTE_READONLY
, ret
, done
, "attrib incorrect");
1781 torture_assert_int_equal_goto(tctx
, nt_time_to_unix(finfo
.all_info
.out
.write_time
), basetime
, ret
, done
, "time incorrect");
1786 smbcli_close(cli
->tree
, fnum
);
1788 smbcli_unlink(cli
->tree
, fname
);
1789 smbcli_deltree(cli
->tree
, BASEDIR
);
1794 * A rough approximation of how a windows client creates the streams for use
1795 * in the summary tab.
1797 static bool test_stream_summary_tab(struct torture_context
*tctx
,
1798 struct smbcli_state
*cli
)
1803 const char *fname
= BASEDIR
"\\stream_summary.txt";
1804 const char *stream
= ":\005SummaryInformation:$DATA";
1805 const char *fname_stream
= NULL
;
1806 const char *tmp_stream
= ":Updt_\005SummaryInformation:$DATA";
1807 const char *fname_tmp_stream
= NULL
;
1809 union smb_fileinfo finfo
;
1810 union smb_rename rio
;
1813 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1815 fname_stream
= talloc_asprintf(tctx
, "%s%s", fname
, stream
);
1816 fname_tmp_stream
= talloc_asprintf(tctx
, "%s%s", fname
,
1819 /* Create summary info stream */
1820 ret
= create_file_with_stream(tctx
, cli
, fname_stream
);
1825 /* Create summary info tmp update stream */
1826 ret
= create_file_with_stream(tctx
, cli
, fname_tmp_stream
);
1831 /* Open tmp stream and write to it */
1832 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1833 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1834 io
.ntcreatex
.in
.flags
= 0;
1835 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
;
1836 io
.ntcreatex
.in
.create_options
= 0;
1837 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1838 io
.ntcreatex
.in
.share_access
= 0;
1839 io
.ntcreatex
.in
.alloc_size
= 0;
1840 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1841 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1842 io
.ntcreatex
.in
.security_flags
= 0;
1843 io
.ntcreatex
.in
.fname
= fname_tmp_stream
;
1845 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1846 CHECK_STATUS(status
, NT_STATUS_OK
);
1847 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1849 retsize
= smbcli_write(cli
->tree
, fnum
, 0, "test data", 0, 9);
1850 CHECK_VALUE(retsize
, 9);
1852 /* close the tmp stream. */
1853 smbcli_close(cli
->tree
, fnum
);
1856 /* Delete the current stream */
1857 smbcli_unlink(cli
->tree
, fname_stream
);
1859 /* Do the rename. */
1860 rio
.generic
.level
= RAW_RENAME_RENAME
;
1861 rio
.rename
.in
.pattern1
= fname_tmp_stream
;
1862 rio
.rename
.in
.pattern2
= stream
;
1863 rio
.rename
.in
.attrib
= FILE_ATTRIBUTE_SYSTEM
|
1864 FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_DIRECTORY
;
1865 status
= smb_raw_rename(cli
->tree
, &rio
);
1866 CHECK_STATUS(status
, NT_STATUS_OK
);
1868 /* Try to open the tmp stream that we just renamed away. */
1869 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1870 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1872 /* Query the base file to make sure it's still there. */
1873 finfo
.generic
.level
= RAW_FILEINFO_BASIC_INFO
;
1874 finfo
.generic
.in
.file
.path
= fname
;
1876 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1877 CHECK_STATUS(status
, NT_STATUS_OK
);
1882 smbcli_close(cli
->tree
, fnum
);
1884 smbcli_unlink(cli
->tree
, fname
);
1886 smbcli_deltree(cli
->tree
, BASEDIR
);
1890 /* Test how streams interact with base file permissions */
1891 /* Regression test for bug:
1892 https://bugzilla.samba.org/show_bug.cgi?id=10229
1893 bug #10229 - No access check verification on stream files.
1895 static bool test_stream_permissions(struct torture_context
*tctx
,
1896 struct smbcli_state
*cli
)
1901 const char *fname
= BASEDIR
"\\stream_permissions.txt";
1902 const char *stream
= "Stream One:$DATA";
1903 const char *fname_stream
;
1904 union smb_fileinfo finfo
;
1905 union smb_setfileinfo sfinfo
;
1907 union smb_fileinfo q
;
1908 union smb_setfileinfo set
;
1909 struct security_ace ace
;
1910 struct security_descriptor
*sd
;
1912 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
),
1913 "Failed to setup up test directory: " BASEDIR
);
1915 torture_comment(tctx
, "(%s) testing permissions on streams\n", __location__
);
1917 fname_stream
= talloc_asprintf(tctx
, "%s:%s", fname
, stream
);
1919 /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */
1920 ret
= create_file_with_stream(tctx
, cli
, fname_stream
);
1926 finfo
.generic
.level
= RAW_FILEINFO_BASIC_INFO
;
1927 finfo
.generic
.in
.file
.path
= fname
;
1928 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1929 CHECK_STATUS(status
, NT_STATUS_OK
);
1931 torture_assert_int_equal_goto(tctx
,
1932 finfo
.all_info
.out
.attrib
& ~FILE_ATTRIBUTE_NONINDEXED
,
1933 FILE_ATTRIBUTE_ARCHIVE
, ret
, done
, "attrib incorrect");
1935 /* Change the attributes on the base file name. */
1936 ZERO_STRUCT(sfinfo
);
1937 sfinfo
.generic
.level
= RAW_SFILEINFO_SETATTR
;
1938 sfinfo
.generic
.in
.file
.path
= fname
;
1939 sfinfo
.setattr
.in
.attrib
= FILE_ATTRIBUTE_READONLY
;
1941 status
= smb_raw_setpathinfo(cli
->tree
, &sfinfo
);
1942 CHECK_STATUS(status
, NT_STATUS_OK
);
1944 /* Try and open the stream name for WRITE_DATA. Should
1945 fail with ACCESS_DENIED. */
1948 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1949 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1950 io
.ntcreatex
.in
.flags
= 0;
1951 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
1952 io
.ntcreatex
.in
.create_options
= 0;
1953 io
.ntcreatex
.in
.file_attr
= 0;
1954 io
.ntcreatex
.in
.share_access
= 0;
1955 io
.ntcreatex
.in
.alloc_size
= 0;
1956 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1957 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1958 io
.ntcreatex
.in
.security_flags
= 0;
1959 io
.ntcreatex
.in
.fname
= fname_stream
;
1961 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1962 CHECK_STATUS(status
, NT_STATUS_ACCESS_DENIED
);
1964 /* Change the attributes on the base file back. */
1965 ZERO_STRUCT(sfinfo
);
1966 sfinfo
.generic
.level
= RAW_SFILEINFO_SETATTR
;
1967 sfinfo
.generic
.in
.file
.path
= fname
;
1968 sfinfo
.setattr
.in
.attrib
= 0;
1970 status
= smb_raw_setpathinfo(cli
->tree
, &sfinfo
);
1971 CHECK_STATUS(status
, NT_STATUS_OK
);
1973 /* Re-open the file name. */
1976 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1977 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1978 io
.ntcreatex
.in
.flags
= 0;
1979 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1980 SEC_STD_READ_CONTROL
|SEC_STD_WRITE_DAC
|
1981 SEC_FILE_WRITE_ATTRIBUTE
);
1982 io
.ntcreatex
.in
.create_options
= 0;
1983 io
.ntcreatex
.in
.file_attr
= 0;
1984 io
.ntcreatex
.in
.share_access
= 0;
1985 io
.ntcreatex
.in
.alloc_size
= 0;
1986 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1987 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1988 io
.ntcreatex
.in
.security_flags
= 0;
1989 io
.ntcreatex
.in
.fname
= fname
;
1991 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1992 CHECK_STATUS(status
, NT_STATUS_OK
);
1994 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1996 /* Get the existing security descriptor. */
1998 q
.query_secdesc
.level
= RAW_FILEINFO_SEC_DESC
;
1999 q
.query_secdesc
.in
.file
.fnum
= fnum
;
2000 q
.query_secdesc
.in
.secinfo_flags
=
2004 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &q
);
2005 CHECK_STATUS(status
, NT_STATUS_OK
);
2006 sd
= q
.query_secdesc
.out
.sd
;
2008 /* Now add a DENY WRITE security descriptor for Everyone. */
2009 torture_comment(tctx
, "add a new ACE to the DACL\n");
2011 ace
.type
= SEC_ACE_TYPE_ACCESS_DENIED
;
2013 ace
.access_mask
= SEC_FILE_WRITE_DATA
;
2014 ace
.trustee
= *dom_sid_parse_talloc(tctx
, SID_WORLD
);
2016 status
= security_descriptor_dacl_add(sd
, &ace
);
2017 CHECK_STATUS(status
, NT_STATUS_OK
);
2019 /* security_descriptor_dacl_add adds to the *end* of
2020 the ace array, we need it at the start. Swap.. */
2021 ace
= sd
->dacl
->aces
[0];
2022 sd
->dacl
->aces
[0] = sd
->dacl
->aces
[sd
->dacl
->num_aces
-1];
2023 sd
->dacl
->aces
[sd
->dacl
->num_aces
-1] = ace
;
2026 set
.set_secdesc
.level
= RAW_SFILEINFO_SEC_DESC
;
2027 set
.set_secdesc
.in
.file
.fnum
= fnum
;
2028 set
.set_secdesc
.in
.secinfo_flags
= SECINFO_DACL
;
2029 set
.set_secdesc
.in
.sd
= sd
;
2031 status
= smb_raw_setfileinfo(cli
->tree
, &set
);
2032 CHECK_STATUS(status
, NT_STATUS_OK
);
2034 smbcli_close(cli
->tree
, fnum
);
2037 /* Try and open the stream name for WRITE_DATA. Should
2038 fail with ACCESS_DENIED. */
2041 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
2042 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
2043 io
.ntcreatex
.in
.flags
= 0;
2044 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
2045 io
.ntcreatex
.in
.create_options
= 0;
2046 io
.ntcreatex
.in
.file_attr
= 0;
2047 io
.ntcreatex
.in
.share_access
= 0;
2048 io
.ntcreatex
.in
.alloc_size
= 0;
2049 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
2050 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
2051 io
.ntcreatex
.in
.security_flags
= 0;
2052 io
.ntcreatex
.in
.fname
= fname_stream
;
2054 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
2055 CHECK_STATUS(status
, NT_STATUS_ACCESS_DENIED
);
2060 smbcli_close(cli
->tree
, fnum
);
2062 smbcli_unlink(cli
->tree
, fname
);
2064 smbcli_deltree(cli
->tree
, BASEDIR
);
2069 basic testing of streams calls
2071 struct torture_suite
*torture_raw_streams(TALLOC_CTX
*tctx
)
2073 struct torture_suite
*suite
= torture_suite_create(tctx
, "streams");
2075 torture_suite_add_1smb_test(suite
, "dir", test_stream_dir
);
2076 torture_suite_add_1smb_test(suite
, "io", test_stream_io
);
2077 torture_suite_add_1smb_test(suite
, "sharemodes", test_stream_sharemodes
);
2078 torture_suite_add_1smb_test(suite
, "delete", test_stream_delete
);
2079 torture_suite_add_1smb_test(suite
, "names", test_stream_names
);
2080 torture_suite_add_1smb_test(suite
, "names2", test_stream_names2
);
2081 torture_suite_add_1smb_test(suite
, "rename", test_stream_rename
);
2082 torture_suite_add_1smb_test(suite
, "rename2", test_stream_rename2
);
2083 torture_suite_add_1smb_test(suite
, "rename3", test_stream_rename3
);
2084 torture_suite_add_1smb_test(suite
, "createdisp",
2085 test_stream_create_disposition
);
2086 torture_suite_add_1smb_test(suite
, "attr", test_stream_attributes
);
2087 torture_suite_add_1smb_test(suite
, "sumtab", test_stream_summary_tab
);
2088 torture_suite_add_1smb_test(suite
, "perms", test_stream_permissions
);
2091 torture_suite_add_1smb_test(suite
, "LARGESTREAMINFO",
2092 test_stream_large_streaminfo
);