r14205: move smb specific stuff out of includes.h (finally!!!:-)
[kai/samba-autobuild/.git] / source4 / 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 "smb.h"
23 #include "libcli/raw/libcliraw.h"
24 #include "libcli/libcli.h"
25 #include "system/shmem.h"
26 #include "system/time.h"
27
28
29 /*
30   setup a directory ready for a test
31 */
32 BOOL torture_setup_dir(struct smbcli_state *cli, const char *dname)
33 {
34         smb_raw_exit(cli->session);
35         if (smbcli_deltree(cli->tree, dname) == -1 ||
36             NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, dname))) {
37                 printf("Unable to setup %s - %s\n", dname, smbcli_errstr(cli->tree));
38                 return False;
39         }
40         return True;
41 }
42
43 /*
44   create a directory, returning a handle to it
45 */
46 NTSTATUS create_directory_handle(struct smbcli_tree *tree, const char *dname, int *fnum)
47 {
48         NTSTATUS status;
49         union smb_open io;
50         TALLOC_CTX *mem_ctx;
51
52         mem_ctx = talloc_init("create_directory_handle");
53
54         io.generic.level = RAW_OPEN_NTCREATEX;
55         io.ntcreatex.in.root_fid = 0;
56         io.ntcreatex.in.flags = 0;
57         io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
58         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
59         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
60         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
61         io.ntcreatex.in.alloc_size = 0;
62         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
63         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
64         io.ntcreatex.in.security_flags = 0;
65         io.ntcreatex.in.fname = dname;
66
67         status = smb_raw_open(tree, mem_ctx, &io);
68         talloc_free(mem_ctx);
69
70         if (NT_STATUS_IS_OK(status)) {
71                 *fnum = io.ntcreatex.file.fnum;
72         }
73
74         return status;
75 }
76
77
78 /*
79   sometimes we need a fairly complex file to work with, so we can test
80   all possible attributes. 
81 */
82 int create_complex_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *fname)
83 {
84         int fnum;
85         char buf[7] = "abc";
86         union smb_setfileinfo setfile;
87         union smb_fileinfo fileinfo;
88         time_t t = (time(NULL) & ~1);
89         NTSTATUS status;
90
91         smbcli_unlink(cli->tree, fname);
92         fnum = smbcli_nt_create_full(cli->tree, fname, 0, 
93                                      SEC_RIGHTS_FILE_ALL,
94                                      FILE_ATTRIBUTE_NORMAL,
95                                      NTCREATEX_SHARE_ACCESS_DELETE|
96                                      NTCREATEX_SHARE_ACCESS_READ|
97                                      NTCREATEX_SHARE_ACCESS_WRITE, 
98                                      NTCREATEX_DISP_OVERWRITE_IF,
99                                      0, 0);
100         if (fnum == -1) return -1;
101
102         smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf));
103
104         if (strchr(fname, ':') == NULL) {
105                 /* setup some EAs */
106                 setfile.generic.level = RAW_SFILEINFO_EA_SET;
107                 setfile.generic.file.fnum = fnum;
108                 setfile.ea_set.in.num_eas = 2;  
109                 setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
110                 setfile.ea_set.in.eas[0].flags = 0;
111                 setfile.ea_set.in.eas[0].name.s = "EAONE";
112                 setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6);
113                 setfile.ea_set.in.eas[1].flags = 0;
114                 setfile.ea_set.in.eas[1].name.s = "SECONDEA";
115                 setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8);
116                 status = smb_raw_setfileinfo(cli->tree, &setfile);
117                 if (!NT_STATUS_IS_OK(status)) {
118                         printf("Failed to setup EAs\n");
119                 }
120         }
121
122         /* make sure all the timestamps aren't the same, and are also 
123            in different DST zones*/
124         setfile.generic.level = RAW_SFILEINFO_SETATTRE;
125         setfile.generic.file.fnum = fnum;
126
127         setfile.setattre.in.create_time = t + 9*30*24*60*60;
128         setfile.setattre.in.access_time = t + 6*30*24*60*60;
129         setfile.setattre.in.write_time  = t + 3*30*24*60*60;
130
131         status = smb_raw_setfileinfo(cli->tree, &setfile);
132         if (!NT_STATUS_IS_OK(status)) {
133                 printf("Failed to setup file times - %s\n", nt_errstr(status));
134         }
135
136         /* make sure all the timestamps aren't the same */
137         fileinfo.generic.level = RAW_FILEINFO_GETATTRE;
138         fileinfo.generic.file.fnum = fnum;
139
140         status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
141         if (!NT_STATUS_IS_OK(status)) {
142                 printf("Failed to query file times - %s\n", nt_errstr(status));
143         }
144
145         if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) {
146                 printf("create_time not setup correctly\n");
147         }
148         if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
149                 printf("access_time not setup correctly\n");
150         }
151         if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
152                 printf("write_time not setup correctly\n");
153         }
154
155         return fnum;
156 }
157
158
159 /*
160   sometimes we need a fairly complex directory to work with, so we can test
161   all possible attributes. 
162 */
163 int create_complex_dir(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *dname)
164 {
165         int fnum;
166         union smb_setfileinfo setfile;
167         union smb_fileinfo fileinfo;
168         time_t t = (time(NULL) & ~1);
169         NTSTATUS status;
170
171         smbcli_deltree(cli->tree, dname);
172         fnum = smbcli_nt_create_full(cli->tree, dname, 0, 
173                                      SEC_RIGHTS_DIR_ALL,
174                                      FILE_ATTRIBUTE_DIRECTORY,
175                                      NTCREATEX_SHARE_ACCESS_READ|
176                                      NTCREATEX_SHARE_ACCESS_WRITE, 
177                                      NTCREATEX_DISP_OPEN_IF,
178                                      NTCREATEX_OPTIONS_DIRECTORY, 0);
179         if (fnum == -1) return -1;
180
181         if (strchr(dname, ':') == NULL) {
182                 /* setup some EAs */
183                 setfile.generic.level = RAW_SFILEINFO_EA_SET;
184                 setfile.generic.file.fnum = fnum;
185                 setfile.ea_set.in.num_eas = 2;  
186                 setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
187                 setfile.ea_set.in.eas[0].flags = 0;
188                 setfile.ea_set.in.eas[0].name.s = "EAONE";
189                 setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6);
190                 setfile.ea_set.in.eas[1].flags = 0;
191                 setfile.ea_set.in.eas[1].name.s = "SECONDEA";
192                 setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8);
193                 status = smb_raw_setfileinfo(cli->tree, &setfile);
194                 if (!NT_STATUS_IS_OK(status)) {
195                         printf("Failed to setup EAs\n");
196                 }
197         }
198
199         /* make sure all the timestamps aren't the same, and are also 
200            in different DST zones*/
201         setfile.generic.level = RAW_SFILEINFO_SETATTRE;
202         setfile.generic.file.fnum = fnum;
203
204         setfile.setattre.in.create_time = t + 9*30*24*60*60;
205         setfile.setattre.in.access_time = t + 6*30*24*60*60;
206         setfile.setattre.in.write_time  = t + 3*30*24*60*60;
207
208         status = smb_raw_setfileinfo(cli->tree, &setfile);
209         if (!NT_STATUS_IS_OK(status)) {
210                 printf("Failed to setup file times - %s\n", nt_errstr(status));
211         }
212
213         /* make sure all the timestamps aren't the same */
214         fileinfo.generic.level = RAW_FILEINFO_GETATTRE;
215         fileinfo.generic.file.fnum = fnum;
216
217         status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
218         if (!NT_STATUS_IS_OK(status)) {
219                 printf("Failed to query file times - %s\n", nt_errstr(status));
220         }
221
222         if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) {
223                 printf("create_time not setup correctly\n");
224         }
225         if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
226                 printf("access_time not setup correctly\n");
227         }
228         if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
229                 printf("write_time not setup correctly\n");
230         }
231
232         return fnum;
233 }
234
235
236
237 /* return a pointer to a anonymous shared memory segment of size "size"
238    which will persist across fork() but will disappear when all processes
239    exit 
240
241    The memory is not zeroed 
242
243    This function uses system5 shared memory. It takes advantage of a property
244    that the memory is not destroyed if it is attached when the id is removed
245    */
246 void *shm_setup(int size)
247 {
248         int shmid;
249         void *ret;
250
251         shmid = shmget(IPC_PRIVATE, size, SHM_R | SHM_W);
252         if (shmid == -1) {
253                 printf("can't get shared memory\n");
254                 exit(1);
255         }
256         ret = (void *)shmat(shmid, 0, 0);
257         if (!ret || ret == (void *)-1) {
258                 printf("can't attach to shared memory\n");
259                 return NULL;
260         }
261         /* the following releases the ipc, but note that this process
262            and all its children will still have access to the memory, its
263            just that the shmid is no longer valid for other shm calls. This
264            means we don't leave behind lots of shm segments after we exit 
265
266            See Stevens "advanced programming in unix env" for details
267            */
268         shmctl(shmid, IPC_RMID, 0);
269         
270         return ret;
271 }
272
273
274 /*
275   check that a wire string matches the flags specified 
276   not 100% accurate, but close enough for testing
277 */
278 BOOL wire_bad_flags(struct smb_wire_string *str, int flags, struct smbcli_state *cli)
279 {
280         BOOL server_unicode;
281         int len;
282         if (!str || !str->s) return True;
283         len = strlen(str->s);
284         if (flags & STR_TERMINATE) len++;
285
286         server_unicode = (cli->transport->negotiate.capabilities&CAP_UNICODE)?True:False;
287         if (getenv("CLI_FORCE_ASCII") || !lp_unicode()) {
288                 server_unicode = False;
289         }
290
291         if ((flags & STR_UNICODE) || server_unicode) {
292                 len *= 2;
293         } else if (flags & STR_TERMINATE_ASCII) {
294                 len++;
295         }
296         if (str->private_length != len) {
297                 printf("Expected wire_length %d but got %d for '%s'\n", 
298                        len, str->private_length, str->s);
299                 return True;
300         }
301         return False;
302 }
303
304 /*
305   check if 2 NTTIMEs are equal
306 */
307 BOOL nt_time_equal(NTTIME *t1, NTTIME *t2)
308 {
309         return *t1 == *t2;
310 }
311
312 /*
313   dump a all_info QFILEINFO structure
314 */
315 void dump_all_info(TALLOC_CTX *mem_ctx, union smb_fileinfo *finfo)
316 {
317         d_printf("\tcreate_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.create_time));
318         d_printf("\taccess_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.access_time));
319         d_printf("\twrite_time:     %s\n", nt_time_string(mem_ctx, finfo->all_info.out.write_time));
320         d_printf("\tchange_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.change_time));
321         d_printf("\tattrib:         0x%x\n", finfo->all_info.out.attrib);
322         d_printf("\talloc_size:     %llu\n", (long long)finfo->all_info.out.alloc_size);
323         d_printf("\tsize:           %llu\n", (long long)finfo->all_info.out.size);
324         d_printf("\tnlink:          %u\n", finfo->all_info.out.nlink);
325         d_printf("\tdelete_pending: %u\n", finfo->all_info.out.delete_pending);
326         d_printf("\tdirectory:      %u\n", finfo->all_info.out.directory);
327         d_printf("\tea_size:        %u\n", finfo->all_info.out.ea_size);
328         d_printf("\tfname:          '%s'\n", finfo->all_info.out.fname.s);
329 }
330
331 /*
332   dump file infor by name
333 */
334 void torture_all_info(struct smbcli_tree *tree, const char *fname)
335 {
336         TALLOC_CTX *mem_ctx = talloc_init("%s", fname);
337         union smb_fileinfo finfo;
338         NTSTATUS status;
339
340         finfo.generic.level = RAW_FILEINFO_ALL_INFO;
341         finfo.generic.file.path = fname;
342         status = smb_raw_pathinfo(tree, mem_ctx, &finfo);
343         if (!NT_STATUS_IS_OK(status)) {
344                 d_printf("%s - %s\n", fname, nt_errstr(status));
345                 return;
346         }
347
348         d_printf("%s:\n", fname);
349         dump_all_info(mem_ctx, &finfo);
350         talloc_free(mem_ctx);
351 }
352
353
354 /*
355   split a UNC name into server and share names
356 */
357 BOOL split_unc_name(const char *unc, char **server, char **share)
358 {
359         char *p = strdup(unc);
360         if (!p) return False;
361         all_string_sub(p, "\\", "/", 0);
362         if (strncmp(p, "//", 2) != 0) return False;
363
364         (*server) = p+2;
365         p = strchr(*server, '/');
366         if (!p) return False;
367
368         *p = 0;
369         (*share) = p+1;
370         
371         return True;
372 }
373
374 /*
375   set a attribute on a file
376 */
377 BOOL torture_set_file_attribute(struct smbcli_tree *tree, const char *fname, uint16_t attrib)
378 {
379         union smb_setfileinfo sfinfo;
380         NTSTATUS status;
381
382         sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
383         sfinfo.generic.file.path = fname;
384
385         ZERO_STRUCT(sfinfo.basic_info.in);
386         sfinfo.basic_info.in.attrib = attrib;
387         status = smb_raw_setpathinfo(tree, &sfinfo);
388         return NT_STATUS_IS_OK(status);
389 }
390
391
392 /*
393   set a file descriptor as sparse
394 */
395 NTSTATUS torture_set_sparse(struct smbcli_tree *tree, int fnum)
396 {
397         union smb_ioctl nt;
398         NTSTATUS status;
399         TALLOC_CTX *mem_ctx;
400
401         mem_ctx = talloc_init("torture_set_sparse");
402         if (!mem_ctx) {
403                 return NT_STATUS_NO_MEMORY;
404         }
405
406         nt.ntioctl.level = RAW_IOCTL_NTIOCTL;
407         nt.ntioctl.in.function = 0x900c4;
408         nt.ntioctl.file.fnum = fnum;
409         nt.ntioctl.in.fsctl = True;
410         nt.ntioctl.in.filter = 0;
411
412         status = smb_raw_ioctl(tree, mem_ctx, &nt);
413
414         talloc_free(mem_ctx);
415
416         return status;
417 }
418
419 /*
420   check that an EA has the right value 
421 */
422 NTSTATUS torture_check_ea(struct smbcli_state *cli, 
423                           const char *fname, const char *eaname, const char *value)
424 {
425         union smb_fileinfo info;
426         NTSTATUS status;
427         struct ea_name ea;
428         TALLOC_CTX *mem_ctx = talloc_new(cli);
429
430         info.ea_list.level = RAW_FILEINFO_EA_LIST;
431         info.ea_list.file.path = fname;
432         info.ea_list.in.num_names = 1;
433         info.ea_list.in.ea_names = &ea;
434
435         ea.name.s = eaname;
436
437         status = smb_raw_pathinfo(cli->tree, mem_ctx, &info);
438         if (!NT_STATUS_IS_OK(status)) {
439                 talloc_free(mem_ctx);
440                 return status;
441         }
442
443         if (info.ea_list.out.num_eas != 1) {
444                 printf("Expected 1 ea in ea_list\n");
445                 talloc_free(mem_ctx);
446                 return NT_STATUS_EA_CORRUPT_ERROR;
447         }
448
449         if (strcasecmp_m(eaname, info.ea_list.out.eas[0].name.s) != 0) {
450                 printf("Expected ea '%s' not '%s' in ea_list\n",
451                        eaname, info.ea_list.out.eas[0].name.s);
452                 talloc_free(mem_ctx);
453                 return NT_STATUS_EA_CORRUPT_ERROR;
454         }
455
456         if (value == NULL) {
457                 if (info.ea_list.out.eas[0].value.length != 0) {
458                         printf("Expected zero length ea for %s\n", eaname);
459                         talloc_free(mem_ctx);
460                         return NT_STATUS_EA_CORRUPT_ERROR;
461                 }
462                 talloc_free(mem_ctx);
463                 return NT_STATUS_OK;
464         }
465
466         if (strlen(value) == info.ea_list.out.eas[0].value.length &&
467             memcmp(value, info.ea_list.out.eas[0].value.data,
468                    info.ea_list.out.eas[0].value.length) == 0) {
469                 talloc_free(mem_ctx);
470                 return NT_STATUS_OK;
471         }
472
473         printf("Expected value '%s' not '%*.*s' for ea %s\n",
474                value, 
475                (int)info.ea_list.out.eas[0].value.length,
476                (int)info.ea_list.out.eas[0].value.length,
477                info.ea_list.out.eas[0].value.data,
478                eaname);
479
480         talloc_free(mem_ctx);
481
482         return NT_STATUS_EA_CORRUPT_ERROR;
483 }
484