r8058: added testing of delete on close for files and directories
[ira/wip.git] / source / torture / torture_util.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMB torture tester utility functions
4    Copyright (C) Andrew Tridgell 2003
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22 #include "libcli/raw/libcliraw.h"
23 #include "system/shmem.h"
24 #include "system/time.h"
25 #include "librpc/gen_ndr/ndr_security.h"
26
27
28 /*
29   setup a directory ready for a test
30 */
31 BOOL torture_setup_dir(struct smbcli_state *cli, const char *dname)
32 {
33         smb_raw_exit(cli->session);
34         if (smbcli_deltree(cli->tree, dname) == -1 ||
35             NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, dname))) {
36                 printf("Unable to setup %s - %s\n", dname, smbcli_errstr(cli->tree));
37                 return False;
38         }
39         return True;
40 }
41
42 /*
43   create a directory, returning a handle to it
44 */
45 int create_directory_handle(struct smbcli_tree *tree, const char *dname)
46 {
47         NTSTATUS status;
48         union smb_open io;
49         TALLOC_CTX *mem_ctx;
50
51         mem_ctx = talloc_init("create_directory_handle");
52
53         io.generic.level = RAW_OPEN_NTCREATEX;
54         io.ntcreatex.in.root_fid = 0;
55         io.ntcreatex.in.flags = 0;
56         io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
57         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
58         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
59         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
60         io.ntcreatex.in.alloc_size = 0;
61         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
62         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
63         io.ntcreatex.in.security_flags = 0;
64         io.ntcreatex.in.fname = dname;
65
66         status = smb_raw_open(tree, mem_ctx, &io);
67         if (!NT_STATUS_IS_OK(status)) {
68                 talloc_free(mem_ctx);
69                 return -1;
70         }
71
72         talloc_free(mem_ctx);
73         return io.ntcreatex.out.fnum;
74 }
75
76 /*
77   sometimes we need a fairly complex file to work with, so we can test
78   all possible attributes. 
79 */
80 int create_complex_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *fname)
81 {
82         int fnum;
83         char buf[7] = "abc";
84         union smb_setfileinfo setfile;
85         union smb_fileinfo fileinfo;
86         time_t t = (time(NULL) & ~1);
87         NTSTATUS status;
88
89         smbcli_unlink(cli->tree, fname);
90         fnum = smbcli_nt_create_full(cli->tree, fname, 0, 
91                                      SEC_RIGHTS_FILE_ALL,
92                                      FILE_ATTRIBUTE_NORMAL,
93                                      NTCREATEX_SHARE_ACCESS_DELETE|
94                                      NTCREATEX_SHARE_ACCESS_READ|
95                                      NTCREATEX_SHARE_ACCESS_WRITE, 
96                                      NTCREATEX_DISP_OVERWRITE_IF,
97                                      0, 0);
98         if (fnum == -1) return -1;
99
100         smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf));
101
102         /* setup some EAs */
103         setfile.generic.level = RAW_SFILEINFO_EA_SET;
104         setfile.generic.file.fnum = fnum;
105         setfile.ea_set.in.num_eas = 2;  
106         setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
107         setfile.ea_set.in.eas[0].flags = 0;
108         setfile.ea_set.in.eas[0].name.s = "EAONE";
109         setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6);
110         setfile.ea_set.in.eas[1].flags = 0;
111         setfile.ea_set.in.eas[1].name.s = "SECONDEA";
112         setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8);
113         status = smb_raw_setfileinfo(cli->tree, &setfile);
114         if (!NT_STATUS_IS_OK(status)) {
115                 printf("Failed to setup EAs\n");
116         }
117
118         /* make sure all the timestamps aren't the same, and are also 
119            in different DST zones*/
120         setfile.generic.level = RAW_SFILEINFO_SETATTRE;
121         setfile.generic.file.fnum = fnum;
122
123         setfile.setattre.in.create_time = t + 9*30*24*60*60;
124         setfile.setattre.in.access_time = t + 6*30*24*60*60;
125         setfile.setattre.in.write_time  = t + 3*30*24*60*60;
126
127         status = smb_raw_setfileinfo(cli->tree, &setfile);
128         if (!NT_STATUS_IS_OK(status)) {
129                 printf("Failed to setup file times - %s\n", nt_errstr(status));
130         }
131
132         /* make sure all the timestamps aren't the same */
133         fileinfo.generic.level = RAW_FILEINFO_GETATTRE;
134         fileinfo.generic.in.fnum = fnum;
135
136         status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
137         if (!NT_STATUS_IS_OK(status)) {
138                 printf("Failed to query file times - %s\n", nt_errstr(status));
139         }
140
141         if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) {
142                 printf("create_time not setup correctly\n");
143         }
144         if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
145                 printf("access_time not setup correctly\n");
146         }
147         if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
148                 printf("write_time not setup correctly\n");
149         }
150
151         return fnum;
152 }
153
154
155
156 /* return a pointer to a anonymous shared memory segment of size "size"
157    which will persist across fork() but will disappear when all processes
158    exit 
159
160    The memory is not zeroed 
161
162    This function uses system5 shared memory. It takes advantage of a property
163    that the memory is not destroyed if it is attached when the id is removed
164    */
165 void *shm_setup(int size)
166 {
167         int shmid;
168         void *ret;
169
170         shmid = shmget(IPC_PRIVATE, size, SHM_R | SHM_W);
171         if (shmid == -1) {
172                 printf("can't get shared memory\n");
173                 exit(1);
174         }
175         ret = (void *)shmat(shmid, 0, 0);
176         if (!ret || ret == (void *)-1) {
177                 printf("can't attach to shared memory\n");
178                 return NULL;
179         }
180         /* the following releases the ipc, but note that this process
181            and all its children will still have access to the memory, its
182            just that the shmid is no longer valid for other shm calls. This
183            means we don't leave behind lots of shm segments after we exit 
184
185            See Stevens "advanced programming in unix env" for details
186            */
187         shmctl(shmid, IPC_RMID, 0);
188         
189         return ret;
190 }
191
192
193 /*
194   check that a wire string matches the flags specified 
195   not 100% accurate, but close enough for testing
196 */
197 BOOL wire_bad_flags(WIRE_STRING *str, int flags, struct smbcli_state *cli)
198 {
199         BOOL server_unicode;
200         int len;
201         if (!str || !str->s) return True;
202         len = strlen(str->s);
203         if (flags & STR_TERMINATE) len++;
204
205         server_unicode = (cli->transport->negotiate.capabilities&CAP_UNICODE)?True:False;
206         if (getenv("CLI_FORCE_ASCII") || !lp_unicode()) {
207                 server_unicode = False;
208         }
209
210         if ((flags & STR_UNICODE) || server_unicode) {
211                 len *= 2;
212         } else if (flags & STR_TERMINATE_ASCII) {
213                 len++;
214         }
215         if (str->private_length != len) {
216                 printf("Expected wire_length %d but got %d for '%s'\n", 
217                        len, str->private_length, str->s);
218                 return True;
219         }
220         return False;
221 }
222
223 /*
224   check if 2 NTTIMEs are equal
225 */
226 BOOL nt_time_equal(NTTIME *t1, NTTIME *t2)
227 {
228         return *t1 == *t2;
229 }
230
231 /*
232   dump a all_info QFILEINFO structure
233 */
234 void dump_all_info(TALLOC_CTX *mem_ctx, union smb_fileinfo *finfo)
235 {
236         d_printf("\tcreate_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.create_time));
237         d_printf("\taccess_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.access_time));
238         d_printf("\twrite_time:     %s\n", nt_time_string(mem_ctx, finfo->all_info.out.write_time));
239         d_printf("\tchange_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.change_time));
240         d_printf("\tattrib:         0x%x\n", finfo->all_info.out.attrib);
241         d_printf("\talloc_size:     %llu\n", (uint64_t)finfo->all_info.out.alloc_size);
242         d_printf("\tsize:           %llu\n", (uint64_t)finfo->all_info.out.size);
243         d_printf("\tnlink:          %u\n", finfo->all_info.out.nlink);
244         d_printf("\tdelete_pending: %u\n", finfo->all_info.out.delete_pending);
245         d_printf("\tdirectory:      %u\n", finfo->all_info.out.directory);
246         d_printf("\tea_size:        %u\n", finfo->all_info.out.ea_size);
247         d_printf("\tfname:          '%s'\n", finfo->all_info.out.fname.s);
248 }
249
250 /*
251   dump file infor by name
252 */
253 void torture_all_info(struct smbcli_tree *tree, const char *fname)
254 {
255         TALLOC_CTX *mem_ctx = talloc_init("%s", fname);
256         union smb_fileinfo finfo;
257         NTSTATUS status;
258
259         finfo.generic.level = RAW_FILEINFO_ALL_INFO;
260         finfo.generic.in.fname = fname;
261         status = smb_raw_pathinfo(tree, mem_ctx, &finfo);
262         if (!NT_STATUS_IS_OK(status)) {
263                 d_printf("%s - %s\n", fname, nt_errstr(status));
264                 return;
265         }
266
267         d_printf("%s:\n", fname);
268         dump_all_info(mem_ctx, &finfo);
269         talloc_free(mem_ctx);
270 }
271
272
273 /*
274   split a UNC name into server and share names
275 */
276 BOOL split_unc_name(const char *unc, char **server, char **share)
277 {
278         char *p = strdup(unc);
279         if (!p) return False;
280         all_string_sub(p, "\\", "/", 0);
281         if (strncmp(p, "//", 2) != 0) return False;
282
283         (*server) = p+2;
284         p = strchr(*server, '/');
285         if (!p) return False;
286
287         *p = 0;
288         (*share) = p+1;
289         
290         return True;
291 }
292
293 /*
294   set a attribute on a file
295 */
296 BOOL torture_set_file_attribute(struct smbcli_tree *tree, const char *fname, uint16_t attrib)
297 {
298         union smb_setfileinfo sfinfo;
299         NTSTATUS status;
300
301         sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
302         sfinfo.generic.file.fname = fname;
303
304         ZERO_STRUCT(sfinfo.basic_info.in);
305         sfinfo.basic_info.in.attrib = attrib;
306         status = smb_raw_setpathinfo(tree, &sfinfo);
307         return NT_STATUS_IS_OK(status);
308 }
309
310
311 /*
312   set a file descriptor as sparse
313 */
314 NTSTATUS torture_set_sparse(struct smbcli_tree *tree, int fnum)
315 {
316         union smb_ioctl nt;
317         NTSTATUS status;
318         TALLOC_CTX *mem_ctx;
319
320         mem_ctx = talloc_init("torture_set_sparse");
321         if (!mem_ctx) {
322                 return NT_STATUS_NO_MEMORY;
323         }
324
325         nt.ntioctl.level = RAW_IOCTL_NTIOCTL;
326         nt.ntioctl.in.function = 0x900c4;
327         nt.ntioctl.in.fnum = fnum;
328         nt.ntioctl.in.fsctl = True;
329         nt.ntioctl.in.filter = 0;
330
331         status = smb_raw_ioctl(tree, mem_ctx, &nt);
332
333         talloc_free(mem_ctx);
334
335         return status;
336 }
337
338 /*
339   check that an EA has the right value 
340 */
341 NTSTATUS torture_check_ea(struct smbcli_state *cli, 
342                           const char *fname, const char *eaname, const char *value)
343 {
344         union smb_fileinfo info;
345         NTSTATUS status;
346         struct ea_name ea;
347         TALLOC_CTX *mem_ctx = talloc_new(cli);
348
349         info.ea_list.level = RAW_FILEINFO_EA_LIST;
350         info.ea_list.file.fname = fname;
351         info.ea_list.in.num_names = 1;
352         info.ea_list.in.ea_names = &ea;
353
354         ea.name.s = eaname;
355
356         status = smb_raw_pathinfo(cli->tree, mem_ctx, &info);
357         if (!NT_STATUS_IS_OK(status)) {
358                 talloc_free(mem_ctx);
359                 return status;
360         }
361
362         if (info.ea_list.out.num_eas != 1) {
363                 printf("Expected 1 ea in ea_list\n");
364                 talloc_free(mem_ctx);
365                 return NT_STATUS_EA_CORRUPT_ERROR;
366         }
367
368         if (StrCaseCmp(eaname, info.ea_list.out.eas[0].name.s) != 0) {
369                 printf("Expected ea '%s' not '%s' in ea_list\n",
370                        eaname, info.ea_list.out.eas[0].name.s);
371                 talloc_free(mem_ctx);
372                 return NT_STATUS_EA_CORRUPT_ERROR;
373         }
374
375         if (value == NULL) {
376                 if (info.ea_list.out.eas[0].value.length != 0) {
377                         printf("Expected zero length ea for %s\n", eaname);
378                         talloc_free(mem_ctx);
379                         return NT_STATUS_EA_CORRUPT_ERROR;
380                 }
381                 talloc_free(mem_ctx);
382                 return NT_STATUS_OK;
383         }
384
385         if (strlen(value) == info.ea_list.out.eas[0].value.length &&
386             memcmp(value, info.ea_list.out.eas[0].value.data,
387                    info.ea_list.out.eas[0].value.length) == 0) {
388                 talloc_free(mem_ctx);
389                 return NT_STATUS_OK;
390         }
391
392         printf("Expected value '%s' not '%*.*s' for ea %s\n",
393                value, 
394                info.ea_list.out.eas[0].value.length,
395                info.ea_list.out.eas[0].value.length,
396                info.ea_list.out.eas[0].value.data,
397                eaname);
398
399         talloc_free(mem_ctx);
400
401         return NT_STATUS_EA_CORRUPT_ERROR;
402 }
403