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) \
46 torture_assert_str_equal(tctx,v,correct,"CHECK_STR")
49 check that a stream has the right contents
51 static bool check_stream(struct smbcli_state
*cli
, const char *location
,
53 const char *fname
, const char *sname
,
57 const char *full_name
;
61 full_name
= talloc_asprintf(mem_ctx
, "%s:%s", fname
, sname
);
63 fnum
= smbcli_open(cli
->tree
, full_name
, O_RDONLY
, DENY_NONE
);
67 printf("(%s) should have failed stream open of %s\n",
75 printf("(%s) Failed to open stream '%s' - %s\n",
76 location
, full_name
, smbcli_errstr(cli
->tree
));
80 buf
= talloc_array(mem_ctx
, uint8_t, strlen(value
)+11);
82 ret
= smbcli_read(cli
->tree
, fnum
, buf
, 0, strlen(value
)+11);
83 if (ret
!= strlen(value
)) {
84 printf("(%s) Failed to read %lu bytes from stream '%s' - got %d\n",
85 location
, (long)strlen(value
), full_name
, (int)ret
);
89 if (memcmp(buf
, value
, strlen(value
)) != 0) {
90 printf("(%s) Bad data in stream\n", location
);
94 smbcli_close(cli
->tree
, fnum
);
98 static int qsort_string(char * const *s1
, char * const *s2
)
100 return strcmp(*s1
, *s2
);
103 static int qsort_stream(const struct stream_struct
*s1
, const struct stream_struct
*s2
)
105 return strcmp(s1
->stream_name
.s
, s2
->stream_name
.s
);
108 static bool check_stream_list(struct torture_context
*tctx
,
109 struct smbcli_state
*cli
, const char *fname
,
110 int num_exp
, const char **exp
)
112 union smb_fileinfo finfo
;
115 TALLOC_CTX
*tmp_ctx
= talloc_new(cli
);
117 struct stream_struct
*stream_sort
;
121 finfo
.generic
.level
= RAW_FILEINFO_STREAM_INFO
;
122 finfo
.generic
.in
.file
.path
= fname
;
124 status
= smb_raw_pathinfo(cli
->tree
, tmp_ctx
, &finfo
);
125 CHECK_STATUS(status
, NT_STATUS_OK
);
127 CHECK_VALUE(finfo
.stream_info
.out
.num_streams
, num_exp
);
134 exp_sort
= (char **)talloc_memdup(tmp_ctx
, exp
, num_exp
* sizeof(*exp
));
136 if (exp_sort
== NULL
) {
140 TYPESAFE_QSORT(exp_sort
, num_exp
, qsort_string
);
142 stream_sort
= (struct stream_struct
*)talloc_memdup(tmp_ctx
,
143 finfo
.stream_info
.out
.streams
,
144 finfo
.stream_info
.out
.num_streams
*
145 sizeof(*stream_sort
));
147 if (stream_sort
== NULL
) {
151 TYPESAFE_QSORT(stream_sort
, finfo
.stream_info
.out
.num_streams
, qsort_stream
);
153 for (i
=0; i
<num_exp
; i
++) {
154 if (strcmp(exp_sort
[i
], stream_sort
[i
].stream_name
.s
) != 0) {
162 talloc_free(tmp_ctx
);
166 for (i
=0; i
<num_exp
; i
++) {
167 torture_comment(tctx
, "stream names '%s' '%s'\n",
168 exp_sort
[i
], stream_sort
[i
].stream_name
.s
);
170 CHECK_STR(stream_sort
[fail
].stream_name
.s
, exp_sort
[fail
]);
171 talloc_free(tmp_ctx
);
176 test behavior of streams on directories
178 static bool test_stream_dir(struct torture_context
*tctx
,
179 struct smbcli_state
*cli
)
183 const char *fname
= BASEDIR
"\\stream.txt";
186 const char *basedir_data
;
188 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
190 basedir_data
= talloc_asprintf(tctx
, "%s::$DATA", BASEDIR
);
191 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "Stream One");
193 printf("(%s) opening non-existent directory stream\n", __location__
);
194 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
195 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
196 io
.ntcreatex
.in
.flags
= 0;
197 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
198 io
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
;
199 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
200 io
.ntcreatex
.in
.share_access
= 0;
201 io
.ntcreatex
.in
.alloc_size
= 0;
202 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
203 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
204 io
.ntcreatex
.in
.security_flags
= 0;
205 io
.ntcreatex
.in
.fname
= sname1
;
206 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
207 CHECK_STATUS(status
, NT_STATUS_NOT_A_DIRECTORY
);
209 printf("(%s) opening basedir stream\n", __location__
);
210 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
211 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
212 io
.ntcreatex
.in
.flags
= 0;
213 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
214 io
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
;
215 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_DIRECTORY
;
216 io
.ntcreatex
.in
.share_access
= 0;
217 io
.ntcreatex
.in
.alloc_size
= 0;
218 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
219 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
220 io
.ntcreatex
.in
.security_flags
= 0;
221 io
.ntcreatex
.in
.fname
= basedir_data
;
222 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
223 CHECK_STATUS(status
, NT_STATUS_NOT_A_DIRECTORY
);
225 printf("(%s) opening basedir ::$DATA stream\n", __location__
);
226 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
227 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
228 io
.ntcreatex
.in
.flags
= 0x10;
229 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
230 io
.ntcreatex
.in
.create_options
= 0;
231 io
.ntcreatex
.in
.file_attr
= 0;
232 io
.ntcreatex
.in
.share_access
= 0;
233 io
.ntcreatex
.in
.alloc_size
= 0;
234 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
235 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
236 io
.ntcreatex
.in
.security_flags
= 0;
237 io
.ntcreatex
.in
.fname
= basedir_data
;
238 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
239 CHECK_STATUS(status
, NT_STATUS_FILE_IS_A_DIRECTORY
);
241 printf("(%s) list the streams on the basedir\n", __location__
);
242 ret
&= check_stream_list(tctx
, cli
, BASEDIR
, 0, NULL
);
244 smbcli_deltree(cli
->tree
, BASEDIR
);
249 test basic behavior of streams on directories
251 static bool test_stream_io(struct torture_context
*tctx
,
252 struct smbcli_state
*cli
)
256 const char *fname
= BASEDIR
"\\stream.txt";
257 const char *sname1
, *sname2
;
262 const char *one
[] = { "::$DATA" };
263 const char *two
[] = { "::$DATA", ":Second Stream:$DATA" };
264 const char *three
[] = { "::$DATA", ":Stream One:$DATA",
265 ":Second Stream:$DATA" };
267 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
269 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "Stream One");
270 sname2
= talloc_asprintf(tctx
, "%s:%s:$DaTa", fname
, "Second Stream");
272 printf("(%s) creating a stream on a non-existent file\n", __location__
);
273 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
274 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
275 io
.ntcreatex
.in
.flags
= 0;
276 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
277 io
.ntcreatex
.in
.create_options
= 0;
278 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
279 io
.ntcreatex
.in
.share_access
= 0;
280 io
.ntcreatex
.in
.alloc_size
= 0;
281 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
282 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
283 io
.ntcreatex
.in
.security_flags
= 0;
284 io
.ntcreatex
.in
.fname
= sname1
;
285 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
286 CHECK_STATUS(status
, NT_STATUS_OK
);
287 fnum
= io
.ntcreatex
.out
.file
.fnum
;
289 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Stream One", NULL
);
291 printf("(%s) check that open of base file is allowed\n", __location__
);
292 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
293 io
.ntcreatex
.in
.fname
= fname
;
294 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
295 CHECK_STATUS(status
, NT_STATUS_OK
);
296 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
298 printf("(%s) writing to stream\n", __location__
);
299 retsize
= smbcli_write(cli
->tree
, fnum
, 0, "test data", 0, 9);
300 CHECK_VALUE(retsize
, 9);
302 smbcli_close(cli
->tree
, fnum
);
304 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Stream One", "test data");
306 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
307 io
.ntcreatex
.in
.fname
= sname1
;
308 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
309 CHECK_STATUS(status
, NT_STATUS_OK
);
310 fnum
= io
.ntcreatex
.out
.file
.fnum
;
312 printf("(%s) modifying stream\n", __location__
);
313 retsize
= smbcli_write(cli
->tree
, fnum
, 0, "MORE DATA ", 5, 10);
314 CHECK_VALUE(retsize
, 10);
316 smbcli_close(cli
->tree
, fnum
);
318 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Stream One:$FOO", NULL
);
320 printf("(%s) creating a stream2 on a existing file\n", __location__
);
321 io
.ntcreatex
.in
.fname
= sname2
;
322 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
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, "SECOND STREAM", 0, 13);
329 CHECK_VALUE(retsize
, 13);
331 smbcli_close(cli
->tree
, fnum
);
333 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Stream One", "test MORE DATA ");
334 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Stream One:$DATA", "test MORE DATA ");
335 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Stream One:", NULL
);
336 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Second Stream", "SECOND STREAM");
337 ret
&= check_stream(cli
, __location__
, tctx
, fname
,
338 "SECOND STREAM:$DATA", "SECOND STREAM");
339 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Second Stream:$DATA", "SECOND STREAM");
340 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Second Stream:", NULL
);
341 ret
&= check_stream(cli
, __location__
, tctx
, fname
, "Second Stream:$FOO", NULL
);
343 check_stream_list(tctx
, cli
, fname
, 3, three
);
345 printf("(%s) deleting stream\n", __location__
);
346 status
= smbcli_unlink(cli
->tree
, sname1
);
347 CHECK_STATUS(status
, NT_STATUS_OK
);
349 check_stream_list(tctx
, cli
, fname
, 2, two
);
351 printf("(%s) delete a stream via delete-on-close\n", __location__
);
352 io
.ntcreatex
.in
.fname
= sname2
;
353 io
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
354 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_DELETE
;
355 io
.ntcreatex
.in
.access_mask
= SEC_STD_DELETE
;
356 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
358 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
359 CHECK_STATUS(status
, NT_STATUS_OK
);
360 fnum
= io
.ntcreatex
.out
.file
.fnum
;
362 smbcli_close(cli
->tree
, fnum
);
363 status
= smbcli_unlink(cli
->tree
, sname2
);
364 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
366 check_stream_list(tctx
, cli
, fname
, 1, one
);
368 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
369 io
.ntcreatex
.in
.fname
= sname1
;
370 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
371 CHECK_STATUS(status
, NT_STATUS_OK
);
372 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
373 io
.ntcreatex
.in
.fname
= sname2
;
374 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
375 CHECK_STATUS(status
, NT_STATUS_OK
);
376 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
378 printf("(%s) deleting file\n", __location__
);
379 status
= smbcli_unlink(cli
->tree
, fname
);
380 CHECK_STATUS(status
, NT_STATUS_OK
);
383 smbcli_close(cli
->tree
, fnum
);
384 smbcli_deltree(cli
->tree
, BASEDIR
);
389 test stream sharemodes
391 static bool test_stream_sharemodes(struct torture_context
*tctx
,
392 struct smbcli_state
*cli
)
396 const char *fname
= BASEDIR
"\\stream.txt";
397 const char *sname1
, *sname2
;
402 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
404 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "Stream One");
405 sname2
= talloc_asprintf(tctx
, "%s:%s:$DaTa", fname
, "Second Stream");
407 printf("(%s) testing stream share mode conflicts\n", __location__
);
408 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
409 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
410 io
.ntcreatex
.in
.flags
= 0;
411 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
412 io
.ntcreatex
.in
.create_options
= 0;
413 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
414 io
.ntcreatex
.in
.share_access
= 0;
415 io
.ntcreatex
.in
.alloc_size
= 0;
416 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
417 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
418 io
.ntcreatex
.in
.security_flags
= 0;
419 io
.ntcreatex
.in
.fname
= sname1
;
421 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
422 CHECK_STATUS(status
, NT_STATUS_OK
);
423 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
426 * A different stream does not give a sharing violation
429 io
.ntcreatex
.in
.fname
= sname2
;
430 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
431 CHECK_STATUS(status
, NT_STATUS_OK
);
432 fnum2
= io
.ntcreatex
.out
.file
.fnum
;
435 * ... whereas the same stream does with unchanged access/share_access
439 io
.ntcreatex
.in
.fname
= sname1
;
440 io
.ntcreatex
.in
.open_disposition
= 0;
441 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
442 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
444 io
.ntcreatex
.in
.fname
= sname2
;
445 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
446 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
449 if (fnum1
!= -1) smbcli_close(cli
->tree
, fnum1
);
450 if (fnum2
!= -1) smbcli_close(cli
->tree
, fnum2
);
451 status
= smbcli_unlink(cli
->tree
, fname
);
452 smbcli_deltree(cli
->tree
, BASEDIR
);
457 * Test FILE_SHARE_DELETE on streams
459 * A stream opened with !FILE_SHARE_DELETE prevents the main file to be opened
460 * with SEC_STD_DELETE.
462 * The main file opened with !FILE_SHARE_DELETE does *not* prevent a stream to
463 * be opened with SEC_STD_DELETE.
465 * A stream held open with FILE_SHARE_DELETE allows the file to be
466 * deleted. After the main file is deleted, access to the open file descriptor
467 * still works, but all name-based access to both the main file as well as the
468 * stream is denied with DELETE pending.
470 * This means, an open of the main file with SEC_STD_DELETE should walk all
471 * streams and also open them with SEC_STD_DELETE. If any of these opens gives
472 * SHARING_VIOLATION, the main open fails.
474 * Closing the main file after delete_on_close has been set does not really
475 * unlink it but leaves the corresponding share mode entry with
476 * delete_on_close being set around until all streams are closed.
478 * Opening a stream must also look at the main file's share mode entry, look
479 * at the delete_on_close bit and potentially return DELETE_PENDING.
482 static bool test_stream_delete(struct torture_context
*tctx
,
483 struct smbcli_state
*cli
)
487 const char *fname
= BASEDIR
"\\stream.txt";
493 union smb_fileinfo finfo
;
495 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
497 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "Stream One");
499 printf("(%s) opening non-existent file stream\n", __location__
);
500 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
501 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
502 io
.ntcreatex
.in
.flags
= 0;
503 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
;
504 io
.ntcreatex
.in
.create_options
= 0;
505 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
506 io
.ntcreatex
.in
.share_access
= 0;
507 io
.ntcreatex
.in
.alloc_size
= 0;
508 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
509 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
510 io
.ntcreatex
.in
.security_flags
= 0;
511 io
.ntcreatex
.in
.fname
= sname1
;
513 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
514 CHECK_STATUS(status
, NT_STATUS_OK
);
515 fnum
= io
.ntcreatex
.out
.file
.fnum
;
517 retsize
= smbcli_write(cli
->tree
, fnum
, 0, "test data", 0, 9);
518 CHECK_VALUE(retsize
, 9);
521 * One stream opened without FILE_SHARE_DELETE prevents the main file
522 * to be deleted or even opened with DELETE access
525 status
= smbcli_unlink(cli
->tree
, fname
);
526 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
528 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
529 io
.ntcreatex
.in
.fname
= fname
;
530 io
.ntcreatex
.in
.access_mask
= SEC_STD_DELETE
;
531 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
532 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
534 smbcli_close(cli
->tree
, fnum
);
537 * ... but unlink works if a stream is opened with FILE_SHARE_DELETE
540 io
.ntcreatex
.in
.fname
= sname1
;
541 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
;
542 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_DELETE
;
543 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
544 CHECK_STATUS(status
, NT_STATUS_OK
);
545 fnum
= io
.ntcreatex
.out
.file
.fnum
;
547 status
= smbcli_unlink(cli
->tree
, fname
);
548 CHECK_STATUS(status
, NT_STATUS_OK
);
551 * file access still works on the stream while the main file is closed
554 retsize
= smbcli_read(cli
->tree
, fnum
, buf
, 0, 9);
555 CHECK_VALUE(retsize
, 9);
557 finfo
.generic
.level
= RAW_FILEINFO_STANDARD
;
558 finfo
.generic
.in
.file
.path
= fname
;
561 * name-based access to both the main file and the stream does not
562 * work anymore but gives DELETE_PENDING
565 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
566 CHECK_STATUS(status
, NT_STATUS_DELETE_PENDING
);
569 * older S3 doesn't do this
571 finfo
.generic
.in
.file
.path
= sname1
;
572 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
573 CHECK_STATUS(status
, NT_STATUS_DELETE_PENDING
);
576 * fd-based qfileinfo on the stream still works, the stream does not
577 * have the delete-on-close bit set. This could mean that open on the
578 * stream first opens the main file
581 finfo
.all_info
.level
= RAW_FILEINFO_ALL_INFO
;
582 finfo
.all_info
.in
.file
.fnum
= fnum
;
584 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &finfo
);
585 CHECK_STATUS(status
, NT_STATUS_OK
);
587 /* w2k and w2k3 return 0 and w2k8 returns 1 */
588 if (TARGET_IS_WINXP(tctx
) || TARGET_IS_W2K3(tctx
) ||
589 TARGET_IS_SAMBA3(tctx
)) {
590 CHECK_VALUE(finfo
.all_info
.out
.delete_pending
, 0);
592 CHECK_VALUE(finfo
.all_info
.out
.delete_pending
, 1);
595 smbcli_close(cli
->tree
, fnum
);
598 * After closing the stream the file is really gone.
601 finfo
.generic
.in
.file
.path
= fname
;
602 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
603 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
605 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
607 io
.ntcreatex
.in
.create_options
= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
608 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
609 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
610 CHECK_STATUS(status
, NT_STATUS_OK
);
611 fnum
= io
.ntcreatex
.out
.file
.fnum
;
613 finfo
.generic
.in
.file
.path
= fname
;
614 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
615 CHECK_STATUS(status
, NT_STATUS_OK
);
617 smbcli_close(cli
->tree
, fnum
);
619 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
620 CHECK_STATUS(status
, NT_STATUS_OK
);
622 smbcli_close(cli
->tree
, fnum
);
623 smbcli_unlink(cli
->tree
, fname
);
624 smbcli_deltree(cli
->tree
, BASEDIR
);
631 static bool test_stream_names(struct torture_context
*tctx
,
632 struct smbcli_state
*cli
)
636 union smb_fileinfo info
;
637 union smb_fileinfo finfo
;
638 union smb_fileinfo stinfo
;
639 union smb_setfileinfo sinfo
;
640 const char *fname
= BASEDIR
"\\stream_names.txt";
641 const char *sname1
, *sname1b
, *sname1c
, *sname1d
;
642 const char *sname2
, *snamew
, *snamew2
;
649 const char *four
[4] = {
651 ":\x05Stream\n One:$DATA",
652 ":MStream Two:$DATA",
655 const char *five1
[5] = {
657 ":\x05Stream\n One:$DATA",
658 ":BeforeRename:$DATA",
659 ":MStream Two:$DATA",
662 const char *five2
[5] = {
664 ":\x05Stream\n One:$DATA",
665 ":AfterRename:$DATA",
666 ":MStream Two:$DATA",
670 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
672 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "\x05Stream\n One");
673 sname1b
= talloc_asprintf(tctx
, "%s:", sname1
);
674 sname1c
= talloc_asprintf(tctx
, "%s:$FOO", sname1
);
675 sname1d
= talloc_asprintf(tctx
, "%s:?D*a", sname1
);
676 sname2
= talloc_asprintf(tctx
, "%s:%s:$DaTa", fname
, "MStream Two");
677 snamew
= talloc_asprintf(tctx
, "%s:%s:$DATA", fname
, "?Stream*");
678 snamew2
= talloc_asprintf(tctx
, "%s\\stream*:%s:$DATA", BASEDIR
, "?Stream*");
679 snamer1
= talloc_asprintf(tctx
, "%s:%s:$DATA", fname
, "BeforeRename");
681 printf("(%s) testing stream names\n", __location__
);
682 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
683 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
684 io
.ntcreatex
.in
.flags
= 0;
685 io
.ntcreatex
.in
.access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
686 io
.ntcreatex
.in
.create_options
= 0;
687 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
688 io
.ntcreatex
.in
.share_access
=
689 NTCREATEX_SHARE_ACCESS_READ
|
690 NTCREATEX_SHARE_ACCESS_WRITE
;
691 io
.ntcreatex
.in
.alloc_size
= 0;
692 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
693 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
694 io
.ntcreatex
.in
.security_flags
= 0;
695 io
.ntcreatex
.in
.fname
= fname
;
697 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
698 CHECK_STATUS(status
, NT_STATUS_OK
);
699 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
701 torture_comment(tctx
, "Adding two EAs to base file\n");
703 sinfo
.generic
.level
= RAW_SFILEINFO_EA_SET
;
704 sinfo
.generic
.in
.file
.fnum
= fnum1
;
705 sinfo
.ea_set
.in
.num_eas
= 2;
706 sinfo
.ea_set
.in
.eas
= talloc_array(tctx
, struct ea_struct
, 2);
707 sinfo
.ea_set
.in
.eas
[0].flags
= 0;
708 sinfo
.ea_set
.in
.eas
[0].name
.s
= "EAONE";
709 sinfo
.ea_set
.in
.eas
[0].value
= data_blob_string_const("VALUE1");
710 sinfo
.ea_set
.in
.eas
[1].flags
= 0;
711 sinfo
.ea_set
.in
.eas
[1].name
.s
= "SECONDEA";
712 sinfo
.ea_set
.in
.eas
[1].value
= data_blob_string_const("ValueTwo");
714 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
715 CHECK_STATUS(status
, NT_STATUS_OK
);
718 * Make sure the create time of the streams are different from the
722 smbcli_close(cli
->tree
, fnum1
);
724 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
725 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
726 io
.ntcreatex
.in
.flags
= 0;
727 io
.ntcreatex
.in
.access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
728 io
.ntcreatex
.in
.create_options
= 0;
729 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
730 io
.ntcreatex
.in
.share_access
=
731 NTCREATEX_SHARE_ACCESS_READ
|
732 NTCREATEX_SHARE_ACCESS_WRITE
;
733 io
.ntcreatex
.in
.alloc_size
= 0;
734 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
735 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
736 io
.ntcreatex
.in
.security_flags
= 0;
737 io
.ntcreatex
.in
.fname
= sname1
;
739 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
740 CHECK_STATUS(status
, NT_STATUS_OK
);
741 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
743 torture_comment(tctx
, "Adding one EAs to first stream file\n");
745 sinfo
.generic
.level
= RAW_SFILEINFO_EA_SET
;
746 sinfo
.generic
.in
.file
.fnum
= fnum1
;
747 sinfo
.ea_set
.in
.num_eas
= 1;
748 sinfo
.ea_set
.in
.eas
= talloc_array(tctx
, struct ea_struct
, 1);
749 sinfo
.ea_set
.in
.eas
[0].flags
= 0;
750 sinfo
.ea_set
.in
.eas
[0].name
.s
= "STREAMEA";
751 sinfo
.ea_set
.in
.eas
[0].value
= data_blob_string_const("EA_VALUE1");
753 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
754 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
756 status
= torture_check_ea(cli
, sname1
, "STREAMEA", "EA_VALUE1");
757 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
760 info
.generic
.level
= RAW_FILEINFO_ALL_EAS
;
761 info
.all_eas
.in
.file
.path
= sname1
;
763 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &info
);
764 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
767 * A different stream does not give a sharing violation
770 io
.ntcreatex
.in
.fname
= sname2
;
771 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
772 CHECK_STATUS(status
, NT_STATUS_OK
);
773 fnum2
= io
.ntcreatex
.out
.file
.fnum
;
776 * ... whereas the same stream does with unchanged access/share_access
780 io
.ntcreatex
.in
.fname
= sname1
;
781 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_SUPERSEDE
;
782 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
783 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
785 io
.ntcreatex
.in
.fname
= sname1b
;
786 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
787 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
789 io
.ntcreatex
.in
.fname
= sname1c
;
790 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
791 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
)) {
792 /* w2k returns INVALID_PARAMETER */
793 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
795 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
798 io
.ntcreatex
.in
.fname
= sname1d
;
799 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
800 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
)) {
801 /* w2k returns INVALID_PARAMETER */
802 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
804 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
807 io
.ntcreatex
.in
.fname
= sname2
;
808 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
809 CHECK_STATUS(status
, NT_STATUS_SHARING_VIOLATION
);
811 io
.ntcreatex
.in
.fname
= snamew
;
812 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
813 CHECK_STATUS(status
, NT_STATUS_OK
);
814 fnum3
= io
.ntcreatex
.out
.file
.fnum
;
816 io
.ntcreatex
.in
.fname
= snamew2
;
817 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
818 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
820 ret
&= check_stream_list(tctx
, cli
, fname
, 4, four
);
822 smbcli_close(cli
->tree
, fnum1
);
823 smbcli_close(cli
->tree
, fnum2
);
824 smbcli_close(cli
->tree
, fnum3
);
826 finfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
827 finfo
.generic
.in
.file
.path
= fname
;
828 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
829 CHECK_STATUS(status
, NT_STATUS_OK
);
831 ret
&= check_stream_list(tctx
, cli
, fname
, 4, four
);
833 for (i
=0; i
< 4; i
++) {
835 uint64_t stream_size
;
836 char *path
= talloc_asprintf(tctx
, "%s%s",
839 char *rpath
= talloc_strdup(path
, path
);
840 char *p
= strrchr(rpath
, ':');
848 printf("(%s): i[%u][%s]\n", __location__
, i
, path
);
849 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
850 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_ATTRIBUTE
|
851 SEC_FILE_WRITE_ATTRIBUTE
|
853 io
.ntcreatex
.in
.fname
= path
;
854 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
855 CHECK_STATUS(status
, NT_STATUS_OK
);
856 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
858 finfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
859 finfo
.generic
.in
.file
.path
= fname
;
860 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
861 CHECK_STATUS(status
, NT_STATUS_OK
);
863 stinfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
864 stinfo
.generic
.in
.file
.fnum
= fnum1
;
865 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &stinfo
);
866 CHECK_STATUS(status
, NT_STATUS_OK
);
867 if (!torture_setting_bool(tctx
, "samba3", false)) {
868 CHECK_NTTIME(stinfo
.all_info
.out
.create_time
,
869 finfo
.all_info
.out
.create_time
);
870 CHECK_NTTIME(stinfo
.all_info
.out
.access_time
,
871 finfo
.all_info
.out
.access_time
);
872 CHECK_NTTIME(stinfo
.all_info
.out
.write_time
,
873 finfo
.all_info
.out
.write_time
);
874 CHECK_NTTIME(stinfo
.all_info
.out
.change_time
,
875 finfo
.all_info
.out
.change_time
);
877 CHECK_VALUE(stinfo
.all_info
.out
.attrib
,
878 finfo
.all_info
.out
.attrib
);
879 CHECK_VALUE(stinfo
.all_info
.out
.size
,
880 finfo
.all_info
.out
.size
);
881 CHECK_VALUE(stinfo
.all_info
.out
.delete_pending
,
882 finfo
.all_info
.out
.delete_pending
);
883 CHECK_VALUE(stinfo
.all_info
.out
.directory
,
884 finfo
.all_info
.out
.directory
);
885 CHECK_VALUE(stinfo
.all_info
.out
.ea_size
,
886 finfo
.all_info
.out
.ea_size
);
888 stinfo
.generic
.level
= RAW_FILEINFO_NAME_INFO
;
889 stinfo
.generic
.in
.file
.fnum
= fnum1
;
890 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &stinfo
);
891 CHECK_STATUS(status
, NT_STATUS_OK
);
892 if (!torture_setting_bool(tctx
, "samba3", false)) {
893 CHECK_STR(stinfo
.name_info
.out
.fname
.s
, rpath
);
896 write_time
= finfo
.all_info
.out
.write_time
;
897 write_time
+= i
*1000000;
898 write_time
/= 1000000;
899 write_time
*= 1000000;
902 sinfo
.basic_info
.level
= RAW_SFILEINFO_BASIC_INFO
;
903 sinfo
.basic_info
.in
.file
.fnum
= fnum1
;
904 sinfo
.basic_info
.in
.write_time
= write_time
;
905 sinfo
.basic_info
.in
.attrib
= stinfo
.all_info
.out
.attrib
;
906 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
907 CHECK_STATUS(status
, NT_STATUS_OK
);
909 stream_size
= i
*8192;
912 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFO
;
913 sinfo
.end_of_file_info
.in
.file
.fnum
= fnum1
;
914 sinfo
.end_of_file_info
.in
.size
= stream_size
;
915 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
916 CHECK_STATUS(status
, NT_STATUS_OK
);
918 stinfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
919 stinfo
.generic
.in
.file
.fnum
= fnum1
;
920 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &stinfo
);
921 CHECK_STATUS(status
, NT_STATUS_OK
);
922 if (!torture_setting_bool(tctx
, "samba3", false)) {
923 CHECK_NTTIME(stinfo
.all_info
.out
.write_time
,
925 CHECK_VALUE(stinfo
.all_info
.out
.attrib
,
926 finfo
.all_info
.out
.attrib
);
928 CHECK_VALUE(stinfo
.all_info
.out
.size
,
930 CHECK_VALUE(stinfo
.all_info
.out
.delete_pending
,
931 finfo
.all_info
.out
.delete_pending
);
932 CHECK_VALUE(stinfo
.all_info
.out
.directory
,
933 finfo
.all_info
.out
.directory
);
934 CHECK_VALUE(stinfo
.all_info
.out
.ea_size
,
935 finfo
.all_info
.out
.ea_size
);
937 ret
&= check_stream_list(tctx
, cli
, fname
, 4, four
);
939 smbcli_close(cli
->tree
, fnum1
);
943 printf("(%s): testing stream renames\n", __location__
);
944 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
945 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_ATTRIBUTE
|
946 SEC_FILE_WRITE_ATTRIBUTE
|
948 io
.ntcreatex
.in
.fname
= snamer1
;
949 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
950 CHECK_STATUS(status
, NT_STATUS_OK
);
951 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
953 ret
&= check_stream_list(tctx
, cli
, fname
, 5, five1
);
956 sinfo
.rename_information
.level
= RAW_SFILEINFO_RENAME_INFORMATION
;
957 sinfo
.rename_information
.in
.file
.fnum
= fnum1
;
958 sinfo
.rename_information
.in
.overwrite
= true;
959 sinfo
.rename_information
.in
.root_fid
= 0;
960 sinfo
.rename_information
.in
.new_name
= ":AfterRename:$DATA";
961 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
962 CHECK_STATUS(status
, NT_STATUS_OK
);
964 ret
&= check_stream_list(tctx
, cli
, fname
, 5, five2
);
967 sinfo
.rename_information
.level
= RAW_SFILEINFO_RENAME_INFORMATION
;
968 sinfo
.rename_information
.in
.file
.fnum
= fnum1
;
969 sinfo
.rename_information
.in
.overwrite
= false;
970 sinfo
.rename_information
.in
.root_fid
= 0;
971 sinfo
.rename_information
.in
.new_name
= ":MStream Two:$DATA";
972 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
973 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_COLLISION
);
975 ret
&= check_stream_list(tctx
, cli
, fname
, 5, five2
);
978 sinfo
.rename_information
.level
= RAW_SFILEINFO_RENAME_INFORMATION
;
979 sinfo
.rename_information
.in
.file
.fnum
= fnum1
;
980 sinfo
.rename_information
.in
.overwrite
= true;
981 sinfo
.rename_information
.in
.root_fid
= 0;
982 sinfo
.rename_information
.in
.new_name
= ":MStream Two:$DATA";
983 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
984 if (torture_setting_bool(tctx
, "samba4", false) ||
985 torture_setting_bool(tctx
, "samba3", false)) {
986 /* why should this rename be considered invalid?? */
987 CHECK_STATUS(status
, NT_STATUS_OK
);
988 ret
&= check_stream_list(tctx
, cli
, fname
, 4, four
);
990 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
991 ret
&= check_stream_list(tctx
, cli
, fname
, 5, five2
);
995 /* TODO: we need to test more rename combinations */
998 if (fnum1
!= -1) smbcli_close(cli
->tree
, fnum1
);
999 if (fnum2
!= -1) smbcli_close(cli
->tree
, fnum2
);
1000 if (fnum3
!= -1) smbcli_close(cli
->tree
, fnum3
);
1001 status
= smbcli_unlink(cli
->tree
, fname
);
1002 smbcli_deltree(cli
->tree
, BASEDIR
);
1009 static bool test_stream_names2(struct torture_context
*tctx
,
1010 struct smbcli_state
*cli
)
1014 const char *fname
= BASEDIR
"\\stream_names2.txt";
1019 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1021 printf("(%s) testing stream names\n", __location__
);
1022 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1023 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1024 io
.ntcreatex
.in
.flags
= 0;
1025 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
1026 io
.ntcreatex
.in
.create_options
= 0;
1027 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1028 io
.ntcreatex
.in
.share_access
= 0;
1029 io
.ntcreatex
.in
.alloc_size
= 0;
1030 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1031 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1032 io
.ntcreatex
.in
.security_flags
= 0;
1033 io
.ntcreatex
.in
.fname
= fname
;
1034 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1035 CHECK_STATUS(status
, NT_STATUS_OK
);
1036 fnum1
= io
.ntcreatex
.out
.file
.fnum
;
1038 for (i
=0x01; i
< 0x7F; i
++) {
1039 char *path
= talloc_asprintf(tctx
, "%s:Stream%c0x%02X:$DATA",
1047 expected
= NT_STATUS_OBJECT_NAME_INVALID
;
1050 expected
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1055 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1056 io
.ntcreatex
.in
.fname
= path
;
1057 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1058 if (!NT_STATUS_EQUAL(status
, expected
)) {
1059 printf("(%s) %s:Stream%c0x%02X:$DATA%s => expected[%s]\n",
1060 __location__
, fname
, isprint(i
)?(char)i
:' ', i
,
1061 isprint(i
)?"":" (not printable)",
1062 nt_errstr(expected
));
1064 CHECK_STATUS(status
, expected
);
1070 if (fnum1
!= -1) smbcli_close(cli
->tree
, fnum1
);
1071 status
= smbcli_unlink(cli
->tree
, fname
);
1072 smbcli_deltree(cli
->tree
, BASEDIR
);
1076 #define CHECK_CALL_FNUM(call, rightstatus) do { \
1077 sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
1078 sfinfo.generic.in.file.fnum = fnum; \
1079 status = smb_raw_setfileinfo(cli->tree, &sfinfo); \
1080 torture_assert_ntstatus_equal_goto(tctx, status, rightstatus, ret, done, #call); \
1081 finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \
1082 finfo1.generic.in.file.fnum = fnum; \
1083 status2 = smb_raw_fileinfo(cli->tree, tctx, &finfo1); \
1084 torture_assert_ntstatus_ok_goto(tctx, status2, ret, done, "ALL_INFO"); \
1090 static bool test_stream_rename(struct torture_context
*tctx
,
1091 struct smbcli_state
*cli
)
1093 NTSTATUS status
, status2
;
1095 const char *fname
= BASEDIR
"\\stream_rename.txt";
1096 const char *sname1
, *sname2
;
1097 union smb_fileinfo finfo1
;
1098 union smb_setfileinfo sfinfo
;
1102 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1104 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "Stream One");
1105 sname2
= talloc_asprintf(tctx
, "%s:%s:$DaTa", fname
, "Second Stream");
1107 printf("(%s) testing stream renames\n", __location__
);
1108 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1109 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1110 io
.ntcreatex
.in
.flags
= 0;
1111 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_ATTRIBUTE
|
1112 SEC_FILE_WRITE_ATTRIBUTE
|
1113 SEC_RIGHTS_FILE_ALL
;
1114 io
.ntcreatex
.in
.create_options
= 0;
1115 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1116 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
| NTCREATEX_SHARE_ACCESS_WRITE
| NTCREATEX_SHARE_ACCESS_DELETE
;
1117 io
.ntcreatex
.in
.alloc_size
= 0;
1118 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1119 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1120 io
.ntcreatex
.in
.security_flags
= 0;
1121 io
.ntcreatex
.in
.fname
= sname1
;
1123 /* Create two streams. */
1124 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1125 CHECK_STATUS(status
, NT_STATUS_OK
);
1126 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1127 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1129 io
.ntcreatex
.in
.fname
= sname2
;
1130 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1131 CHECK_STATUS(status
, NT_STATUS_OK
);
1132 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1134 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1137 * Open the second stream.
1140 io
.ntcreatex
.in
.access_mask
= SEC_STD_DELETE
;
1141 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
1142 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1143 CHECK_STATUS(status
, NT_STATUS_OK
);
1144 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1147 * Now rename the second stream onto the first.
1150 ZERO_STRUCT(sfinfo
);
1152 sfinfo
.rename_information
.in
.overwrite
= 1;
1153 sfinfo
.rename_information
.in
.root_fid
= 0;
1154 sfinfo
.rename_information
.in
.new_name
= ":Stream One";
1155 CHECK_CALL_FNUM(RENAME_INFORMATION
, NT_STATUS_OK
);
1158 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1159 status
= smbcli_unlink(cli
->tree
, fname
);
1160 smbcli_deltree(cli
->tree
, BASEDIR
);
1164 static bool test_stream_rename2(struct torture_context
*tctx
,
1165 struct smbcli_state
*cli
)
1169 const char *fname1
= BASEDIR
"\\stream.txt";
1170 const char *fname2
= BASEDIR
"\\stream2.txt";
1171 const char *stream_name1
= ":Stream One:$DATA";
1172 const char *stream_name2
= ":Stream Two:$DATA";
1173 const char *stream_name_default
= "::$DATA";
1178 union smb_setfileinfo sinfo
;
1179 union smb_rename rio
;
1181 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1183 sname1
= talloc_asprintf(tctx
, "%s:%s", fname1
, "Stream One");
1184 sname2
= talloc_asprintf(tctx
, "%s:%s", fname1
, "Stream Two");
1186 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1187 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1188 io
.ntcreatex
.in
.flags
= 0;
1189 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1190 SEC_STD_DELETE
|SEC_FILE_APPEND_DATA
|SEC_STD_READ_CONTROL
);
1191 io
.ntcreatex
.in
.create_options
= 0;
1192 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1193 io
.ntcreatex
.in
.share_access
= (NTCREATEX_SHARE_ACCESS_READ
|
1194 NTCREATEX_SHARE_ACCESS_WRITE
|
1195 NTCREATEX_SHARE_ACCESS_DELETE
);
1196 io
.ntcreatex
.in
.alloc_size
= 0;
1197 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1198 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1199 io
.ntcreatex
.in
.security_flags
= 0;
1200 io
.ntcreatex
.in
.fname
= sname1
;
1202 /* Open/create new stream. */
1203 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1204 CHECK_STATUS(status
, NT_STATUS_OK
);
1206 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1209 * Check raw rename with <base>:<stream>.
1211 printf("(%s) Checking NTRENAME of a stream using <base>:<stream>\n",
1213 rio
.generic
.level
= RAW_RENAME_NTRENAME
;
1214 rio
.ntrename
.in
.old_name
= sname1
;
1215 rio
.ntrename
.in
.new_name
= sname2
;
1216 rio
.ntrename
.in
.attrib
= 0;
1217 rio
.ntrename
.in
.cluster_size
= 0;
1218 rio
.ntrename
.in
.flags
= RENAME_FLAG_RENAME
;
1219 status
= smb_raw_rename(cli
->tree
, &rio
);
1220 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1223 * Check raw rename to the default stream using :<stream>.
1225 printf("(%s) Checking NTRENAME to default stream using :<stream>\n",
1227 rio
.ntrename
.in
.new_name
= stream_name_default
;
1228 status
= smb_raw_rename(cli
->tree
, &rio
);
1229 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_COLLISION
);
1232 * Check raw rename using :<stream>.
1234 printf("(%s) Checking NTRENAME of a stream using :<stream>\n",
1236 rio
.ntrename
.in
.new_name
= stream_name2
;
1237 status
= smb_raw_rename(cli
->tree
, &rio
);
1238 CHECK_STATUS(status
, NT_STATUS_OK
);
1241 * Check raw rename of a stream to a file.
1243 printf("(%s) Checking NTRENAME of a stream to a file\n",
1245 rio
.ntrename
.in
.old_name
= sname2
;
1246 rio
.ntrename
.in
.new_name
= fname2
;
1247 status
= smb_raw_rename(cli
->tree
, &rio
);
1248 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1251 * Check raw rename of a file to a stream.
1253 printf("(%s) Checking NTRENAME of a file to a stream\n",
1256 /* Create the file. */
1257 io
.ntcreatex
.in
.fname
= fname2
;
1258 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1259 CHECK_STATUS(status
, NT_STATUS_OK
);
1260 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1262 /* Try the rename. */
1263 rio
.ntrename
.in
.old_name
= fname2
;
1264 rio
.ntrename
.in
.new_name
= sname1
;
1265 status
= smb_raw_rename(cli
->tree
, &rio
);
1266 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_INVALID
);
1269 * Reopen the stream for trans2 renames.
1271 io
.ntcreatex
.in
.fname
= sname2
;
1272 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1273 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1274 CHECK_STATUS(status
, NT_STATUS_OK
);
1275 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1278 * Check trans2 rename of a stream using :<stream>.
1280 printf("(%s) Checking trans2 rename of a stream using :<stream>\n",
1283 sinfo
.rename_information
.level
= RAW_SFILEINFO_RENAME_INFORMATION
;
1284 sinfo
.rename_information
.in
.file
.fnum
= fnum
;
1285 sinfo
.rename_information
.in
.overwrite
= 1;
1286 sinfo
.rename_information
.in
.root_fid
= 0;
1287 sinfo
.rename_information
.in
.new_name
= stream_name1
;
1288 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
1289 CHECK_STATUS(status
, NT_STATUS_OK
);
1292 * Check trans2 rename of an overwriting stream using :<stream>.
1294 printf("(%s) Checking trans2 rename of an overwriting stream using "
1295 ":<stream>\n", __location__
);
1297 /* Create second stream. */
1298 io
.ntcreatex
.in
.fname
= sname2
;
1299 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1300 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1301 CHECK_STATUS(status
, NT_STATUS_OK
);
1302 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1304 /* Rename the first stream onto the second. */
1305 sinfo
.rename_information
.in
.file
.fnum
= fnum
;
1306 sinfo
.rename_information
.in
.new_name
= stream_name2
;
1307 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
1308 CHECK_STATUS(status
, NT_STATUS_OK
);
1310 smbcli_close(cli
->tree
, fnum
);
1313 * Reopen the stream with the new name.
1315 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1316 io
.ntcreatex
.in
.fname
= sname2
;
1317 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1318 CHECK_STATUS(status
, NT_STATUS_OK
);
1319 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1322 * Check trans2 rename of a stream using <base>:<stream>.
1324 printf("(%s) Checking trans2 rename of a stream using "
1325 "<base>:<stream>\n", __location__
);
1326 sinfo
.rename_information
.in
.file
.fnum
= fnum
;
1327 sinfo
.rename_information
.in
.new_name
= sname1
;
1328 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
1329 CHECK_STATUS(status
, NT_STATUS_NOT_SUPPORTED
);
1332 * Samba3 doesn't currently support renaming a stream to the default
1333 * stream. This test does pass on windows.
1335 if (torture_setting_bool(tctx
, "samba3", false) ||
1336 torture_setting_bool(tctx
, "samba4", false)) {
1341 * Check trans2 rename to the default stream using :<stream>.
1343 printf("(%s) Checking trans2 rename to defaualt stream using "
1344 ":<stream>\n", __location__
);
1345 sinfo
.rename_information
.in
.file
.fnum
= fnum
;
1346 sinfo
.rename_information
.in
.new_name
= stream_name_default
;
1347 status
= smb_raw_setfileinfo(cli
->tree
, &sinfo
);
1348 CHECK_STATUS(status
, NT_STATUS_OK
);
1350 smbcli_close(cli
->tree
, fnum
);
1353 smbcli_close(cli
->tree
, fnum
);
1354 status
= smbcli_unlink(cli
->tree
, fname1
);
1355 status
= smbcli_unlink(cli
->tree
, fname2
);
1356 smbcli_deltree(cli
->tree
, BASEDIR
);
1363 static bool test_stream_rename3(struct torture_context
*tctx
,
1364 struct smbcli_state
*cli
)
1366 NTSTATUS status
, status2
;
1368 const char *fname
= BASEDIR
"\\stream_rename.txt";
1369 const char *sname1
, *sname2
;
1370 union smb_fileinfo finfo1
;
1371 union smb_setfileinfo sfinfo
;
1376 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1378 sname1
= talloc_asprintf(tctx
, "%s:%s", fname
, "MStream Two:$DATA");
1379 sname2
= talloc_asprintf(tctx
, "%s:%s:$DaTa", fname
, "Second Stream");
1381 printf("(%s) testing stream renames\n", __location__
);
1382 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1383 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1384 io
.ntcreatex
.in
.flags
= 0;
1385 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_ATTRIBUTE
|
1386 SEC_FILE_WRITE_ATTRIBUTE
|
1387 SEC_RIGHTS_FILE_ALL
;
1388 io
.ntcreatex
.in
.create_options
= 0;
1389 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1390 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
1391 NTCREATEX_SHARE_ACCESS_WRITE
| NTCREATEX_SHARE_ACCESS_DELETE
;
1392 io
.ntcreatex
.in
.alloc_size
= 0;
1393 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1394 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1395 io
.ntcreatex
.in
.security_flags
= 0;
1396 io
.ntcreatex
.in
.fname
= sname1
;
1398 /* Create two streams. */
1399 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1400 CHECK_STATUS(status
, NT_STATUS_OK
);
1401 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1402 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1404 io
.ntcreatex
.in
.fname
= sname2
;
1405 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1406 CHECK_STATUS(status
, NT_STATUS_OK
);
1407 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1409 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1411 /* open the second stream. */
1412 io
.ntcreatex
.in
.access_mask
= SEC_STD_DELETE
;
1413 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
1414 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1415 CHECK_STATUS(status
, NT_STATUS_OK
);
1416 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1418 /* Keep a handle to the first stream open. */
1419 io
.ntcreatex
.in
.fname
= sname1
;
1420 io
.ntcreatex
.in
.access_mask
= SEC_STD_DELETE
;
1421 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
1422 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1423 CHECK_STATUS(status
, NT_STATUS_OK
);
1424 fnum2
= io
.ntcreatex
.out
.file
.fnum
;
1426 ZERO_STRUCT(sfinfo
);
1427 sfinfo
.rename_information
.in
.overwrite
= 1;
1428 sfinfo
.rename_information
.in
.root_fid
= 0;
1429 sfinfo
.rename_information
.in
.new_name
= ":MStream Two:$DATA";
1430 if (torture_setting_bool(tctx
, "samba4", false) ||
1431 torture_setting_bool(tctx
, "samba3", false)) {
1432 CHECK_CALL_FNUM(RENAME_INFORMATION
, NT_STATUS_OK
);
1434 CHECK_CALL_FNUM(RENAME_INFORMATION
,
1435 NT_STATUS_INVALID_PARAMETER
);
1440 if (fnum
!= -1) smbcli_close(cli
->tree
, fnum
);
1441 if (fnum2
!= -1) smbcli_close(cli
->tree
, fnum2
);
1442 status
= smbcli_unlink(cli
->tree
, fname
);
1443 smbcli_deltree(cli
->tree
, BASEDIR
);
1447 static bool create_file_with_stream(struct torture_context
*tctx
,
1448 struct smbcli_state
*cli
,
1457 /* Create a file with a stream */
1458 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1459 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1460 io
.ntcreatex
.in
.flags
= 0;
1461 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1462 SEC_FILE_APPEND_DATA
|SEC_STD_READ_CONTROL
);
1463 io
.ntcreatex
.in
.create_options
= 0;
1464 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1465 io
.ntcreatex
.in
.share_access
= 0;
1466 io
.ntcreatex
.in
.alloc_size
= 0;
1467 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_CREATE
;
1468 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1469 io
.ntcreatex
.in
.security_flags
= 0;
1470 io
.ntcreatex
.in
.fname
= stream
;
1472 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1473 CHECK_STATUS(status
, NT_STATUS_OK
);
1476 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1480 /* Test how streams interact with create dispositions */
1481 static bool test_stream_create_disposition(struct torture_context
*tctx
,
1482 struct smbcli_state
*cli
)
1486 const char *fname
= BASEDIR
"\\stream.txt";
1487 const char *stream
= "Stream One:$DATA";
1488 const char *fname_stream
;
1489 const char *default_stream_name
= "::$DATA";
1490 const char *stream_list
[2];
1494 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1496 fname_stream
= talloc_asprintf(tctx
, "%s:%s", fname
, stream
);
1498 stream_list
[0] = talloc_asprintf(tctx
, ":%s", stream
);
1499 stream_list
[1] = default_stream_name
;
1501 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1505 /* Open the base file with OPEN */
1506 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1507 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1508 io
.ntcreatex
.in
.flags
= 0;
1509 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1510 SEC_FILE_APPEND_DATA
|SEC_STD_READ_CONTROL
);
1511 io
.ntcreatex
.in
.create_options
= 0;
1512 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1513 io
.ntcreatex
.in
.share_access
= 0;
1514 io
.ntcreatex
.in
.alloc_size
= 0;
1515 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1516 io
.ntcreatex
.in
.security_flags
= 0;
1517 io
.ntcreatex
.in
.fname
= fname
;
1520 * check ntcreatex open: sanity check
1522 printf("(%s) Checking ntcreatex disp: open\n", __location__
);
1523 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1524 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1525 CHECK_STATUS(status
, NT_STATUS_OK
);
1526 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1527 if (!check_stream_list(tctx
, cli
, fname
, 2, stream_list
)) {
1532 * check ntcreatex overwrite
1534 printf("(%s) Checking ntcreatex disp: overwrite\n", __location__
);
1535 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OVERWRITE
;
1536 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1537 CHECK_STATUS(status
, NT_STATUS_OK
);
1538 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1539 if (!check_stream_list(tctx
, cli
, fname
, 1, &default_stream_name
)) {
1544 * check ntcreatex overwrite_if
1546 printf("(%s) Checking ntcreatex disp: overwrite_if\n", __location__
);
1547 smbcli_unlink(cli
->tree
, fname
);
1548 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1552 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OVERWRITE_IF
;
1553 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1554 CHECK_STATUS(status
, NT_STATUS_OK
);
1555 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1556 if (!check_stream_list(tctx
, cli
, fname
, 1, &default_stream_name
)) {
1561 * check ntcreatex supersede
1563 printf("(%s) Checking ntcreatex disp: supersede\n", __location__
);
1564 smbcli_unlink(cli
->tree
, fname
);
1565 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1569 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_SUPERSEDE
;
1570 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1571 CHECK_STATUS(status
, NT_STATUS_OK
);
1572 smbcli_close(cli
->tree
, io
.ntcreatex
.out
.file
.fnum
);
1573 if (!check_stream_list(tctx
, cli
, fname
, 1, &default_stream_name
)) {
1578 * check ntcreatex overwrite_if on a stream.
1580 printf("(%s) Checking ntcreatex disp: overwrite_if on stream\n",
1582 smbcli_unlink(cli
->tree
, fname
);
1583 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1587 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OVERWRITE_IF
;
1588 io
.ntcreatex
.in
.fname
= fname_stream
;
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
, 2, stream_list
)) {
1597 * check openx overwrite_if
1599 printf("(%s) Checking openx disp: overwrite_if\n", __location__
);
1600 smbcli_unlink(cli
->tree
, fname
);
1601 if (!create_file_with_stream(tctx
, cli
, fname_stream
)) {
1605 io
.openx
.level
= RAW_OPEN_OPENX
;
1606 io
.openx
.in
.flags
= OPENX_FLAGS_ADDITIONAL_INFO
;
1607 io
.openx
.in
.open_mode
= OPENX_MODE_ACCESS_RDWR
| OPEN_FLAGS_DENY_NONE
;
1608 io
.openx
.in
.search_attrs
= 0;
1609 io
.openx
.in
.file_attrs
= 0;
1610 io
.openx
.in
.write_time
= 0;
1611 io
.openx
.in
.size
= 1024*1024;
1612 io
.openx
.in
.timeout
= 0;
1613 io
.openx
.in
.fname
= fname
;
1615 io
.openx
.in
.open_func
= OPENX_OPEN_FUNC_TRUNC
| OPENX_OPEN_FUNC_CREATE
;
1616 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1617 CHECK_STATUS(status
, NT_STATUS_OK
);
1618 smbcli_close(cli
->tree
, io
.openx
.out
.file
.fnum
);
1619 if (!check_stream_list(tctx
, cli
, fname
, 1, &default_stream_name
)) {
1626 smbcli_close(cli
->tree
, fnum
);
1627 smbcli_unlink(cli
->tree
, fname
);
1628 smbcli_deltree(cli
->tree
, BASEDIR
);
1633 /* Test streaminfo with enough streams on a file to fill up the buffer. */
1634 static bool test_stream_large_streaminfo(struct torture_context
*tctx
,
1635 struct smbcli_state
*cli
)
1637 #define LONG_STREAM_SIZE 2
1639 const char *fname
= BASEDIR
"\\stream.txt";
1640 const char *fname_stream
;
1644 union smb_fileinfo finfo
;
1646 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1648 lstream_name
= talloc_array(tctx
, char, LONG_STREAM_SIZE
);
1650 for (i
= 0; i
< LONG_STREAM_SIZE
- 1; i
++) {
1651 lstream_name
[i
] = (char)('a' + i
%26);
1653 lstream_name
[LONG_STREAM_SIZE
- 1] = '\0';
1655 torture_comment(tctx
, "(%s) Creating a file with a lot of streams\n", __location__
);
1656 for (i
= 0; i
< 10000; i
++) {
1657 fname_stream
= talloc_asprintf(tctx
, "%s:%s%d", fname
,
1659 ret
= create_file_with_stream(tctx
, cli
, fname_stream
);
1665 finfo
.generic
.level
= RAW_FILEINFO_STREAM_INFO
;
1666 finfo
.generic
.in
.file
.path
= fname
;
1668 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1669 CHECK_STATUS(status
, STATUS_BUFFER_OVERFLOW
);
1672 smbcli_unlink(cli
->tree
, fname
);
1673 smbcli_deltree(cli
->tree
, BASEDIR
);
1678 /* Test the effect of setting attributes on a stream. */
1679 static bool test_stream_attributes(struct torture_context
*tctx
,
1680 struct smbcli_state
*cli
)
1685 const char *fname
= BASEDIR
"\\stream_attr.txt";
1686 const char *stream
= "Stream One:$DATA";
1687 const char *fname_stream
;
1689 union smb_fileinfo finfo
;
1690 union smb_setfileinfo sfinfo
;
1691 time_t basetime
= (time(NULL
) - 86400) & ~1;
1693 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1695 torture_comment(tctx
, "(%s) testing attribute setting on stream\n", __location__
);
1697 fname_stream
= talloc_asprintf(tctx
, "%s:%s", fname
, stream
);
1699 /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */
1700 ret
= create_file_with_stream(tctx
, cli
, fname_stream
);
1706 finfo
.generic
.level
= RAW_FILEINFO_BASIC_INFO
;
1707 finfo
.generic
.in
.file
.path
= fname
;
1708 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1709 CHECK_STATUS(status
, NT_STATUS_OK
);
1711 torture_assert_int_equal_goto(tctx
, finfo
.all_info
.out
.attrib
& ~FILE_ATTRIBUTE_NONINDEXED
, FILE_ATTRIBUTE_ARCHIVE
, ret
, done
, "attrib incorrect");
1713 /* Now open the stream name. */
1715 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1716 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1717 io
.ntcreatex
.in
.flags
= 0;
1718 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1719 SEC_FILE_APPEND_DATA
|SEC_STD_READ_CONTROL
|SEC_FILE_WRITE_ATTRIBUTE
);
1720 io
.ntcreatex
.in
.create_options
= 0;
1721 io
.ntcreatex
.in
.file_attr
= 0;
1722 io
.ntcreatex
.in
.share_access
= 0;
1723 io
.ntcreatex
.in
.alloc_size
= 0;
1724 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
1725 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1726 io
.ntcreatex
.in
.security_flags
= 0;
1727 io
.ntcreatex
.in
.fname
= fname_stream
;
1729 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1730 CHECK_STATUS(status
, NT_STATUS_OK
);
1732 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1734 /* Change the attributes + time on the stream fnum. */
1735 ZERO_STRUCT(sfinfo
);
1736 sfinfo
.basic_info
.in
.attrib
= FILE_ATTRIBUTE_READONLY
;
1737 unix_to_nt_time(&sfinfo
.basic_info
.in
.write_time
, basetime
);
1739 sfinfo
.generic
.level
= RAW_SFILEINFO_BASIC_INFORMATION
;
1740 sfinfo
.generic
.in
.file
.fnum
= fnum
;
1741 status
= smb_raw_setfileinfo(cli
->tree
, &sfinfo
);
1742 torture_assert_ntstatus_equal_goto(tctx
, status
, NT_STATUS_OK
, ret
, done
, "smb_raw_setfileinfo failed");
1744 smbcli_close(cli
->tree
, fnum
);
1748 finfo
.generic
.level
= RAW_FILEINFO_ALL_INFO
;
1749 finfo
.generic
.in
.file
.path
= fname
;
1750 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1751 torture_assert_ntstatus_equal_goto(tctx
, status
, NT_STATUS_OK
, ret
, done
, "smb_raw_pathinfo failed");
1753 torture_assert_int_equal_goto(tctx
, finfo
.all_info
.out
.attrib
& ~FILE_ATTRIBUTE_NONINDEXED
, FILE_ATTRIBUTE_READONLY
, ret
, done
, "attrib incorrect");
1755 torture_assert_int_equal_goto(tctx
, nt_time_to_unix(finfo
.all_info
.out
.write_time
), basetime
, ret
, done
, "time incorrect");
1760 smbcli_close(cli
->tree
, fnum
);
1762 smbcli_unlink(cli
->tree
, fname
);
1763 smbcli_deltree(cli
->tree
, BASEDIR
);
1768 * A rough approximation of how a windows client creates the streams for use
1769 * in the summary tab.
1771 static bool test_stream_summary_tab(struct torture_context
*tctx
,
1772 struct smbcli_state
*cli
)
1777 const char *fname
= BASEDIR
"\\stream_summary.txt";
1778 const char *stream
= ":\005SummaryInformation:$DATA";
1779 const char *fname_stream
= NULL
;
1780 const char *tmp_stream
= ":Updt_\005SummaryInformation:$DATA";
1781 const char *fname_tmp_stream
= NULL
;
1783 union smb_fileinfo finfo
;
1784 union smb_rename rio
;
1787 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
), "Failed to setup up test directory: " BASEDIR
);
1789 fname_stream
= talloc_asprintf(tctx
, "%s%s", fname
, stream
);
1790 fname_tmp_stream
= talloc_asprintf(tctx
, "%s%s", fname
,
1793 /* Create summary info stream */
1794 ret
= create_file_with_stream(tctx
, cli
, fname_stream
);
1799 /* Create summary info tmp update stream */
1800 ret
= create_file_with_stream(tctx
, cli
, fname_tmp_stream
);
1805 /* Open tmp stream and write to it */
1806 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1807 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1808 io
.ntcreatex
.in
.flags
= 0;
1809 io
.ntcreatex
.in
.access_mask
= SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
;
1810 io
.ntcreatex
.in
.create_options
= 0;
1811 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
1812 io
.ntcreatex
.in
.share_access
= 0;
1813 io
.ntcreatex
.in
.alloc_size
= 0;
1814 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1815 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1816 io
.ntcreatex
.in
.security_flags
= 0;
1817 io
.ntcreatex
.in
.fname
= fname_tmp_stream
;
1819 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1820 CHECK_STATUS(status
, NT_STATUS_OK
);
1821 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1823 retsize
= smbcli_write(cli
->tree
, fnum
, 0, "test data", 0, 9);
1824 CHECK_VALUE(retsize
, 9);
1826 /* close the tmp stream. */
1827 smbcli_close(cli
->tree
, fnum
);
1830 /* Delete the current stream */
1831 smbcli_unlink(cli
->tree
, fname_stream
);
1833 /* Do the rename. */
1834 rio
.generic
.level
= RAW_RENAME_RENAME
;
1835 rio
.rename
.in
.pattern1
= fname_tmp_stream
;
1836 rio
.rename
.in
.pattern2
= stream
;
1837 rio
.rename
.in
.attrib
= FILE_ATTRIBUTE_SYSTEM
|
1838 FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_DIRECTORY
;
1839 status
= smb_raw_rename(cli
->tree
, &rio
);
1840 CHECK_STATUS(status
, NT_STATUS_OK
);
1842 /* Try to open the tmp stream that we just renamed away. */
1843 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1844 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1846 /* Query the base file to make sure it's still there. */
1847 finfo
.generic
.level
= RAW_FILEINFO_BASIC_INFO
;
1848 finfo
.generic
.in
.file
.path
= fname
;
1850 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1851 CHECK_STATUS(status
, NT_STATUS_OK
);
1856 smbcli_close(cli
->tree
, fnum
);
1858 smbcli_unlink(cli
->tree
, fname
);
1860 smbcli_deltree(cli
->tree
, BASEDIR
);
1864 /* Test how streams interact with base file permissions */
1865 /* Regression test for bug:
1866 https://bugzilla.samba.org/show_bug.cgi?id=10229
1867 bug #10229 - No access check verification on stream files.
1869 static bool test_stream_permissions(struct torture_context
*tctx
,
1870 struct smbcli_state
*cli
)
1875 const char *fname
= BASEDIR
"\\stream_permissions.txt";
1876 const char *stream
= "Stream One:$DATA";
1877 const char *fname_stream
;
1878 union smb_fileinfo finfo
;
1879 union smb_setfileinfo sfinfo
;
1881 union smb_fileinfo q
;
1882 union smb_setfileinfo set
;
1883 struct security_ace ace
= {};
1884 struct security_descriptor
*sd
;
1886 torture_assert(tctx
, torture_setup_dir(cli
, BASEDIR
),
1887 "Failed to setup up test directory: " BASEDIR
);
1889 torture_comment(tctx
, "(%s) testing permissions on streams\n", __location__
);
1891 fname_stream
= talloc_asprintf(tctx
, "%s:%s", fname
, stream
);
1893 /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */
1894 ret
= create_file_with_stream(tctx
, cli
, fname_stream
);
1900 finfo
.generic
.level
= RAW_FILEINFO_BASIC_INFO
;
1901 finfo
.generic
.in
.file
.path
= fname
;
1902 status
= smb_raw_pathinfo(cli
->tree
, tctx
, &finfo
);
1903 CHECK_STATUS(status
, NT_STATUS_OK
);
1905 torture_assert_int_equal_goto(tctx
,
1906 finfo
.all_info
.out
.attrib
& ~FILE_ATTRIBUTE_NONINDEXED
,
1907 FILE_ATTRIBUTE_ARCHIVE
, ret
, done
, "attrib incorrect");
1909 /* Change the attributes on the base file name. */
1910 ZERO_STRUCT(sfinfo
);
1911 sfinfo
.generic
.level
= RAW_SFILEINFO_SETATTR
;
1912 sfinfo
.generic
.in
.file
.path
= fname
;
1913 sfinfo
.setattr
.in
.attrib
= FILE_ATTRIBUTE_READONLY
;
1915 status
= smb_raw_setpathinfo(cli
->tree
, &sfinfo
);
1916 CHECK_STATUS(status
, NT_STATUS_OK
);
1918 /* Try and open the stream name for WRITE_DATA. Should
1919 fail with ACCESS_DENIED. */
1922 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1923 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1924 io
.ntcreatex
.in
.flags
= 0;
1925 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
1926 io
.ntcreatex
.in
.create_options
= 0;
1927 io
.ntcreatex
.in
.file_attr
= 0;
1928 io
.ntcreatex
.in
.share_access
= 0;
1929 io
.ntcreatex
.in
.alloc_size
= 0;
1930 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1931 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1932 io
.ntcreatex
.in
.security_flags
= 0;
1933 io
.ntcreatex
.in
.fname
= fname_stream
;
1935 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1936 CHECK_STATUS(status
, NT_STATUS_ACCESS_DENIED
);
1938 /* Change the attributes on the base file back. */
1939 ZERO_STRUCT(sfinfo
);
1940 sfinfo
.generic
.level
= RAW_SFILEINFO_SETATTR
;
1941 sfinfo
.generic
.in
.file
.path
= fname
;
1942 sfinfo
.setattr
.in
.attrib
= 0;
1944 status
= smb_raw_setpathinfo(cli
->tree
, &sfinfo
);
1945 CHECK_STATUS(status
, NT_STATUS_OK
);
1947 /* Re-open the file name. */
1950 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
1951 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
1952 io
.ntcreatex
.in
.flags
= 0;
1953 io
.ntcreatex
.in
.access_mask
= (SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
|
1954 SEC_STD_READ_CONTROL
|SEC_STD_WRITE_DAC
|
1955 SEC_FILE_WRITE_ATTRIBUTE
);
1956 io
.ntcreatex
.in
.create_options
= 0;
1957 io
.ntcreatex
.in
.file_attr
= 0;
1958 io
.ntcreatex
.in
.share_access
= 0;
1959 io
.ntcreatex
.in
.alloc_size
= 0;
1960 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
1961 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
1962 io
.ntcreatex
.in
.security_flags
= 0;
1963 io
.ntcreatex
.in
.fname
= fname
;
1965 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
1966 CHECK_STATUS(status
, NT_STATUS_OK
);
1968 fnum
= io
.ntcreatex
.out
.file
.fnum
;
1970 /* Get the existing security descriptor. */
1972 q
.query_secdesc
.level
= RAW_FILEINFO_SEC_DESC
;
1973 q
.query_secdesc
.in
.file
.fnum
= fnum
;
1974 q
.query_secdesc
.in
.secinfo_flags
=
1978 status
= smb_raw_fileinfo(cli
->tree
, tctx
, &q
);
1979 CHECK_STATUS(status
, NT_STATUS_OK
);
1980 sd
= q
.query_secdesc
.out
.sd
;
1982 /* Now add a DENY WRITE security descriptor for Everyone. */
1983 torture_comment(tctx
, "add a new ACE to the DACL\n");
1985 ace
.type
= SEC_ACE_TYPE_ACCESS_DENIED
;
1987 ace
.access_mask
= SEC_FILE_WRITE_DATA
;
1988 ace
.trustee
= global_sid_World
;
1990 status
= security_descriptor_dacl_add(sd
, &ace
);
1991 CHECK_STATUS(status
, NT_STATUS_OK
);
1993 /* security_descriptor_dacl_add adds to the *end* of
1994 the ace array, we need it at the start. Swap.. */
1995 ace
= sd
->dacl
->aces
[0];
1996 sd
->dacl
->aces
[0] = sd
->dacl
->aces
[sd
->dacl
->num_aces
-1];
1997 sd
->dacl
->aces
[sd
->dacl
->num_aces
-1] = ace
;
2000 set
.set_secdesc
.level
= RAW_SFILEINFO_SEC_DESC
;
2001 set
.set_secdesc
.in
.file
.fnum
= fnum
;
2002 set
.set_secdesc
.in
.secinfo_flags
= SECINFO_DACL
;
2003 set
.set_secdesc
.in
.sd
= sd
;
2005 status
= smb_raw_setfileinfo(cli
->tree
, &set
);
2006 CHECK_STATUS(status
, NT_STATUS_OK
);
2008 smbcli_close(cli
->tree
, fnum
);
2011 /* Try and open the stream name for WRITE_DATA. Should
2012 fail with ACCESS_DENIED. */
2015 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
2016 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
2017 io
.ntcreatex
.in
.flags
= 0;
2018 io
.ntcreatex
.in
.access_mask
= SEC_FILE_WRITE_DATA
;
2019 io
.ntcreatex
.in
.create_options
= 0;
2020 io
.ntcreatex
.in
.file_attr
= 0;
2021 io
.ntcreatex
.in
.share_access
= 0;
2022 io
.ntcreatex
.in
.alloc_size
= 0;
2023 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
2024 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
2025 io
.ntcreatex
.in
.security_flags
= 0;
2026 io
.ntcreatex
.in
.fname
= fname_stream
;
2028 status
= smb_raw_open(cli
->tree
, tctx
, &io
);
2029 CHECK_STATUS(status
, NT_STATUS_ACCESS_DENIED
);
2034 smbcli_close(cli
->tree
, fnum
);
2036 smbcli_unlink(cli
->tree
, fname
);
2038 smbcli_deltree(cli
->tree
, BASEDIR
);
2043 basic testing of streams calls
2045 struct torture_suite
*torture_raw_streams(TALLOC_CTX
*tctx
)
2047 struct torture_suite
*suite
= torture_suite_create(tctx
, "streams");
2049 torture_suite_add_1smb_test(suite
, "dir", test_stream_dir
);
2050 torture_suite_add_1smb_test(suite
, "io", test_stream_io
);
2051 torture_suite_add_1smb_test(suite
, "sharemodes", test_stream_sharemodes
);
2052 torture_suite_add_1smb_test(suite
, "delete", test_stream_delete
);
2053 torture_suite_add_1smb_test(suite
, "names", test_stream_names
);
2054 torture_suite_add_1smb_test(suite
, "names2", test_stream_names2
);
2055 torture_suite_add_1smb_test(suite
, "rename", test_stream_rename
);
2056 torture_suite_add_1smb_test(suite
, "rename2", test_stream_rename2
);
2057 torture_suite_add_1smb_test(suite
, "rename3", test_stream_rename3
);
2058 torture_suite_add_1smb_test(suite
, "createdisp",
2059 test_stream_create_disposition
);
2060 torture_suite_add_1smb_test(suite
, "attr", test_stream_attributes
);
2061 torture_suite_add_1smb_test(suite
, "sumtab", test_stream_summary_tab
);
2062 torture_suite_add_1smb_test(suite
, "perms", test_stream_permissions
);
2065 torture_suite_add_1smb_test(suite
, "LARGESTREAMINFO",
2066 test_stream_large_streaminfo
);