RAW-STREAMS: test valid character in the range of 0x01 => 0x7F
[metze/samba/wip.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 "system/locale.h"
24 #include "torture/torture.h"
25 #include "libcli/raw/libcliraw.h"
26 #include "system/filesys.h"
27 #include "libcli/libcli.h"
28 #include "torture/util.h"
29
30 #define BASEDIR "\\teststreams"
31
32 #define CHECK_STATUS(status, correct) do { \
33         if (!NT_STATUS_EQUAL(status, correct)) { \
34                 printf("(%s) Incorrect status %s - should be %s\n", \
35                        __location__, nt_errstr(status), nt_errstr(correct)); \
36                 ret = false; \
37                 goto done; \
38         }} while (0)
39
40 #define CHECK_VALUE(v, correct) do { \
41         if ((v) != (correct)) { \
42                 printf("(%s) Incorrect value %s=%d - should be %d\n", \
43                        __location__, #v, (int)v, (int)correct); \
44                 ret = false; \
45         }} while (0)
46
47 /*
48   check that a stream has the right contents
49 */
50 static bool check_stream(struct smbcli_state *cli, const char *location,
51                          TALLOC_CTX *mem_ctx,
52                          const char *fname, const char *sname, 
53                          const char *value)
54 {
55         int fnum;
56         const char *full_name;
57         uint8_t *buf;
58         ssize_t ret;
59
60         full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname);
61
62         fnum = smbcli_open(cli->tree, full_name, O_RDONLY, DENY_NONE);
63
64         if (value == NULL) {
65                 if (fnum != -1) {
66                         printf("(%s) should have failed stream open of %s\n",
67                                location, full_name);
68                         return false;
69                 }
70                 return true;
71         }
72             
73         if (fnum == -1) {
74                 printf("(%s) Failed to open stream '%s' - %s\n",
75                        location, full_name, smbcli_errstr(cli->tree));
76                 return false;
77         }
78
79         buf = talloc_array(mem_ctx, uint8_t, strlen(value)+11);
80         
81         ret = smbcli_read(cli->tree, fnum, buf, 0, strlen(value)+11);
82         if (ret != strlen(value)) {
83                 printf("(%s) Failed to read %lu bytes from stream '%s' - got %d\n",
84                        location, (long)strlen(value), full_name, (int)ret);
85                 return false;
86         }
87
88         if (memcmp(buf, value, strlen(value)) != 0) {
89                 printf("(%s) Bad data in stream\n", location);
90                 return false;
91         }
92
93         smbcli_close(cli->tree, fnum);
94         return true;
95 }
96
97 static int qsort_string(const void *v1, const void *v2)
98 {
99         char * const *s1 = v1;
100         char * const *s2 = v2;
101         return strcmp(*s1, *s2);
102 }
103
104 static int qsort_stream(const void *v1, const void *v2)
105 {
106         const struct stream_struct * s1 = v1;
107         const struct stream_struct * s2 = v2;
108         return strcmp(s1->stream_name.s, s2->stream_name.s);
109 }
110
111 static bool check_stream_list(struct smbcli_state *cli, const char *fname,
112                               int num_exp, const char **exp)
113 {
114         union smb_fileinfo finfo;
115         NTSTATUS status;
116         int i;
117         TALLOC_CTX *tmp_ctx = talloc_new(cli);
118         char **exp_sort;
119         struct stream_struct *stream_sort;
120         bool ret = false;
121
122         finfo.generic.level = RAW_FILEINFO_STREAM_INFO;
123         finfo.generic.in.file.path = fname;
124
125         status = smb_raw_pathinfo(cli->tree, tmp_ctx, &finfo);
126         if (!NT_STATUS_IS_OK(status)) {
127                 d_fprintf(stderr, "(%s) smb_raw_pathinfo failed: %s\n",
128                           __location__, nt_errstr(status));
129                 goto fail;
130         }
131
132         if (finfo.stream_info.out.num_streams != num_exp) {
133                 d_fprintf(stderr, "(%s) expected %d streams, got %d\n",
134                           __location__, num_exp,
135                           finfo.stream_info.out.num_streams);
136                 goto fail;
137         }
138
139         if (num_exp == 0) {
140                 ret = true;
141                 goto fail;
142         }
143
144         exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
145
146         if (exp_sort == NULL) {
147                 goto fail;
148         }
149
150         qsort(exp_sort, num_exp, sizeof(*exp_sort), qsort_string);
151
152         stream_sort = talloc_memdup(tmp_ctx, finfo.stream_info.out.streams,
153                                     finfo.stream_info.out.num_streams *
154                                     sizeof(*stream_sort));
155
156         if (stream_sort == NULL) {
157                 goto fail;
158         }
159
160         qsort(stream_sort, finfo.stream_info.out.num_streams,
161               sizeof(*stream_sort), qsort_stream);
162
163         for (i=0; i<num_exp; i++) {
164                 if (strcmp(exp_sort[i], stream_sort[i].stream_name.s) != 0) {
165                         d_fprintf(stderr, "(%s) expected stream name %s, got "
166                                   "%s\n", __location__, exp_sort[i],
167                                   stream_sort[i].stream_name.s);
168                         goto fail;
169                 }
170         }
171
172         ret = true;
173  fail:
174         talloc_free(tmp_ctx);
175         return ret;
176 }
177
178 /*
179   test bahavior of streams on directories
180 */
181 static bool test_stream_dir(struct torture_context *tctx,
182                            struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
183 {
184         NTSTATUS status;
185         union smb_open io;
186         const char *fname = BASEDIR "\\stream.txt";
187         const char *sname1;
188         bool ret = true;
189         const char *basedir_data;
190
191         basedir_data = talloc_asprintf(mem_ctx, "%s::$DATA", BASEDIR);
192         sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
193
194         printf("(%s) opening non-existant directory stream\n", __location__);
195         io.generic.level = RAW_OPEN_NTCREATEX;
196         io.ntcreatex.in.root_fid = 0;
197         io.ntcreatex.in.flags = 0;
198         io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
199         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
200         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
201         io.ntcreatex.in.share_access = 0;
202         io.ntcreatex.in.alloc_size = 0;
203         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
204         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
205         io.ntcreatex.in.security_flags = 0;
206         io.ntcreatex.in.fname = sname1;
207         status = smb_raw_open(cli->tree, mem_ctx, &io);
208         CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
209
210         printf("(%s) opening basedir  stream\n", __location__);
211         io.generic.level = RAW_OPEN_NTCREATEX;
212         io.ntcreatex.in.root_fid = 0;
213         io.ntcreatex.in.flags = 0;
214         io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
215         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
216         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY;
217         io.ntcreatex.in.share_access = 0;
218         io.ntcreatex.in.alloc_size = 0;
219         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
220         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
221         io.ntcreatex.in.security_flags = 0;
222         io.ntcreatex.in.fname = basedir_data;
223         status = smb_raw_open(cli->tree, mem_ctx, &io);
224         CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
225
226         printf("(%s) opening basedir ::$DATA stream\n", __location__);
227         io.generic.level = RAW_OPEN_NTCREATEX;
228         io.ntcreatex.in.root_fid = 0;
229         io.ntcreatex.in.flags = 0x10;
230         io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
231         io.ntcreatex.in.create_options = 0;
232         io.ntcreatex.in.file_attr = 0;
233         io.ntcreatex.in.share_access = 0;
234         io.ntcreatex.in.alloc_size = 0;
235         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
236         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
237         io.ntcreatex.in.security_flags = 0;
238         io.ntcreatex.in.fname = basedir_data;
239         status = smb_raw_open(cli->tree, mem_ctx, &io);
240         if (torture_setting_bool(tctx, "samba3", false)) {
241                 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
242         } else {
243                 CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
244         }
245
246         printf("(%s) list the streams on the basedir\n", __location__);
247         ret &= check_stream_list(cli, BASEDIR, 0, NULL);
248 done:
249         return ret;
250 }
251
252 /*
253   test basic behavior of streams on directories
254 */
255 static bool test_stream_io(struct torture_context *tctx,
256                            struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
257 {
258         NTSTATUS status;
259         union smb_open io;
260         const char *fname = BASEDIR "\\stream.txt";
261         const char *sname1, *sname2;
262         bool ret = true;
263         int fnum = -1;
264         ssize_t retsize;
265
266         const char *one[] = { "::$DATA" };
267         const char *two[] = { "::$DATA", ":Second Stream:$DATA" };
268         const char *three[] = { "::$DATA", ":Stream One:$DATA",
269                                 ":Second Stream:$DATA" };
270
271         sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
272         sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "Second Stream");
273
274         printf("(%s) creating a stream on a non-existant file\n", __location__);
275         io.generic.level = RAW_OPEN_NTCREATEX;
276         io.ntcreatex.in.root_fid = 0;
277         io.ntcreatex.in.flags = 0;
278         io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
279         io.ntcreatex.in.create_options = 0;
280         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
281         io.ntcreatex.in.share_access = 0;
282         io.ntcreatex.in.alloc_size = 0;
283         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
284         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
285         io.ntcreatex.in.security_flags = 0;
286         io.ntcreatex.in.fname = sname1;
287         status = smb_raw_open(cli->tree, mem_ctx, &io);
288         CHECK_STATUS(status, NT_STATUS_OK);
289         fnum = io.ntcreatex.out.file.fnum;
290
291         ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One", NULL);
292
293         printf("(%s) check that open of base file is allowed\n", __location__);
294         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
295         io.ntcreatex.in.fname = fname;
296         status = smb_raw_open(cli->tree, mem_ctx, &io);
297         CHECK_STATUS(status, NT_STATUS_OK);
298         smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
299
300         printf("(%s) writing to stream\n", __location__);
301         retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9);
302         CHECK_VALUE(retsize, 9);
303
304         smbcli_close(cli->tree, fnum);
305
306         ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One", "test data");
307
308         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
309         io.ntcreatex.in.fname = sname1;
310         status = smb_raw_open(cli->tree, mem_ctx, &io);
311         CHECK_STATUS(status, NT_STATUS_OK);
312         fnum = io.ntcreatex.out.file.fnum;
313
314         printf("(%s) modifying stream\n", __location__);
315         retsize = smbcli_write(cli->tree, fnum, 0, "MORE DATA ", 5, 10);
316         CHECK_VALUE(retsize, 10);
317
318         smbcli_close(cli->tree, fnum);
319
320         ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:$FOO", NULL);
321
322         printf("(%s) creating a stream2 on a existing file\n", __location__);
323         io.ntcreatex.in.fname = sname2;
324         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
325         status = smb_raw_open(cli->tree, mem_ctx, &io);
326         CHECK_STATUS(status, NT_STATUS_OK);
327         fnum = io.ntcreatex.out.file.fnum;
328
329         printf("(%s) modifying stream\n", __location__);
330         retsize = smbcli_write(cli->tree, fnum, 0, "SECOND STREAM", 0, 13);
331         CHECK_VALUE(retsize, 13);
332
333         smbcli_close(cli->tree, fnum);
334
335         ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One", "test MORE DATA ");
336         ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:$DATA", "test MORE DATA ");
337         ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:", NULL);
338         ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream", "SECOND STREAM");
339         if (!torture_setting_bool(tctx, "samba4", false)) {
340                 ret &= check_stream(cli, __location__, mem_ctx, fname,
341                                     "SECOND STREAM:$DATA", "SECOND STREAM");
342         }
343         ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:$DATA", "SECOND STREAM");
344         ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:", NULL);
345         ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:$FOO", NULL);
346
347         check_stream_list(cli, fname, 3, three);
348
349         printf("(%s) deleting stream\n", __location__);
350         status = smbcli_unlink(cli->tree, sname1);
351         CHECK_STATUS(status, NT_STATUS_OK);
352
353         check_stream_list(cli, fname, 2, two);
354
355         printf("(%s) delete a stream via delete-on-close\n", __location__);
356         io.ntcreatex.in.fname = sname2;
357         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
358         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
359         io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
360         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
361
362         status = smb_raw_open(cli->tree, mem_ctx, &io);
363         CHECK_STATUS(status, NT_STATUS_OK);
364         fnum = io.ntcreatex.out.file.fnum;
365         
366         smbcli_close(cli->tree, fnum);
367         status = smbcli_unlink(cli->tree, sname2);
368         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
369
370         check_stream_list(cli, fname, 1, one);
371
372         if (!torture_setting_bool(tctx, "samba4", false)) {
373                 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
374                 io.ntcreatex.in.fname = sname1;
375                 status = smb_raw_open(cli->tree, mem_ctx, &io);
376                 CHECK_STATUS(status, NT_STATUS_OK);
377                 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
378                 io.ntcreatex.in.fname = sname2;
379                 status = smb_raw_open(cli->tree, mem_ctx, &io);
380                 CHECK_STATUS(status, NT_STATUS_OK);
381                 smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
382         }
383
384         printf("(%s) deleting file\n", __location__);
385         status = smbcli_unlink(cli->tree, fname);
386         CHECK_STATUS(status, NT_STATUS_OK);
387
388 done:
389         smbcli_close(cli->tree, fnum);
390         return ret;
391 }
392
393 /*
394   test stream sharemodes
395 */
396 static bool test_stream_sharemodes(struct torture_context *tctx,
397                                    struct smbcli_state *cli,
398                                    TALLOC_CTX *mem_ctx)
399 {
400         NTSTATUS status;
401         union smb_open io;
402         const char *fname = BASEDIR "\\stream.txt";
403         const char *sname1, *sname2;
404         bool ret = true;
405         int fnum1 = -1;
406         int fnum2 = -1;
407
408         sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
409         sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "Second Stream");
410
411         printf("(%s) testing stream share mode conflicts\n", __location__);
412         io.generic.level = RAW_OPEN_NTCREATEX;
413         io.ntcreatex.in.root_fid = 0;
414         io.ntcreatex.in.flags = 0;
415         io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
416         io.ntcreatex.in.create_options = 0;
417         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
418         io.ntcreatex.in.share_access = 0;
419         io.ntcreatex.in.alloc_size = 0;
420         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
421         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
422         io.ntcreatex.in.security_flags = 0;
423         io.ntcreatex.in.fname = sname1;
424
425         status = smb_raw_open(cli->tree, mem_ctx, &io);
426         CHECK_STATUS(status, NT_STATUS_OK);
427         fnum1 = io.ntcreatex.out.file.fnum;
428
429         /*
430          * A different stream does not give a sharing violation
431          */
432
433         io.ntcreatex.in.fname = sname2;
434         status = smb_raw_open(cli->tree, mem_ctx, &io);
435         CHECK_STATUS(status, NT_STATUS_OK);
436         fnum2 = io.ntcreatex.out.file.fnum;
437
438         /*
439          * ... whereas the same stream does with unchanged access/share_access
440          * flags
441          */
442
443         io.ntcreatex.in.fname = sname1;
444         io.ntcreatex.in.open_disposition = 0;
445         status = smb_raw_open(cli->tree, mem_ctx, &io);
446         CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
447
448         io.ntcreatex.in.fname = sname2;
449         status = smb_raw_open(cli->tree, mem_ctx, &io);
450         CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
451
452 done:
453         if (fnum1 != -1) smbcli_close(cli->tree, fnum1);
454         if (fnum2 != -1) smbcli_close(cli->tree, fnum2);
455         status = smbcli_unlink(cli->tree, fname);
456         return ret;
457 }
458
459 /* 
460  *  Test FILE_SHARE_DELETE on streams
461  *
462  * A stream opened with !FILE_SHARE_DELETE prevents the main file to be opened
463  * with SEC_STD_DELETE.
464  *
465  * The main file opened with !FILE_SHARE_DELETE does *not* prevent a stream to
466  * be opened with SEC_STD_DELETE.
467  *
468  * A stream held open with FILE_SHARE_DELETE allows the file to be
469  * deleted. After the main file is deleted, access to the open file descriptor
470  * still works, but all name-based access to both the main file as well as the
471  * stream is denied with DELETE ending.
472  *
473  * This means, an open of the main file with SEC_STD_DELETE should walk all
474  * streams and also open them with SEC_STD_DELETE. If any of these opens gives
475  * SHARING_VIOLATION, the main open fails.
476  *
477  * Closing the main file after delete_on_close has been set does not really
478  * unlink it but leaves the corresponding share mode entry with
479  * delete_on_close being set around until all streams are closed.
480  *
481  * Opening a stream must also look at the main file's share mode entry, look
482  * at the delete_on_close bit and potentially return DELETE_PENDING.
483  */
484
485 static bool test_stream_delete(struct torture_context *tctx,
486                                struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
487 {
488         NTSTATUS status;
489         union smb_open io;
490         const char *fname = BASEDIR "\\stream.txt";
491         const char *sname1;
492         bool ret = true;
493         int fnum = -1;
494         uint8_t buf[9];
495         ssize_t retsize;
496         union smb_fileinfo finfo;
497
498         sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
499
500         printf("(%s) opening non-existant file stream\n", __location__);
501         io.generic.level = RAW_OPEN_NTCREATEX;
502         io.ntcreatex.in.root_fid = 0;
503         io.ntcreatex.in.flags = 0;
504         io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
505         io.ntcreatex.in.create_options = 0;
506         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
507         io.ntcreatex.in.share_access = 0;
508         io.ntcreatex.in.alloc_size = 0;
509         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
510         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
511         io.ntcreatex.in.security_flags = 0;
512         io.ntcreatex.in.fname = sname1;
513
514         status = smb_raw_open(cli->tree, mem_ctx, &io);
515         CHECK_STATUS(status, NT_STATUS_OK);
516         fnum = io.ntcreatex.out.file.fnum;
517
518         retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9);
519         CHECK_VALUE(retsize, 9);
520
521         /*
522          * One stream opened without FILE_SHARE_DELETE prevents the main file
523          * to be deleted or even opened with DELETE access
524          */
525
526         status = smbcli_unlink(cli->tree, fname);
527         CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
528
529         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
530         io.ntcreatex.in.fname = fname;
531         io.ntcreatex.in.access_mask = SEC_STD_DELETE;
532         status = smb_raw_open(cli->tree, mem_ctx, &io);
533         CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
534
535         smbcli_close(cli->tree, fnum);
536
537         /*
538          * ... but unlink works if a stream is opened with FILE_SHARE_DELETE
539          */
540
541         io.ntcreatex.in.fname = sname1;
542         io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
543         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
544         status = smb_raw_open(cli->tree, mem_ctx, &io);
545         CHECK_STATUS(status, NT_STATUS_OK);
546         fnum = io.ntcreatex.out.file.fnum;
547
548         status = smbcli_unlink(cli->tree, fname);
549         CHECK_STATUS(status, NT_STATUS_OK);
550
551         /*
552          * file access still works on the stream while the main file is closed
553          */
554
555         retsize = smbcli_read(cli->tree, fnum, buf, 0, 9);
556         CHECK_VALUE(retsize, 9);
557
558         finfo.generic.level = RAW_FILEINFO_STANDARD;
559         finfo.generic.in.file.path = fname;
560
561         /*
562          * name-based access to both the main file and the stream does not
563          * work anymore but gives DELETE_PENDING
564          */
565
566         status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
567         CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
568
569         if (!torture_setting_bool(tctx, "samba3", false)) {
570
571                 /*
572                  * S3 doesn't do this yet
573                  */
574
575                 finfo.generic.in.file.path = sname1;
576                 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
577                 CHECK_STATUS(status, NT_STATUS_DELETE_PENDING);
578         }
579
580         /*
581          * fd-based qfileinfo on the stream still works, the stream does not
582          * have the delete-on-close bit set. This could mean that open on the
583          * stream first opens the main file
584          */
585
586         finfo.all_info.level = RAW_FILEINFO_ALL_INFO;
587         finfo.all_info.in.file.fnum = fnum;
588
589         status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo);
590         CHECK_STATUS(status, NT_STATUS_OK);
591         CHECK_VALUE(finfo.all_info.out.delete_pending, 0);
592
593         smbcli_close(cli->tree, fnum);
594
595         /*
596          * After closing the stream the file is really gone.
597          */
598
599         finfo.generic.in.file.path = fname;
600         status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
601         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
602
603         io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA
604                 |SEC_STD_DELETE;
605         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
606         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
607         status = smb_raw_open(cli->tree, mem_ctx, &io);
608         CHECK_STATUS(status, NT_STATUS_OK);
609         fnum = io.ntcreatex.out.file.fnum;
610
611         finfo.generic.in.file.path = fname;
612         status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
613         CHECK_STATUS(status, NT_STATUS_OK);
614
615         smbcli_close(cli->tree, fnum);
616
617         status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
618         CHECK_STATUS(status, NT_STATUS_OK);
619 done:
620         smbcli_close(cli->tree, fnum);
621         return ret;
622 }
623
624 /*
625   test stream names
626 */
627 static bool test_stream_names(struct torture_context *tctx,
628                               struct smbcli_state *cli,
629                               TALLOC_CTX *mem_ctx)
630 {
631         NTSTATUS status;
632         union smb_open io;
633         const char *fname = BASEDIR "\\stream_names.txt";
634         const char *sname1, *sname1b, *sname1c, *sname1d;
635         const char *sname2, *snamew, *snamew2;
636         bool ret = true;
637         int fnum1 = -1;
638         int fnum2 = -1;
639         const char *four[4] = {
640                 "::$DATA",
641                 ":\x05Stream\n One:$DATA",
642                 ":MStream Two:$DATA",
643                 ":?Stream*:$DATA"
644         };
645
646         sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "\x05Stream\n One");
647         sname1b = talloc_asprintf(mem_ctx, "%s:", sname1);
648         sname1c = talloc_asprintf(mem_ctx, "%s:$FOO", sname1);
649         sname1d = talloc_asprintf(mem_ctx, "%s:?D*a", sname1);
650         sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "MStream Two");
651         snamew = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "?Stream*");
652         snamew2 = talloc_asprintf(mem_ctx, "%s\\stream*:%s:$DATA", BASEDIR, "?Stream*");
653
654         printf("(%s) testing stream names\n", __location__);
655         io.generic.level = RAW_OPEN_NTCREATEX;
656         io.ntcreatex.in.root_fid = 0;
657         io.ntcreatex.in.flags = 0;
658         io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
659         io.ntcreatex.in.create_options = 0;
660         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
661         io.ntcreatex.in.share_access = 0;
662         io.ntcreatex.in.alloc_size = 0;
663         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
664         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
665         io.ntcreatex.in.security_flags = 0;
666         io.ntcreatex.in.fname = sname1;
667
668         status = smb_raw_open(cli->tree, mem_ctx, &io);
669         CHECK_STATUS(status, NT_STATUS_OK);
670         fnum1 = io.ntcreatex.out.file.fnum;
671
672         /*
673          * A different stream does not give a sharing violation
674          */
675
676         io.ntcreatex.in.fname = sname2;
677         status = smb_raw_open(cli->tree, mem_ctx, &io);
678         CHECK_STATUS(status, NT_STATUS_OK);
679         fnum2 = io.ntcreatex.out.file.fnum;
680
681         /*
682          * ... whereas the same stream does with unchanged access/share_access
683          * flags
684          */
685
686         io.ntcreatex.in.fname = sname1;
687         io.ntcreatex.in.open_disposition = 0;
688         status = smb_raw_open(cli->tree, mem_ctx, &io);
689         CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
690
691         io.ntcreatex.in.fname = sname1b;
692         status = smb_raw_open(cli->tree, mem_ctx, &io);
693         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
694
695         io.ntcreatex.in.fname = sname1c;
696         status = smb_raw_open(cli->tree, mem_ctx, &io);
697         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
698
699         io.ntcreatex.in.fname = sname1d;
700         status = smb_raw_open(cli->tree, mem_ctx, &io);
701         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
702
703         io.ntcreatex.in.fname = sname2;
704         status = smb_raw_open(cli->tree, mem_ctx, &io);
705         CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
706
707         io.ntcreatex.in.fname = snamew;
708         status = smb_raw_open(cli->tree, mem_ctx, &io);
709         CHECK_STATUS(status, NT_STATUS_OK);
710
711         io.ntcreatex.in.fname = snamew2;
712         status = smb_raw_open(cli->tree, mem_ctx, &io);
713         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
714
715         ret &= check_stream_list(cli, fname, 4, four);
716
717 done:
718         if (fnum1 != -1) smbcli_close(cli->tree, fnum1);
719         if (fnum2 != -1) smbcli_close(cli->tree, fnum2);
720         status = smbcli_unlink(cli->tree, fname);
721         return ret;
722 }
723
724 /*
725   test stream names
726 */
727 static bool test_stream_names2(struct torture_context *tctx,
728                                struct smbcli_state *cli,
729                                TALLOC_CTX *mem_ctx)
730 {
731         NTSTATUS status;
732         union smb_open io;
733         const char *fname = BASEDIR "\\stream_names2.txt";
734         bool ret = true;
735         int fnum1 = -1;
736         uint8_t i;
737
738         printf("(%s) testing stream names\n", __location__);
739         io.generic.level = RAW_OPEN_NTCREATEX;
740         io.ntcreatex.in.root_fid = 0;
741         io.ntcreatex.in.flags = 0;
742         io.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
743         io.ntcreatex.in.create_options = 0;
744         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
745         io.ntcreatex.in.share_access = 0;
746         io.ntcreatex.in.alloc_size = 0;
747         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
748         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
749         io.ntcreatex.in.security_flags = 0;
750         io.ntcreatex.in.fname = fname;
751         status = smb_raw_open(cli->tree, mem_ctx, &io);
752         CHECK_STATUS(status, NT_STATUS_OK);
753         fnum1 = io.ntcreatex.out.file.fnum;
754
755         for (i=0x01; i < 0x7F; i++) {
756                 char *path = talloc_asprintf(tctx, "%s:Stream%c0x%02X:$DATA",
757                                              fname, i, i);
758                 NTSTATUS expected;
759
760                 switch (i) {
761                 case '/':/*0x2F*/
762                 case ':':/*0x3A*/
763                 case '\\':/*0x5C*/
764                         expected = NT_STATUS_OBJECT_NAME_INVALID;
765                         break;
766                 default:
767                         expected = NT_STATUS_OBJECT_NAME_NOT_FOUND;
768                         break;
769                 }
770
771                 printf("(%s) %s:Stream%c0x%02X:$DATA%s => expected[%s]\n",
772                        __location__, fname, isprint(i)?(char)i:' ', i,
773                        isprint(i)?"":" (not printable)",
774                        nt_errstr(expected));
775
776                 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
777                 io.ntcreatex.in.fname = path;
778                 status = smb_raw_open(cli->tree, mem_ctx, &io);
779                 CHECK_STATUS(status, expected);
780
781                 talloc_free(path);
782         }
783
784 done:
785         if (fnum1 != -1) smbcli_close(cli->tree, fnum1);
786         status = smbcli_unlink(cli->tree, fname);
787         return ret;
788 }
789
790 /* 
791    basic testing of streams calls
792 */
793 bool torture_raw_streams(struct torture_context *torture, 
794                          struct smbcli_state *cli)
795 {
796         bool ret = true;
797
798         if (!torture_setup_dir(cli, BASEDIR)) {
799                 return false;
800         }
801
802         ret &= test_stream_dir(torture, cli, torture);
803         smb_raw_exit(cli->session);
804         ret &= test_stream_io(torture, cli, torture);
805         smb_raw_exit(cli->session);
806         ret &= test_stream_sharemodes(torture, cli, torture);
807         smb_raw_exit(cli->session);
808         ret &= test_stream_names(torture, cli, torture);
809         smb_raw_exit(cli->session);
810         ret &= test_stream_names2(torture, cli, torture);
811         smb_raw_exit(cli->session);
812         if (!torture_setting_bool(torture, "samba4", false)) {
813                 ret &= test_stream_delete(torture, cli, torture);
814         }
815
816         smb_raw_exit(cli->session);
817         smbcli_deltree(cli->tree, BASEDIR);
818
819         return ret;
820 }