Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into v4-0-wsgi
[samba.git] / source4 / torture / raw / streams.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    test alternate data streams
5
6    Copyright (C) Andrew Tridgell 2004
7    
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.
12    
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.
17    
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/>.
20 */
21
22 #include "includes.h"
23 #include "torture/torture.h"
24 #include "libcli/raw/libcliraw.h"
25 #include "system/filesys.h"
26 #include "libcli/libcli.h"
27 #include "torture/util.h"
28
29 #define BASEDIR "\\teststreams"
30
31 #define CHECK_STATUS(status, correct) do { \
32         if (!NT_STATUS_EQUAL(status, correct)) { \
33                 printf("(%s) Incorrect status %s - should be %s\n", \
34                        __location__, nt_errstr(status), nt_errstr(correct)); \
35                 ret = false; \
36                 goto done; \
37         }} while (0)
38
39 #define CHECK_VALUE(v, correct) do { \
40         if ((v) != (correct)) { \
41                 printf("(%s) Incorrect value %s=%d - should be %d\n", \
42                        __location__, #v, (int)v, (int)correct); \
43                 ret = false; \
44         }} while (0)
45
46 /*
47   check that a stream has the right contents
48 */
49 static bool check_stream(struct smbcli_state *cli, const char *location,
50                          TALLOC_CTX *mem_ctx,
51                          const char *fname, const char *sname, 
52                          const char *value)
53 {
54         int fnum;
55         const char *full_name;
56         uint8_t *buf;
57         ssize_t ret;
58
59         full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname);
60
61         fnum = smbcli_open(cli->tree, full_name, O_RDONLY, DENY_NONE);
62
63         if (value == NULL) {
64                 if (fnum != -1) {
65                         printf("(%s) should have failed stream open of %s\n",
66                                location, full_name);
67                         return false;
68                 }
69                 return true;
70         }
71             
72         if (fnum == -1) {
73                 printf("(%s) Failed to open stream '%s' - %s\n",
74                        location, full_name, smbcli_errstr(cli->tree));
75                 return false;
76         }
77
78         buf = talloc_array(mem_ctx, uint8_t, strlen(value)+11);
79         
80         ret = smbcli_read(cli->tree, fnum, buf, 0, strlen(value)+11);
81         if (ret != strlen(value)) {
82                 printf("(%s) Failed to read %lu bytes from stream '%s' - got %d\n",
83                        location, (long)strlen(value), full_name, (int)ret);
84                 return false;
85         }
86
87         if (memcmp(buf, value, strlen(value)) != 0) {
88                 printf("(%s) Bad data in stream\n", location);
89                 return false;
90         }
91
92         smbcli_close(cli->tree, fnum);
93         return true;
94 }
95
96 static int qsort_string(const void *v1, const void *v2)
97 {
98         char * const *s1 = v1;
99         char * const *s2 = v2;
100         return strcmp(*s1, *s2);
101 }
102
103 static int qsort_stream(const void *v1, const void *v2)
104 {
105         const struct stream_struct * s1 = v1;
106         const struct stream_struct * s2 = v2;
107         return strcmp(s1->stream_name.s, s2->stream_name.s);
108 }
109
110 static bool check_stream_list(struct smbcli_state *cli, const char *fname,
111                               int num_exp, const char **exp)
112 {
113         union smb_fileinfo finfo;
114         NTSTATUS status;
115         int i;
116         TALLOC_CTX *tmp_ctx = talloc_new(cli);
117         char **exp_sort;
118         struct stream_struct *stream_sort;
119         bool ret = false;
120
121         finfo.generic.level = RAW_FILEINFO_STREAM_INFO;
122         finfo.generic.in.file.path = fname;
123
124         status = smb_raw_pathinfo(cli->tree, tmp_ctx, &finfo);
125         if (!NT_STATUS_IS_OK(status)) {
126                 d_fprintf(stderr, "(%s) smb_raw_pathinfo failed: %s\n",
127                           __location__, nt_errstr(status));
128                 goto fail;
129         }
130
131         if (finfo.stream_info.out.num_streams != num_exp) {
132                 d_fprintf(stderr, "(%s) expected %d streams, got %d\n",
133                           __location__, num_exp,
134                           finfo.stream_info.out.num_streams);
135                 goto fail;
136         }
137
138         if (num_exp == 0) {
139                 ret = true;
140                 goto fail;
141         }
142
143         exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
144
145         if (exp_sort == NULL) {
146                 goto fail;
147         }
148
149         qsort(exp_sort, num_exp, sizeof(*exp_sort), qsort_string);
150
151         stream_sort = talloc_memdup(tmp_ctx, finfo.stream_info.out.streams,
152                                     finfo.stream_info.out.num_streams *
153                                     sizeof(*stream_sort));
154
155         if (stream_sort == NULL) {
156                 goto fail;
157         }
158
159         qsort(stream_sort, finfo.stream_info.out.num_streams,
160               sizeof(*stream_sort), qsort_stream);
161
162         for (i=0; i<num_exp; i++) {
163                 if (strcmp(exp_sort[i], stream_sort[i].stream_name.s) != 0) {
164                         d_fprintf(stderr, "(%s) expected stream name %s, got "
165                                   "%s\n", __location__, exp_sort[i],
166                                   stream_sort[i].stream_name.s);
167                         goto fail;
168                 }
169         }
170
171         ret = true;
172  fail:
173         talloc_free(tmp_ctx);
174         return ret;
175 }
176
177 /*
178   test bahavior of streams on directories
179 */
180 static bool test_stream_dir(struct torture_context *tctx,
181                            struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
182 {
183         NTSTATUS status;
184         union smb_open io;
185         const char *fname = BASEDIR "\\stream.txt";
186         const char *sname1;
187         bool ret = true;
188         const char *basedir_data;
189
190         basedir_data = talloc_asprintf(mem_ctx, "%s::$DATA", BASEDIR);
191         sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
192
193         printf("(%s) opening non-existant directory stream\n", __location__);
194         io.generic.level = RAW_OPEN_NTCREATEX;
195         io.ntcreatex.in.root_fid = 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, mem_ctx, &io);
207         CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
208
209         printf("(%s) opening basedir  stream\n", __location__);
210         io.generic.level = RAW_OPEN_NTCREATEX;
211         io.ntcreatex.in.root_fid = 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, mem_ctx, &io);
223         CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
224
225         printf("(%s) opening basedir ::$DATA stream\n", __location__);
226         io.generic.level = RAW_OPEN_NTCREATEX;
227         io.ntcreatex.in.root_fid = 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, mem_ctx, &io);
239         if (torture_setting_bool(tctx, "samba3", false)) {
240                 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
241         } else {
242                 CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
243         }
244
245         printf("(%s) list the streams on the basedir\n", __location__);
246         ret &= check_stream_list(cli, BASEDIR, 0, NULL);
247 done:
248         return ret;
249 }
250
251 /*
252   test basic behavior of streams on directories
253 */
254 static bool test_stream_io(struct torture_context *tctx,
255                            struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
256 {
257         NTSTATUS status;
258         union smb_open io;
259         const char *fname = BASEDIR "\\stream.txt";
260         const char *sname1, *sname2;
261         bool ret = true;
262         int fnum = -1;
263         ssize_t retsize;
264
265         const char *one[] = { "::$DATA" };
266         const char *two[] = { "::$DATA", ":Second Stream:$DATA" };
267         const char *three[] = { "::$DATA", ":Stream One:$DATA",
268                                 ":Second Stream:$DATA" };
269
270         sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
271         sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "Second Stream");
272
273         printf("(%s) creating a stream on a non-existant file\n", __location__);
274         io.generic.level = RAW_OPEN_NTCREATEX;
275         io.ntcreatex.in.root_fid = 0;
276         io.ntcreatex.in.flags = 0;
277         io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
278         io.ntcreatex.in.create_options = 0;
279         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
280         io.ntcreatex.in.share_access = 0;
281         io.ntcreatex.in.alloc_size = 0;
282         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
283         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
284         io.ntcreatex.in.security_flags = 0;
285         io.ntcreatex.in.fname = sname1;
286         status = smb_raw_open(cli->tree, mem_ctx, &io);
287         CHECK_STATUS(status, NT_STATUS_OK);
288         fnum = io.ntcreatex.out.file.fnum;
289
290         ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One", NULL);
291
292         printf("(%s) check that open of base file is allowed\n", __location__);
293         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
294         io.ntcreatex.in.fname = fname;
295         status = smb_raw_open(cli->tree, mem_ctx, &io);
296         CHECK_STATUS(status, NT_STATUS_OK);
297         smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
298
299         printf("(%s) writing to stream\n", __location__);
300         retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9);
301         CHECK_VALUE(retsize, 9);
302
303         smbcli_close(cli->tree, fnum);
304
305         ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One", "test data");
306
307         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
308         io.ntcreatex.in.fname = sname1;
309         status = smb_raw_open(cli->tree, mem_ctx, &io);
310         CHECK_STATUS(status, NT_STATUS_OK);
311         fnum = io.ntcreatex.out.file.fnum;
312
313         printf("(%s) modifying stream\n", __location__);
314         retsize = smbcli_write(cli->tree, fnum, 0, "MORE DATA ", 5, 10);
315         CHECK_VALUE(retsize, 10);
316
317         smbcli_close(cli->tree, fnum);
318
319         ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:$FOO", NULL);
320
321         printf("(%s) creating a stream2 on a existing file\n", __location__);
322         io.ntcreatex.in.fname = sname2;
323         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
324         status = smb_raw_open(cli->tree, mem_ctx, &io);
325         CHECK_STATUS(status, NT_STATUS_OK);
326         fnum = io.ntcreatex.out.file.fnum;
327
328         printf("(%s) modifying stream\n", __location__);
329         retsize = smbcli_write(cli->tree, fnum, 0, "SECOND STREAM", 0, 13);
330         CHECK_VALUE(retsize, 13);
331
332         smbcli_close(cli->tree, fnum);
333
334         ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One", "test MORE DATA ");
335         ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:$DATA", "test MORE DATA ");
336         ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:", NULL);
337         ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream", "SECOND STREAM");
338         if (!torture_setting_bool(tctx, "samba4", false)) {
339                 ret &= check_stream(cli, __location__, mem_ctx, fname,
340                                     "SECOND STREAM:$DATA", "SECOND STREAM");
341         }
342         ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:$DATA", "SECOND STREAM");
343         ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:", NULL);
344         ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:$FOO", NULL);
345
346         check_stream_list(cli, fname, 3, three);
347
348         printf("(%s) deleting stream\n", __location__);
349         status = smbcli_unlink(cli->tree, sname1);
350         CHECK_STATUS(status, NT_STATUS_OK);
351
352         check_stream_list(cli, fname, 2, two);
353
354         printf("(%s) delete a stream via delete-on-close\n", __location__);
355         io.ntcreatex.in.fname = sname2;
356         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
357         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
358         io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
359         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
360
361         status = smb_raw_open(cli->tree, mem_ctx, &io);
362         CHECK_STATUS(status, NT_STATUS_OK);
363         fnum = io.ntcreatex.out.file.fnum;
364         
365         smbcli_close(cli->tree, fnum);
366         status = smbcli_unlink(cli->tree, sname2);
367         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
368
369         check_stream_list(cli, fname, 1, one);
370
371         if (!torture_setting_bool(tctx, "samba4", false)) {
372                 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
373                 io.ntcreatex.in.fname = sname1;
374                 status = smb_raw_open(cli->tree, mem_ctx, &io);
375                 CHECK_STATUS(status, NT_STATUS_OK);
376                 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
377                 io.ntcreatex.in.fname = sname2;
378                 status = smb_raw_open(cli->tree, mem_ctx, &io);
379                 CHECK_STATUS(status, NT_STATUS_OK);
380                 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
381         }
382
383         printf("(%s) deleting file\n", __location__);
384         status = smbcli_unlink(cli->tree, fname);
385         CHECK_STATUS(status, NT_STATUS_OK);
386
387 done:
388         smbcli_close(cli->tree, fnum);
389         return ret;
390 }
391
392 /*
393   test stream sharemodes
394 */
395 static bool test_stream_sharemodes(struct torture_context *tctx,
396                                    struct smbcli_state *cli,
397                                    TALLOC_CTX *mem_ctx)
398 {
399         NTSTATUS status;
400         union smb_open io;
401         const char *fname = BASEDIR "\\stream.txt";
402         const char *sname1, *sname2;
403         bool ret = true;
404         int fnum1 = -1;
405         int fnum2 = -1;
406
407         sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
408         sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "Second Stream");
409
410         printf("(%s) testing stream share mode conflicts\n", __location__);
411         io.generic.level = RAW_OPEN_NTCREATEX;
412         io.ntcreatex.in.root_fid = 0;
413         io.ntcreatex.in.flags = 0;
414         io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
415         io.ntcreatex.in.create_options = 0;
416         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
417         io.ntcreatex.in.share_access = 0;
418         io.ntcreatex.in.alloc_size = 0;
419         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
420         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
421         io.ntcreatex.in.security_flags = 0;
422         io.ntcreatex.in.fname = sname1;
423
424         status = smb_raw_open(cli->tree, mem_ctx, &io);
425         CHECK_STATUS(status, NT_STATUS_OK);
426         fnum1 = io.ntcreatex.out.file.fnum;
427
428         /*
429          * A different stream does not give a sharing violation
430          */
431
432         io.ntcreatex.in.fname = sname2;
433         status = smb_raw_open(cli->tree, mem_ctx, &io);
434         CHECK_STATUS(status, NT_STATUS_OK);
435         fnum2 = io.ntcreatex.out.file.fnum;
436
437         /*
438          * ... whereas the same stream does with unchanged access/share_access
439          * flags
440          */
441
442         io.ntcreatex.in.fname = sname1;
443         io.ntcreatex.in.open_disposition = 0;
444         status = smb_raw_open(cli->tree, mem_ctx, &io);
445         CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
446
447         io.ntcreatex.in.fname = sname2;
448         status = smb_raw_open(cli->tree, mem_ctx, &io);
449         CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
450
451 done:
452         if (fnum1 != -1) smbcli_close(cli->tree, fnum1);
453         if (fnum2 != -1) smbcli_close(cli->tree, fnum2);
454         status = smbcli_unlink(cli->tree, fname);
455         return ret;
456 }
457
458 /* 
459  *  Test FILE_SHARE_DELETE on streams
460  *
461  * A stream opened with !FILE_SHARE_DELETE prevents the main file to be opened
462  * with SEC_STD_DELETE.
463  *
464  * The main file opened with !FILE_SHARE_DELETE does *not* prevent a stream to
465  * be opened with SEC_STD_DELETE.
466  *
467  * A stream held open with FILE_SHARE_DELETE allows the file to be
468  * deleted. After the main file is deleted, access to the open file descriptor
469  * still works, but all name-based access to both the main file as well as the
470  * stream is denied with DELETE ending.
471  *
472  * This means, an open of the main file with SEC_STD_DELETE should walk all
473  * streams and also open them with SEC_STD_DELETE. If any of these opens gives
474  * SHARING_VIOLATION, the main open fails.
475  *
476  * Closing the main file after delete_on_close has been set does not really
477  * unlink it but leaves the corresponding share mode entry with
478  * delete_on_close being set around until all streams are closed.
479  *
480  * Opening a stream must also look at the main file's share mode entry, look
481  * at the delete_on_close bit and potentially return DELETE_PENDING.
482  */
483
484 static bool test_stream_delete(struct torture_context *tctx,
485                                struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
486 {
487         NTSTATUS status;
488         union smb_open io;
489         const char *fname = BASEDIR "\\stream.txt";
490         const char *sname1;
491         bool ret = true;
492         int fnum = -1;
493         uint8_t buf[9];
494         ssize_t retsize;
495         union smb_fileinfo finfo;
496
497         sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
498
499         printf("(%s) opening non-existant file stream\n", __location__);
500         io.generic.level = RAW_OPEN_NTCREATEX;
501         io.ntcreatex.in.root_fid = 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;
512
513         status = smb_raw_open(cli->tree, mem_ctx, &io);
514         CHECK_STATUS(status, NT_STATUS_OK);
515         fnum = io.ntcreatex.out.file.fnum;
516
517         retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9);
518         CHECK_VALUE(retsize, 9);
519
520         /*
521          * One stream opened without FILE_SHARE_DELETE prevents the main file
522          * to be deleted or even opened with DELETE access
523          */
524
525         status = smbcli_unlink(cli->tree, fname);
526         CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
527
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, mem_ctx, &io);
532         CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
533
534         smbcli_close(cli->tree, fnum);
535
536         /*
537          * ... but unlink works if a stream is opened with FILE_SHARE_DELETE
538          */
539
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, mem_ctx, &io);
544         CHECK_STATUS(status, NT_STATUS_OK);
545         fnum = io.ntcreatex.out.file.fnum;
546
547         status = smbcli_unlink(cli->tree, fname);
548         CHECK_STATUS(status, NT_STATUS_OK);
549
550         /*
551          * file access still works on the stream while the main file is closed
552          */
553
554         retsize = smbcli_read(cli->tree, fnum, buf, 0, 9);
555         CHECK_VALUE(retsize, 9);
556
557         finfo.generic.level = RAW_FILEINFO_STANDARD;
558         finfo.generic.in.file.path = fname;
559
560         /*
561          * name-based access to both the main file and the stream does not
562          * work anymore but gives DELETE_PENDING
563          */
564
565         status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
566         CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
567
568         if (!torture_setting_bool(tctx, "samba3", false)) {
569
570                 /*
571                  * S3 doesn't do this yet
572                  */
573
574                 finfo.generic.in.file.path = sname1;
575                 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
576                 CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
577         }
578
579         /*
580          * fd-based qfileinfo on the stream still works, the stream does not
581          * have the delete-on-close bit set. This could mean that open on the
582          * stream first opens the main file
583          */
584
585         finfo.all_info.level = RAW_FILEINFO_ALL_INFO;
586         finfo.all_info.in.file.fnum = fnum;
587
588         status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo);
589         CHECK_STATUS(status, NT_STATUS_OK);
590         CHECK_VALUE(finfo.all_info.out.delete_pending, 0);
591
592         smbcli_close(cli->tree, fnum);
593
594         /*
595          * After closing the stream the file is really gone.
596          */
597
598         finfo.generic.in.file.path = fname;
599         status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
600         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
601
602         io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA
603                 |SEC_STD_DELETE;
604         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
605         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
606         status = smb_raw_open(cli->tree, mem_ctx, &io);
607         CHECK_STATUS(status, NT_STATUS_OK);
608         fnum = io.ntcreatex.out.file.fnum;
609
610         finfo.generic.in.file.path = fname;
611         status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
612         CHECK_STATUS(status, NT_STATUS_OK);
613
614         smbcli_close(cli->tree, fnum);
615
616         status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
617         CHECK_STATUS(status, NT_STATUS_OK);
618 done:
619         smbcli_close(cli->tree, fnum);
620         return ret;
621 }
622
623 /* 
624    basic testing of streams calls
625 */
626 bool torture_raw_streams(struct torture_context *torture, 
627                          struct smbcli_state *cli)
628 {
629         bool ret = true;
630
631         if (!torture_setup_dir(cli, BASEDIR)) {
632                 return false;
633         }
634
635         ret &= test_stream_dir(torture, cli, torture);
636         smb_raw_exit(cli->session);
637         ret &= test_stream_io(torture, cli, torture);
638         smb_raw_exit(cli->session);
639         ret &= test_stream_sharemodes(torture, cli, torture);
640         smb_raw_exit(cli->session);
641         if (!torture_setting_bool(torture, "samba4", false)) {
642                 ret &= test_stream_delete(torture, cli, torture);
643         }
644
645         smb_raw_exit(cli->session);
646         smbcli_deltree(cli->tree, BASEDIR);
647
648         return ret;
649 }