/*
Unix SMB/CIFS implementation.
generic testing tool
Copyright (C) Andrew Tridgell 2003
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "includes.h"
#include "lib/cmdline/popt_common.h"
#include "lib/events/events.h"
#include "system/time.h"
#include "system/filesys.h"
#include "libcli/raw/request.h"
#include "libcli/libcli.h"
#include "libcli/raw/libcliraw.h"
#include "librpc/gen_ndr/security.h"
#include "auth/credentials/credentials.h"
#include "libcli/resolve/resolve.h"
#include "auth/gensec/gensec.h"
#include "param/param.h"
#include "dynconfig/dynconfig.h"
#define NSERVERS 2
#define NINSTANCES 2
/* global options */
static struct gentest_options {
int showall;
int analyze;
int analyze_always;
int analyze_continuous;
uint_t max_open_handles;
uint_t seed;
uint_t numops;
int use_oplocks;
char **ignore_patterns;
const char *seeds_file;
int use_preset_seeds;
int fast_reconnect;
} options;
/* mapping between open handles on the server and local handles */
static struct {
bool active;
uint_t instance;
uint_t server_fnum[NSERVERS];
const char *name;
} *open_handles;
static uint_t num_open_handles;
/* state information for the servers. We open NINSTANCES connections to
each server */
static struct {
struct smbcli_state *cli[NINSTANCES];
char *server_name;
char *share_name;
struct cli_credentials *credentials;
} servers[NSERVERS];
/* the seeds and flags for each operation */
static struct {
uint_t seed;
bool disabled;
} *op_parms;
/* oplock break info */
static struct {
bool got_break;
uint16_t fnum;
uint16_t handle;
uint8_t level;
bool do_close;
} oplocks[NSERVERS][NINSTANCES];
/* change notify reply info */
static struct {
int notify_count;
NTSTATUS status;
union smb_notify notify;
} notifies[NSERVERS][NINSTANCES];
/* info relevant to the current operation */
static struct {
const char *name;
uint_t seed;
NTSTATUS status;
uint_t opnum;
TALLOC_CTX *mem_ctx;
} current_op;
#define BAD_HANDLE 0xFFFE
static bool oplock_handler(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private);
static void idle_func(struct smbcli_transport *transport, void *private);
/*
check if a string should be ignored. This is used as the basis
for all error ignore settings
*/
static bool ignore_pattern(const char *str)
{
int i;
if (!options.ignore_patterns) return false;
for (i=0;options.ignore_patterns[i];i++) {
if (strcmp(options.ignore_patterns[i], str) == 0 ||
gen_fnmatch(options.ignore_patterns[i], str) == 0) {
DEBUG(2,("Ignoring '%s'\n", str));
return true;
}
}
return false;
}
/*****************************************************
connect to the servers
*******************************************************/
static bool connect_servers_fast(void)
{
int h, i;
/* close all open files */
for (h=0;htree,
open_handles[h].server_fnum[i])))) {
return false;
}
open_handles[h].active = false;
}
}
return true;
}
/*****************************************************
connect to the servers
*******************************************************/
static bool connect_servers(struct event_context *ev,
struct loadparm_context *lp_ctx)
{
int i, j;
if (options.fast_reconnect && servers[0].cli[0]) {
if (connect_servers_fast()) {
return true;
}
}
/* close any existing connections */
for (i=0;iusername, j);
cli_credentials_set_workstation(servers[i].credentials,
"gentest", CRED_SPECIFIED);
lp_smbcli_options(lp_ctx, &smb_options);
status = smbcli_full_connection(NULL, &servers[i].cli[j],
servers[i].server_name,
lp_smb_ports(lp_ctx),
servers[i].share_name, NULL,
servers[i].credentials,
lp_resolve_context(lp_ctx),
ev, &smb_options);
if (!NT_STATUS_IS_OK(status)) {
printf("Failed to connect to \\\\%s\\%s - %s\n",
servers[i].server_name, servers[i].share_name,
nt_errstr(status));
return false;
}
smbcli_oplock_handler(servers[i].cli[j]->transport, oplock_handler, NULL);
smbcli_transport_idle_handler(servers[i].cli[j]->transport, idle_func, 50000, NULL);
}
}
return true;
}
/*
work out the time skew between the servers - be conservative
*/
static uint_t time_skew(void)
{
uint_t ret;
ret = labs(servers[0].cli[0]->transport->negotiate.server_time -
servers[1].cli[0]->transport->negotiate.server_time);
return ret + 300;
}
/*
turn an fnum for an instance into a handle
*/
static uint_t fnum_to_handle(int server, int instance, uint16_t fnum)
{
uint_t i;
for (i=0;itree,
open_handles[h].server_fnum[i])))) {
printf("INTERNAL ERROR: Close failed when recovering handle! - %s\n",
smbcli_errstr(servers[i].cli[open_handles[h].instance]->tree));
}
}
printf("Recovered handle %d\n", h);
num_open_handles--;
}
for (i=0;i 0 && count++ < 10*options.max_open_handles) {
h = random() % options.max_open_handles;
if (open_handles[h].active &&
open_handles[h].instance == instance) {
return h;
}
}
return BAD_HANDLE;
}
/*
return a file handle, but skewed so we don't close the last
couple of handles too readily
*/
static uint16_t gen_fnum_close(int instance)
{
if (num_open_handles < 3) {
if (gen_chance(80)) return BAD_HANDLE;
}
return gen_fnum(instance);
}
/*
generate an integer in a specified range
*/
static int gen_int_range(uint_t min, uint_t max)
{
uint_t r = random();
return min + (r % (1+max-min));
}
/*
return a fnum for use as a root fid
be careful to call GEN_SET_FNUM() when you use this!
*/
static uint16_t gen_root_fid(int instance)
{
if (gen_chance(5)) return gen_fnum(instance);
return 0;
}
/*
generate a file offset
*/
static int gen_offset(void)
{
if (gen_chance(20)) return 0;
return gen_int_range(0, 1024*1024);
}
/*
generate a io count
*/
static int gen_io_count(void)
{
if (gen_chance(20)) return 0;
return gen_int_range(0, 4096);
}
/*
generate a filename
*/
static const char *gen_fname(void)
{
const char *names[] = {"\\gentest\\gentest.dat",
"\\gentest\\foo",
"\\gentest\\foo2.sym",
"\\gentest\\foo3.dll",
"\\gentest\\foo4",
"\\gentest\\foo4:teststream1",
"\\gentest\\foo4:teststream2",
"\\gentest\\foo5.exe",
"\\gentest\\foo5.exe:teststream3",
"\\gentest\\foo5.exe:teststream4",
"\\gentest\\foo6.com",
"\\gentest\\blah",
"\\gentest\\blah\\blergh.txt",
"\\gentest\\blah\\blergh2",
"\\gentest\\blah\\blergh3.txt",
"\\gentest\\blah\\blergh4",
"\\gentest\\blah\\blergh5.txt",
"\\gentest\\blah\\blergh5",
"\\gentest\\blah\\.",
#if 0
/* this causes problem with w2k3 */
"\\gentest\\blah\\..",
#endif
"\\gentest\\a_very_long_name.bin",
"\\gentest\\x.y",
"\\gentest\\blah"};
int i;
do {
i = gen_int_range(0, ARRAY_SIZE(names)-1);
} while (ignore_pattern(names[i]));
return names[i];
}
/*
generate a filename with a higher chance of choosing an already
open file
*/
static const char *gen_fname_open(int instance)
{
uint16_t h;
h = gen_fnum(instance);
if (h == BAD_HANDLE) {
return gen_fname();
}
return open_handles[h].name;
}
/*
generate a wildcard pattern
*/
static const char *gen_pattern(void)
{
int i;
const char *names[] = {"\\gentest\\*.dat",
"\\gentest\\*",
"\\gentest\\*.*",
"\\gentest\\blah\\*.*",
"\\gentest\\blah\\*",
"\\gentest\\?"};
if (gen_chance(50)) return gen_fname();
do {
i = gen_int_range(0, ARRAY_SIZE(names)-1);
} while (ignore_pattern(names[i]));
return names[i];
}
/*
generate a bitmask
*/
static uint32_t gen_bits_mask(uint_t mask)
{
uint_t ret = random();
return ret & mask;
}
/*
generate a bitmask with high probability of the first mask
and low of the second
*/
static uint32_t gen_bits_mask2(uint32_t mask1, uint32_t mask2)
{
if (gen_chance(10)) return gen_bits_mask(mask2);
return gen_bits_mask(mask1);
}
/*
generate a boolean
*/
static bool gen_bool(void)
{
return gen_bits_mask2(0x1, 0xFF);
}
/*
generate ntrename flags
*/
static uint16_t gen_rename_flags(void)
{
if (gen_chance(30)) return RENAME_FLAG_RENAME;
if (gen_chance(30)) return RENAME_FLAG_HARD_LINK;
if (gen_chance(30)) return RENAME_FLAG_COPY;
return gen_bits_mask(0xFFFF);
}
/*
return a lockingx lock mode
*/
static uint16_t gen_lock_mode(void)
{
if (gen_chance(5)) return gen_bits_mask(0xFFFF);
if (gen_chance(20)) return gen_bits_mask(0x1F);
return gen_bits_mask(LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES);
}
/*
generate a pid
*/
static uint16_t gen_pid(void)
{
if (gen_chance(10)) return gen_bits_mask(0xFFFF);
return getpid();
}
/*
generate a lock count
*/
static off_t gen_lock_count(void)
{
return gen_int_range(0, 3);
}
/*
generate a ntcreatex flags field
*/
static uint32_t gen_ntcreatex_flags(void)
{
if (gen_chance(70)) return NTCREATEX_FLAGS_EXTENDED;
return gen_bits_mask2(0x1F, 0xFFFFFFFF);
}
/*
generate a NT access mask
*/
static uint32_t gen_access_mask(void)
{
if (gen_chance(50)) return SEC_FLAG_MAXIMUM_ALLOWED;
if (gen_chance(20)) return SEC_FILE_ALL;
return gen_bits_mask(0xFFFFFFFF);
}
/*
generate a ntcreatex create options bitfield
*/
static uint32_t gen_create_options(void)
{
if (gen_chance(20)) return gen_bits_mask(0xFFFFFFFF);
if (gen_chance(50)) return 0;
return gen_bits_mask(NTCREATEX_OPTIONS_DELETE_ON_CLOSE | NTCREATEX_OPTIONS_DIRECTORY);
}
/*
generate a ntcreatex open disposition
*/
static uint32_t gen_open_disp(void)
{
if (gen_chance(10)) return gen_bits_mask(0xFFFFFFFF);
return gen_int_range(0, 5);
}
/*
generate an openx open mode
*/
static uint16_t gen_openx_mode(void)
{
if (gen_chance(20)) return gen_bits_mask(0xFFFF);
if (gen_chance(20)) return gen_bits_mask(0xFF);
return OPENX_MODE_DENY_NONE | gen_bits_mask(0x3);
}
/*
generate an openx flags field
*/
static uint16_t gen_openx_flags(void)
{
if (gen_chance(20)) return gen_bits_mask(0xFFFF);
return gen_bits_mask(0x7);
}
/*
generate an openx open function
*/
static uint16_t gen_openx_func(void)
{
if (gen_chance(20)) return gen_bits_mask(0xFFFF);
return gen_bits_mask(0x13);
}
/*
generate a file attrib combination
*/
static uint32_t gen_attrib(void)
{
if (gen_chance(20)) return gen_bits_mask(0xFFFFFFFF);
return gen_bits_mask(FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY);
}
/*
generate a unix timestamp
*/
static time_t gen_timet(void)
{
if (gen_chance(30)) return 0;
return (time_t)random();
}
/*
generate a unix timestamp
*/
static NTTIME gen_nttime(void)
{
NTTIME ret;
unix_to_nt_time(&ret, gen_timet());
return ret;
}
/*
generate a milliseconds protocol timeout
*/
static uint32_t gen_timeout(void)
{
if (gen_chance(98)) return 0;
return random() % 50;
}
/*
generate a file allocation size
*/
static uint_t gen_alloc_size(void)
{
uint_t ret;
if (gen_chance(30)) return 0;
ret = random() % 4*1024*1024;
/* give a high chance of a round number */
if (gen_chance(60)) {
ret &= ~(1024*1024 - 1);
}
return ret;
}
/*
generate an ea_struct
*/
static struct ea_struct gen_ea_struct(void)
{
struct ea_struct ea;
const char *names[] = {"EAONE",
"",
"FOO!",
" WITH SPACES ",
".",
"AVERYLONGATTRIBUTENAME"};
const char *values[] = {"VALUE1",
"",
"NOT MUCH FOO",
" LEADING SPACES ",
":",
"ASOMEWHATLONGERATTRIBUTEVALUE"};
int i;
ZERO_STRUCT(ea);
do {
i = gen_int_range(0, ARRAY_SIZE(names)-1);
} while (ignore_pattern(names[i]));
ea.name.s = names[i];
do {
i = gen_int_range(0, ARRAY_SIZE(values)-1);
} while (ignore_pattern(values[i]));
ea.value = data_blob(values[i], strlen(values[i]));
if (gen_chance(10)) ea.flags = gen_bits_mask(0xFF);
ea.flags = 0;
return ea;
}
/*
this is called when a change notify reply comes in
*/
static void async_notify(struct smbcli_request *req)
{
union smb_notify notify;
NTSTATUS status;
int i, j;
uint16_t tid;
struct smbcli_transport *transport = req->transport;
tid = SVAL(req->in.hdr, HDR_TID);
notify.nttrans.level = RAW_NOTIFY_NTTRANS;
status = smb_raw_changenotify_recv(req, current_op.mem_ctx, ¬ify);
if (NT_STATUS_IS_OK(status)) {
printf("notify tid=%d num_changes=%d action=%d name=%s\n",
tid,
notify.nttrans.out.num_changes,
notify.nttrans.out.changes[0].action,
notify.nttrans.out.changes[0].name.s);
}
for (i=0;itransport &&
tid == servers[i].cli[j]->tree->tid) {
notifies[i][j].notify_count++;
notifies[i][j].status = status;
notifies[i][j].notify = notify;
}
}
}
}
static void oplock_handler_close_recv(struct smbcli_request *req)
{
NTSTATUS status;
status = smbcli_request_simple_recv(req);
if (!NT_STATUS_IS_OK(status)) {
printf("close failed in oplock_handler\n");
smb_panic("close failed in oplock_handler");
}
}
/*
the oplock handler will either ack the break or close the file
*/
static bool oplock_handler(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private)
{
union smb_close io;
int i, j;
bool do_close;
struct smbcli_tree *tree = NULL;
struct smbcli_request *req;
srandom(current_op.seed);
do_close = gen_chance(50);
for (i=0;itransport &&
tid == servers[i].cli[j]->tree->tid) {
oplocks[i][j].got_break = true;
oplocks[i][j].fnum = fnum;
oplocks[i][j].handle = fnum_to_handle(i, j, fnum);
oplocks[i][j].level = level;
oplocks[i][j].do_close = do_close;
tree = servers[i].cli[j]->tree;
}
}
}
if (!tree) {
printf("Oplock break not for one of our trees!?\n");
return false;
}
if (!do_close) {
printf("oplock ack fnum=%d\n", fnum);
return smbcli_oplock_ack(tree, fnum, level);
}
printf("oplock close fnum=%d\n", fnum);
io.close.level = RAW_CLOSE_CLOSE;
io.close.in.file.fnum = fnum;
io.close.in.write_time = 0;
req = smb_raw_close_send(tree, &io);
if (req == NULL) {
printf("WARNING: close failed in oplock_handler_close\n");
return false;
}
req->async.fn = oplock_handler_close_recv;
req->async.private = NULL;
return true;
}
/*
the idle function tries to cope with getting an oplock break on a connection, and
an operation on another connection blocking until that break is acked
we check for operations on all transports in the idle function
*/
static void idle_func(struct smbcli_transport *transport, void *private)
{
int i, j;
for (i=0;itransport) {
smbcli_transport_process(servers[i].cli[j]->transport);
}
}
}
}
/*
compare NTSTATUS, using checking ignored patterns
*/
static bool compare_status(NTSTATUS status1, NTSTATUS status2)
{
if (NT_STATUS_EQUAL(status1, status2)) return true;
/* one code being an error and the other OK is always an error */
if (NT_STATUS_IS_OK(status1) || NT_STATUS_IS_OK(status2)) return false;
/* if we are ignoring one of the status codes then consider this a match */
if (ignore_pattern(nt_errstr(status1)) ||
ignore_pattern(nt_errstr(status2))) {
return true;
}
return false;
}
/*
check for pending packets on all connections
*/
static void check_pending(void)
{
int i, j;
msleep(20);
for (j=0;jtransport);
}
}
}
/*
check that the same oplock breaks have been received by all instances
*/
static bool check_oplocks(const char *call)
{
int i, j;
int tries = 0;
again:
check_pending();
for (j=0;jtree; \
status[i] = call; \
} \
current_op.status = status[0]; \
for (i=1;i time_skew() && \
!ignore_pattern(#field)) { \
printf("Mismatch in %s - 0x%x 0x%x\n", #field, \
(int)parm[0].field, (int)parm[1].field); \
return false; \
} \
} while(0)
#define CHECK_NTTIMES_EQUAL(field) do { \
if (labs(nt_time_to_unix(parm[0].field) - \
nt_time_to_unix(parm[1].field)) > time_skew() && \
!ignore_pattern(#field)) { \
printf("Mismatch in %s - 0x%x 0x%x\n", #field, \
(int)nt_time_to_unix(parm[0].field), \
(int)nt_time_to_unix(parm[1].field)); \
return false; \
} \
} while(0)
/*
generate openx operations
*/
static bool handler_openx(int instance)
{
union smb_open parm[NSERVERS];
NTSTATUS status[NSERVERS];
parm[0].openx.level = RAW_OPEN_OPENX;
parm[0].openx.in.flags = gen_openx_flags();
parm[0].openx.in.open_mode = gen_openx_mode();
parm[0].openx.in.search_attrs = gen_attrib();
parm[0].openx.in.file_attrs = gen_attrib();
parm[0].openx.in.write_time = gen_timet();
parm[0].openx.in.open_func = gen_openx_func();
parm[0].openx.in.size = gen_io_count();
parm[0].openx.in.timeout = gen_timeout();
parm[0].openx.in.fname = gen_fname_open(instance);
if (!options.use_oplocks) {
/* mask out oplocks */
parm[0].openx.in.flags &= ~(OPENX_FLAGS_REQUEST_OPLOCK|
OPENX_FLAGS_REQUEST_BATCH_OPLOCK);
}
GEN_COPY_PARM;
GEN_CALL(smb_raw_open(tree, current_op.mem_ctx, &parm[i]));
CHECK_EQUAL(openx.out.attrib);
CHECK_EQUAL(openx.out.size);
CHECK_EQUAL(openx.out.access);
CHECK_EQUAL(openx.out.ftype);
CHECK_EQUAL(openx.out.devstate);
CHECK_EQUAL(openx.out.action);
CHECK_EQUAL(openx.out.access_mask);
CHECK_EQUAL(openx.out.unknown);
CHECK_TIMES_EQUAL(openx.out.write_time);
/* open creates a new file handle */
ADD_HANDLE(parm[0].openx.in.fname, openx.out.file.fnum);
return true;
}
/*
generate open operations
*/
static bool handler_open(int instance)
{
union smb_open parm[NSERVERS];
NTSTATUS status[NSERVERS];
parm[0].openold.level = RAW_OPEN_OPEN;
parm[0].openold.in.open_mode = gen_bits_mask2(0xF, 0xFFFF);
parm[0].openold.in.search_attrs = gen_attrib();
parm[0].openold.in.fname = gen_fname_open(instance);
if (!options.use_oplocks) {
/* mask out oplocks */
parm[0].openold.in.open_mode &= ~(OPENX_FLAGS_REQUEST_OPLOCK|
OPENX_FLAGS_REQUEST_BATCH_OPLOCK);
}
GEN_COPY_PARM;
GEN_CALL(smb_raw_open(tree, current_op.mem_ctx, &parm[i]));
CHECK_EQUAL(openold.out.attrib);
CHECK_TIMES_EQUAL(openold.out.write_time);
CHECK_EQUAL(openold.out.size);
CHECK_EQUAL(openold.out.rmode);
/* open creates a new file handle */
ADD_HANDLE(parm[0].openold.in.fname, openold.out.file.fnum);
return true;
}
/*
generate ntcreatex operations
*/
static bool handler_ntcreatex(int instance)
{
union smb_open parm[NSERVERS];
NTSTATUS status[NSERVERS];
parm[0].ntcreatex.level = RAW_OPEN_NTCREATEX;
parm[0].ntcreatex.in.flags = gen_ntcreatex_flags();
parm[0].ntcreatex.in.root_fid = gen_root_fid(instance);
parm[0].ntcreatex.in.access_mask = gen_access_mask();
parm[0].ntcreatex.in.alloc_size = gen_alloc_size();
parm[0].ntcreatex.in.file_attr = gen_attrib();
parm[0].ntcreatex.in.share_access = gen_bits_mask2(0x7, 0xFFFFFFFF);
parm[0].ntcreatex.in.open_disposition = gen_open_disp();
parm[0].ntcreatex.in.create_options = gen_create_options();
parm[0].ntcreatex.in.impersonation = gen_bits_mask2(0, 0xFFFFFFFF);
parm[0].ntcreatex.in.security_flags = gen_bits_mask2(0, 0xFF);
parm[0].ntcreatex.in.fname = gen_fname_open(instance);
if (!options.use_oplocks) {
/* mask out oplocks */
parm[0].ntcreatex.in.flags &= ~(NTCREATEX_FLAGS_REQUEST_OPLOCK|
NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK);
}
GEN_COPY_PARM;
if (parm[0].ntcreatex.in.root_fid != 0) {
GEN_SET_FNUM(ntcreatex.in.root_fid);
}
GEN_CALL(smb_raw_open(tree, current_op.mem_ctx, &parm[i]));
CHECK_EQUAL(ntcreatex.out.oplock_level);
CHECK_EQUAL(ntcreatex.out.create_action);
CHECK_NTTIMES_EQUAL(ntcreatex.out.create_time);
CHECK_NTTIMES_EQUAL(ntcreatex.out.access_time);
CHECK_NTTIMES_EQUAL(ntcreatex.out.write_time);
CHECK_NTTIMES_EQUAL(ntcreatex.out.change_time);
CHECK_EQUAL(ntcreatex.out.attrib);
CHECK_EQUAL(ntcreatex.out.alloc_size);
CHECK_EQUAL(ntcreatex.out.size);
CHECK_EQUAL(ntcreatex.out.file_type);
CHECK_EQUAL(ntcreatex.out.ipc_state);
CHECK_EQUAL(ntcreatex.out.is_directory);
/* ntcreatex creates a new file handle */
ADD_HANDLE(parm[0].ntcreatex.in.fname, ntcreatex.out.file.fnum);
return true;
}
/*
generate close operations
*/
static bool handler_close(int instance)
{
union smb_close parm[NSERVERS];
NTSTATUS status[NSERVERS];
parm[0].close.level = RAW_CLOSE_CLOSE;
parm[0].close.in.file.fnum = gen_fnum_close(instance);
parm[0].close.in.write_time = gen_timet();
GEN_COPY_PARM;
GEN_SET_FNUM(close.in.file.fnum);
GEN_CALL(smb_raw_close(tree, &parm[i]));
REMOVE_HANDLE(close.in.file.fnum);
return true;
}
/*
generate unlink operations
*/
static bool handler_unlink(int instance)
{
union smb_unlink parm[NSERVERS];
NTSTATUS status[NSERVERS];
parm[0].unlink.in.pattern = gen_pattern();
parm[0].unlink.in.attrib = gen_attrib();
GEN_COPY_PARM;
GEN_CALL(smb_raw_unlink(tree, &parm[i]));
return true;
}
/*
generate chkpath operations
*/
static bool handler_chkpath(int instance)
{
union smb_chkpath parm[NSERVERS];
NTSTATUS status[NSERVERS];
parm[0].chkpath.in.path = gen_fname_open(instance);
GEN_COPY_PARM;
GEN_CALL(smb_raw_chkpath(tree, &parm[i]));
return true;
}
/*
generate mkdir operations
*/
static bool handler_mkdir(int instance)
{
union smb_mkdir parm[NSERVERS];
NTSTATUS status[NSERVERS];
parm[0].mkdir.level = RAW_MKDIR_MKDIR;
parm[0].mkdir.in.path = gen_fname_open(instance);
GEN_COPY_PARM;
GEN_CALL(smb_raw_mkdir(tree, &parm[i]));
return true;
}
/*
generate rmdir operations
*/
static bool handler_rmdir(int instance)
{
struct smb_rmdir parm[NSERVERS];
NTSTATUS status[NSERVERS];
parm[0].in.path = gen_fname_open(instance);
GEN_COPY_PARM;
GEN_CALL(smb_raw_rmdir(tree, &parm[i]));
return true;
}
/*
generate rename operations
*/
static bool handler_rename(int instance)
{
union smb_rename parm[NSERVERS];
NTSTATUS status[NSERVERS];
parm[0].generic.level = RAW_RENAME_RENAME;
parm[0].rename.in.pattern1 = gen_pattern();
parm[0].rename.in.pattern2 = gen_pattern();
parm[0].rename.in.attrib = gen_attrib();
GEN_COPY_PARM;
GEN_CALL(smb_raw_rename(tree, &parm[i]));
return true;
}
/*
generate ntrename operations
*/
static bool handler_ntrename(int instance)
{
union smb_rename parm[NSERVERS];
NTSTATUS status[NSERVERS];
parm[0].generic.level = RAW_RENAME_NTRENAME;
parm[0].ntrename.in.old_name = gen_fname();
parm[0].ntrename.in.new_name = gen_fname();
parm[0].ntrename.in.attrib = gen_attrib();
parm[0].ntrename.in.cluster_size = gen_bits_mask2(0, 0xFFFFFFF);
parm[0].ntrename.in.flags = gen_rename_flags();
GEN_COPY_PARM;
GEN_CALL(smb_raw_rename(tree, &parm[i]));
return true;
}
/*
generate seek operations
*/
static bool handler_seek(int instance)
{
union smb_seek parm[NSERVERS];
NTSTATUS status[NSERVERS];
parm[0].lseek.in.file.fnum = gen_fnum(instance);
parm[0].lseek.in.mode = gen_bits_mask2(0x3, 0xFFFF);
parm[0].lseek.in.offset = gen_offset();
GEN_COPY_PARM;
GEN_SET_FNUM(lseek.in.file.fnum);
GEN_CALL(smb_raw_seek(tree, &parm[i]));
CHECK_EQUAL(lseek.out.offset);
return true;
}
/*
generate readx operations
*/
static bool handler_readx(int instance)
{
union smb_read parm[NSERVERS];
NTSTATUS status[NSERVERS];
parm[0].readx.level = RAW_READ_READX;
parm[0].readx.in.file.fnum = gen_fnum(instance);
parm[0].readx.in.offset = gen_offset();
parm[0].readx.in.mincnt = gen_io_count();
parm[0].readx.in.maxcnt = gen_io_count();
parm[0].readx.in.remaining = gen_io_count();
parm[0].readx.in.read_for_execute = gen_bool();
parm[0].readx.out.data = talloc_array(current_op.mem_ctx, uint8_t,
MAX(parm[0].readx.in.mincnt, parm[0].readx.in.maxcnt));
GEN_COPY_PARM;
GEN_SET_FNUM(readx.in.file.fnum);
GEN_CALL(smb_raw_read(tree, &parm[i]));
CHECK_EQUAL(readx.out.remaining);
CHECK_EQUAL(readx.out.compaction_mode);
CHECK_EQUAL(readx.out.nread);
return true;
}
/*
generate writex operations
*/
static bool handler_writex(int instance)
{
union smb_write parm[NSERVERS];
NTSTATUS status[NSERVERS];
parm[0].writex.level = RAW_WRITE_WRITEX;
parm[0].writex.in.file.fnum = gen_fnum(instance);
parm[0].writex.in.offset = gen_offset();
parm[0].writex.in.wmode = gen_bits_mask(0xFFFF);
parm[0].writex.in.remaining = gen_io_count();
parm[0].writex.in.count = gen_io_count();
parm[0].writex.in.data = talloc_zero_array(current_op.mem_ctx, uint8_t, parm[0].writex.in.count);
GEN_COPY_PARM;
GEN_SET_FNUM(writex.in.file.fnum);
GEN_CALL(smb_raw_write(tree, &parm[i]));
CHECK_EQUAL(writex.out.nwritten);
CHECK_EQUAL(writex.out.remaining);
return true;
}
/*
generate lockingx operations
*/
static bool handler_lockingx(int instance)
{
union smb_lock parm[NSERVERS];
NTSTATUS status[NSERVERS];
int n, nlocks;
parm[0].lockx.level = RAW_LOCK_LOCKX;
parm[0].lockx.in.file.fnum = gen_fnum(instance);
parm[0].lockx.in.mode = gen_lock_mode();
parm[0].lockx.in.timeout = gen_timeout();
do {
/* make sure we don't accidentially generate an oplock
break ack - otherwise the server can just block forever */
parm[0].lockx.in.ulock_cnt = gen_lock_count();
parm[0].lockx.in.lock_cnt = gen_lock_count();
nlocks = parm[0].lockx.in.ulock_cnt + parm[0].lockx.in.lock_cnt;
} while (nlocks == 0);
if (nlocks > 0) {
parm[0].lockx.in.locks = talloc_array(current_op.mem_ctx,
struct smb_lock_entry,
nlocks);
for (n=0;ngeneric.level = levels[i].level;
}
/*
compare returned fileinfo structures
*/
static bool cmp_fileinfo(int instance,
union smb_fileinfo parm[NSERVERS],
NTSTATUS status[NSERVERS])
{
int i;
switch (parm[0].generic.level) {
case RAW_FILEINFO_GENERIC:
return false;
case RAW_FILEINFO_GETATTR:
CHECK_EQUAL(getattr.out.attrib);
CHECK_EQUAL(getattr.out.size);
CHECK_TIMES_EQUAL(getattr.out.write_time);
break;
case RAW_FILEINFO_GETATTRE:
CHECK_TIMES_EQUAL(getattre.out.create_time);
CHECK_TIMES_EQUAL(getattre.out.access_time);
CHECK_TIMES_EQUAL(getattre.out.write_time);
CHECK_EQUAL(getattre.out.size);
CHECK_EQUAL(getattre.out.alloc_size);
CHECK_EQUAL(getattre.out.attrib);
break;
case RAW_FILEINFO_STANDARD:
CHECK_TIMES_EQUAL(standard.out.create_time);
CHECK_TIMES_EQUAL(standard.out.access_time);
CHECK_TIMES_EQUAL(standard.out.write_time);
CHECK_EQUAL(standard.out.size);
CHECK_EQUAL(standard.out.alloc_size);
CHECK_EQUAL(standard.out.attrib);
break;
case RAW_FILEINFO_EA_SIZE:
CHECK_TIMES_EQUAL(ea_size.out.create_time);
CHECK_TIMES_EQUAL(ea_size.out.access_time);
CHECK_TIMES_EQUAL(ea_size.out.write_time);
CHECK_EQUAL(ea_size.out.size);
CHECK_EQUAL(ea_size.out.alloc_size);
CHECK_EQUAL(ea_size.out.attrib);
CHECK_EQUAL(ea_size.out.ea_size);
break;
case RAW_FILEINFO_ALL_EAS:
CHECK_EQUAL(all_eas.out.num_eas);
for (i=0;igeneric.level = levels[i].level;
switch (info->generic.level) {
case RAW_SFILEINFO_SETATTR:
info->setattr.in.attrib = gen_attrib();
info->setattr.in.write_time = gen_timet();
break;
case RAW_SFILEINFO_SETATTRE:
info->setattre.in.create_time = gen_timet();
info->setattre.in.access_time = gen_timet();
info->setattre.in.write_time = gen_timet();
break;
case RAW_SFILEINFO_STANDARD:
info->standard.in.create_time = gen_timet();
info->standard.in.access_time = gen_timet();
info->standard.in.write_time = gen_timet();
break;
case RAW_SFILEINFO_EA_SET: {
static struct ea_struct ea;
info->ea_set.in.num_eas = 1;
info->ea_set.in.eas = &ea;
info->ea_set.in.eas[0] = gen_ea_struct();
}
break;
case RAW_SFILEINFO_BASIC_INFO:
case RAW_SFILEINFO_BASIC_INFORMATION:
info->basic_info.in.create_time = gen_nttime();
info->basic_info.in.access_time = gen_nttime();
info->basic_info.in.write_time = gen_nttime();
info->basic_info.in.change_time = gen_nttime();
info->basic_info.in.attrib = gen_attrib();
break;
case RAW_SFILEINFO_DISPOSITION_INFO:
case RAW_SFILEINFO_DISPOSITION_INFORMATION:
info->disposition_info.in.delete_on_close = gen_bool();
break;
case RAW_SFILEINFO_ALLOCATION_INFO:
case RAW_SFILEINFO_ALLOCATION_INFORMATION:
info->allocation_info.in.alloc_size = gen_alloc_size();
break;
case RAW_SFILEINFO_END_OF_FILE_INFO:
case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
info->end_of_file_info.in.size = gen_offset();
break;
case RAW_SFILEINFO_RENAME_INFORMATION:
case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
info->rename_information.in.overwrite = gen_bool();
info->rename_information.in.root_fid = gen_root_fid(instance);
info->rename_information.in.new_name = gen_fname_open(instance);
break;
case RAW_SFILEINFO_POSITION_INFORMATION:
info->position_information.in.position = gen_offset();
break;
case RAW_SFILEINFO_MODE_INFORMATION:
info->mode_information.in.mode = gen_bits_mask(0xFFFFFFFF);
break;
case RAW_SFILEINFO_GENERIC:
case RAW_SFILEINFO_SEC_DESC:
case RAW_SFILEINFO_UNIX_BASIC:
case RAW_SFILEINFO_UNIX_LINK:
case RAW_SFILEINFO_UNIX_HLINK:
case RAW_SFILEINFO_1023:
case RAW_SFILEINFO_1025:
case RAW_SFILEINFO_1029:
case RAW_SFILEINFO_1032:
case RAW_SFILEINFO_1039:
case RAW_SFILEINFO_1040:
case RAW_SFILEINFO_UNIX_INFO2:
/* Untested */
break;
}
}
/*
generate setpathinfo operations
*/
static bool handler_spathinfo(int instance)
{
union smb_setfileinfo parm[NSERVERS];
NTSTATUS status[NSERVERS];
parm[0].generic.in.file.path = gen_fname_open(instance);
gen_setfileinfo(instance, &parm[0]);
GEN_COPY_PARM;
/* a special case for the fid in a RENAME */
if (parm[0].generic.level == RAW_SFILEINFO_RENAME_INFORMATION &&
parm[0].rename_information.in.root_fid != 0) {
GEN_SET_FNUM(rename_information.in.root_fid);
}
GEN_CALL(smb_raw_setpathinfo(tree, &parm[i]));
return true;
}
/*
generate setfileinfo operations
*/
static bool handler_sfileinfo(int instance)
{
union smb_setfileinfo parm[NSERVERS];
NTSTATUS status[NSERVERS];
parm[0].generic.in.file.fnum = gen_fnum(instance);
gen_setfileinfo(instance, &parm[0]);
GEN_COPY_PARM;
GEN_SET_FNUM(generic.in.file.fnum);
GEN_CALL(smb_raw_setfileinfo(tree, &parm[i]));
return true;
}
/*
generate change notify operations
*/
static bool handler_notify(int instance)
{
union smb_notify parm[NSERVERS];
int n;
ZERO_STRUCT(parm[0]);
parm[0].nttrans.level = RAW_NOTIFY_NTTRANS;
parm[0].nttrans.in.buffer_size = gen_io_count();
parm[0].nttrans.in.completion_filter = gen_bits_mask(0xFF);
parm[0].nttrans.in.file.fnum = gen_fnum(instance);
parm[0].nttrans.in.recursive = gen_bool();
GEN_COPY_PARM;
GEN_SET_FNUM(nttrans.in.file.fnum);
for (n=0;ntree, &parm[n]);
req->async.fn = async_notify;
}
return true;
}
/*
wipe any relevant files
*/
static void wipe_files(void)
{
int i;
for (i=0;itree, "\\gentest");
if (n == -1) {
printf("Failed to wipe tree on server %d\n", i);
exit(1);
}
if (NT_STATUS_IS_ERR(smbcli_mkdir(servers[i].cli[0]->tree, "\\gentest"))) {
printf("Failed to create \\gentest - %s\n",
smbcli_errstr(servers[i].cli[0]->tree));
exit(1);
}
if (n > 0) {
printf("Deleted %d files on server %d\n", n, i);
}
}
}
/*
dump the current seeds - useful for continuing a backtrack
*/
static void dump_seeds(void)
{
int i;
FILE *f;
if (!options.seeds_file) {
return;
}
f = fopen("seeds.tmp", "w");
if (!f) return;
for (i=0;i 0 && base+chunk < options.numops && options.numops > 1; ) {
int i, max;
chunk = MIN(chunk, options.numops / 2);
/* mark this range as disabled */
max = MIN(options.numops, base+chunk);
for (i=base;i 0);
printf("Reduced to %d ops\n", options.numops);
ret = run_test(ev, lp_ctx);
if (ret != options.numops - 1) {
printf("Inconsistent result? ret=%d numops=%d\n", ret, options.numops);
}
}
/*
start the main gentest process
*/
static bool start_gentest(struct event_context *ev,
struct loadparm_context *lp_ctx)
{
int op;
int ret;
/* allocate the open_handles array */
open_handles = calloc(options.max_open_handles, sizeof(open_handles[0]));
srandom(options.seed);
op_parms = calloc(options.numops, sizeof(op_parms[0]));
/* generate the seeds - after this everything is deterministic */
if (options.use_preset_seeds) {
int numops;
char **preset = file_lines_load(options.seeds_file, &numops, NULL);
if (!preset) {
printf("Failed to load %s - %s\n", options.seeds_file, strerror(errno));
exit(1);
}
if (numops < options.numops) {
options.numops = numops;
}
for (op=0;op ");
lp_ctx = cmdline_lp_ctx;
servers[0].credentials = cli_credentials_init(talloc_autofree_context());
servers[1].credentials = cli_credentials_init(talloc_autofree_context());
cli_credentials_guess(servers[0].credentials, lp_ctx);
cli_credentials_guess(servers[1].credentials, lp_ctx);
while((opt = poptGetNextOpt(pc)) != -1) {
switch (opt) {
case OPT_UNCLIST:
lp_set_cmdline(cmdline_lp_ctx, "torture:unclist", poptGetOptArg(pc));
break;
case 'U':
if (username_count == 2) {
usage(pc);
exit(1);
}
cli_credentials_parse_string(servers[username_count].credentials, poptGetOptArg(pc), CRED_SPECIFIED);
username_count++;
break;
}
}
if (ignore_file) {
options.ignore_patterns = file_lines_load(ignore_file, NULL, NULL);
}
argv_new = discard_const_p(char *, poptGetArgs(pc));
argc_new = argc;
for (i=0; i= 3)) {
usage(pc);
exit(1);
}
setlinebuf(stdout);
setup_logging("gentest", DEBUG_STDOUT);
if (argc < 3 || argv[1][0] == '-') {
usage(pc);
exit(1);
}
setup_logging(argv[0], DEBUG_STDOUT);
for (i=0;i