Move waf into third_party/.
[Samba.git] / source4 / torture / raw / streams.c
blob103a2c3057dee03763960f32af7c35ce9f573e39
1 /*
2 Unix SMB/CIFS implementation.
4 test alternate data streams
6 Copyright (C) Andrew Tridgell 2004
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "system/locale.h"
24 #include "torture/torture.h"
25 #include "libcli/raw/libcliraw.h"
26 #include "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 { \
46 bool ok; \
47 if ((v) && !(correct)) { \
48 ok = false; \
49 } else if (!(v) && (correct)) { \
50 ok = false; \
51 } else if (!(v) && !(correct)) { \
52 ok = true; \
53 } else if (strcmp((v), (correct)) == 0) { \
54 ok = true; \
55 } else { \
56 ok = false; \
57 } \
58 torture_assert(tctx,ok,\
59 talloc_asprintf(tctx, "got '%s', expected '%s'",\
60 (v)?(v):"NULL", (correct)?(correct):"NULL")); \
61 } while (0)
64 check that a stream has the right contents
66 static bool check_stream(struct smbcli_state *cli, const char *location,
67 TALLOC_CTX *mem_ctx,
68 const char *fname, const char *sname,
69 const char *value)
71 int fnum;
72 const char *full_name;
73 uint8_t *buf;
74 ssize_t ret;
76 full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname);
78 fnum = smbcli_open(cli->tree, full_name, O_RDONLY, DENY_NONE);
80 if (value == NULL) {
81 if (fnum != -1) {
82 printf("(%s) should have failed stream open of %s\n",
83 location, full_name);
84 return false;
86 return true;
89 if (fnum == -1) {
90 printf("(%s) Failed to open stream '%s' - %s\n",
91 location, full_name, smbcli_errstr(cli->tree));
92 return false;
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);
101 return false;
104 if (memcmp(buf, value, strlen(value)) != 0) {
105 printf("(%s) Bad data in stream\n", location);
106 return false;
109 smbcli_close(cli->tree, fnum);
110 return true;
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;
128 NTSTATUS status;
129 int i;
130 TALLOC_CTX *tmp_ctx = talloc_new(cli);
131 char **exp_sort;
132 struct stream_struct *stream_sort;
133 bool ret = false;
134 int fail = -1;
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);
144 if (num_exp == 0) {
145 ret = true;
146 goto done;
149 exp_sort = (char **)talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
151 if (exp_sort == NULL) {
152 goto done;
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) {
163 goto done;
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) {
170 fail = i;
171 goto show_streams;
175 ret = true;
176 done:
177 talloc_free(tmp_ctx);
178 return ret;
180 show_streams:
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);
187 return ret;
191 test bahavior of streams on directories
193 static bool test_stream_dir(struct torture_context *tctx,
194 struct smbcli_state *cli)
196 NTSTATUS status;
197 union smb_open io;
198 const char *fname = BASEDIR "\\stream.txt";
199 const char *sname1;
200 bool ret = true;
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);
258 done:
259 smbcli_deltree(cli->tree, BASEDIR);
260 return ret;
264 test basic behavior of streams on directories
266 static bool test_stream_io(struct torture_context *tctx,
267 struct smbcli_state *cli)
269 NTSTATUS status;
270 union smb_open io;
271 const char *fname = BASEDIR "\\stream.txt";
272 const char *sname1, *sname2;
273 bool ret = true;
274 int fnum = -1;
275 ssize_t retsize;
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);
397 done:
398 smbcli_close(cli->tree, fnum);
399 smbcli_deltree(cli->tree, BASEDIR);
400 return ret;
404 test stream sharemodes
406 static bool test_stream_sharemodes(struct torture_context *tctx,
407 struct smbcli_state *cli)
409 NTSTATUS status;
410 union smb_open io;
411 const char *fname = BASEDIR "\\stream.txt";
412 const char *sname1, *sname2;
413 bool ret = true;
414 int fnum1 = -1;
415 int fnum2 = -1;
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
451 * flags
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);
463 done:
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);
468 return ret;
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)
500 NTSTATUS status;
501 union smb_open io;
502 const char *fname = BASEDIR "\\stream.txt";
503 const char *sname1;
504 bool ret = true;
505 int fnum = -1;
506 uint8_t buf[9];
507 ssize_t retsize;
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);
606 } else {
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
621 |SEC_STD_DELETE;
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);
636 done:
637 smbcli_close(cli->tree, fnum);
638 smbcli_unlink(cli->tree, fname);
639 smbcli_deltree(cli->tree, BASEDIR);
640 return ret;
644 test stream names
646 static bool test_stream_names(struct torture_context *tctx,
647 struct smbcli_state *cli)
649 NTSTATUS status;
650 union smb_open io;
651 union smb_fileinfo info;
652 union smb_fileinfo finfo;
653 union smb_fileinfo stinfo;
654 union smb_setfileinfo sinfo;
655 const char *fname = BASEDIR "\\stream_names.txt";
656 const char *sname1, *sname1b, *sname1c, *sname1d;
657 const char *sname2, *snamew, *snamew2;
658 const char *snamer1;
659 bool ret = true;
660 int fnum1 = -1;
661 int fnum2 = -1;
662 int fnum3 = -1;
663 int i;
664 const char *four[4] = {
665 "::$DATA",
666 ":\x05Stream\n One:$DATA",
667 ":MStream Two:$DATA",
668 ":?Stream*:$DATA"
670 const char *five1[5] = {
671 "::$DATA",
672 ":\x05Stream\n One:$DATA",
673 ":BeforeRename:$DATA",
674 ":MStream Two:$DATA",
675 ":?Stream*:$DATA"
677 const char *five2[5] = {
678 "::$DATA",
679 ":\x05Stream\n One:$DATA",
680 ":AfterRename:$DATA",
681 ":MStream Two:$DATA",
682 ":?Stream*:$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");
717 ZERO_STRUCT(sinfo);
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
734 * base file.
736 sleep(2);
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");
759 ZERO_STRUCT(sinfo);
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);
774 ZERO_STRUCT(info);
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
792 * flags
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);
809 } else {
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);
818 } else {
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++) {
849 NTTIME write_time;
850 uint64_t stream_size;
851 char *path = talloc_asprintf(tctx, "%s%s",
852 fname, four[i]);
854 char *rpath = talloc_strdup(path, path);
855 char *p = strrchr(rpath, ':');
856 /* eat :$DATA */
857 *p = 0;
858 p--;
859 if (*p == ':') {
860 /* eat ::$DATA */
861 *p = 0;
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 |
867 SEC_RIGHTS_FILE_ALL;
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;
916 ZERO_STRUCT(sinfo);
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;
926 ZERO_STRUCT(sinfo);
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,
939 write_time);
940 CHECK_VALUE(stinfo.all_info.out.attrib,
941 finfo.all_info.out.attrib);
943 CHECK_VALUE(stinfo.all_info.out.size,
944 stream_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);
955 talloc_free(path);
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 |
962 SEC_RIGHTS_FILE_ALL;
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);
970 ZERO_STRUCT(sinfo);
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);
981 ZERO_STRUCT(sinfo);
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);
992 ZERO_STRUCT(sinfo);
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);
1004 } else {
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 */
1012 done:
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);
1018 return ret;
1022 test stream names
1024 static bool test_stream_names2(struct torture_context *tctx,
1025 struct smbcli_state *cli)
1027 NTSTATUS status;
1028 union smb_open io;
1029 const char *fname = BASEDIR "\\stream_names2.txt";
1030 bool ret = true;
1031 int fnum1 = -1;
1032 uint8_t i;
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",
1055 fname, i, i);
1056 NTSTATUS expected;
1058 switch (i) {
1059 case '/':/*0x2F*/
1060 case ':':/*0x3A*/
1061 case '\\':/*0x5C*/
1062 expected = NT_STATUS_OBJECT_NAME_INVALID;
1063 break;
1064 default:
1065 expected = NT_STATUS_OBJECT_NAME_NOT_FOUND;
1066 break;
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);
1081 talloc_free(path);
1084 done:
1085 if (fnum1 != -1) smbcli_close(cli->tree, fnum1);
1086 status = smbcli_unlink(cli->tree, fname);
1087 smbcli_deltree(cli->tree, BASEDIR);
1088 return ret;
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)); \
1098 ret = false; \
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)); \
1105 ret = false; \
1106 }} while (0)
1109 test stream renames
1111 static bool test_stream_rename(struct torture_context *tctx,
1112 struct smbcli_state *cli)
1114 NTSTATUS status, status2;
1115 union smb_open io;
1116 const char *fname = BASEDIR "\\stream_rename.txt";
1117 const char *sname1, *sname2;
1118 union smb_fileinfo finfo1;
1119 union smb_setfileinfo sfinfo;
1120 bool ret = true;
1121 int fnum = -1;
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);
1178 done:
1179 if (fnum != -1) smbcli_close(cli->tree, fnum);
1180 status = smbcli_unlink(cli->tree, fname);
1181 smbcli_deltree(cli->tree, BASEDIR);
1182 return ret;
1185 static bool test_stream_rename2(struct torture_context *tctx,
1186 struct smbcli_state *cli)
1188 NTSTATUS status;
1189 union smb_open io;
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";
1195 const char *sname1;
1196 const char *sname2;
1197 bool ret = true;
1198 int fnum = -1;
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",
1233 __location__);
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",
1247 __location__);
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",
1256 __location__);
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",
1265 __location__);
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",
1275 __location__);
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",
1302 __location__);
1303 ZERO_STRUCT(sinfo);
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)) {
1358 goto done;
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);
1373 done:
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);
1378 return ret;
1382 test stream renames
1384 static bool test_stream_rename3(struct torture_context *tctx,
1385 struct smbcli_state *cli)
1387 NTSTATUS status, status2;
1388 union smb_open io;
1389 const char *fname = BASEDIR "\\stream_rename.txt";
1390 const char *sname1, *sname2;
1391 union smb_fileinfo finfo1;
1392 union smb_setfileinfo sfinfo;
1393 bool ret = true;
1394 int fnum = -1;
1395 int fnum2 = -1;
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);
1454 } else {
1455 CHECK_CALL_FNUM(RENAME_INFORMATION,
1456 NT_STATUS_INVALID_PARAMETER);
1460 done:
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);
1465 return ret;
1468 static bool create_file_with_stream(struct torture_context *tctx,
1469 struct smbcli_state *cli,
1470 const char *stream)
1472 NTSTATUS status;
1473 bool ret = true;
1474 union smb_open io;
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);
1494 done:
1495 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
1496 return ret;
1499 /* Test how streams interact with create dispositions */
1500 static bool test_stream_create_disposition(struct torture_context *tctx,
1501 struct smbcli_state *cli)
1503 NTSTATUS status;
1504 union smb_open io;
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];
1510 bool ret = false;
1511 int fnum = -1;
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)) {
1521 goto done;
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)) {
1547 goto done;
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)) {
1559 goto done;
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)) {
1568 goto done;
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)) {
1576 goto done;
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)) {
1585 goto done;
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)) {
1593 goto done;
1597 * check ntcreatex overwrite_if on a stream.
1599 printf("(%s) Checking ntcreatex disp: overwrite_if on stream\n",
1600 __location__);
1601 smbcli_unlink(cli->tree, fname);
1602 if (!create_file_with_stream(tctx, cli, fname_stream)) {
1603 goto done;
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)) {
1612 goto done;
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)) {
1621 goto done;
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)) {
1639 goto done;
1642 ret = true;
1644 done:
1645 smbcli_close(cli->tree, fnum);
1646 smbcli_unlink(cli->tree, fname);
1647 smbcli_deltree(cli->tree, BASEDIR);
1648 return ret;
1651 #if 0
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
1657 char *lstream_name;
1658 const char *fname = BASEDIR "\\stream.txt";
1659 const char *fname_stream;
1660 NTSTATUS status;
1661 bool ret = true;
1662 int i;
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,
1677 lstream_name, i);
1678 ret = create_file_with_stream(tctx, cli, fname_stream);
1679 if (!ret) {
1680 goto done;
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);
1690 done:
1691 smbcli_unlink(cli->tree, fname);
1692 smbcli_deltree(cli->tree, BASEDIR);
1693 return ret;
1695 #endif
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)
1701 bool ret = true;
1702 NTSTATUS status;
1703 union smb_open io;
1704 const char *fname = BASEDIR "\\stream_attr.txt";
1705 const char *stream = "Stream One:$DATA";
1706 const char *fname_stream;
1707 int fnum = -1;
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);
1720 if (!ret) {
1721 goto done;
1724 ZERO_STRUCT(finfo);
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);
1764 fnum = -1;
1766 ZERO_STRUCT(finfo);
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");
1776 done:
1778 if (fnum != -1) {
1779 smbcli_close(cli->tree, fnum);
1781 smbcli_unlink(cli->tree, fname);
1782 smbcli_deltree(cli->tree, BASEDIR);
1783 return ret;
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)
1793 bool ret = true;
1794 NTSTATUS status;
1795 union smb_open io;
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;
1801 int fnum = -1;
1802 union smb_fileinfo finfo;
1803 union smb_rename rio;
1804 ssize_t retsize;
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,
1810 tmp_stream);
1812 /* Create summary info stream */
1813 ret = create_file_with_stream(tctx, cli, fname_stream);
1814 if (!ret) {
1815 goto done;
1818 /* Create summary info tmp update stream */
1819 ret = create_file_with_stream(tctx, cli, fname_tmp_stream);
1820 if (!ret) {
1821 goto done;
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);
1847 fnum = -1;
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);
1872 done:
1874 if (fnum != -1) {
1875 smbcli_close(cli->tree, fnum);
1877 smbcli_unlink(cli->tree, fname);
1879 smbcli_deltree(cli->tree, BASEDIR);
1880 return ret;
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)
1891 NTSTATUS status;
1892 bool ret = true;
1893 union smb_open io;
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;
1899 int fnum = -1;
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);
1914 if (!ret) {
1915 goto done;
1918 ZERO_STRUCT(finfo);
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. */
1940 ZERO_STRUCT(io);
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. */
1968 ZERO_STRUCT(io);
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. */
1990 ZERO_STRUCT(q);
1991 q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
1992 q.query_secdesc.in.file.fnum = fnum;
1993 q.query_secdesc.in.secinfo_flags =
1994 SECINFO_OWNER |
1995 SECINFO_GROUP |
1996 SECINFO_DACL;
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;
2005 ace.flags = 0;
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;
2018 ZERO_STRUCT(set);
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);
2028 fnum = -1;
2030 /* Try and open the stream name for WRITE_DATA. Should
2031 fail with ACCESS_DENIED. */
2033 ZERO_STRUCT(io);
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);
2050 done:
2052 if (fnum != -1) {
2053 smbcli_close(cli->tree, fnum);
2055 smbcli_unlink(cli->tree, fname);
2057 smbcli_deltree(cli->tree, BASEDIR);
2058 return ret;
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);
2083 #if 0
2084 torture_suite_add_1smb_test(suite, "LARGESTREAMINFO",
2085 test_stream_large_streaminfo);
2086 #endif
2088 return suite;