2 Unix SMB/CIFS implementation.
3 SMB torture tester utility functions
4 Copyright (C) Andrew Tridgell 2003
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.
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.
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.
23 #include "lib/cmdline/popt_common.h"
24 #include "libcli/raw/libcliraw.h"
25 #include "libcli/raw/ioctl.h"
26 #include "libcli/libcli.h"
27 #include "system/shmem.h"
28 #include "system/time.h"
29 #include "torture/torture.h"
33 setup a directory ready for a test
35 _PUBLIC_ BOOL torture_setup_dir(struct smbcli_state *cli, const char *dname)
37 smb_raw_exit(cli->session);
38 if (smbcli_deltree(cli->tree, dname) == -1 ||
39 NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, dname))) {
40 printf("Unable to setup %s - %s\n", dname, smbcli_errstr(cli->tree));
47 create a directory, returning a handle to it
49 NTSTATUS create_directory_handle(struct smbcli_tree *tree, const char *dname, int *fnum)
55 mem_ctx = talloc_init("create_directory_handle");
57 io.generic.level = RAW_OPEN_NTCREATEX;
58 io.ntcreatex.in.root_fid = 0;
59 io.ntcreatex.in.flags = 0;
60 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
61 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
62 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
63 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
64 io.ntcreatex.in.alloc_size = 0;
65 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
66 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
67 io.ntcreatex.in.security_flags = 0;
68 io.ntcreatex.in.fname = dname;
70 status = smb_raw_open(tree, mem_ctx, &io);
73 if (NT_STATUS_IS_OK(status)) {
74 *fnum = io.ntcreatex.out.file.fnum;
82 sometimes we need a fairly complex file to work with, so we can test
83 all possible attributes.
85 _PUBLIC_ int create_complex_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *fname)
89 union smb_setfileinfo setfile;
90 union smb_fileinfo fileinfo;
91 time_t t = (time(NULL) & ~1);
94 smbcli_unlink(cli->tree, fname);
95 fnum = smbcli_nt_create_full(cli->tree, fname, 0,
97 FILE_ATTRIBUTE_NORMAL,
98 NTCREATEX_SHARE_ACCESS_DELETE|
99 NTCREATEX_SHARE_ACCESS_READ|
100 NTCREATEX_SHARE_ACCESS_WRITE,
101 NTCREATEX_DISP_OVERWRITE_IF,
103 if (fnum == -1) return -1;
105 smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf));
107 if (strchr(fname, ':') == NULL) {
109 setfile.generic.level = RAW_SFILEINFO_EA_SET;
110 setfile.generic.in.file.fnum = fnum;
111 setfile.ea_set.in.num_eas = 2;
112 setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
113 setfile.ea_set.in.eas[0].flags = 0;
114 setfile.ea_set.in.eas[0].name.s = "EAONE";
115 setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6);
116 setfile.ea_set.in.eas[1].flags = 0;
117 setfile.ea_set.in.eas[1].name.s = "SECONDEA";
118 setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8);
119 status = smb_raw_setfileinfo(cli->tree, &setfile);
120 if (!NT_STATUS_IS_OK(status)) {
121 printf("Failed to setup EAs\n");
125 /* make sure all the timestamps aren't the same, and are also
126 in different DST zones*/
127 setfile.generic.level = RAW_SFILEINFO_SETATTRE;
128 setfile.generic.in.file.fnum = fnum;
130 setfile.setattre.in.create_time = t + 9*30*24*60*60;
131 setfile.setattre.in.access_time = t + 6*30*24*60*60;
132 setfile.setattre.in.write_time = t + 3*30*24*60*60;
134 status = smb_raw_setfileinfo(cli->tree, &setfile);
135 if (!NT_STATUS_IS_OK(status)) {
136 printf("Failed to setup file times - %s\n", nt_errstr(status));
139 /* make sure all the timestamps aren't the same */
140 fileinfo.generic.level = RAW_FILEINFO_GETATTRE;
141 fileinfo.generic.in.file.fnum = fnum;
143 status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
144 if (!NT_STATUS_IS_OK(status)) {
145 printf("Failed to query file times - %s\n", nt_errstr(status));
148 if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) {
149 printf("create_time not setup correctly\n");
151 if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
152 printf("access_time not setup correctly\n");
154 if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
155 printf("write_time not setup correctly\n");
163 sometimes we need a fairly complex directory to work with, so we can test
164 all possible attributes.
166 int create_complex_dir(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *dname)
169 union smb_setfileinfo setfile;
170 union smb_fileinfo fileinfo;
171 time_t t = (time(NULL) & ~1);
174 smbcli_deltree(cli->tree, dname);
175 fnum = smbcli_nt_create_full(cli->tree, dname, 0,
177 FILE_ATTRIBUTE_DIRECTORY,
178 NTCREATEX_SHARE_ACCESS_READ|
179 NTCREATEX_SHARE_ACCESS_WRITE,
180 NTCREATEX_DISP_OPEN_IF,
181 NTCREATEX_OPTIONS_DIRECTORY, 0);
182 if (fnum == -1) return -1;
184 if (strchr(dname, ':') == NULL) {
186 setfile.generic.level = RAW_SFILEINFO_EA_SET;
187 setfile.generic.in.file.fnum = fnum;
188 setfile.ea_set.in.num_eas = 2;
189 setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
190 setfile.ea_set.in.eas[0].flags = 0;
191 setfile.ea_set.in.eas[0].name.s = "EAONE";
192 setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6);
193 setfile.ea_set.in.eas[1].flags = 0;
194 setfile.ea_set.in.eas[1].name.s = "SECONDEA";
195 setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8);
196 status = smb_raw_setfileinfo(cli->tree, &setfile);
197 if (!NT_STATUS_IS_OK(status)) {
198 printf("Failed to setup EAs\n");
202 /* make sure all the timestamps aren't the same, and are also
203 in different DST zones*/
204 setfile.generic.level = RAW_SFILEINFO_SETATTRE;
205 setfile.generic.in.file.fnum = fnum;
207 setfile.setattre.in.create_time = t + 9*30*24*60*60;
208 setfile.setattre.in.access_time = t + 6*30*24*60*60;
209 setfile.setattre.in.write_time = t + 3*30*24*60*60;
211 status = smb_raw_setfileinfo(cli->tree, &setfile);
212 if (!NT_STATUS_IS_OK(status)) {
213 printf("Failed to setup file times - %s\n", nt_errstr(status));
216 /* make sure all the timestamps aren't the same */
217 fileinfo.generic.level = RAW_FILEINFO_GETATTRE;
218 fileinfo.generic.in.file.fnum = fnum;
220 status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
221 if (!NT_STATUS_IS_OK(status)) {
222 printf("Failed to query file times - %s\n", nt_errstr(status));
225 if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) {
226 printf("create_time not setup correctly\n");
228 if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
229 printf("access_time not setup correctly\n");
231 if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
232 printf("write_time not setup correctly\n");
240 /* return a pointer to a anonymous shared memory segment of size "size"
241 which will persist across fork() but will disappear when all processes
244 The memory is not zeroed
246 This function uses system5 shared memory. It takes advantage of a property
247 that the memory is not destroyed if it is attached when the id is removed
249 void *shm_setup(int size)
254 shmid = shmget(IPC_PRIVATE, size, SHM_R | SHM_W);
256 printf("can't get shared memory\n");
259 ret = (void *)shmat(shmid, 0, 0);
260 if (!ret || ret == (void *)-1) {
261 printf("can't attach to shared memory\n");
264 /* the following releases the ipc, but note that this process
265 and all its children will still have access to the memory, its
266 just that the shmid is no longer valid for other shm calls. This
267 means we don't leave behind lots of shm segments after we exit
269 See Stevens "advanced programming in unix env" for details
271 shmctl(shmid, IPC_RMID, 0);
278 check that a wire string matches the flags specified
279 not 100% accurate, but close enough for testing
281 BOOL wire_bad_flags(struct smb_wire_string *str, int flags, struct smbcli_state *cli)
285 if (!str || !str->s) return True;
286 len = strlen(str->s);
287 if (flags & STR_TERMINATE) len++;
289 server_unicode = (cli->transport->negotiate.capabilities&CAP_UNICODE)?True:False;
290 if (getenv("CLI_FORCE_ASCII") || !lp_unicode()) {
291 server_unicode = False;
294 if ((flags & STR_UNICODE) || server_unicode) {
296 } else if (flags & STR_TERMINATE_ASCII) {
299 if (str->private_length != len) {
300 printf("Expected wire_length %d but got %d for '%s'\n",
301 len, str->private_length, str->s);
308 check if 2 NTTIMEs are equal
310 BOOL nt_time_equal(NTTIME *t1, NTTIME *t2)
316 dump a all_info QFILEINFO structure
318 void dump_all_info(TALLOC_CTX *mem_ctx, union smb_fileinfo *finfo)
320 d_printf("\tcreate_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.create_time));
321 d_printf("\taccess_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.access_time));
322 d_printf("\twrite_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.write_time));
323 d_printf("\tchange_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.change_time));
324 d_printf("\tattrib: 0x%x\n", finfo->all_info.out.attrib);
325 d_printf("\talloc_size: %llu\n", (long long)finfo->all_info.out.alloc_size);
326 d_printf("\tsize: %llu\n", (long long)finfo->all_info.out.size);
327 d_printf("\tnlink: %u\n", finfo->all_info.out.nlink);
328 d_printf("\tdelete_pending: %u\n", finfo->all_info.out.delete_pending);
329 d_printf("\tdirectory: %u\n", finfo->all_info.out.directory);
330 d_printf("\tea_size: %u\n", finfo->all_info.out.ea_size);
331 d_printf("\tfname: '%s'\n", finfo->all_info.out.fname.s);
335 dump file infor by name
337 void torture_all_info(struct smbcli_tree *tree, const char *fname)
339 TALLOC_CTX *mem_ctx = talloc_init("%s", fname);
340 union smb_fileinfo finfo;
343 finfo.generic.level = RAW_FILEINFO_ALL_INFO;
344 finfo.generic.in.file.path = fname;
345 status = smb_raw_pathinfo(tree, mem_ctx, &finfo);
346 if (!NT_STATUS_IS_OK(status)) {
347 d_printf("%s - %s\n", fname, nt_errstr(status));
351 d_printf("%s:\n", fname);
352 dump_all_info(mem_ctx, &finfo);
353 talloc_free(mem_ctx);
358 set a attribute on a file
360 BOOL torture_set_file_attribute(struct smbcli_tree *tree, const char *fname, uint16_t attrib)
362 union smb_setfileinfo sfinfo;
365 ZERO_STRUCT(sfinfo.basic_info.in);
366 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
367 sfinfo.basic_info.in.file.path = fname;
368 sfinfo.basic_info.in.attrib = attrib;
369 status = smb_raw_setpathinfo(tree, &sfinfo);
370 return NT_STATUS_IS_OK(status);
375 set a file descriptor as sparse
377 NTSTATUS torture_set_sparse(struct smbcli_tree *tree, int fnum)
383 mem_ctx = talloc_init("torture_set_sparse");
385 return NT_STATUS_NO_MEMORY;
388 nt.ntioctl.level = RAW_IOCTL_NTIOCTL;
389 nt.ntioctl.in.function = FSCTL_SET_SPARSE;
390 nt.ntioctl.in.file.fnum = fnum;
391 nt.ntioctl.in.fsctl = True;
392 nt.ntioctl.in.filter = 0;
393 nt.ntioctl.in.max_data = 0;
394 nt.ntioctl.in.blob = data_blob(NULL, 0);
396 status = smb_raw_ioctl(tree, mem_ctx, &nt);
398 talloc_free(mem_ctx);
404 check that an EA has the right value
406 NTSTATUS torture_check_ea(struct smbcli_state *cli,
407 const char *fname, const char *eaname, const char *value)
409 union smb_fileinfo info;
412 TALLOC_CTX *mem_ctx = talloc_new(cli);
414 info.ea_list.level = RAW_FILEINFO_EA_LIST;
415 info.ea_list.in.file.path = fname;
416 info.ea_list.in.num_names = 1;
417 info.ea_list.in.ea_names = &ea;
421 status = smb_raw_pathinfo(cli->tree, mem_ctx, &info);
422 if (!NT_STATUS_IS_OK(status)) {
423 talloc_free(mem_ctx);
427 if (info.ea_list.out.num_eas != 1) {
428 printf("Expected 1 ea in ea_list\n");
429 talloc_free(mem_ctx);
430 return NT_STATUS_EA_CORRUPT_ERROR;
433 if (strcasecmp_m(eaname, info.ea_list.out.eas[0].name.s) != 0) {
434 printf("Expected ea '%s' not '%s' in ea_list\n",
435 eaname, info.ea_list.out.eas[0].name.s);
436 talloc_free(mem_ctx);
437 return NT_STATUS_EA_CORRUPT_ERROR;
441 if (info.ea_list.out.eas[0].value.length != 0) {
442 printf("Expected zero length ea for %s\n", eaname);
443 talloc_free(mem_ctx);
444 return NT_STATUS_EA_CORRUPT_ERROR;
446 talloc_free(mem_ctx);
450 if (strlen(value) == info.ea_list.out.eas[0].value.length &&
451 memcmp(value, info.ea_list.out.eas[0].value.data,
452 info.ea_list.out.eas[0].value.length) == 0) {
453 talloc_free(mem_ctx);
457 printf("Expected value '%s' not '%*.*s' for ea %s\n",
459 (int)info.ea_list.out.eas[0].value.length,
460 (int)info.ea_list.out.eas[0].value.length,
461 info.ea_list.out.eas[0].value.data,
464 talloc_free(mem_ctx);
466 return NT_STATUS_EA_CORRUPT_ERROR;
469 BOOL torture_open_connection_share(TALLOC_CTX *mem_ctx,
470 struct smbcli_state **c,
471 const char *hostname,
472 const char *sharename,
473 struct event_context *ev)
477 status = smbcli_full_connection(mem_ctx, c, hostname,
479 cmdline_credentials, ev);
480 if (!NT_STATUS_IS_OK(status)) {
481 printf("Failed to open connection - %s\n", nt_errstr(status));
485 (*c)->transport->options.use_oplocks = use_oplocks;
486 (*c)->transport->options.use_level2_oplocks = use_level_II_oplocks;
491 _PUBLIC_ BOOL torture_open_connection(struct smbcli_state **c)
493 const char *host = lp_parm_string(-1, "torture", "host");
494 const char *share = lp_parm_string(-1, "torture", "share");
496 return torture_open_connection_share(NULL, c, host, share, NULL);
499 _PUBLIC_ BOOL torture_open_connection_ev(struct smbcli_state **c,
500 struct event_context *ev)
502 const char *host = lp_parm_string(-1, "torture", "host");
503 const char *share = lp_parm_string(-1, "torture", "share");
505 return torture_open_connection_share(NULL, c, host, share, ev);
510 _PUBLIC_ BOOL torture_close_connection(struct smbcli_state *c)
514 if (NT_STATUS_IS_ERR(smbcli_tdis(c))) {
515 printf("tdis failed (%s)\n", smbcli_errstr(c->tree));
523 /* check if the server produced the expected error code */
524 _PUBLIC_ BOOL check_error(const char *location, struct smbcli_state *c,
525 uint8_t eclass, uint32_t ecode, NTSTATUS nterr)
529 status = smbcli_nt_error(c->tree);
530 if (NT_STATUS_IS_DOS(status)) {
532 class = NT_STATUS_DOS_CLASS(status);
533 num = NT_STATUS_DOS_CODE(status);
534 if (eclass != class || ecode != num) {
535 printf("unexpected error code %s\n", nt_errstr(status));
536 printf(" expected %s or %s (at %s)\n",
537 nt_errstr(NT_STATUS_DOS(eclass, ecode)),
538 nt_errstr(nterr), location);
542 if (!NT_STATUS_EQUAL(nterr, status)) {
543 printf("unexpected error code %s\n", nt_errstr(status));
544 printf(" expected %s (at %s)\n", nt_errstr(nterr), location);