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
;
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");
696 printf("(%s) testing stream names\n", __location__
);
697 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
698 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
699 io
.ntcreatex
.in
.flags
= 0;
700 io
.ntcreatex
.in
.access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
701 io
.ntcreatex
.in
.create_options
= 0;
702 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
703 io
.ntcreatex
.in
.share_access
=
704 NTCREATEX_SHARE_ACCESS_READ
|
705 NTCREATEX_SHARE_ACCESS_WRITE
;
706 io
.ntcreatex
.in
.alloc_size
= 0;
707 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
708 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
709 io
.ntcreatex
.in
.security_flags
= 0;
710 io
.ntcreatex
.in
.fname
= fname
;
712 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
713 CHECK_STATUS(status
, NT_STATUS_OK
);
714 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
716 torture_comment(tctx
, "Adding two EAs to base file\n");
718 sinfo
.generic
.level
= RAW_SFILEINFO_EA_SET
;
719 sinfo
.generic
.in
.file
.fnum
= fnum1
;
720 sinfo
.ea_set
.in
.num_eas
= 2;
721 sinfo
.ea_set
.in
.eas
= talloc_array(tctx
, struct ea_struct
, 2);
722 sinfo
.ea_set
.in
.eas
[0].flags
= 0;
723 sinfo
.ea_set
.in
.eas
[0].name
.s
= "EAONE";
724 sinfo
.ea_set
.in
.eas
[0].value
= data_blob_string_const("VALUE1");
725 sinfo
.ea_set
.in
.eas
[1].flags
= 0;
726 sinfo
.ea_set
.in
.eas
[1].name
.s
= "SECONDEA";
727 sinfo
.ea_set
.in
.eas
[1].value
= data_blob_string_const("ValueTwo");
729 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
730 CHECK_STATUS(status
, NT_STATUS_OK
);
733 * Make sure the create time of the streams are different from the
737 smbcli_close(cli
->tree
, fnum1
);
739 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
740 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
741 io
.ntcreatex
.in
.flags
= 0;
742 io
.ntcreatex
.in
.access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
743 io
.ntcreatex
.in
.create_options
= 0;
744 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
745 io
.ntcreatex
.in
.share_access
=
746 NTCREATEX_SHARE_ACCESS_READ
|
747 NTCREATEX_SHARE_ACCESS_WRITE
;
748 io
.ntcreatex
.in
.alloc_size
= 0;
749 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
750 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
751 io
.ntcreatex
.in
.security_flags
= 0;
752 io
.ntcreatex
.in
.fname
= sname1
;
754 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
755 CHECK_STATUS(status
, NT_STATUS_OK
);
756 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
758 torture_comment(tctx
, "Adding one EAs to first stream file\n");
760 sinfo
.generic
.level
= RAW_SFILEINFO_EA_SET
;
761 sinfo
.generic
.in
.file
.fnum
= fnum1
;
762 sinfo
.ea_set
.in
.num_eas
= 1;
763 sinfo
.ea_set
.in
.eas
= talloc_array(tctx
, struct ea_struct
, 1);
764 sinfo
.ea_set
.in
.eas
[0].flags
= 0;
765 sinfo
.ea_set
.in
.eas
[0].name
.s
= "STREAMEA";
766 sinfo
.ea_set
.in
.eas
[0].value
= data_blob_string_const("EA_VALUE1");
768 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
769 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
771 status
= torture_check_ea(cli
, sname1
, "STREAMEA", "EA_VALUE1");
772 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
775 info
.generic
.level
= RAW_FILEINFO_ALL_EAS
;
776 info
.all_eas
.in
.file
.path
= sname1
;
778 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &info
);
779 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
782 * A different stream does not give a sharing violation
785 io
.ntcreatex
.in
.fname
= sname2
;
786 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
787 CHECK_STATUS(status
, NT_STATUS_OK
);
788 fnum2
= io
.ntcreatex
.out
.file
.fnum
;
791 * ... whereas the same stream does with unchanged access/share_access
795 io
.ntcreatex
.in
.fname
= sname1
;
796 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_SUPERSEDE
;
797 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
798 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
800 io
.ntcreatex
.in
.fname
= sname1b
;
801 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
802 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
804 io
.ntcreatex
.in
.fname
= sname1c
;
805 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
806 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
)) {
807 /* w2k returns INVALID_PARAMETER */
808 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
810 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
813 io
.ntcreatex
.in
.fname
= sname1d
;
814 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
815 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
)) {
816 /* w2k returns INVALID_PARAMETER */
817 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
819 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
822 io
.ntcreatex
.in
.fname
= sname2
;
823 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
824 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
826 io
.ntcreatex
.in
.fname
= snamew
;
827 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
828 CHECK_STATUS(status
, NT_STATUS_OK
);
829 fnum3
= io
.ntcreatex
.out
.file
.fnum
;
831 io
.ntcreatex
.in
.fname
= snamew2
;
832 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
833 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
835 ret
&= check_stream_list(tctx
, cli
, fname
, 4, four
);
837 smbcli_close(cli
->tree
, fnum1
);
838 smbcli_close(cli
->tree
, fnum2
);
839 smbcli_close(cli
->tree
, fnum3
);
841 finfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
842 finfo
.generic
.in
.file
.path
= fname
;
843 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
844 CHECK_STATUS(status
, NT_STATUS_OK
);
846 ret
&= check_stream_list(tctx
, cli
, fname
, 4, four
);
848 for (i
=0; i
< 4; i
++) {
850 uint64_t stream_size
;
851 char *path
= talloc_asprintf(tctx
, "%s%s",
854 char *rpath
= talloc_strdup(path
, path
);
855 char *p
= strrchr(rpath
, ':');
863 printf("(%s): i[%u][%s]\n", __location__
, i
, path
);
864 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
865 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_ATTRIBUTE
|
866 SEC_FILE_WRITE_ATTRIBUTE
|
868 io
.ntcreatex
.in
.fname
= path
;
869 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
870 CHECK_STATUS(status
, NT_STATUS_OK
);
871 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
873 finfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
874 finfo
.generic
.in
.file
.path
= fname
;
875 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
876 CHECK_STATUS(status
, NT_STATUS_OK
);
878 stinfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
879 stinfo
.generic
.in
.file
.fnum
= fnum1
;
880 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &stinfo
);
881 CHECK_STATUS(status
, NT_STATUS_OK
);
882 if (!torture_setting_bool(tctx
, "samba3", false)) {
883 CHECK_NTTIME(stinfo
.all_info
.out
.create_time
,
884 finfo
.all_info
.out
.create_time
);
885 CHECK_NTTIME(stinfo
.all_info
.out
.access_time
,
886 finfo
.all_info
.out
.access_time
);
887 CHECK_NTTIME(stinfo
.all_info
.out
.write_time
,
888 finfo
.all_info
.out
.write_time
);
889 CHECK_NTTIME(stinfo
.all_info
.out
.change_time
,
890 finfo
.all_info
.out
.change_time
);
892 CHECK_VALUE(stinfo
.all_info
.out
.attrib
,
893 finfo
.all_info
.out
.attrib
);
894 CHECK_VALUE(stinfo
.all_info
.out
.size
,
895 finfo
.all_info
.out
.size
);
896 CHECK_VALUE(stinfo
.all_info
.out
.delete_pending
,
897 finfo
.all_info
.out
.delete_pending
);
898 CHECK_VALUE(stinfo
.all_info
.out
.directory
,
899 finfo
.all_info
.out
.directory
);
900 CHECK_VALUE(stinfo
.all_info
.out
.ea_size
,
901 finfo
.all_info
.out
.ea_size
);
903 stinfo
.generic
.level
= RAW_FILEINFO_NAME_INFO
;
904 stinfo
.generic
.in
.file
.fnum
= fnum1
;
905 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &stinfo
);
906 CHECK_STATUS(status
, NT_STATUS_OK
);
907 if (!torture_setting_bool(tctx
, "samba3", false)) {
908 CHECK_STR(stinfo
.name_info
.out
.fname
.s
, rpath
);
911 write_time
= finfo
.all_info
.out
.write_time
;
912 write_time
+= i
*1000000;
913 write_time
/= 1000000;
914 write_time
*= 1000000;
917 sinfo
.basic_info
.level
= RAW_SFILEINFO_BASIC_INFO
;
918 sinfo
.basic_info
.in
.file
.fnum
= fnum1
;
919 sinfo
.basic_info
.in
.write_time
= write_time
;
920 sinfo
.basic_info
.in
.attrib
= stinfo
.all_info
.out
.attrib
;
921 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
922 CHECK_STATUS(status
, NT_STATUS_OK
);
924 stream_size
= i
*8192;
927 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFO
;
928 sinfo
.end_of_file_info
.in
.file
.fnum
= fnum1
;
929 sinfo
.end_of_file_info
.in
.size
= stream_size
;
930 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
931 CHECK_STATUS(status
, NT_STATUS_OK
);
933 stinfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
934 stinfo
.generic
.in
.file
.fnum
= fnum1
;
935 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &stinfo
);
936 CHECK_STATUS(status
, NT_STATUS_OK
);
937 if (!torture_setting_bool(tctx
, "samba3", false)) {
938 CHECK_NTTIME(stinfo
.all_info
.out
.write_time
,
940 CHECK_VALUE(stinfo
.all_info
.out
.attrib
,
941 finfo
.all_info
.out
.attrib
);
943 CHECK_VALUE(stinfo
.all_info
.out
.size
,
945 CHECK_VALUE(stinfo
.all_info
.out
.delete_pending
,
946 finfo
.all_info
.out
.delete_pending
);
947 CHECK_VALUE(stinfo
.all_info
.out
.directory
,
948 finfo
.all_info
.out
.directory
);
949 CHECK_VALUE(stinfo
.all_info
.out
.ea_size
,
950 finfo
.all_info
.out
.ea_size
);
952 ret
&= check_stream_list(tctx
, cli
, fname
, 4, four
);
954 smbcli_close(cli
->tree
, fnum1
);
958 printf("(%s): testing stream renames\n", __location__
);
959 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
960 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_ATTRIBUTE
|
961 SEC_FILE_WRITE_ATTRIBUTE
|
963 io
.ntcreatex
.in
.fname
= snamer1
;
964 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
965 CHECK_STATUS(status
, NT_STATUS_OK
);
966 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
968 ret
&= check_stream_list(tctx
, cli
, fname
, 5, five1
);
971 sinfo
.rename_information
.level
= RAW_SFILEINFO_RENAME_INFORMATION
;
972 sinfo
.rename_information
.in
.file
.fnum
= fnum1
;
973 sinfo
.rename_information
.in
.overwrite
= true;
974 sinfo
.rename_information
.in
.root_fid
= 0;
975 sinfo
.rename_information
.in
.new_name
= ":AfterRename:$DATA";
976 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
977 CHECK_STATUS(status
, NT_STATUS_OK
);
979 ret
&= check_stream_list(tctx
, cli
, fname
, 5, five2
);
982 sinfo
.rename_information
.level
= RAW_SFILEINFO_RENAME_INFORMATION
;
983 sinfo
.rename_information
.in
.file
.fnum
= fnum1
;
984 sinfo
.rename_information
.in
.overwrite
= false;
985 sinfo
.rename_information
.in
.root_fid
= 0;
986 sinfo
.rename_information
.in
.new_name
= ":MStream Two:$DATA";
987 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
988 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_COLLISION
);
990 ret
&= check_stream_list(tctx
, cli
, fname
, 5, five2
);
993 sinfo
.rename_information
.level
= RAW_SFILEINFO_RENAME_INFORMATION
;
994 sinfo
.rename_information
.in
.file
.fnum
= fnum1
;
995 sinfo
.rename_information
.in
.overwrite
= true;
996 sinfo
.rename_information
.in
.root_fid
= 0;
997 sinfo
.rename_information
.in
.new_name
= ":MStream Two:$DATA";
998 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
999 if (torture_setting_bool(tctx
, "samba4", false) ||
1000 torture_setting_bool(tctx
, "samba3", false)) {
1001 /* why should this rename be considered invalid?? */
1002 CHECK_STATUS(status
, NT_STATUS_OK
);
1003 ret
&= check_stream_list(tctx
, cli
, fname
, 4, four
);
1005 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1006 ret
&= check_stream_list(tctx
, cli
, fname
, 5, five2
);
1010 /* TODO: we need to test more rename combinations */
1013 if (fnum1
!= -1) smbcli_close(cli
->tree
, fnum1
);
1014 if (fnum2
!= -1) smbcli_close(cli
->tree
, fnum2
);
1015 if (fnum3
!= -1) smbcli_close(cli
->tree
, fnum3
);
1016 status
= smbcli_unlink(cli
->tree
, fname
);
1017 smbcli_deltree(cli
->tree
, BASEDIR
);
1024 static bool test_stream_names2(struct torture_context
*tctx
,
1025 struct smbcli_state
*cli
)
1029 const char *fname
= BASEDIR
"\\stream_names2.txt";
1034 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1036 printf("(%s) testing stream names\n", __location__
);
1037 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1038 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1039 io
.ntcreatex
.in
.flags
= 0;
1040 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
1041 io
.ntcreatex
.in
.create_options
= 0;
1042 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1043 io
.ntcreatex
.in
.share_access
= 0;
1044 io
.ntcreatex
.in
.alloc_size
= 0;
1045 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1046 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1047 io
.ntcreatex
.in
.security_flags
= 0;
1048 io
.ntcreatex
.in
.fname
= fname
;
1049 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1050 CHECK_STATUS(status
, NT_STATUS_OK
);
1051 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
1053 for (i
=0x01; i
< 0x7F; i
++) {
1054 char *path
= talloc_asprintf(tctx
, "%s:Stream%c0x%02X:$DATA",
1062 expected
= NT_STATUS_OBJECT_NAME_INVALID
;
1065 expected
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1070 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1071 io
.ntcreatex
.in
.fname
= path
;
1072 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1073 if (!NT_STATUS_EQUAL(status
, expected
)) {
1074 printf("(%s) %s:Stream%c0x%02X:$DATA%s => expected[%s]\n",
1075 __location__
, fname
, isprint(i
)?(char)i
:' ', i
,
1076 isprint(i
)?"":" (not printable)",
1077 nt_errstr(expected
));
1079 CHECK_STATUS(status
, expected
);
1085 if (fnum1
!= -1) smbcli_close(cli
->tree
, fnum1
);
1086 status
= smbcli_unlink(cli
->tree
, fname
);
1087 smbcli_deltree(cli
->tree
, BASEDIR
);
1091 #define CHECK_CALL_FNUM(call, rightstatus) do { \
1092 sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
1093 sfinfo.generic.in.file.fnum = fnum; \
1094 status = smb_raw_setfileinfo(cli->tree, &sfinfo); \
1095 if (!NT_STATUS_EQUAL(status, rightstatus)) { \
1096 printf("(%s) %s - %s (should be %s)\n", __location__, #call, \
1097 nt_errstr(status), nt_errstr(rightstatus)); \
1100 finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \
1101 finfo1.generic.in.file.fnum = fnum; \
1102 status2 = smb_raw_fileinfo(cli->tree, tctx, &finfo1); \
1103 if (!NT_STATUS_IS_OK(status2)) { \
1104 printf("(%s) %s pathinfo - %s\n", __location__, #call, nt_errstr(status)); \
1111 static bool test_stream_rename(struct torture_context
*tctx
,
1112 struct smbcli_state
*cli
)
1114 NTSTATUS status
, status2
;
1116 const char *fname
= BASEDIR
"\\stream_rename.txt";
1117 const char *sname1
, *sname2
;
1118 union smb_fileinfo finfo1
;
1119 union smb_setfileinfo sfinfo
;
1123 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1125 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "Stream One");
1126 sname2
= talloc_asprintf(tctx
, "%s:%s:$DaTa", fname
, "Second Stream");
1128 printf("(%s) testing stream renames\n", __location__
);
1129 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1130 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1131 io
.ntcreatex
.in
.flags
= 0;
1132 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_ATTRIBUTE
|
1133 SEC_FILE_WRITE_ATTRIBUTE
|
1134 SEC_RIGHTS_FILE_ALL
;
1135 io
.ntcreatex
.in
.create_options
= 0;
1136 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1137 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
| NTCREATEX_SHARE_ACCESS_WRITE
| NTCREATEX_SHARE_ACCESS_DELETE
;
1138 io
.ntcreatex
.in
.alloc_size
= 0;
1139 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1140 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1141 io
.ntcreatex
.in
.security_flags
= 0;
1142 io
.ntcreatex
.in
.fname
= sname1
;
1144 /* Create two streams. */
1145 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1146 CHECK_STATUS(status
, NT_STATUS_OK
);
1147 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1148 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1150 io
.ntcreatex
.in
.fname
= sname2
;
1151 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1152 CHECK_STATUS(status
, NT_STATUS_OK
);
1153 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1155 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1158 * Open the second stream.
1161 io
.ntcreatex
.in
.access_mask
= SEC_STD_DELETE
;
1162 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
1163 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1164 CHECK_STATUS(status
, NT_STATUS_OK
);
1165 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1168 * Now rename the second stream onto the first.
1171 ZERO_STRUCT(sfinfo
);
1173 sfinfo
.rename_information
.in
.overwrite
= 1;
1174 sfinfo
.rename_information
.in
.root_fid
= 0;
1175 sfinfo
.rename_information
.in
.new_name
= ":Stream One";
1176 CHECK_CALL_FNUM(RENAME_INFORMATION
, NT_STATUS_OK
);
1179 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1180 status
= smbcli_unlink(cli
->tree
, fname
);
1181 smbcli_deltree(cli
->tree
, BASEDIR
);
1185 static bool test_stream_rename2(struct torture_context
*tctx
,
1186 struct smbcli_state
*cli
)
1190 const char *fname1
= BASEDIR
"\\stream.txt";
1191 const char *fname2
= BASEDIR
"\\stream2.txt";
1192 const char *stream_name1
= ":Stream One:$DATA";
1193 const char *stream_name2
= ":Stream Two:$DATA";
1194 const char *stream_name_default
= "::$DATA";
1199 union smb_setfileinfo sinfo
;
1200 union smb_rename rio
;
1202 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1204 sname1
= talloc_asprintf(tctx
, "%s:%s", fname1
, "Stream One");
1205 sname2
= talloc_asprintf(tctx
, "%s:%s", fname1
, "Stream Two");
1207 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1208 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1209 io
.ntcreatex
.in
.flags
= 0;
1210 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1211 SEC_STD_DELETE
|SEC_FILE_APPEND_DATA
|SEC_STD_READ_CONTROL
);
1212 io
.ntcreatex
.in
.create_options
= 0;
1213 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1214 io
.ntcreatex
.in
.share_access
= (NTCREATEX_SHARE_ACCESS_READ
|
1215 NTCREATEX_SHARE_ACCESS_WRITE
|
1216 NTCREATEX_SHARE_ACCESS_DELETE
);
1217 io
.ntcreatex
.in
.alloc_size
= 0;
1218 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1219 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1220 io
.ntcreatex
.in
.security_flags
= 0;
1221 io
.ntcreatex
.in
.fname
= sname1
;
1223 /* Open/create new stream. */
1224 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1225 CHECK_STATUS(status
, NT_STATUS_OK
);
1227 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1230 * Check raw rename with <base>:<stream>.
1232 printf("(%s) Checking NTRENAME of a stream using <base>:<stream>\n",
1234 rio
.generic
.level
= RAW_RENAME_NTRENAME
;
1235 rio
.ntrename
.in
.old_name
= sname1
;
1236 rio
.ntrename
.in
.new_name
= sname2
;
1237 rio
.ntrename
.in
.attrib
= 0;
1238 rio
.ntrename
.in
.cluster_size
= 0;
1239 rio
.ntrename
.in
.flags
= RENAME_FLAG_RENAME
;
1240 status
= smb_raw_rename(cli
->tree
, &rio
);
1241 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1244 * Check raw rename to the default stream using :<stream>.
1246 printf("(%s) Checking NTRENAME to default stream using :<stream>\n",
1248 rio
.ntrename
.in
.new_name
= stream_name_default
;
1249 status
= smb_raw_rename(cli
->tree
, &rio
);
1250 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_COLLISION
);
1253 * Check raw rename using :<stream>.
1255 printf("(%s) Checking NTRENAME of a stream using :<stream>\n",
1257 rio
.ntrename
.in
.new_name
= stream_name2
;
1258 status
= smb_raw_rename(cli
->tree
, &rio
);
1259 CHECK_STATUS(status
, NT_STATUS_OK
);
1262 * Check raw rename of a stream to a file.
1264 printf("(%s) Checking NTRENAME of a stream to a file\n",
1266 rio
.ntrename
.in
.old_name
= sname2
;
1267 rio
.ntrename
.in
.new_name
= fname2
;
1268 status
= smb_raw_rename(cli
->tree
, &rio
);
1269 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1272 * Check raw rename of a file to a stream.
1274 printf("(%s) Checking NTRENAME of a file to a stream\n",
1277 /* Create the file. */
1278 io
.ntcreatex
.in
.fname
= fname2
;
1279 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1280 CHECK_STATUS(status
, NT_STATUS_OK
);
1281 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1283 /* Try the rename. */
1284 rio
.ntrename
.in
.old_name
= fname2
;
1285 rio
.ntrename
.in
.new_name
= sname1
;
1286 status
= smb_raw_rename(cli
->tree
, &rio
);
1287 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
1290 * Reopen the stream for trans2 renames.
1292 io
.ntcreatex
.in
.fname
= sname2
;
1293 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1294 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1295 CHECK_STATUS(status
, NT_STATUS_OK
);
1296 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1299 * Check trans2 rename of a stream using :<stream>.
1301 printf("(%s) Checking trans2 rename of a stream using :<stream>\n",
1304 sinfo
.rename_information
.level
= RAW_SFILEINFO_RENAME_INFORMATION
;
1305 sinfo
.rename_information
.in
.file
.fnum
= fnum
;
1306 sinfo
.rename_information
.in
.overwrite
= 1;
1307 sinfo
.rename_information
.in
.root_fid
= 0;
1308 sinfo
.rename_information
.in
.new_name
= stream_name1
;
1309 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
1310 CHECK_STATUS(status
, NT_STATUS_OK
);
1313 * Check trans2 rename of an overwriting stream using :<stream>.
1315 printf("(%s) Checking trans2 rename of an overwriting stream using "
1316 ":<stream>\n", __location__
);
1318 /* Create second stream. */
1319 io
.ntcreatex
.in
.fname
= sname2
;
1320 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1321 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1322 CHECK_STATUS(status
, NT_STATUS_OK
);
1323 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1325 /* Rename the first stream onto the second. */
1326 sinfo
.rename_information
.in
.file
.fnum
= fnum
;
1327 sinfo
.rename_information
.in
.new_name
= stream_name2
;
1328 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
1329 CHECK_STATUS(status
, NT_STATUS_OK
);
1331 smbcli_close(cli
->tree
, fnum
);
1334 * Reopen the stream with the new name.
1336 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1337 io
.ntcreatex
.in
.fname
= sname2
;
1338 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1339 CHECK_STATUS(status
, NT_STATUS_OK
);
1340 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1343 * Check trans2 rename of a stream using <base>:<stream>.
1345 printf("(%s) Checking trans2 rename of a stream using "
1346 "<base>:<stream>\n", __location__
);
1347 sinfo
.rename_information
.in
.file
.fnum
= fnum
;
1348 sinfo
.rename_information
.in
.new_name
= sname1
;
1349 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
1350 CHECK_STATUS(status
, NT_STATUS_NOT_SUPPORTED
);
1353 * Samba3 doesn't currently support renaming a stream to the default
1354 * stream. This test does pass on windows.
1356 if (torture_setting_bool(tctx
, "samba3", false) ||
1357 torture_setting_bool(tctx
, "samba4", false)) {
1362 * Check trans2 rename to the default stream using :<stream>.
1364 printf("(%s) Checking trans2 rename to defaualt stream using "
1365 ":<stream>\n", __location__
);
1366 sinfo
.rename_information
.in
.file
.fnum
= fnum
;
1367 sinfo
.rename_information
.in
.new_name
= stream_name_default
;
1368 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
1369 CHECK_STATUS(status
, NT_STATUS_OK
);
1371 smbcli_close(cli
->tree
, fnum
);
1374 smbcli_close(cli
->tree
, fnum
);
1375 status
= smbcli_unlink(cli
->tree
, fname1
);
1376 status
= smbcli_unlink(cli
->tree
, fname2
);
1377 smbcli_deltree(cli
->tree
, BASEDIR
);
1384 static bool test_stream_rename3(struct torture_context
*tctx
,
1385 struct smbcli_state
*cli
)
1387 NTSTATUS status
, status2
;
1389 const char *fname
= BASEDIR
"\\stream_rename.txt";
1390 const char *sname1
, *sname2
;
1391 union smb_fileinfo finfo1
;
1392 union smb_setfileinfo sfinfo
;
1397 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1399 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "MStream Two:$DATA");
1400 sname2
= talloc_asprintf(tctx
, "%s:%s:$DaTa", fname
, "Second Stream");
1402 printf("(%s) testing stream renames\n", __location__
);
1403 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1404 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1405 io
.ntcreatex
.in
.flags
= 0;
1406 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_ATTRIBUTE
|
1407 SEC_FILE_WRITE_ATTRIBUTE
|
1408 SEC_RIGHTS_FILE_ALL
;
1409 io
.ntcreatex
.in
.create_options
= 0;
1410 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1411 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
1412 NTCREATEX_SHARE_ACCESS_WRITE
| NTCREATEX_SHARE_ACCESS_DELETE
;
1413 io
.ntcreatex
.in
.alloc_size
= 0;
1414 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1415 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1416 io
.ntcreatex
.in
.security_flags
= 0;
1417 io
.ntcreatex
.in
.fname
= sname1
;
1419 /* Create two streams. */
1420 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1421 CHECK_STATUS(status
, NT_STATUS_OK
);
1422 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1423 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1425 io
.ntcreatex
.in
.fname
= sname2
;
1426 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1427 CHECK_STATUS(status
, NT_STATUS_OK
);
1428 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1430 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1432 /* open the second stream. */
1433 io
.ntcreatex
.in
.access_mask
= SEC_STD_DELETE
;
1434 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
1435 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1436 CHECK_STATUS(status
, NT_STATUS_OK
);
1437 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1439 /* Keep a handle to the first stream open. */
1440 io
.ntcreatex
.in
.fname
= sname1
;
1441 io
.ntcreatex
.in
.access_mask
= SEC_STD_DELETE
;
1442 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
1443 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1444 CHECK_STATUS(status
, NT_STATUS_OK
);
1445 fnum2
= io
.ntcreatex
.out
.file
.fnum
;
1447 ZERO_STRUCT(sfinfo
);
1448 sfinfo
.rename_information
.in
.overwrite
= 1;
1449 sfinfo
.rename_information
.in
.root_fid
= 0;
1450 sfinfo
.rename_information
.in
.new_name
= ":MStream Two:$DATA";
1451 if (torture_setting_bool(tctx
, "samba4", false) ||
1452 torture_setting_bool(tctx
, "samba3", false)) {
1453 CHECK_CALL_FNUM(RENAME_INFORMATION
, NT_STATUS_OK
);
1455 CHECK_CALL_FNUM(RENAME_INFORMATION
,
1456 NT_STATUS_INVALID_PARAMETER
);
1461 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1462 if (fnum2
!= -1) smbcli_close(cli
->tree
, fnum2
);
1463 status
= smbcli_unlink(cli
->tree
, fname
);
1464 smbcli_deltree(cli
->tree
, BASEDIR
);
1468 static bool create_file_with_stream(struct torture_context
*tctx
,
1469 struct smbcli_state
*cli
,
1476 /* Create a file with a stream */
1477 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1478 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1479 io
.ntcreatex
.in
.flags
= 0;
1480 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1481 SEC_FILE_APPEND_DATA
|SEC_STD_READ_CONTROL
);
1482 io
.ntcreatex
.in
.create_options
= 0;
1483 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1484 io
.ntcreatex
.in
.share_access
= 0;
1485 io
.ntcreatex
.in
.alloc_size
= 0;
1486 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1487 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1488 io
.ntcreatex
.in
.security_flags
= 0;
1489 io
.ntcreatex
.in
.fname
= stream
;
1491 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1492 CHECK_STATUS(status
, NT_STATUS_OK
);
1495 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1499 /* Test how streams interact with create dispositions */
1500 static bool test_stream_create_disposition(struct torture_context
*tctx
,
1501 struct smbcli_state
*cli
)
1505 const char *fname
= BASEDIR
"\\stream.txt";
1506 const char *stream
= "Stream One:$DATA";
1507 const char *fname_stream
;
1508 const char *default_stream_name
= "::$DATA";
1509 const char *stream_list
[2];
1513 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1515 fname_stream
= talloc_asprintf(tctx
, "%s:%s", fname
, stream
);
1517 stream_list
[0] = talloc_asprintf(tctx
, ":%s", stream
);
1518 stream_list
[1] = default_stream_name
;
1520 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1524 /* Open the base file with OPEN */
1525 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1526 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1527 io
.ntcreatex
.in
.flags
= 0;
1528 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1529 SEC_FILE_APPEND_DATA
|SEC_STD_READ_CONTROL
);
1530 io
.ntcreatex
.in
.create_options
= 0;
1531 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1532 io
.ntcreatex
.in
.share_access
= 0;
1533 io
.ntcreatex
.in
.alloc_size
= 0;
1534 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1535 io
.ntcreatex
.in
.security_flags
= 0;
1536 io
.ntcreatex
.in
.fname
= fname
;
1539 * check ntcreatex open: sanity check
1541 printf("(%s) Checking ntcreatex disp: open\n", __location__
);
1542 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1543 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1544 CHECK_STATUS(status
, NT_STATUS_OK
);
1545 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1546 if (!check_stream_list(tctx
, cli
, fname
, 2, stream_list
)) {
1551 * check ntcreatex overwrite
1553 printf("(%s) Checking ntcreatex disp: overwrite\n", __location__
);
1554 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OVERWRITE
;
1555 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1556 CHECK_STATUS(status
, NT_STATUS_OK
);
1557 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1558 if (!check_stream_list(tctx
, cli
, fname
, 1, &default_stream_name
)) {
1563 * check ntcreatex overwrite_if
1565 printf("(%s) Checking ntcreatex disp: overwrite_if\n", __location__
);
1566 smbcli_unlink(cli
->tree
, fname
);
1567 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1571 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OVERWRITE_IF
;
1572 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1573 CHECK_STATUS(status
, NT_STATUS_OK
);
1574 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1575 if (!check_stream_list(tctx
, cli
, fname
, 1, &default_stream_name
)) {
1580 * check ntcreatex supersede
1582 printf("(%s) Checking ntcreatex disp: supersede\n", __location__
);
1583 smbcli_unlink(cli
->tree
, fname
);
1584 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1588 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_SUPERSEDE
;
1589 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1590 CHECK_STATUS(status
, NT_STATUS_OK
);
1591 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1592 if (!check_stream_list(tctx
, cli
, fname
, 1, &default_stream_name
)) {
1597 * check ntcreatex overwrite_if on a stream.
1599 printf("(%s) Checking ntcreatex disp: overwrite_if on stream\n",
1601 smbcli_unlink(cli
->tree
, fname
);
1602 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1606 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OVERWRITE_IF
;
1607 io
.ntcreatex
.in
.fname
= fname_stream
;
1608 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1609 CHECK_STATUS(status
, NT_STATUS_OK
);
1610 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1611 if (!check_stream_list(tctx
, cli
, fname
, 2, stream_list
)) {
1616 * check openx overwrite_if
1618 printf("(%s) Checking openx disp: overwrite_if\n", __location__
);
1619 smbcli_unlink(cli
->tree
, fname
);
1620 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1624 io
.openx
.level
= RAW_OPEN_OPENX
;
1625 io
.openx
.in
.flags
= OPENX_FLAGS_ADDITIONAL_INFO
;
1626 io
.openx
.in
.open_mode
= OPENX_MODE_ACCESS_RDWR
| OPEN_FLAGS_DENY_NONE
;
1627 io
.openx
.in
.search_attrs
= 0;
1628 io
.openx
.in
.file_attrs
= 0;
1629 io
.openx
.in
.write_time
= 0;
1630 io
.openx
.in
.size
= 1024*1024;
1631 io
.openx
.in
.timeout
= 0;
1632 io
.openx
.in
.fname
= fname
;
1634 io
.openx
.in
.open_func
= OPENX_OPEN_FUNC_TRUNC
| OPENX_OPEN_FUNC_CREATE
;
1635 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1636 CHECK_STATUS(status
, NT_STATUS_OK
);
1637 smbcli_close(cli
->tree
, io
.openx
.out
.file
.fnum
);
1638 if (!check_stream_list(tctx
, cli
, fname
, 1, &default_stream_name
)) {
1645 smbcli_close(cli
->tree
, fnum
);
1646 smbcli_unlink(cli
->tree
, fname
);
1647 smbcli_deltree(cli
->tree
, BASEDIR
);
1652 /* Test streaminfo with enough streams on a file to fill up the buffer. */
1653 static bool test_stream_large_streaminfo(struct torture_context
*tctx
,
1654 struct smbcli_state
*cli
)
1656 #define LONG_STREAM_SIZE 2
1658 const char *fname
= BASEDIR
"\\stream.txt";
1659 const char *fname_stream
;
1663 union smb_fileinfo finfo
;
1665 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1667 lstream_name
= talloc_array(tctx
, char, LONG_STREAM_SIZE
);
1669 for (i
= 0; i
< LONG_STREAM_SIZE
- 1; i
++) {
1670 lstream_name
[i
] = (char)('a' + i
%26);
1672 lstream_name
[LONG_STREAM_SIZE
- 1] = '\0';
1674 torture_comment(tctx
, "(%s) Creating a file with a lot of streams\n", __location__
);
1675 for (i
= 0; i
< 10000; i
++) {
1676 fname_stream
= talloc_asprintf(tctx
, "%s:%s%d", fname
,
1678 ret
= create_file_with_stream(tctx
, cli
, fname_stream
);
1684 finfo
.generic
.level
= RAW_FILEINFO_STREAM_INFO
;
1685 finfo
.generic
.in
.file
.path
= fname
;
1687 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1688 CHECK_STATUS(status
, STATUS_BUFFER_OVERFLOW
);
1691 smbcli_unlink(cli
->tree
, fname
);
1692 smbcli_deltree(cli
->tree
, BASEDIR
);
1697 /* Test the effect of setting attributes on a stream. */
1698 static bool test_stream_attributes(struct torture_context
*tctx
,
1699 struct smbcli_state
*cli
)
1704 const char *fname
= BASEDIR
"\\stream_attr.txt";
1705 const char *stream
= "Stream One:$DATA";
1706 const char *fname_stream
;
1708 union smb_fileinfo finfo
;
1709 union smb_setfileinfo sfinfo
;
1710 time_t basetime
= (time(NULL
) - 86400) & ~1;
1712 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1714 torture_comment(tctx
, "(%s) testing attribute setting on stream\n", __location__
);
1716 fname_stream
= talloc_asprintf(tctx
, "%s:%s", fname
, stream
);
1718 /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */
1719 ret
= create_file_with_stream(tctx
, cli
, fname_stream
);
1725 finfo
.generic
.level
= RAW_FILEINFO_BASIC_INFO
;
1726 finfo
.generic
.in
.file
.path
= fname
;
1727 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1728 CHECK_STATUS(status
, NT_STATUS_OK
);
1730 torture_assert_int_equal_goto(tctx
, finfo
.all_info
.out
.attrib
& ~FILE_ATTRIBUTE_NONINDEXED
, FILE_ATTRIBUTE_ARCHIVE
, ret
, done
, "attrib incorrect");
1732 /* Now open the stream name. */
1734 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1735 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1736 io
.ntcreatex
.in
.flags
= 0;
1737 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1738 SEC_FILE_APPEND_DATA
|SEC_STD_READ_CONTROL
|SEC_FILE_WRITE_ATTRIBUTE
);
1739 io
.ntcreatex
.in
.create_options
= 0;
1740 io
.ntcreatex
.in
.file_attr
= 0;
1741 io
.ntcreatex
.in
.share_access
= 0;
1742 io
.ntcreatex
.in
.alloc_size
= 0;
1743 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
1744 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1745 io
.ntcreatex
.in
.security_flags
= 0;
1746 io
.ntcreatex
.in
.fname
= fname_stream
;
1748 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1749 CHECK_STATUS(status
, NT_STATUS_OK
);
1751 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1753 /* Change the attributes + time on the stream fnum. */
1754 ZERO_STRUCT(sfinfo
);
1755 sfinfo
.basic_info
.in
.attrib
= FILE_ATTRIBUTE_READONLY
;
1756 unix_to_nt_time(&sfinfo
.basic_info
.in
.write_time
, basetime
);
1758 sfinfo
.generic
.level
= RAW_SFILEINFO_BASIC_INFORMATION
;
1759 sfinfo
.generic
.in
.file
.fnum
= fnum
;
1760 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
1761 torture_assert_ntstatus_equal_goto(tctx
, status
, NT_STATUS_OK
, ret
, done
, "smb_raw_setfileinfo failed");
1763 smbcli_close(cli
->tree
, fnum
);
1767 finfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
1768 finfo
.generic
.in
.file
.path
= fname
;
1769 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1770 torture_assert_ntstatus_equal_goto(tctx
, status
, NT_STATUS_OK
, ret
, done
, "smb_raw_pathinfo failed");
1772 torture_assert_int_equal_goto(tctx
, finfo
.all_info
.out
.attrib
& ~FILE_ATTRIBUTE_NONINDEXED
, FILE_ATTRIBUTE_READONLY
, ret
, done
, "attrib incorrect");
1774 torture_assert_int_equal_goto(tctx
, nt_time_to_unix(finfo
.all_info
.out
.write_time
), basetime
, ret
, done
, "time incorrect");
1779 smbcli_close(cli
->tree
, fnum
);
1781 smbcli_unlink(cli
->tree
, fname
);
1782 smbcli_deltree(cli
->tree
, BASEDIR
);
1787 * A rough approximation of how a windows client creates the streams for use
1788 * in the summary tab.
1790 static bool test_stream_summary_tab(struct torture_context
*tctx
,
1791 struct smbcli_state
*cli
)
1796 const char *fname
= BASEDIR
"\\stream_summary.txt";
1797 const char *stream
= ":\005SummaryInformation:$DATA";
1798 const char *fname_stream
= NULL
;
1799 const char *tmp_stream
= ":Updt_\005SummaryInformation:$DATA";
1800 const char *fname_tmp_stream
= NULL
;
1802 union smb_fileinfo finfo
;
1803 union smb_rename rio
;
1806 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1808 fname_stream
= talloc_asprintf(tctx
, "%s%s", fname
, stream
);
1809 fname_tmp_stream
= talloc_asprintf(tctx
, "%s%s", fname
,
1812 /* Create summary info stream */
1813 ret
= create_file_with_stream(tctx
, cli
, fname_stream
);
1818 /* Create summary info tmp update stream */
1819 ret
= create_file_with_stream(tctx
, cli
, fname_tmp_stream
);
1824 /* Open tmp stream and write to it */
1825 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1826 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1827 io
.ntcreatex
.in
.flags
= 0;
1828 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
;
1829 io
.ntcreatex
.in
.create_options
= 0;
1830 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1831 io
.ntcreatex
.in
.share_access
= 0;
1832 io
.ntcreatex
.in
.alloc_size
= 0;
1833 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1834 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1835 io
.ntcreatex
.in
.security_flags
= 0;
1836 io
.ntcreatex
.in
.fname
= fname_tmp_stream
;
1838 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1839 CHECK_STATUS(status
, NT_STATUS_OK
);
1840 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1842 retsize
= smbcli_write(cli
->tree
, fnum
, 0, "test data", 0, 9);
1843 CHECK_VALUE(retsize
, 9);
1845 /* close the tmp stream. */
1846 smbcli_close(cli
->tree
, fnum
);
1849 /* Delete the current stream */
1850 smbcli_unlink(cli
->tree
, fname_stream
);
1852 /* Do the rename. */
1853 rio
.generic
.level
= RAW_RENAME_RENAME
;
1854 rio
.rename
.in
.pattern1
= fname_tmp_stream
;
1855 rio
.rename
.in
.pattern2
= stream
;
1856 rio
.rename
.in
.attrib
= FILE_ATTRIBUTE_SYSTEM
|
1857 FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_DIRECTORY
;
1858 status
= smb_raw_rename(cli
->tree
, &rio
);
1859 CHECK_STATUS(status
, NT_STATUS_OK
);
1861 /* Try to open the tmp stream that we just renamed away. */
1862 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1863 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1865 /* Query the base file to make sure it's still there. */
1866 finfo
.generic
.level
= RAW_FILEINFO_BASIC_INFO
;
1867 finfo
.generic
.in
.file
.path
= fname
;
1869 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1870 CHECK_STATUS(status
, NT_STATUS_OK
);
1875 smbcli_close(cli
->tree
, fnum
);
1877 smbcli_unlink(cli
->tree
, fname
);
1879 smbcli_deltree(cli
->tree
, BASEDIR
);
1883 /* Test how streams interact with base file permissions */
1884 /* Regression test for bug:
1885 https://bugzilla.samba.org/show_bug.cgi?id=10229
1886 bug #10229 - No access check verification on stream files.
1888 static bool test_stream_permissions(struct torture_context
*tctx
,
1889 struct smbcli_state
*cli
)
1894 const char *fname
= BASEDIR
"\\stream_permissions.txt";
1895 const char *stream
= "Stream One:$DATA";
1896 const char *fname_stream
;
1897 union smb_fileinfo finfo
;
1898 union smb_setfileinfo sfinfo
;
1900 union smb_fileinfo q
;
1901 union smb_setfileinfo set
;
1902 struct security_ace ace
;
1903 struct security_descriptor
*sd
;
1905 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
),
1906 "Failed to setup up test directory: " BASEDIR
);
1908 torture_comment(tctx
, "(%s) testing permissions on streams\n", __location__
);
1910 fname_stream
= talloc_asprintf(tctx
, "%s:%s", fname
, stream
);
1912 /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */
1913 ret
= create_file_with_stream(tctx
, cli
, fname_stream
);
1919 finfo
.generic
.level
= RAW_FILEINFO_BASIC_INFO
;
1920 finfo
.generic
.in
.file
.path
= fname
;
1921 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1922 CHECK_STATUS(status
, NT_STATUS_OK
);
1924 torture_assert_int_equal_goto(tctx
,
1925 finfo
.all_info
.out
.attrib
& ~FILE_ATTRIBUTE_NONINDEXED
,
1926 FILE_ATTRIBUTE_ARCHIVE
, ret
, done
, "attrib incorrect");
1928 /* Change the attributes on the base file name. */
1929 ZERO_STRUCT(sfinfo
);
1930 sfinfo
.generic
.level
= RAW_SFILEINFO_SETATTR
;
1931 sfinfo
.generic
.in
.file
.path
= fname
;
1932 sfinfo
.setattr
.in
.attrib
= FILE_ATTRIBUTE_READONLY
;
1934 status
= smb_raw_setpathinfo(cli
->tree
, &sfinfo
);
1935 CHECK_STATUS(status
, NT_STATUS_OK
);
1937 /* Try and open the stream name for WRITE_DATA. Should
1938 fail with ACCESS_DENIED. */
1941 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1942 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1943 io
.ntcreatex
.in
.flags
= 0;
1944 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
1945 io
.ntcreatex
.in
.create_options
= 0;
1946 io
.ntcreatex
.in
.file_attr
= 0;
1947 io
.ntcreatex
.in
.share_access
= 0;
1948 io
.ntcreatex
.in
.alloc_size
= 0;
1949 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1950 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1951 io
.ntcreatex
.in
.security_flags
= 0;
1952 io
.ntcreatex
.in
.fname
= fname_stream
;
1954 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1955 CHECK_STATUS(status
, NT_STATUS_ACCESS_DENIED
);
1957 /* Change the attributes on the base file back. */
1958 ZERO_STRUCT(sfinfo
);
1959 sfinfo
.generic
.level
= RAW_SFILEINFO_SETATTR
;
1960 sfinfo
.generic
.in
.file
.path
= fname
;
1961 sfinfo
.setattr
.in
.attrib
= 0;
1963 status
= smb_raw_setpathinfo(cli
->tree
, &sfinfo
);
1964 CHECK_STATUS(status
, NT_STATUS_OK
);
1966 /* Re-open the file name. */
1969 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1970 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1971 io
.ntcreatex
.in
.flags
= 0;
1972 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1973 SEC_STD_READ_CONTROL
|SEC_STD_WRITE_DAC
|
1974 SEC_FILE_WRITE_ATTRIBUTE
);
1975 io
.ntcreatex
.in
.create_options
= 0;
1976 io
.ntcreatex
.in
.file_attr
= 0;
1977 io
.ntcreatex
.in
.share_access
= 0;
1978 io
.ntcreatex
.in
.alloc_size
= 0;
1979 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1980 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1981 io
.ntcreatex
.in
.security_flags
= 0;
1982 io
.ntcreatex
.in
.fname
= fname
;
1984 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1985 CHECK_STATUS(status
, NT_STATUS_OK
);
1987 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1989 /* Get the existing security descriptor. */
1991 q
.query_secdesc
.level
= RAW_FILEINFO_SEC_DESC
;
1992 q
.query_secdesc
.in
.file
.fnum
= fnum
;
1993 q
.query_secdesc
.in
.secinfo_flags
=
1997 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &q
);
1998 CHECK_STATUS(status
, NT_STATUS_OK
);
1999 sd
= q
.query_secdesc
.out
.sd
;
2001 /* Now add a DENY WRITE security descriptor for Everyone. */
2002 torture_comment(tctx
, "add a new ACE to the DACL\n");
2004 ace
.type
= SEC_ACE_TYPE_ACCESS_DENIED
;
2006 ace
.access_mask
= SEC_FILE_WRITE_DATA
;
2007 ace
.trustee
= *dom_sid_parse_talloc(tctx
, SID_WORLD
);
2009 status
= security_descriptor_dacl_add(sd
, &ace
);
2010 CHECK_STATUS(status
, NT_STATUS_OK
);
2012 /* security_descriptor_dacl_add adds to the *end* of
2013 the ace array, we need it at the start. Swap.. */
2014 ace
= sd
->dacl
->aces
[0];
2015 sd
->dacl
->aces
[0] = sd
->dacl
->aces
[sd
->dacl
->num_aces
-1];
2016 sd
->dacl
->aces
[sd
->dacl
->num_aces
-1] = ace
;
2019 set
.set_secdesc
.level
= RAW_SFILEINFO_SEC_DESC
;
2020 set
.set_secdesc
.in
.file
.fnum
= fnum
;
2021 set
.set_secdesc
.in
.secinfo_flags
= SECINFO_DACL
;
2022 set
.set_secdesc
.in
.sd
= sd
;
2024 status
= smb_raw_setfileinfo(cli
->tree
, &set
);
2025 CHECK_STATUS(status
, NT_STATUS_OK
);
2027 smbcli_close(cli
->tree
, fnum
);
2030 /* Try and open the stream name for WRITE_DATA. Should
2031 fail with ACCESS_DENIED. */
2034 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
2035 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
2036 io
.ntcreatex
.in
.flags
= 0;
2037 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
2038 io
.ntcreatex
.in
.create_options
= 0;
2039 io
.ntcreatex
.in
.file_attr
= 0;
2040 io
.ntcreatex
.in
.share_access
= 0;
2041 io
.ntcreatex
.in
.alloc_size
= 0;
2042 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
2043 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
2044 io
.ntcreatex
.in
.security_flags
= 0;
2045 io
.ntcreatex
.in
.fname
= fname_stream
;
2047 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
2048 CHECK_STATUS(status
, NT_STATUS_ACCESS_DENIED
);
2053 smbcli_close(cli
->tree
, fnum
);
2055 smbcli_unlink(cli
->tree
, fname
);
2057 smbcli_deltree(cli
->tree
, BASEDIR
);
2062 basic testing of streams calls
2064 struct torture_suite
*torture_raw_streams(TALLOC_CTX
*tctx
)
2066 struct torture_suite
*suite
= torture_suite_create(tctx
, "streams");
2068 torture_suite_add_1smb_test(suite
, "dir", test_stream_dir
);
2069 torture_suite_add_1smb_test(suite
, "io", test_stream_io
);
2070 torture_suite_add_1smb_test(suite
, "sharemodes", test_stream_sharemodes
);
2071 torture_suite_add_1smb_test(suite
, "delete", test_stream_delete
);
2072 torture_suite_add_1smb_test(suite
, "names", test_stream_names
);
2073 torture_suite_add_1smb_test(suite
, "names2", test_stream_names2
);
2074 torture_suite_add_1smb_test(suite
, "rename", test_stream_rename
);
2075 torture_suite_add_1smb_test(suite
, "rename2", test_stream_rename2
);
2076 torture_suite_add_1smb_test(suite
, "rename3", test_stream_rename3
);
2077 torture_suite_add_1smb_test(suite
, "createdisp",
2078 test_stream_create_disposition
);
2079 torture_suite_add_1smb_test(suite
, "attr", test_stream_attributes
);
2080 torture_suite_add_1smb_test(suite
, "sumtab", test_stream_summary_tab
);
2081 torture_suite_add_1smb_test(suite
, "perms", test_stream_permissions
);
2084 torture_suite_add_1smb_test(suite
, "LARGESTREAMINFO",
2085 test_stream_large_streaminfo
);