r26103: Leave streams around to unlink
[nivanova/samba-autobuild/.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         exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp));
139
140         if (exp_sort == NULL) {
141                 goto fail;
142         }
143
144         qsort(exp_sort, num_exp, sizeof(*exp_sort), qsort_string);
145
146         stream_sort = talloc_memdup(tmp_ctx, finfo.stream_info.out.streams,
147                                     finfo.stream_info.out.num_streams *
148                                     sizeof(*stream_sort));
149
150         if (stream_sort == NULL) {
151                 goto fail;
152         }
153
154         qsort(stream_sort, finfo.stream_info.out.num_streams,
155               sizeof(*stream_sort), qsort_stream);
156
157         for (i=0; i<num_exp; i++) {
158                 if (strcmp(exp_sort[i], stream_sort[i].stream_name.s) != 0) {
159                         d_fprintf(stderr, "(%s) expected stream name %s, got "
160                                   "%s\n", __location__, exp_sort[i],
161                                   stream_sort[i].stream_name.s);
162                         goto fail;
163                 }
164         }
165
166         ret = true;
167  fail:
168         talloc_free(tmp_ctx);
169         return ret;
170 }
171
172 /*
173   test basic io on streams
174 */
175 static bool test_stream_io(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
176 {
177         NTSTATUS status;
178         union smb_open io;
179         const char *fname = BASEDIR "\\stream.txt";
180         const char *sname1, *sname2;
181         bool ret = true;
182         int fnum = -1;
183         ssize_t retsize;
184
185         const char *one[] = { "::$DATA" };
186         const char *two[] = { "::$DATA", ":Second Stream:$DATA" };
187         const char *three[] = { "::$DATA", ":Stream One:$DATA",
188                                 ":Second Stream:$DATA" };
189
190         sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
191         sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "Second Stream");
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) creating a stream on a non-existant file\n", __location__);
210         io.ntcreatex.in.create_options = 0;
211         io.ntcreatex.in.fname = sname1;
212         status = smb_raw_open(cli->tree, mem_ctx, &io);
213         CHECK_STATUS(status, NT_STATUS_OK);
214         fnum = io.ntcreatex.out.file.fnum;
215
216         ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One", NULL);
217
218         printf("(%s) check that open of base file is allowed\n", __location__);
219         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
220         io.ntcreatex.in.fname = fname;
221         status = smb_raw_open(cli->tree, mem_ctx, &io);
222         CHECK_STATUS(status, NT_STATUS_OK);
223         smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
224
225         printf("(%s) writing to stream\n", __location__);
226         retsize = smbcli_write(cli->tree, fnum, 0, "test data", 0, 9);
227         CHECK_VALUE(retsize, 9);
228
229         smbcli_close(cli->tree, fnum);
230
231         ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One", "test data");
232
233         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
234         io.ntcreatex.in.fname = sname1;
235         status = smb_raw_open(cli->tree, mem_ctx, &io);
236         CHECK_STATUS(status, NT_STATUS_OK);
237         fnum = io.ntcreatex.out.file.fnum;
238
239         printf("(%s) modifying stream\n", __location__);
240         retsize = smbcli_write(cli->tree, fnum, 0, "MORE DATA ", 5, 10);
241         CHECK_VALUE(retsize, 10);
242
243         smbcli_close(cli->tree, fnum);
244
245         ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:$FOO", NULL);
246
247         printf("(%s) creating a stream2 on a existing file\n", __location__);
248         io.ntcreatex.in.fname = sname2;
249         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
250         status = smb_raw_open(cli->tree, mem_ctx, &io);
251         CHECK_STATUS(status, NT_STATUS_OK);
252         fnum = io.ntcreatex.out.file.fnum;
253
254         printf("(%s) modifying stream\n", __location__);
255         retsize = smbcli_write(cli->tree, fnum, 0, "SECOND STREAM", 0, 13);
256         CHECK_VALUE(retsize, 13);
257
258         smbcli_close(cli->tree, fnum);
259
260         ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One", "test MORE DATA ");
261         ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:$DATA", "test MORE DATA ");
262         ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:", NULL);
263         ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream", "SECOND STREAM");
264         ret &= check_stream(cli, __location__, mem_ctx, fname,
265                             "SECOND STREAM:$DATA", "SECOND STREAM");
266         ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:$DATA", "SECOND STREAM");
267         ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:", NULL);
268         ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:$FOO", NULL);
269
270         check_stream_list(cli, fname, 3, three);
271
272         printf("(%s) deleting stream\n", __location__);
273         status = smbcli_unlink(cli->tree, sname1);
274         CHECK_STATUS(status, NT_STATUS_OK);
275
276         check_stream_list(cli, fname, 2, two);
277
278         printf("(%s) delete a stream via delete-on-close\n", __location__);
279         io.ntcreatex.in.fname = sname2;
280         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
281         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE;
282         io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
283         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
284
285         status = smb_raw_open(cli->tree, mem_ctx, &io);
286         CHECK_STATUS(status, NT_STATUS_OK);
287         fnum = io.ntcreatex.out.file.fnum;
288         
289         smbcli_close(cli->tree, fnum);
290         status = smbcli_unlink(cli->tree, sname2);
291         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
292
293         check_stream_list(cli, fname, 1, one);
294
295         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
296         io.ntcreatex.in.fname = sname1;
297         status = smb_raw_open(cli->tree, mem_ctx, &io);
298         CHECK_STATUS(status, NT_STATUS_OK);
299         smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
300         io.ntcreatex.in.fname = sname2;
301         status = smb_raw_open(cli->tree, mem_ctx, &io);
302         CHECK_STATUS(status, NT_STATUS_OK);
303         smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
304
305         printf("(%s) deleting file\n", __location__);
306         status = smbcli_unlink(cli->tree, fname);
307         CHECK_STATUS(status, NT_STATUS_OK);
308
309 done:
310         smbcli_close(cli->tree, fnum);
311         return ret;
312 }
313
314 /* 
315    basic testing of streams calls
316 */
317 bool torture_raw_streams(struct torture_context *torture, 
318                          struct smbcli_state *cli)
319 {
320         bool ret = true;
321
322         if (!torture_setup_dir(cli, BASEDIR)) {
323                 return false;
324         }
325
326         ret &= test_stream_io(cli, torture);
327
328         smb_raw_exit(cli->session);
329         smbcli_deltree(cli->tree, BASEDIR);
330
331         return ret;
332 }