diff options
author | Andrew Bartlett <abartlet@samba.org> | 2008-05-21 12:13:45 +1000 |
---|---|---|
committer | Andrew Bartlett <abartlet@samba.org> | 2008-05-21 12:13:45 +1000 |
commit | fdec7fdaf5d4150e14311d9b3bb2da7358a407ff (patch) | |
tree | 5fbeb5de11214711f780e69c5e9ad1210c2e861e | |
parent | 2f773ff347ec2828f94f4980a3aedbde37009737 (diff) | |
parent | 6d4424cd45333c3029b0272528485dd2b3f8e620 (diff) | |
download | samba-fdec7fdaf5d4150e14311d9b3bb2da7358a407ff.tar.gz samba-fdec7fdaf5d4150e14311d9b3bb2da7358a407ff.tar.bz2 samba-fdec7fdaf5d4150e14311d9b3bb2da7358a407ff.zip |
Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into 4-0-local
(This used to be commit 7594f79db6a0e8a46e2863829e5e050b03dfaec1)
33 files changed, 3789 insertions, 93 deletions
diff --git a/source4/client/client.c b/source4/client/client.c index 120a80ccd2..01197e8a9e 100644 --- a/source4/client/client.c +++ b/source4/client/client.c @@ -214,15 +214,18 @@ check the space on a device ****************************************************************************/ static int do_dskattr(struct smbclient_context *ctx) { - int total, bsize, avail; + uint32_t bsize; + uint64_t total, avail; if (NT_STATUS_IS_ERR(smbcli_dskattr(ctx->cli->tree, &bsize, &total, &avail))) { d_printf("Error in dskattr: %s\n",smbcli_errstr(ctx->cli->tree)); return 1; } - d_printf("\n\t\t%d blocks of size %d. %d blocks available\n", - total, bsize, avail); + d_printf("\n\t\t%llu blocks of size %u. %llu blocks available\n", + (unsigned long long)total, + (unsigned)bsize, + (unsigned long long)avail); return 0; } diff --git a/source4/lib/tdb/common/traverse.c b/source4/lib/tdb/common/traverse.c index 07b0c23858..69c81e6e98 100644 --- a/source4/lib/tdb/common/traverse.c +++ b/source4/lib/tdb/common/traverse.c @@ -204,18 +204,23 @@ int tdb_traverse_read(struct tdb_context *tdb, { struct tdb_traverse_lock tl = { NULL, 0, 0, F_RDLCK }; int ret; + bool in_transaction = (tdb->transaction != NULL); /* we need to get a read lock on the transaction lock here to cope with the lock ordering semantics of solaris10 */ - if (tdb_transaction_lock(tdb, F_RDLCK)) { - return -1; + if (!in_transaction) { + if (tdb_transaction_lock(tdb, F_RDLCK)) { + return -1; + } } tdb->traverse_read++; ret = tdb_traverse_internal(tdb, fn, private_data, &tl); tdb->traverse_read--; - tdb_transaction_unlock(tdb); + if (!in_transaction) { + tdb_transaction_unlock(tdb); + } return ret; } @@ -232,20 +237,25 @@ int tdb_traverse(struct tdb_context *tdb, { struct tdb_traverse_lock tl = { NULL, 0, 0, F_WRLCK }; int ret; + bool in_transaction = (tdb->transaction != NULL); if (tdb->read_only || tdb->traverse_read) { return tdb_traverse_read(tdb, fn, private_data); } - if (tdb_transaction_lock(tdb, F_WRLCK)) { - return -1; + if (!in_transaction) { + if (tdb_transaction_lock(tdb, F_WRLCK)) { + return -1; + } } tdb->traverse_write++; ret = tdb_traverse_internal(tdb, fn, private_data, &tl); tdb->traverse_write--; - tdb_transaction_unlock(tdb); + if (!in_transaction) { + tdb_transaction_unlock(tdb); + } return ret; } diff --git a/source4/lib/util/time.c b/source4/lib/util/time.c index a181885806..978d73cc0a 100644 --- a/source4/lib/util/time.c +++ b/source4/lib/util/time.c @@ -376,7 +376,7 @@ _PUBLIC_ NTTIME pull_nttime(uint8_t *base, uint16_t offset) /** return (tv1 - tv2) in microseconds */ -_PUBLIC_ int64_t usec_time_diff(struct timeval *tv1, struct timeval *tv2) +_PUBLIC_ int64_t usec_time_diff(const struct timeval *tv1, const struct timeval *tv2) { int64_t sec_diff = tv1->tv_sec - tv2->tv_sec; return (sec_diff * 1000000) + (int64_t)(tv1->tv_usec - tv2->tv_usec); diff --git a/source4/lib/util/time.h b/source4/lib/util/time.h index 1ab976ca78..e4008c5782 100644 --- a/source4/lib/util/time.h +++ b/source4/lib/util/time.h @@ -127,7 +127,7 @@ _PUBLIC_ NTTIME nttime_from_string(const char *s); /** return (tv1 - tv2) in microseconds */ -_PUBLIC_ int64_t usec_time_diff(struct timeval *tv1, struct timeval *tv2); +_PUBLIC_ int64_t usec_time_diff(const struct timeval *tv1, const struct timeval *tv2); /** return a zero timeval diff --git a/source4/libcli/clifile.c b/source4/libcli/clifile.c index e59b7f9af3..2cf174060b 100644 --- a/source4/libcli/clifile.c +++ b/source4/libcli/clifile.c @@ -650,7 +650,8 @@ NTSTATUS smbcli_chkpath(struct smbcli_tree *tree, const char *path) /**************************************************************************** Query disk space. ****************************************************************************/ -NTSTATUS smbcli_dskattr(struct smbcli_tree *tree, int *bsize, int *total, int *avail) +NTSTATUS smbcli_dskattr(struct smbcli_tree *tree, uint32_t *bsize, + uint64_t *total, uint64_t *avail) { union smb_fsinfo fsinfo_parms; TALLOC_CTX *mem_ctx; @@ -658,12 +659,12 @@ NTSTATUS smbcli_dskattr(struct smbcli_tree *tree, int *bsize, int *total, int *a mem_ctx = talloc_init("smbcli_dskattr"); - fsinfo_parms.dskattr.level = RAW_QFS_DSKATTR; + fsinfo_parms.dskattr.level = RAW_QFS_SIZE_INFO; status = smb_raw_fsinfo(tree, mem_ctx, &fsinfo_parms); if (NT_STATUS_IS_OK(status)) { - *bsize = fsinfo_parms.dskattr.out.block_size; - *total = fsinfo_parms.dskattr.out.units_total; - *avail = fsinfo_parms.dskattr.out.units_free; + *bsize = fsinfo_parms.size_info.out.bytes_per_sector * fsinfo_parms.size_info.out.sectors_per_unit; + *total = fsinfo_parms.size_info.out.total_alloc_units; + *avail = fsinfo_parms.size_info.out.avail_alloc_units; } talloc_free(mem_ctx); diff --git a/source4/libcli/composite/composite.c b/source4/libcli/composite/composite.c index 966f56cba8..3e3f224f47 100644 --- a/source4/libcli/composite/composite.c +++ b/source4/libcli/composite/composite.c @@ -69,6 +69,17 @@ _PUBLIC_ NTSTATUS composite_wait(struct composite_context *c) return c->status; } +/* + block until a composite function has completed, then return the status. + Free the composite context before returning +*/ +_PUBLIC_ NTSTATUS composite_wait_free(struct composite_context *c) +{ + NTSTATUS status = composite_wait(c); + talloc_free(c); + return status; +} + /* callback from composite_done() and composite_error() @@ -94,6 +105,12 @@ static void composite_trigger(struct event_context *ev, struct timed_event *te, _PUBLIC_ void composite_error(struct composite_context *ctx, NTSTATUS status) { + /* you are allowed to pass NT_STATUS_OK to composite_error(), in which + case it is equivalent to composite_done() */ + if (NT_STATUS_IS_OK(status)) { + composite_done(ctx); + return; + } if (!ctx->used_wait && !ctx->async.fn) { event_add_timed(ctx->event_ctx, ctx, timeval_zero(), composite_trigger, ctx); } @@ -187,7 +204,7 @@ _PUBLIC_ void composite_continue_smb2(struct composite_context *ctx, { if (composite_nomem(new_req, ctx)) return; new_req->async.fn = continuation; - new_req->async.private = private_data; + new_req->async.private_data = private_data; } _PUBLIC_ void composite_continue_nbt(struct composite_context *ctx, diff --git a/source4/libcli/composite/composite.h b/source4/libcli/composite/composite.h index f1bed20361..28cd6a88dc 100644 --- a/source4/libcli/composite/composite.h +++ b/source4/libcli/composite/composite.h @@ -101,6 +101,7 @@ bool composite_is_ok(struct composite_context *ctx); void composite_done(struct composite_context *ctx); void composite_error(struct composite_context *ctx, NTSTATUS status); NTSTATUS composite_wait(struct composite_context *c); +NTSTATUS composite_wait_free(struct composite_context *c); #endif /* __COMPOSITE_H__ */ diff --git a/source4/libcli/config.mk b/source4/libcli/config.mk index 0cc97c058a..f502091b07 100644 --- a/source4/libcli/config.mk +++ b/source4/libcli/config.mk @@ -33,7 +33,8 @@ LIBCLI_SMB_COMPOSITE_OBJ_FILES = $(addprefix $(libclisrcdir)/smb_composite/, \ sesssetup.o \ fetchfile.o \ appendacl.o \ - fsinfo.o) + fsinfo.o \ + smb2.o) $(eval $(call proto_header_template,$(libclisrcdir)/smb_composite/proto.h,$(LIBCLI_SMB_COMPOSITE_OBJ_FILES:.o=.c))) diff --git a/source4/libcli/raw/interfaces.h b/source4/libcli/raw/interfaces.h index bad3743721..871bab01db 100644 --- a/source4/libcli/raw/interfaces.h +++ b/source4/libcli/raw/interfaces.h @@ -2354,10 +2354,11 @@ union smb_search_first { #define SMB2_FIND_ID_BOTH_DIRECTORY_INFO 0x25 #define SMB2_FIND_ID_FULL_DIRECTORY_INFO 0x26 -/* flags for RAW_FILEINFO_SMB2_ALL_EAS */ +/* flags for SMB2 find */ #define SMB2_CONTINUE_FLAG_RESTART 0x01 #define SMB2_CONTINUE_FLAG_SINGLE 0x02 -#define SMB2_CONTINUE_FLAG_NEW 0x10 +#define SMB2_CONTINUE_FLAG_INDEX 0x04 +#define SMB2_CONTINUE_FLAG_REOPEN 0x10 /* SMB2 Find */ struct smb2_find { @@ -2370,7 +2371,7 @@ union smb_search_first { /* uint16_t buffer_code; 0x21 = 0x20 + 1 */ uint8_t level; uint8_t continue_flags; /* SMB2_CONTINUE_FLAG_* */ - uint32_t unknown; /* perhaps a continue token? */ + uint32_t file_index; /* struct smb2_handle handle; */ /* uint16_t pattern_ofs; */ /* uint16_t pattern_size; */ diff --git a/source4/libcli/smb2/config.mk b/source4/libcli/smb2/config.mk index e653fbac1c..00b6305def 100644 --- a/source4/libcli/smb2/config.mk +++ b/source4/libcli/smb2/config.mk @@ -5,6 +5,6 @@ LIBCLI_SMB2_OBJ_FILES = $(addprefix $(libclisrcdir)/smb2/, \ transport.o request.o negprot.o session.o tcon.o \ create.o close.o connect.o getinfo.o write.o read.o \ setinfo.o find.o ioctl.o logoff.o tdis.o flush.o \ - lock.o notify.o cancel.o keepalive.o break.o) + lock.o notify.o cancel.o keepalive.o break.o util.o) $(eval $(call proto_header_template,$(libclisrcdir)/smb2/smb2_proto.h,$(LIBCLI_SMB2_OBJ_FILES:.o=.c))) diff --git a/source4/libcli/smb2/connect.c b/source4/libcli/smb2/connect.c index 59d4e6ea2d..eabfa410ad 100644 --- a/source4/libcli/smb2/connect.c +++ b/source4/libcli/smb2/connect.c @@ -44,7 +44,7 @@ struct smb2_connect_state { */ static void continue_tcon(struct smb2_request *req) { - struct composite_context *c = talloc_get_type(req->async.private, + struct composite_context *c = talloc_get_type(req->async.private_data, struct composite_context); struct smb2_connect_state *state = talloc_get_type(c->private_data, struct smb2_connect_state); @@ -83,7 +83,7 @@ static void continue_session(struct composite_context *creq) if (composite_nomem(req, c)) return; req->async.fn = continue_tcon; - req->async.private = c; + req->async.private_data = c; } /* @@ -91,7 +91,7 @@ static void continue_session(struct composite_context *creq) */ static void continue_negprot(struct smb2_request *req) { - struct composite_context *c = talloc_get_type(req->async.private, + struct composite_context *c = talloc_get_type(req->async.private_data, struct composite_context); struct smb2_connect_state *state = talloc_get_type(c->private_data, struct smb2_connect_state); @@ -101,6 +101,9 @@ static void continue_negprot(struct smb2_request *req) c->status = smb2_negprot_recv(req, c, &state->negprot); if (!composite_is_ok(c)) return; + transport->negotiate.system_time = state->negprot.out.system_time; + transport->negotiate.server_start_time = state->negprot.out.server_start_time; + state->session = smb2_session_init(transport, global_loadparm, state, true); if (composite_nomem(state->session, c)) return; @@ -142,7 +145,7 @@ static void continue_socket(struct composite_context *creq) if (composite_nomem(req, c)) return; req->async.fn = continue_negprot; - req->async.private = c; + req->async.private_data = c; } diff --git a/source4/libcli/smb2/find.c b/source4/libcli/smb2/find.c index 6b4902a026..8ebfd81bcd 100644 --- a/source4/libcli/smb2/find.c +++ b/source4/libcli/smb2/find.c @@ -38,7 +38,7 @@ struct smb2_request *smb2_find_send(struct smb2_tree *tree, struct smb2_find *io SCVAL(req->out.body, 0x02, io->in.level); SCVAL(req->out.body, 0x03, io->in.continue_flags); - SIVAL(req->out.body, 0x04, io->in.unknown); + SIVAL(req->out.body, 0x04, io->in.file_index); smb2_push_handle(req->out.body+0x08, &io->in.file.handle); status = smb2_push_o16s16_string(&req->out, 0x18, io->in.pattern); diff --git a/source4/libcli/smb2/request.c b/source4/libcli/smb2/request.c index f52b0ceef2..64d427f889 100644 --- a/source4/libcli/smb2/request.c +++ b/source4/libcli/smb2/request.c @@ -43,6 +43,18 @@ void smb2_setup_bufinfo(struct smb2_request *req) } } + +/* destroy a request structure */ +static int smb2_request_destructor(struct smb2_request *req) +{ + if (req->transport) { + /* remove it from the list of pending requests (a null op if + its not in the list) */ + DLIST_REMOVE(req->transport->pending_recv, req); + } + return 0; +} + /* initialise a smb2 request */ @@ -122,6 +134,8 @@ struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_ SCVAL(req->out.dynamic, 0, 0); } + talloc_set_destructor(req, smb2_request_destructor); + return req; } @@ -154,18 +168,13 @@ NTSTATUS smb2_request_destroy(struct smb2_request *req) _send() call fails completely */ if (!req) return NT_STATUS_UNSUCCESSFUL; - if (req->transport) { - /* remove it from the list of pending requests (a null op if - its not in the list) */ - DLIST_REMOVE(req->transport->pending_recv, req); - } - if (req->state == SMB2_REQUEST_ERROR && NT_STATUS_IS_OK(req->status)) { - req->status = NT_STATUS_INTERNAL_ERROR; + status = NT_STATUS_INTERNAL_ERROR; + } else { + status = req->status; } - status = req->status; talloc_free(req); return status; } diff --git a/source4/libcli/smb2/session.c b/source4/libcli/smb2/session.c index 18fe3486a4..29af6652f2 100644 --- a/source4/libcli/smb2/session.c +++ b/source4/libcli/smb2/session.c @@ -145,7 +145,7 @@ struct smb2_session_state { */ static void session_request_handler(struct smb2_request *req) { - struct composite_context *c = talloc_get_type(req->async.private, + struct composite_context *c = talloc_get_type(req->async.private_data, struct composite_context); struct smb2_session_state *state = talloc_get_type(c->private_data, struct smb2_session_state); @@ -178,7 +178,7 @@ static void session_request_handler(struct smb2_request *req) } state->req->async.fn = session_request_handler; - state->req->async.private = c; + state->req->async.private_data = c; return; } diff --git a/source4/libcli/smb2/smb2.h b/source4/libcli/smb2/smb2.h index ae66a6e0d3..b55da05e21 100644 --- a/source4/libcli/smb2/smb2.h +++ b/source4/libcli/smb2/smb2.h @@ -19,6 +19,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#ifndef __LIBCLI_SMB2_SMB2_H__ +#define __LIBCLI_SMB2_SMB2_H__ + #include "libcli/raw/request.h" struct smb2_handle; @@ -32,6 +35,8 @@ struct smb2_options { */ struct smb2_negotiate { DATA_BLOB secblob; + NTTIME system_time; + NTTIME server_start_time; }; /* this is the context for the smb2 transport layer */ @@ -165,7 +170,7 @@ struct smb2_request { */ struct { void (*fn)(struct smb2_request *); - void *private; + void *private_data; } async; }; @@ -282,3 +287,5 @@ struct smb2_request { return NT_STATUS_INVALID_PARAMETER; \ } \ } while (0) + +#endif diff --git a/source4/libcli/smb2/util.c b/source4/libcli/smb2/util.c new file mode 100644 index 0000000000..9eb344e83f --- /dev/null +++ b/source4/libcli/smb2/util.c @@ -0,0 +1,200 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client utility functions + + Copyright (C) Andrew Tridgell 2005 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/smb_composite/smb_composite.h" + +/* + simple close wrapper with SMB2 +*/ +NTSTATUS smb2_util_close(struct smb2_tree *tree, struct smb2_handle h) +{ + struct smb2_close c; + + ZERO_STRUCT(c); + c.in.file.handle = h; + + return smb2_close(tree, &c); +} + +/* + unlink a file with SMB2 +*/ +NTSTATUS smb2_util_unlink(struct smb2_tree *tree, const char *fname) +{ + union smb_unlink io; + + ZERO_STRUCT(io); + io.unlink.in.pattern = fname; + + return smb2_composite_unlink(tree, &io); +} + + +/* + rmdir with SMB2 +*/ +NTSTATUS smb2_util_rmdir(struct smb2_tree *tree, const char *dname) +{ + struct smb_rmdir io; + + ZERO_STRUCT(io); + io.in.path = dname; + + return smb2_composite_rmdir(tree, &io); +} + + +/* + mkdir with SMB2 +*/ +NTSTATUS smb2_util_mkdir(struct smb2_tree *tree, const char *dname) +{ + union smb_mkdir io; + + ZERO_STRUCT(io); + io.mkdir.level = RAW_MKDIR_MKDIR; + io.mkdir.in.path = dname; + + return smb2_composite_mkdir(tree, &io); +} + + +/* + set file attribute with SMB2 +*/ +NTSTATUS smb2_util_setatr(struct smb2_tree *tree, const char *name, uint32_t attrib) +{ + union smb_setfileinfo io; + + ZERO_STRUCT(io); + io.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION; + io.basic_info.in.file.path = name; + io.basic_info.in.attrib = attrib; + + return smb2_composite_setpathinfo(tree, &io); +} + + + + +/* + recursively descend a tree deleting all files + returns the number of files deleted, or -1 on error +*/ +int smb2_deltree(struct smb2_tree *tree, const char *dname) +{ + NTSTATUS status; + uint32_t total_deleted = 0; + uint_t count, i; + union smb_search_data *list; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct smb2_find f; + struct smb2_create create_parm; + + /* it might be a file */ + status = smb2_util_unlink(tree, dname); + if (NT_STATUS_IS_OK(status)) { + talloc_free(tmp_ctx); + return 1; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) || + NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND) || + NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_FILE)) { + talloc_free(tmp_ctx); + return 0; + } + + ZERO_STRUCT(create_parm); + create_parm.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + create_parm.in.share_access = + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + create_parm.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + create_parm.in.create_disposition = NTCREATEX_DISP_OPEN; + create_parm.in.fname = dname; + + status = smb2_create(tree, tmp_ctx, &create_parm); + if (NT_STATUS_IS_ERR(status)) { + DEBUG(2,("Failed to open %s - %s\n", dname, nt_errstr(status))); + talloc_free(tmp_ctx); + return -1; + } + + + ZERO_STRUCT(f); + f.in.file.handle = create_parm.out.file.handle; + f.in.max_response_size = 0x10000; + f.in.level = SMB2_FIND_NAME_INFO; + f.in.pattern = "*"; + + status = smb2_find_level(tree, tmp_ctx, &f, &count, &list); + if (NT_STATUS_IS_ERR(status)) { + DEBUG(2,("Failed to list %s - %s\n", + dname, nt_errstr(status))); + smb2_util_close(tree, create_parm.out.file.handle); + talloc_free(tmp_ctx); + return -1; + } + + for (i=0;i<count;i++) { + char *name; + if (strcmp(".", list[i].name_info.name.s) == 0 || + strcmp("..", list[i].name_info.name.s) == 0) { + continue; + } + name = talloc_asprintf(tmp_ctx, "%s\\%s", dname, list[i].name_info.name.s); + status = smb2_util_unlink(tree, name); + if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) { + /* it could be read-only */ + status = smb2_util_setatr(tree, name, FILE_ATTRIBUTE_NORMAL); + status = smb2_util_unlink(tree, name); + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) { + int ret; + ret = smb2_deltree(tree, name); + if (ret > 0) total_deleted += ret; + } + talloc_free(name); + if (NT_STATUS_IS_OK(status)) { + total_deleted++; + } + } + + smb2_util_close(tree, create_parm.out.file.handle); + + status = smb2_util_rmdir(tree, dname); + if (NT_STATUS_IS_ERR(status)) { + DEBUG(2,("Failed to delete %s - %s\n", + dname, nt_errstr(status))); + talloc_free(tmp_ctx); + return -1; + } + + talloc_free(tmp_ctx); + + return total_deleted; +} diff --git a/source4/libcli/smb_composite/smb2.c b/source4/libcli/smb_composite/smb2.c new file mode 100644 index 0000000000..6e005e03c0 --- /dev/null +++ b/source4/libcli/smb_composite/smb2.c @@ -0,0 +1,371 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 2008 + + 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 <http://www.gnu.org/licenses/>. +*/ +/* + a composite API for making SMB-like calls using SMB2. This is useful + as SMB2 often requires more than one requests where a single SMB + request would do. In converting code that uses SMB to use SMB2, + these routines make life a lot easier +*/ + + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/composite/composite.h" +#include "libcli/smb_composite/smb_composite.h" +#include "param/param.h" +#include "libcli/smb2/smb2_calls.h" + +/* + continue after a SMB2 close + */ +static void continue_close(struct smb2_request *req) +{ + struct composite_context *ctx = talloc_get_type(req->async.private_data, + struct composite_context); + NTSTATUS status; + struct smb2_close close_parm; + + status = smb2_close_recv(req, &close_parm); + composite_error(ctx, status); +} + +/* + continue after the create in a composite unlink + */ +static void continue_unlink(struct smb2_request *req) +{ + struct composite_context *ctx = talloc_get_type(req->async.private_data, + struct composite_context); + struct smb2_tree *tree = req->tree; + struct smb2_create create_parm; + struct smb2_close close_parm; + NTSTATUS status; + + status = smb2_create_recv(req, ctx, &create_parm); + if (!NT_STATUS_IS_OK(status)) { + composite_error(ctx, status); + return; + } + + ZERO_STRUCT(close_parm); + close_parm.in.file.handle = create_parm.out.file.handle; + + req = smb2_close_send(tree, &close_parm); + composite_continue_smb2(ctx, req, continue_close, ctx); +} + +/* + composite SMB2 unlink call +*/ +struct composite_context *smb2_composite_unlink_send(struct smb2_tree *tree, + union smb_unlink *io) +{ + struct composite_context *ctx; + struct smb2_create create_parm; + struct smb2_request *req; + + ctx = composite_create(tree, tree->session->transport->socket->event.ctx); + if (ctx == NULL) return NULL; + + /* check for wildcards - we could support these with a + search, but for now they aren't necessary */ + if (strpbrk(io->unlink.in.pattern, "*?<>") != NULL) { + composite_error(ctx, NT_STATUS_NOT_SUPPORTED); + return ctx; + } + + ZERO_STRUCT(create_parm); + create_parm.in.desired_access = SEC_STD_DELETE; + create_parm.in.create_disposition = NTCREATEX_DISP_OPEN; + create_parm.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + create_parm.in.create_options = + NTCREATEX_OPTIONS_DELETE_ON_CLOSE | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + create_parm.in.fname = io->unlink.in.pattern; + if (create_parm.in.fname[0] == '\\') { + create_parm.in.fname++; + } + + req = smb2_create_send(tree, &create_parm); + + composite_continue_smb2(ctx, req, continue_unlink, ctx); + return ctx; +} + + +/* + composite unlink call - sync interface +*/ +NTSTATUS smb2_composite_unlink(struct smb2_tree *tree, union smb_unlink *io) +{ + struct composite_context *c = smb2_composite_unlink_send(tree, io); + return composite_wait_free(c); +} + + + + +/* + continue after the create in a composite mkdir + */ +static void continue_mkdir(struct smb2_request *req) +{ + struct composite_context *ctx = talloc_get_type(req->async.private_data, + struct composite_context); + struct smb2_tree *tree = req->tree; + struct smb2_create create_parm; + struct smb2_close close_parm; + NTSTATUS status; + + status = smb2_create_recv(req, ctx, &create_parm); + if (!NT_STATUS_IS_OK(status)) { + composite_error(ctx, status); + return; + } + + ZERO_STRUCT(close_parm); + close_parm.in.file.handle = create_parm.out.file.handle; + + req = smb2_close_send(tree, &close_parm); + composite_continue_smb2(ctx, req, continue_close, ctx); +} + +/* + composite SMB2 mkdir call +*/ +struct composite_context *smb2_composite_mkdir_send(struct smb2_tree *tree, + union smb_mkdir *io) +{ + struct composite_context *ctx; + struct smb2_create create_parm; + struct smb2_request *req; + + ctx = composite_create(tree, tree->session->transport->socket->event.ctx); + if (ctx == NULL) return NULL; + + ZERO_STRUCT(create_parm); + + create_parm.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + create_parm.in.share_access = + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + create_parm.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + create_parm.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + create_parm.in.create_disposition = NTCREATEX_DISP_CREATE; + create_parm.in.fname = io->mkdir.in.path; + if (create_parm.in.fname[0] == '\\') { + create_parm.in.fname++; + } + + req = smb2_create_send(tree, &create_parm); + + composite_continue_smb2(ctx, req, continue_mkdir, ctx); + + return ctx; +} + + +/* + composite mkdir call - sync interface +*/ +NTSTATUS smb2_composite_mkdir(struct smb2_tree *tree, union smb_mkdir *io) +{ + struct composite_context *c = smb2_composite_mkdir_send(tree, io); + return composite_wait_free(c); +} + + + +/* + continue after the create in a composite rmdir + */ +static void continue_rmdir(struct smb2_request *req) +{ + struct composite_context *ctx = talloc_get_type(req->async.private_data, + struct composite_context); + struct smb2_tree *tree = req->tree; + struct smb2_create create_parm; + struct smb2_close close_parm; + NTSTATUS status; + + status = smb2_create_recv(req, ctx, &create_parm); + if (!NT_STATUS_IS_OK(status)) { + composite_error(ctx, status); + return; + } + + ZERO_STRUCT(close_parm); + close_parm.in.file.handle = create_parm.out.file.handle; + + req = smb2_close_send(tree, &close_parm); + composite_continue_smb2(ctx, req, continue_close, ctx); +} + +/* + composite SMB2 rmdir call +*/ +struct composite_context *smb2_composite_rmdir_send(struct smb2_tree *tree, + struct smb_rmdir *io) +{ + struct composite_context *ctx; + struct smb2_create create_parm; + struct smb2_request *req; + + ctx = composite_create(tree, tree->session->transport->socket->event.ctx); + if (ctx == NULL) return NULL; + + ZERO_STRUCT(create_parm); + create_parm.in.desired_access = SEC_STD_DELETE; + create_parm.in.create_disposition = NTCREATEX_DISP_OPEN; + create_parm.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + create_parm.in.create_options = + NTCREATEX_OPTIONS_DIRECTORY | + NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + create_parm.in.fname = io->in.path; + if (create_parm.in.fname[0] == '\\') { + create_parm.in.fname++; + } + + req = smb2_create_send(tree, &create_parm); + + composite_continue_smb2(ctx, req, continue_rmdir, ctx); + return ctx; +} + + +/* + composite rmdir call - sync interface +*/ +NTSTATUS smb2_composite_rmdir(struct smb2_tree *tree, struct smb_rmdir *io) +{ + struct composite_context *c = smb2_composite_rmdir_send(tree, io); + return composite_wait_free(c); +} + + +/* + continue after the setfileinfo in a composite setpathinfo + */ +static void continue_setpathinfo_close(struct smb2_request *req) +{ + struct composite_context *ctx = talloc_get_type(req->async.private_data, + struct composite_context); + struct smb2_tree *tree = req->tree; + struct smb2_close close_parm; + NTSTATUS status; + union smb_setfileinfo *io2 = talloc_get_type(ctx->private_data, + union smb_setfileinfo); + + status = smb2_setinfo_recv(req); + if (!NT_STATUS_IS_OK(status)) { + composite_error(ctx, status); + return; + } + + ZERO_STRUCT(close_parm); + close_parm.in.file.handle = io2->generic.in.file.handle; + + req = smb2_close_send(tree, &close_parm); + composite_continue_smb2(ctx, req, continue_close, ctx); +} + + +/* + continue after the create in a composite setpathinfo + */ +static void continue_setpathinfo(struct smb2_request *req) +{ + struct composite_context *ctx = talloc_get_type(req->async.private_data, + struct composite_context); + struct smb2_tree *tree = req->tree; + struct smb2_create create_parm; + NTSTATUS status; + union smb_setfileinfo *io2 = talloc_get_type(ctx->private_data, + union smb_setfileinfo); + + status = smb2_create_recv(req, ctx, &create_parm); + if (!NT_STATUS_IS_OK(status)) { + composite_error(ctx, status); + return; + } + + io2->generic.in.file.handle = create_parm.out.file.handle; + + req = smb2_setinfo_file_send(tree, io2); + composite_continue_smb2(ctx, req, continue_setpathinfo_close, ctx); +} + + +/* + composite SMB2 setpathinfo call +*/ +struct composite_context *smb2_composite_setpathinfo_send(struct smb2_tree *tree, + union smb_setfileinfo *io) +{ + struct composite_context *ctx; + struct smb2_create create_parm; + struct smb2_request *req; + union smb_setfileinfo *io2; + + ctx = composite_create(tree, tree->session->transport->socket->event.ctx); + if (ctx == NULL) return NULL; + + ZERO_STRUCT(create_parm); + create_parm.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + create_parm.in.create_disposition = NTCREATEX_DISP_OPEN; + create_parm.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + create_parm.in.create_options = 0; + create_parm.in.fname = io->generic.in.file.path; + if (create_parm.in.fname[0] == '\\') { + create_parm.in.fname++; + } + + req = smb2_create_send(tree, &create_parm); + + io2 = talloc(ctx, union smb_setfileinfo); + if (composite_nomem(io2, ctx)) { + return ctx; + } + *io2 = *io; + + ctx->private_data = io2; + + composite_continue_smb2(ctx, req, continue_setpathinfo, ctx); + return ctx; +} + + +/* + composite setpathinfo call + */ +NTSTATUS smb2_composite_setpathinfo(struct smb2_tree *tree, union smb_setfileinfo *io) +{ + struct composite_context *c = smb2_composite_setpathinfo_send(tree, io); + return composite_wait_free(c); +} diff --git a/source4/libcli/smb_composite/smb_composite.h b/source4/libcli/smb_composite/smb_composite.h index afee11ce3b..7f4b9d73e4 100644 --- a/source4/libcli/smb_composite/smb_composite.h +++ b/source4/libcli/smb_composite/smb_composite.h @@ -29,6 +29,7 @@ #include "libcli/raw/signing.h" #include "libcli/raw/libcliraw.h" +#include "libcli/smb2/smb2.h" /* diff --git a/source4/librpc/rpc/dcerpc_smb2.c b/source4/librpc/rpc/dcerpc_smb2.c index 8adca4caba..4767165fba 100644 --- a/source4/librpc/rpc/dcerpc_smb2.c +++ b/source4/librpc/rpc/dcerpc_smb2.c @@ -83,7 +83,7 @@ static void smb2_read_callback(struct smb2_request *req) uint16_t frag_length; NTSTATUS status; - state = talloc_get_type(req->async.private, struct smb2_read_state); + state = talloc_get_type(req->async.private_data, struct smb2_read_state); smb = talloc_get_type(state->c->transport.private_data, struct smb2_private); status = smb2_read_recv(req, state, &io); @@ -136,7 +136,7 @@ static void smb2_read_callback(struct smb2_request *req) } req->async.fn = smb2_read_callback; - req->async.private = state; + req->async.private_data = state; } @@ -180,7 +180,7 @@ static NTSTATUS send_read_request_continue(struct dcerpc_connection *c, DATA_BLO } req->async.fn = smb2_read_callback; - req->async.private = state; + req->async.private_data = state; return NT_STATUS_OK; } @@ -212,7 +212,7 @@ struct smb2_trans_state { */ static void smb2_trans_callback(struct smb2_request *req) { - struct smb2_trans_state *state = talloc_get_type(req->async.private, + struct smb2_trans_state *state = talloc_get_type(req->async.private_data, struct smb2_trans_state); struct dcerpc_connection *c = state->c; NTSTATUS status; @@ -269,7 +269,7 @@ static NTSTATUS smb2_send_trans_request(struct dcerpc_connection *c, DATA_BLOB * } req->async.fn = smb2_trans_callback; - req->async.private = state; + req->async.private_data = state; talloc_steal(state, req); @@ -281,7 +281,7 @@ static NTSTATUS smb2_send_trans_request(struct dcerpc_connection *c, DATA_BLOB * */ static void smb2_write_callback(struct smb2_request *req) { - struct dcerpc_connection *c = (struct dcerpc_connection *)req->async.private; + struct dcerpc_connection *c = (struct dcerpc_connection *)req->async.private_data; if (!NT_STATUS_IS_OK(req->status)) { DEBUG(0,("dcerpc_smb2: write callback error\n")); @@ -319,7 +319,7 @@ static NTSTATUS smb2_send_request(struct dcerpc_connection *c, DATA_BLOB *blob, } req->async.fn = smb2_write_callback; - req->async.private = c; + req->async.private_data = c; return NT_STATUS_OK; } @@ -444,7 +444,7 @@ struct composite_context *dcerpc_pipe_open_smb2_send(struct dcerpc_pipe *p, static void pipe_open_recv(struct smb2_request *req) { struct pipe_open_smb2_state *state = - talloc_get_type(req->async.private, + talloc_get_type(req->async.private_data, struct pipe_open_smb2_state); struct composite_context *ctx = state->ctx; struct dcerpc_connection *c = state->c; diff --git a/source4/nbt_server/irpc.c b/source4/nbt_server/irpc.c index b14610b2df..8f1f74afcf 100644 --- a/source4/nbt_server/irpc.c +++ b/source4/nbt_server/irpc.c @@ -84,8 +84,6 @@ static void getdc_recv_netlogon_reply(struct dgram_mailslot_handler *dgmslot, goto done; } - status = NT_STATUS_NO_LOGON_SERVERS; - p = netlogon.samlogon.nt4.server; DEBUG(10, ("NTLOGON_SAM_LOGON_REPLY: server: %s, user: %s, " @@ -102,6 +100,8 @@ static void getdc_recv_netlogon_reply(struct dgram_mailslot_handler *dgmslot, goto done; } + status = NT_STATUS_OK; + done: irpc_send_reply(s->msg, status); } diff --git a/source4/ntvfs/config.mk b/source4/ntvfs/config.mk index 8e647516ef..bf34c4082a 100644 --- a/source4/ntvfs/config.mk +++ b/source4/ntvfs/config.mk @@ -16,6 +16,20 @@ PRIVATE_DEPENDENCIES = \ ntvfs_cifs_OBJ_FILES = $(ntvfssrcdir)/cifs/vfs_cifs.o + +################################################ +# Start MODULE ntvfs_smb2 +[MODULE::ntvfs_smb2] +INIT_FUNCTION = ntvfs_smb2_init +SUBSYSTEM = ntvfs +PRIVATE_DEPENDENCIES = \ + LIBCLI_SMB LIBCLI_RAW +# End MODULE ntvfs_smb2 +################################################ + +ntvfs_smb2_OBJ_FILES = ntvfs/smb2/vfs_smb2.o + + ################################################ # Start MODULE ntvfs_simple [MODULE::ntvfs_simple] diff --git a/source4/ntvfs/ntvfs_base.c b/source4/ntvfs/ntvfs_base.c index 3706cd172c..6de13e4a0a 100644 --- a/source4/ntvfs/ntvfs_base.c +++ b/source4/ntvfs/ntvfs_base.c @@ -205,6 +205,7 @@ NTSTATUS ntvfs_init(struct loadparm_context *lp_ctx) static bool initialized = false; extern NTSTATUS ntvfs_posix_init(void); extern NTSTATUS ntvfs_cifs_init(void); + extern NTSTATUS ntvfs_smb2_init(void); extern NTSTATUS ntvfs_nbench_init(void); extern NTSTATUS ntvfs_unixuid_init(void); extern NTSTATUS ntvfs_ipc_init(void); diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c index c9c1c56f14..67937324cc 100644 --- a/source4/ntvfs/posix/pvfs_open.c +++ b/source4/ntvfs/posix/pvfs_open.c @@ -1117,6 +1117,20 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, return status; } + /* if the client specified that it must not be a directory then + check that it isn't */ + if (name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) && + (io->generic.in.create_options & NTCREATEX_OPTIONS_NON_DIRECTORY_FILE)) { + return NT_STATUS_FILE_IS_A_DIRECTORY; + } + + /* if the client specified that it must be a directory then + check that it is */ + if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) && + (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) { + return NT_STATUS_NOT_A_DIRECTORY; + } + /* directory opens are handled separately */ if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) || (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) { diff --git a/source4/ntvfs/smb2/vfs_smb2.c b/source4/ntvfs/smb2/vfs_smb2.c new file mode 100644 index 0000000000..cc09daf83f --- /dev/null +++ b/source4/ntvfs/smb2/vfs_smb2.c @@ -0,0 +1,844 @@ +/* + Unix SMB/CIFS implementation. + + CIFS-to-SMB2 NTVFS filesystem backend + + Copyright (C) Andrew Tridgell 2008 + + largely based on vfs_cifs.c which was + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James J Myers 2003 <myersjj@samba.org> + + 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 <http://www.gnu.org/licenses/>. +*/ +/* + this implements a CIFS->CIFS NTVFS filesystem backend. + +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/composite/composite.h" +#include "libcli/smb_composite/smb_composite.h" +#include "auth/auth.h" +#include "auth/credentials/credentials.h" +#include "ntvfs/ntvfs.h" +#include "lib/util/dlinklist.h" +#include "param/param.h" +#include "libcli/resolve/resolve.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +struct cvfs_file { + struct cvfs_file *prev, *next; + uint16_t fnum; + struct ntvfs_handle *h; +}; + +/* this is stored in ntvfs_private */ +struct cvfs_private { + struct smb2_tree *tree; + struct smb2_transport *transport; + struct ntvfs_module_context *ntvfs; + struct async_info *pending; + struct cvfs_file *files; + + /* a handle on the root of the share */ + /* TODO: leaving this handle open could prevent other users + from opening the share with exclusive access. We probably + need to open it on demand */ + struct smb2_handle roothandle; +}; + + +/* a structure used to pass information to an async handler */ +struct async_info { + struct async_info *next, *prev; + struct cvfs_private *cvfs; + struct ntvfs_request *req; + void *c_req; + struct composite_context *c_comp; + struct cvfs_file *f; + void *parms; +}; + +#define SETUP_FILE_HERE(f) do { \ + f = ntvfs_handle_get_backend_data(io->generic.in.file.ntvfs, ntvfs); \ + if (!f) return NT_STATUS_INVALID_HANDLE; \ + io->generic.in.file.fnum = f->fnum; \ +} while (0) + +#define SETUP_FILE do { \ + struct cvfs_file *f; \ + SETUP_FILE_HERE(f); \ +} while (0) + +#define SMB2_SERVER "smb2:server" +#define SMB2_USER "smb2:user" +#define SMB2_PASSWORD "smb2:password" +#define SMB2_DOMAIN "smb2:domain" +#define SMB2_SHARE "smb2:share" +#define SMB2_USE_MACHINE_ACCT "smb2:use-machine-account" + +#define SMB2_USE_MACHINE_ACCT_DEFAULT false + +/* + a handler for oplock break events from the server - these need to be passed + along to the client + */ +static bool oplock_handler(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *p_private) +{ + struct cvfs_private *private = p_private; + NTSTATUS status; + struct ntvfs_handle *h = NULL; + struct cvfs_file *f; + + for (f=private->files; f; f=f->next) { + if (f->fnum != fnum) continue; + h = f->h; + break; + } + + if (!h) { + DEBUG(5,("vfs_smb2: ignoring oplock break level %d for fnum %d\n", level, fnum)); + return true; + } + + DEBUG(5,("vfs_smb2: sending oplock break level %d for fnum %d\n", level, fnum)); + status = ntvfs_send_oplock_break(private->ntvfs, h, level); + if (!NT_STATUS_IS_OK(status)) return false; + return true; +} + +/* + return a handle to the root of the share +*/ +static NTSTATUS smb2_get_roothandle(struct smb2_tree *tree, struct smb2_handle *handle) +{ + struct smb2_create io; + NTSTATUS status; + + ZERO_STRUCT(io); + io.in.oplock_level = 0; + io.in.desired_access = SEC_STD_SYNCHRONIZE | SEC_DIR_READ_ATTRIBUTE | SEC_DIR_LIST; + io.in.file_attributes = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.in.create_options = 0; + io.in.fname = NULL; + + status = smb2_create(tree, tree, &io); + NT_STATUS_NOT_OK_RETURN(status); + + *handle = io.out.file.handle; + + return NT_STATUS_OK; +} + +/* + connect to a share - used when a tree_connect operation comes in. +*/ +static NTSTATUS cvfs_connect(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, const char *sharename) +{ + NTSTATUS status; + struct cvfs_private *private; + const char *host, *user, *pass, *domain, *remote_share; + struct composite_context *creq; + struct share_config *scfg = ntvfs->ctx->config; + struct smb2_tree *tree; + + struct cli_credentials *credentials; + bool machine_account; + + /* Here we need to determine which server to connect to. + * For now we use parametric options, type cifs. + * Later we will use security=server and auth_server.c. + */ + host = share_string_option(scfg, SMB2_SERVER, NULL); + user = share_string_option(scfg, SMB2_USER, NULL); + pass = share_string_option(scfg, SMB2_PASSWORD, NULL); + domain = share_string_option(scfg, SMB2_DOMAIN, NULL); + remote_share = share_string_option(scfg, SMB2_SHARE, NULL); + if (!remote_share) { + remote_share = sharename; + } + + machine_account = share_bool_option(scfg, SMB2_USE_MACHINE_ACCT, SMB2_USE_MACHINE_ACCT_DEFAULT); + + private = talloc_zero(ntvfs, struct cvfs_private); + if (!private) { + return NT_STATUS_NO_MEMORY; + } + + ntvfs->private_data = private; + + if (!host) { + DEBUG(1,("CIFS backend: You must supply server\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + if (user && pass) { + DEBUG(5, ("CIFS backend: Using specified password\n")); + credentials = cli_credentials_init(private); + if (!credentials) { + return NT_STATUS_NO_MEMORY; + } + cli_credentials_set_conf(credentials, ntvfs->ctx->lp_ctx); + cli_credentials_set_username(credentials, user, CRED_SPECIFIED); + if (domain) { + cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED); + } + cli_credentials_set_password(credentials, pass, CRED_SPECIFIED); + } else if (machine_account) { + DEBUG(5, ("CIFS backend: Using machine account\n")); + credentials = cli_credentials_init(private); + cli_credentials_set_conf(credentials, ntvfs->ctx->lp_ctx); + if (domain) { + cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED); + } + status = cli_credentials_set_machine_account(credentials, ntvfs->ctx->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } else if (req->session_info->credentials) { + DEBUG(5, ("CIFS backend: Using delegated credentials\n")); + credentials = req->session_info->credentials; + } else { + DEBUG(1,("CIFS backend: NO delegated credentials found: You must supply server, user and password or the client must supply delegated credentials\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + creq = smb2_connect_send(private, host, remote_share, + lp_resolve_context(ntvfs->ctx->lp_ctx), + credentials, + ntvfs->ctx->event_ctx); + + status = smb2_connect_recv(creq, private, &tree); + NT_STATUS_NOT_OK_RETURN(status); + + status = smb2_get_roothandle(tree, &private->roothandle); + NT_STATUS_NOT_OK_RETURN(status); + + private->tree = tree; + private->transport = private->tree->session->transport; + private->ntvfs = ntvfs; + + ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "NTFS"); + NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type); + ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "A:"); + NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type); + + /* we need to receive oplock break requests from the server */ + /* TODO: enable oplocks + smbcli_oplock_handler(private->transport, oplock_handler, private); + */ + return NT_STATUS_OK; +} + +/* + disconnect from a share +*/ +static NTSTATUS cvfs_disconnect(struct ntvfs_module_context *ntvfs) +{ + struct cvfs_private *private = ntvfs->private_data; + struct async_info *a, *an; + + /* first cleanup pending requests */ + for (a=private->pending; a; a = an) { + an = a->next; + talloc_free(a->c_req); + talloc_free(a); + } + + talloc_free(private); + ntvfs->private_data = NULL; + + return NT_STATUS_OK; +} + +/* + destroy an async info structure +*/ +static int async_info_destructor(struct async_info *async) +{ + DLIST_REMOVE(async->cvfs->pending, async); + return 0; +} + +/* + a handler for simple async SMB2 replies + this handler can only be used for functions that don't return any + parameters (those that just return a status code) + */ +static void async_simple_smb2(struct smb2_request *c_req) +{ + struct async_info *async = c_req->async.private_data; + struct ntvfs_request *req = async->req; + + smb2_request_receive(c_req); + req->async_states->status = smb2_request_destroy(c_req); + talloc_free(async); + req->async_states->send_fn(req); +} + +/* + a handler for simple async composite replies + this handler can only be used for functions that don't return any + parameters (those that just return a status code) + */ +static void async_simple_composite(struct composite_context *c_req) +{ + struct async_info *async = c_req->async.private_data; + struct ntvfs_request *req = async->req; + + req->async_states->status = composite_wait_free(c_req); + talloc_free(async); + req->async_states->send_fn(req); +} + + +/* save some typing for the simple functions */ +#define ASYNC_RECV_TAIL_F(io, async_fn, file) do { \ + if (!c_req) return NT_STATUS_UNSUCCESSFUL; \ + { \ + struct async_info *async; \ + async = talloc(req, struct async_info); \ + if (!async) return NT_STATUS_NO_MEMORY; \ + async->parms = io; \ + async->req = req; \ + async->f = file; \ + async->cvfs = private; \ + async->c_req = c_req; \ + DLIST_ADD(private->pending, async); \ + c_req->async.private_data = async; \ + talloc_set_destructor(async, async_info_destructor); \ + } \ + c_req->async.fn = async_fn; \ + req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC; \ + return NT_STATUS_OK; \ +} while (0) + +#define ASYNC_RECV_TAIL(io, async_fn) ASYNC_RECV_TAIL_F(io, async_fn, NULL) + +#define SIMPLE_ASYNC_TAIL ASYNC_RECV_TAIL(NULL, async_simple_smb2) +#define SIMPLE_COMPOSITE_TAIL ASYNC_RECV_TAIL(NULL, async_simple_composite) + +#define CHECK_ASYNC(req) do { \ + if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { \ + DEBUG(0,("SMB2 proxy backend does not support sync operation at %s\n", \ + __location__)); \ + return NT_STATUS_NOT_IMPLEMENTED; \ + }} while (0) + +/* + delete a file - the dirtype specifies the file types to include in the search. + The name can contain CIFS wildcards, but rarely does (except with OS/2 clients) + + BUGS: + - doesn't handle wildcards + - doesn't obey attrib restrictions +*/ +static NTSTATUS cvfs_unlink(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_unlink *unl) +{ + struct cvfs_private *private = ntvfs->private_data; + struct composite_context *c_req; + + CHECK_ASYNC(req); + + c_req = smb2_composite_unlink_send(private->tree, unl); + + SIMPLE_COMPOSITE_TAIL; +} + +/* + ioctl interface +*/ +static NTSTATUS cvfs_ioctl(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_ioctl *io) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* + check if a directory exists +*/ +static NTSTATUS cvfs_chkpath(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_chkpath *cp) +{ + struct cvfs_private *private = ntvfs->private_data; + struct smb2_request *c_req; + struct smb2_find f; + + CHECK_ASYNC(req); + + /* SMB2 doesn't have a chkpath operation, and also doesn't + have a query path info call, so the best seems to be to do a + find call, using the roothandle we established at connect + time */ + ZERO_STRUCT(f); + f.in.file.handle = private->roothandle; + f.in.level = SMB2_FIND_DIRECTORY_INFO; + f.in.pattern = cp->chkpath.in.path; + /* SMB2 find doesn't accept \ or the empty string - this is the best + approximation */ + if (strcmp(f.in.pattern, "\\") == 0 || + strcmp(f.in.pattern, "") == 0) { + f.in.pattern = "?"; + } + f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE | SMB2_CONTINUE_FLAG_RESTART; + f.in.max_response_size = 0x1000; + + c_req = smb2_find_send(private->tree, &f); + + SIMPLE_ASYNC_TAIL; +} + +/* + return info on a pathname +*/ +static NTSTATUS cvfs_qpathinfo(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_fileinfo *info) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* + query info on a open file +*/ +static NTSTATUS cvfs_qfileinfo(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_fileinfo *io) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + + +/* + set info on a pathname +*/ +static NTSTATUS cvfs_setpathinfo(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_setfileinfo *st) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + + +/* + open a file +*/ +static NTSTATUS cvfs_open(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_open *io) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* + create a directory +*/ +static NTSTATUS cvfs_mkdir(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_mkdir *md) +{ + struct cvfs_private *private = ntvfs->private_data; + struct composite_context *c_req; + + CHECK_ASYNC(req); + + c_req = smb2_composite_mkdir_send(private->tree, md); + + SIMPLE_COMPOSITE_TAIL; +} + +/* + remove a directory +*/ +static NTSTATUS cvfs_rmdir(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, struct smb_rmdir *rd) +{ + struct cvfs_private *private = ntvfs->private_data; + struct composite_context *c_req; + + CHECK_ASYNC(req); + + c_req = smb2_composite_rmdir_send(private->tree, rd); + + SIMPLE_COMPOSITE_TAIL; +} + +/* + rename a set of files +*/ +static NTSTATUS cvfs_rename(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_rename *ren) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* + copy a set of files +*/ +static NTSTATUS cvfs_copy(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, struct smb_copy *cp) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + read from a file +*/ +static NTSTATUS cvfs_read(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_read *io) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* + write to a file +*/ +static NTSTATUS cvfs_write(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_write *io) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* + seek in a file +*/ +static NTSTATUS cvfs_seek(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, + union smb_seek *io) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* + flush a file +*/ +static NTSTATUS cvfs_flush(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, + union smb_flush *io) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* + close a file +*/ +static NTSTATUS cvfs_close(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_close *io) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* + exit - closing files open by the pid +*/ +static NTSTATUS cvfs_exit(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* + logoff - closing files open by the user +*/ +static NTSTATUS cvfs_logoff(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req) +{ + /* we can't do this right in the cifs backend .... */ + return NT_STATUS_OK; +} + +/* + setup for an async call - nothing to do yet +*/ +static NTSTATUS cvfs_async_setup(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, + void *private) +{ + return NT_STATUS_OK; +} + +/* + cancel an async call +*/ +static NTSTATUS cvfs_cancel(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* + lock a byte range +*/ +static NTSTATUS cvfs_lock(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_lock *io) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* + set info on a open file +*/ +static NTSTATUS cvfs_setfileinfo(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, + union smb_setfileinfo *io) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + + +/* + a handler for async fsinfo replies + */ +static void async_fsinfo(struct smb2_request *c_req) +{ + struct async_info *async = c_req->async.private_data; + struct ntvfs_request *req = async->req; + req->async_states->status = smb2_getinfo_fs_recv(c_req, req, async->parms); + talloc_free(async); + req->async_states->send_fn(req); +} + +/* + return filesystem space info +*/ +static NTSTATUS cvfs_fsinfo(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_fsinfo *fs) +{ + struct cvfs_private *private = ntvfs->private_data; + struct smb2_request *c_req; + enum smb_fsinfo_level level = fs->generic.level; + + CHECK_ASYNC(req); + + switch (level) { + /* some levels go straight through */ + case RAW_QFS_VOLUME_INFORMATION: + case RAW_QFS_SIZE_INFORMATION: + case RAW_QFS_DEVICE_INFORMATION: + case RAW_QFS_ATTRIBUTE_INFORMATION: + case RAW_QFS_QUOTA_INFORMATION: + case RAW_QFS_FULL_SIZE_INFORMATION: + case RAW_QFS_OBJECTID_INFORMATION: + break; + + /* some get mapped */ + case RAW_QFS_VOLUME_INFO: + level = RAW_QFS_VOLUME_INFORMATION; + break; + case RAW_QFS_SIZE_INFO: + level = RAW_QFS_SIZE_INFORMATION; + break; + case RAW_QFS_DEVICE_INFO: + level = RAW_QFS_DEVICE_INFORMATION; + break; + case RAW_QFS_ATTRIBUTE_INFO: + level = RAW_QFS_ATTRIBUTE_INFO; + break; + + default: + /* the rest get refused for now */ + DEBUG(0,("fsinfo level %u not possible on SMB2\n", + (unsigned)fs->generic.level)); + break; + } + + fs->generic.level = level; + fs->generic.handle = private->roothandle; + + c_req = smb2_getinfo_fs_send(private->tree, fs); + + ASYNC_RECV_TAIL(fs, async_fsinfo); +} + +/* + return print queue info +*/ +static NTSTATUS cvfs_lpq(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_lpq *lpq) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + list files in a directory matching a wildcard pattern +*/ +static NTSTATUS cvfs_search_first(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_search_first *io, + void *search_private, + bool (*callback)(void *, const union smb_search_data *)) +{ + struct cvfs_private *private = ntvfs->private_data; + struct smb2_find f; + enum smb_search_data_level smb2_level; + uint_t count, i; + union smb_search_data *data; + NTSTATUS status; + + if (io->generic.level != RAW_SEARCH_TRANS2) { + DEBUG(0,("We only support trans2 search in smb2 backend\n")); + return NT_STATUS_NOT_SUPPORTED; + } + + switch (io->generic.data_level) { + case RAW_SEARCH_DATA_DIRECTORY_INFO: + smb2_level = SMB2_FIND_DIRECTORY_INFO; + break; + case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO: + smb2_level = SMB2_FIND_FULL_DIRECTORY_INFO; + break; + case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO: + smb2_level = SMB2_FIND_BOTH_DIRECTORY_INFO; + break; + case RAW_SEARCH_DATA_NAME_INFO: + smb2_level = SMB2_FIND_NAME_INFO; + break; + case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO: + smb2_level = SMB2_FIND_ID_FULL_DIRECTORY_INFO; + break; + case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO: + smb2_level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO; + break; + default: + DEBUG(0,("Unsupported search level %u for smb2 backend\n", + (unsigned)io->generic.data_level)); + return NT_STATUS_INVALID_INFO_CLASS; + } + + /* we do the search on the roothandle. This only works because + search is synchronous, otherwise we'd have no way to + distinguish multiple searches happening at once + */ + ZERO_STRUCT(f); + f.in.file.handle = private->roothandle; + f.in.level = smb2_level; + f.in.pattern = io->t2ffirst.in.pattern; + while (f.in.pattern[0] == '\\') { + f.in.pattern++; + } + f.in.continue_flags = 0; + f.in.max_response_size = 0x10000; + + status = smb2_find_level(private->tree, req, &f, &count, &data); + NT_STATUS_NOT_OK_RETURN(status); + + for (i=0;i<count;i++) { + if (!callback(search_private, &data[i])) break; + } + + io->t2ffirst.out.handle = 0; + io->t2ffirst.out.count = i; + /* TODO: fix end_of_file */ + io->t2ffirst.out.end_of_search = 1; + + talloc_free(data); + + return NT_STATUS_OK; +} + +/* continue a search */ +static NTSTATUS cvfs_search_next(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_search_next *io, + void *search_private, + bool (*callback)(void *, const union smb_search_data *)) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* close a search */ +static NTSTATUS cvfs_search_close(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_search_close *io) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* SMBtrans - not used on file shares */ +static NTSTATUS cvfs_trans(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, + struct smb_trans2 *trans2) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* change notify request - always async */ +static NTSTATUS cvfs_notify(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, + union smb_notify *io) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* + initialise the CIFS->CIFS backend, registering ourselves with the ntvfs subsystem + */ +NTSTATUS ntvfs_smb2_init(void) +{ + NTSTATUS ret; + struct ntvfs_ops ops; + NTVFS_CURRENT_CRITICAL_SIZES(vers); + + ZERO_STRUCT(ops); + + /* fill in the name and type */ + ops.name = "smb2"; + ops.type = NTVFS_DISK; + + /* fill in all the operations */ + ops.connect = cvfs_connect; + ops.disconnect = cvfs_disconnect; + ops.unlink = cvfs_unlink; + ops.chkpath = cvfs_chkpath; + ops.qpathinfo = cvfs_qpathinfo; + ops.setpathinfo = cvfs_setpathinfo; + ops.open = cvfs_open; + ops.mkdir = cvfs_mkdir; + ops.rmdir = cvfs_rmdir; + ops.rename = cvfs_rename; + ops.copy = cvfs_copy; + ops.ioctl = cvfs_ioctl; + ops.read = cvfs_read; + ops.write = cvfs_write; + ops.seek = cvfs_seek; + ops.flush = cvfs_flush; + ops.close = cvfs_close; + ops.exit = cvfs_exit; + ops.lock = cvfs_lock; + ops.setfileinfo = cvfs_setfileinfo; + ops.qfileinfo = cvfs_qfileinfo; + ops.fsinfo = cvfs_fsinfo; + ops.lpq = cvfs_lpq; + ops.search_first = cvfs_search_first; + ops.search_next = cvfs_search_next; + ops.search_close = cvfs_search_close; + ops.trans = cvfs_trans; + ops.logoff = cvfs_logoff; + ops.async_setup = cvfs_async_setup; + ops.cancel = cvfs_cancel; + ops.notify = cvfs_notify; + + /* register ourselves with the NTVFS subsystem. We register + under the name 'smb2'. */ + ret = ntvfs_register(&ops, &vers); + + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,("Failed to register SMB2 backend\n")); + } + + return ret; +} diff --git a/source4/smb_server/smb/receive.c b/source4/smb_server/smb/receive.c index 6cf33cf7c3..0afa3a652d 100644 --- a/source4/smb_server/smb/receive.c +++ b/source4/smb_server/smb/receive.c @@ -508,7 +508,8 @@ static void switch_message(int type, struct smbsrv_request *req) } } - DEBUG(5,("switch message %s (task_id %d)\n",smb_fn_name(type), req->smb_conn->connection->server_id.id)); + DEBUG(5,("switch message %s (task_id %u)\n", + smb_fn_name(type), (unsigned)req->smb_conn->connection->server_id.id)); /* this must be called before we do any reply */ if (flags & SIGNING_NO_REPLY) { diff --git a/source4/smb_server/smb2/find.c b/source4/smb_server/smb2/find.c index 6018f1958f..32b280c5c2 100644 --- a/source4/smb_server/smb2/find.c +++ b/source4/smb_server/smb2/find.c @@ -112,7 +112,7 @@ static NTSTATUS smb2srv_find_backend(struct smb2srv_find_state *state) return NT_STATUS_FOOBAR; } - if (info->in.continue_flags & SMB2_CONTINUE_FLAG_NEW) { + if (info->in.continue_flags & SMB2_CONTINUE_FLAG_REOPEN) { state->ff = talloc(state, union smb_search_first); NT_STATUS_HAVE_NO_MEMORY(state->ff); @@ -156,7 +156,7 @@ void smb2srv_find_recv(struct smb2srv_request *req) info->data_level = RAW_SEARCH_DATA_GENERIC;/* will be overwritten later */ info->in.level = CVAL(req->in.body, 0x02); info->in.continue_flags = CVAL(req->in.body, 0x03); - info->in.unknown = IVAL(req->in.body, 0x04); + info->in.file_index = IVAL(req->in.body, 0x04); info->in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08); SMB2SRV_CHECK(smb2_pull_o16s16_string(&req->in, info, req->in.body+0x18, &info->in.pattern)); info->in.max_response_size = IVAL(req->in.body, 0x1C); diff --git a/source4/torture/config.mk b/source4/torture/config.mk index 5fde227031..2857b99582 100644 --- a/source4/torture/config.mk +++ b/source4/torture/config.mk @@ -70,6 +70,7 @@ TORTURE_RAW_OBJ_FILES = $(addprefix $(torturesrcdir)/raw/, \ pingpong.o \ lockbench.o \ lookuprate.o \ + tconrate.o \ openbench.o \ rename.o \ eas.o \ @@ -262,6 +263,23 @@ gentest_OBJ_FILES = $(torturesrcdir)/gentest.o MANPAGES += $(torturesrcdir)/man/gentest.1 ################################# +# Start BINARY gentest_smb2 +[BINARY::gentest_smb2] +INSTALLDIR = BINDIR +PRIVATE_DEPENDENCIES = \ + LIBSAMBA-HOSTCONFIG \ + LIBSAMBA-UTIL \ + LIBPOPT \ + POPT_SAMBA \ + POPT_CREDENTIALS \ + LIBCLI_SMB \ + LIBCLI_RAW +# End BINARY gentest_smb2 +################################# + +gentest_smb2_OBJ_FILES = $(torturesrcdir)/gentest_smb2.o + +################################# # Start BINARY masktest [BINARY::masktest] INSTALLDIR = BINDIR diff --git a/source4/torture/gentest_smb2.c b/source4/torture/gentest_smb2.c new file mode 100644 index 0000000000..9c4be90b3c --- /dev/null +++ b/source4/torture/gentest_smb2.c @@ -0,0 +1,1952 @@ +/* + Unix SMB/CIFS implementation. + + generic testing tool - version with SMB2 support + + Copyright (C) Andrew Tridgell 2003-2008 + + 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 <http://www.gnu.org/licenses/>. +*/ + +#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 "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.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.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; + struct smb2_handle server_handle[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 smb2_tree *tree[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; + struct smb2_handle server_handle; + 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; + +static struct smb2_handle bad_smb2_handle; + + +#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 smb2_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;h<options.max_open_handles;h++) { + if (!open_handles[h].active) continue; + for (i=0;i<NSERVERS;i++) { + NTSTATUS status = smb2_util_close(servers[i].tree[open_handles[h].instance], + open_handles[h].server_handle[i]); + if (NT_STATUS_IS_ERR(status)) { + 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].tree[0]) { + if (connect_servers_fast()) { + return true; + } + } + + /* close any existing connections */ + for (i=0;i<NSERVERS;i++) { + for (j=0;j<NINSTANCES;j++) { + if (servers[i].tree[j]) { + smb2_tdis(servers[i].tree[j]); + talloc_free(servers[i].tree[j]); + servers[i].tree[j] = NULL; + } + } + } + + for (i=0;i<NSERVERS;i++) { + for (j=0;j<NINSTANCES;j++) { + NTSTATUS status; + printf("Connecting to \\\\%s\\%s as %s - instance %d\n", + servers[i].server_name, servers[i].share_name, + servers[i].credentials->username, j); + + cli_credentials_set_workstation(servers[i].credentials, + "gentest", CRED_SPECIFIED); + + status = smb2_connect(NULL, servers[i].server_name, + servers[i].share_name, + lp_resolve_context(lp_ctx), + servers[i].credentials, + &servers[i].tree[j], + ev); + 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; + } + +// smb2_oplock_handler(servers[i].cli[j]->transport, oplock_handler, NULL); + smb2_transport_idle_handler(servers[i].tree[j]->session->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].tree[0]->session->transport->negotiate.system_time - + servers[1].tree[0]->session->transport->negotiate.system_time); + return ret + 300; +} + + +static bool smb2_handle_equal(const struct smb2_handle *h1, const struct smb2_handle *h2) +{ + return memcmp(h1, h2, sizeof(struct smb2_handle)) == 0; +} + +/* + turn a server handle into a local handle +*/ +static uint_t fnum_to_handle(int server, int instance, struct smb2_handle server_handle) +{ + uint_t i; + for (i=0;i<options.max_open_handles;i++) { + if (!open_handles[i].active || + instance != open_handles[i].instance) continue; + if (smb2_handle_equal(&open_handles[i].server_handle[server], &server_handle)) { + return i; + } + } + printf("Invalid server handle in fnum_to_handle on server %d instance %d\n", + server, instance); + return BAD_HANDLE; +} + +/* + add some newly opened handles +*/ +static void gen_add_handle(int instance, const char *name, struct smb2_handle handles[NSERVERS]) +{ + int i, h; + for (h=0;h<options.max_open_handles;h++) { + if (!open_handles[h].active) break; + } + if (h == options.max_open_handles) { + /* we have to force close a random handle */ + h = random() % options.max_open_handles; + for (i=0;i<NSERVERS;i++) { + NTSTATUS status; + status = smb2_util_close(servers[i].tree[open_handles[h].instance], + open_handles[h].server_handle[i]); + if (NT_STATUS_IS_ERR(status)) { + printf("INTERNAL ERROR: Close failed when recovering handle! - %s\n", + nt_errstr(status)); + } + } + printf("Recovered handle %d\n", h); + num_open_handles--; + } + for (i=0;i<NSERVERS;i++) { + open_handles[h].server_handle[i] = handles[i]; + open_handles[h].instance = instance; + open_handles[h].active = true; + open_handles[h].name = name; + } + num_open_handles++; + + printf("OPEN num_open_handles=%d h=%d (%s)\n", + num_open_handles, h, name); +} + +/* + remove a closed handle +*/ +static void gen_remove_handle(int instance, struct smb2_handle handles[NSERVERS]) +{ + int h; + for (h=0;h<options.max_open_handles;h++) { + if (instance == open_handles[h].instance && + smb2_handle_equal(&open_handles[h].server_handle[0], &handles[0])) { + open_handles[h].active = false; + num_open_handles--; + printf("CLOSE num_open_handles=%d h=%d (%s)\n", + num_open_handles, h, + open_handles[h].name); + return; + } + } + printf("Removing invalid handle!?\n"); + exit(1); +} + +/* + return true with 'chance' probability as a percentage +*/ +static bool gen_chance(uint_t chance) +{ + return ((random() % 100) <= chance); +} + +/* + map an internal handle number to a server handle +*/ +static struct smb2_handle gen_lookup_handle(int server, uint16_t handle) +{ + if (handle == BAD_HANDLE) return bad_smb2_handle; + return open_handles[handle].server_handle[server]; +} + +/* + return a file handle +*/ +static uint16_t gen_fnum(int instance) +{ + uint16_t h; + int count = 0; + + if (gen_chance(20)) return BAD_HANDLE; + + while (num_open_handles > 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(uint64_t min, uint64_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; +// if (gen_chance(5)) return gen_int_range(0, 0xFFFFFFFF); + return gen_int_range(0, 1024*1024); +} + +/* + generate a io count +*/ +static int gen_io_count(void) +{ + if (gen_chance(20)) return 0; +// if (gen_chance(5)) return gen_int_range(0, 0xFFFFFFFF); + 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]; +} + +static uint32_t gen_bits_levels(int nlevels, ...) +{ + va_list ap; + uint32_t pct; + uint32_t mask; + int i; + va_start(ap, nlevels); + for (i=0;i<nlevels;i++) { + pct = va_arg(ap, uint32_t); + mask = va_arg(ap, uint32_t); + if (pct == 100 || gen_chance(pct)) { + va_end(ap); + return mask & random(); + } + } + va_end(ap); + return 0; +} + +/* + 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; +} + +/* + 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 smb2_transport *transport, void *private) +{ + int i, j; + for (i=0;i<NSERVERS;i++) { + for (j=0;j<NINSTANCES;j++) { + if (servers[i].tree[j] && + transport != servers[i].tree[j]->session->transport) { + // smb2_transport_process(servers[i].tree[j]->session->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;j<NINSTANCES;j++) { + for (i=0;i<NSERVERS;i++) { + // smb2_transport_process(servers[i].tree[j]->session->transport); + } + } +} + +/* + check that the same oplock breaks have been received by all instances +*/ +static bool check_oplocks(const char *call) +{ +#if 0 + int i, j; + int tries = 0; + +again: + check_pending(); + + for (j=0;j<NINSTANCES;j++) { + for (i=1;i<NSERVERS;i++) { + if (oplocks[0][j].got_break != oplocks[i][j].got_break || + oplocks[0][j].handle != oplocks[i][j].handle || + oplocks[0][j].level != oplocks[i][j].level) { + if (tries++ < 10) goto again; + printf("oplock break inconsistent - %d/%d/%d vs %d/%d/%d\n", + oplocks[0][j].got_break, + oplocks[0][j].handle, + oplocks[0][j].level, + oplocks[i][j].got_break, + oplocks[i][j].handle, + oplocks[i][j].level); + return false; + } + } + } + + /* if we got a break and closed then remove the handle */ + for (j=0;j<NINSTANCES;j++) { + if (oplocks[0][j].got_break && + oplocks[0][j].do_close) { + uint16_t fnums[NSERVERS]; + for (i=0;i<NSERVERS;i++) { + fnums[i] = oplocks[i][j].fnum; + } + gen_remove_handle(j, fnums); + break; + } + } +#endif + return true; +} + + +/* + check that the same change notify info has been received by all instances +*/ +static bool check_notifies(const char *call) +{ +#if 0 + int i, j; + int tries = 0; + +again: + check_pending(); + + for (j=0;j<NINSTANCES;j++) { + for (i=1;i<NSERVERS;i++) { + int n; + union smb_notify not1, not2; + + if (notifies[0][j].notify_count != notifies[i][j].notify_count) { + if (tries++ < 10) goto again; + printf("Notify count inconsistent %d %d\n", + notifies[0][j].notify_count, + notifies[i][j].notify_count); + return false; + } + + if (notifies[0][j].notify_count == 0) continue; + + if (!NT_STATUS_EQUAL(notifies[0][j].status, + notifies[i][j].status)) { + printf("Notify status mismatch - %s - %s\n", + nt_errstr(notifies[0][j].status), + nt_errstr(notifies[i][j].status)); + return false; + } + + if (!NT_STATUS_IS_OK(notifies[0][j].status)) { + continue; + } + + not1 = notifies[0][j].notify; + not2 = notifies[i][j].notify; + + for (n=0;n<not1.nttrans.out.num_changes;n++) { + if (not1.nttrans.out.changes[n].action != + not2.nttrans.out.changes[n].action) { + printf("Notify action %d inconsistent %d %d\n", n, + not1.nttrans.out.changes[n].action, + not2.nttrans.out.changes[n].action); + return false; + } + if (strcmp(not1.nttrans.out.changes[n].name.s, + not2.nttrans.out.changes[n].name.s)) { + printf("Notify name %d inconsistent %s %s\n", n, + not1.nttrans.out.changes[n].name.s, + not2.nttrans.out.changes[n].name.s); + return false; + } + if (not1.nttrans.out.changes[n].name.private_length != + not2.nttrans.out.changes[n].name.private_length) { + printf("Notify name length %d inconsistent %d %d\n", n, + not1.nttrans.out.changes[n].name.private_length, + not2.nttrans.out.changes[n].name.private_length); + return false; + } + } + } + } + + ZERO_STRUCT(notifies); + +#endif + return true; +} + +#define GEN_COPY_PARM do { \ + int i; \ + for (i=1;i<NSERVERS;i++) { \ + parm[i] = parm[0]; \ + } \ +} while (0) + +#define GEN_CALL(call) do { \ + int i; \ + ZERO_STRUCT(oplocks); \ + ZERO_STRUCT(notifies); \ + for (i=0;i<NSERVERS;i++) { \ + struct smb2_tree *tree = servers[i].tree[instance]; \ + status[i] = call; \ + } \ + current_op.status = status[0]; \ + for (i=1;i<NSERVERS;i++) { \ + if (!compare_status(status[i], status[0])) { \ + printf("status different in %s - %s %s\n", #call, \ + nt_errstr(status[0]), nt_errstr(status[i])); \ + return false; \ + } \ + } \ + if (!check_oplocks(#call)) return false; \ + if (!check_notifies(#call)) return false; \ + if (!NT_STATUS_IS_OK(status[0])) { \ + return true; \ + } \ +} while(0) + +#define ADD_HANDLE(name, field) do { \ + struct smb2_handle handles[NSERVERS]; \ + int i; \ + for (i=0;i<NSERVERS;i++) { \ + handles[i] = parm[i].field; \ + } \ + gen_add_handle(instance, name, handles); \ +} while(0) + +#define REMOVE_HANDLE(field) do { \ + struct smb2_handle handles[NSERVERS]; \ + int i; \ + for (i=0;i<NSERVERS;i++) { \ + handles[i] = parm[i].field; \ + } \ + gen_remove_handle(instance, handles); \ +} while(0) + +#define GEN_SET_FNUM(field) do { \ + int i; \ + for (i=0;i<NSERVERS;i++) { \ + parm[i].field = gen_lookup_handle(i, parm[i].field.data[0]); \ + } \ +} while(0) + +#define CHECK_EQUAL(field) do { \ + if (parm[0].field != parm[1].field && !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_WSTR_EQUAL(field) do { \ + if ((!parm[0].field.s && parm[1].field.s) || (parm[0].field.s && !parm[1].field.s)) { \ + printf("%s is NULL!\n", #field); \ + return false; \ + } \ + if (parm[0].field.s && strcmp(parm[0].field.s, parm[1].field.s) != 0 && !ignore_pattern(#field)) { \ + printf("Mismatch in %s - %s %s\n", #field, \ + parm[0].field.s, parm[1].field.s); \ + return false; \ + } \ + CHECK_EQUAL(field.private_length); \ +} while(0) + +#define CHECK_BLOB_EQUAL(field) do { \ + if (memcmp(parm[0].field.data, parm[1].field.data, parm[0].field.length) != 0 && !ignore_pattern(#field)) { \ + printf("Mismatch in %s\n", #field); \ + return false; \ + } \ + CHECK_EQUAL(field.length); \ +} while(0) + +#define CHECK_TIMES_EQUAL(field) do { \ + if (labs(parm[0].field - parm[1].field) > 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 ntcreatex operations +*/ +static bool handler_ntcreatex(int instance) +{ + struct smb2_create parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + ZERO_STRUCT(parm[0]); + parm[0].in.security_flags = gen_bits_levels(3, 70, 0x0, 70, 0x3, 100, 0xFF); + parm[0].in.oplock_level = gen_bits_levels(3, 70, 0x0, 70, 0x9, 100, 0xFF); + parm[0].in.impersonation_level = gen_bits_levels(3, 70, 0x0, 70, 0x3, 100, 0xFFFFFFFF); + parm[0].in.create_flags = gen_bits_levels(2, 80, 0x0, 100, 0xFFFFFFFF); + if (gen_chance(2)) { + parm[0].in.create_flags |= gen_bits_mask(0xFFFFFFFF); + } + parm[0].in.reserved = gen_bits_levels(2, 80, 0x0, 100, 0xFFFFFFFF); + if (gen_chance(2)) { + parm[0].in.reserved |= gen_bits_mask(0xFFFFFFFF); + } + parm[0].in.desired_access = gen_access_mask(); + parm[0].in.file_attributes = gen_attrib(); + parm[0].in.share_access = gen_bits_mask2(0x7, 0xFFFFFFFF); + parm[0].in.create_disposition = gen_open_disp(); + parm[0].in.create_options = gen_create_options(); + parm[0].in.fname = gen_fname_open(instance); + + if (!options.use_oplocks) { + /* mask out oplocks */ + parm[0].in.oplock_level = 0; + } + + GEN_COPY_PARM; + GEN_CALL(smb2_create(tree, current_op.mem_ctx, &parm[i])); + + CHECK_EQUAL(out.oplock_level); + CHECK_EQUAL(out.reserved); + CHECK_EQUAL(out.create_action); + CHECK_NTTIMES_EQUAL(out.create_time); + CHECK_NTTIMES_EQUAL(out.access_time); + CHECK_NTTIMES_EQUAL(out.write_time); + CHECK_NTTIMES_EQUAL(out.change_time); + CHECK_EQUAL(out.alloc_size); + CHECK_EQUAL(out.size); + CHECK_EQUAL(out.file_attr); + CHECK_EQUAL(out.reserved2); + + /* ntcreatex creates a new file handle */ + ADD_HANDLE(parm[0].in.fname, out.file.handle); + + return true; +} + +/* + generate close operations +*/ +static bool handler_close(int instance) +{ + struct smb2_close parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + ZERO_STRUCT(parm[0]); + parm[0].in.file.handle.data[0] = gen_fnum_close(instance); + parm[0].in.flags = gen_bits_mask2(0x1, 0xFFFF); + + GEN_COPY_PARM; + GEN_SET_FNUM(in.file.handle); + GEN_CALL(smb2_close(tree, &parm[i])); + + CHECK_EQUAL(out.flags); + CHECK_EQUAL(out._pad); + CHECK_NTTIMES_EQUAL(out.create_time); + CHECK_NTTIMES_EQUAL(out.access_time); + CHECK_NTTIMES_EQUAL(out.write_time); + CHECK_NTTIMES_EQUAL(out.change_time); + CHECK_EQUAL(out.alloc_size); + CHECK_EQUAL(out.size); + CHECK_EQUAL(out.file_attr); + + REMOVE_HANDLE(in.file.handle); + + return true; +} + +/* + generate read operations +*/ +static bool handler_read(int instance) +{ + struct smb2_read parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].in.file.handle.data[0] = gen_fnum(instance); + parm[0].in.reserved = gen_bits_mask2(0x0, 0xFF); + parm[0].in.length = gen_io_count(); + parm[0].in.offset = gen_offset(); + parm[0].in.min_count = gen_io_count(); + parm[0].in.channel = gen_bits_mask2(0x0, 0xFFFFFFFF); + parm[0].in.remaining = gen_bits_mask2(0x0, 0xFFFFFFFF); + parm[0].in.channel_offset = gen_bits_mask2(0x0, 0xFFFF); + parm[0].in.channel_length = gen_bits_mask2(0x0, 0xFFFF); + + GEN_COPY_PARM; + GEN_SET_FNUM(in.file.handle); + GEN_CALL(smb2_read(tree, current_op.mem_ctx, &parm[i])); + + CHECK_EQUAL(out.remaining); + CHECK_EQUAL(out.reserved); + CHECK_EQUAL(out.data.length); + + return true; +} + +/* + generate write operations +*/ +static bool handler_write(int instance) +{ + struct smb2_write parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].in.file.handle.data[0] = gen_fnum(instance); + parm[0].in.offset = gen_offset(); + parm[0].in.unknown1 = gen_bits_mask2(0, 0xFFFFFFFF); + parm[0].in.unknown2 = gen_bits_mask2(0, 0xFFFFFFFF); + parm[0].in.data = data_blob_talloc(current_op.mem_ctx, NULL, + gen_io_count()); + + GEN_COPY_PARM; + GEN_SET_FNUM(in.file.handle); + GEN_CALL(smb2_write(tree, &parm[i])); + + CHECK_EQUAL(out._pad); + CHECK_EQUAL(out.nwritten); + CHECK_EQUAL(out.unknown1); + + return true; +} + +#if 0 +/* + generate lockingx operations +*/ +static bool handler_lock(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;n<nlocks;n++) { + parm[0].lockx.in.locks[n].pid = gen_pid(); + parm[0].lockx.in.locks[n].offset = gen_offset(); + parm[0].lockx.in.locks[n].count = gen_io_count(); + } + } + + GEN_COPY_PARM; + GEN_SET_FNUM(lockx.in.file.fnum); + GEN_CALL(smb_raw_lock(tree, &parm[i])); + + return true; +} + +/* + generate a fileinfo query structure +*/ +static void gen_fileinfo(int instance, union smb_fileinfo *info) +{ + int i; + #define LVL(v) {RAW_FILEINFO_ ## v, "RAW_FILEINFO_" #v} + struct { + enum smb_fileinfo_level level; + const char *name; + } levels[] = { + LVL(GETATTR), LVL(GETATTRE), LVL(STANDARD), + LVL(EA_SIZE), LVL(ALL_EAS), LVL(IS_NAME_VALID), + LVL(BASIC_INFO), LVL(STANDARD_INFO), LVL(EA_INFO), + LVL(NAME_INFO), LVL(ALL_INFO), LVL(ALT_NAME_INFO), + LVL(STREAM_INFO), LVL(COMPRESSION_INFO), LVL(BASIC_INFORMATION), + LVL(STANDARD_INFORMATION), LVL(INTERNAL_INFORMATION), LVL(EA_INFORMATION), + LVL(ACCESS_INFORMATION), LVL(NAME_INFORMATION), LVL(POSITION_INFORMATION), + LVL(MODE_INFORMATION), LVL(ALIGNMENT_INFORMATION), LVL(ALL_INFORMATION), + LVL(ALT_NAME_INFORMATION), LVL(STREAM_INFORMATION), LVL(COMPRESSION_INFORMATION), + LVL(NETWORK_OPEN_INFORMATION), LVL(ATTRIBUTE_TAG_INFORMATION) + }; + do { + i = gen_int_range(0, ARRAY_SIZE(levels)-1); + } while (ignore_pattern(levels[i].name)); + + info->generic.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;i<parm[0].all_eas.out.num_eas;i++) { + CHECK_EQUAL(all_eas.out.eas[i].flags); + CHECK_WSTR_EQUAL(all_eas.out.eas[i].name); + CHECK_BLOB_EQUAL(all_eas.out.eas[i].value); + } + break; + + case RAW_FILEINFO_IS_NAME_VALID: + break; + + case RAW_FILEINFO_BASIC_INFO: + case RAW_FILEINFO_BASIC_INFORMATION: + CHECK_NTTIMES_EQUAL(basic_info.out.create_time); + CHECK_NTTIMES_EQUAL(basic_info.out.access_time); + CHECK_NTTIMES_EQUAL(basic_info.out.write_time); + CHECK_NTTIMES_EQUAL(basic_info.out.change_time); + CHECK_EQUAL(basic_info.out.attrib); + break; + + case RAW_FILEINFO_STANDARD_INFO: + case RAW_FILEINFO_STANDARD_INFORMATION: + CHECK_EQUAL(standard_info.out.alloc_size); + CHECK_EQUAL(standard_info.out.size); + CHECK_EQUAL(standard_info.out.nlink); + CHECK_EQUAL(standard_info.out.delete_pending); + CHECK_EQUAL(standard_info.out.directory); + break; + + case RAW_FILEINFO_EA_INFO: + case RAW_FILEINFO_EA_INFORMATION: + CHECK_EQUAL(ea_info.out.ea_size); + break; + + case RAW_FILEINFO_NAME_INFO: + case RAW_FILEINFO_NAME_INFORMATION: + CHECK_WSTR_EQUAL(name_info.out.fname); + break; + + case RAW_FILEINFO_ALL_INFO: + case RAW_FILEINFO_ALL_INFORMATION: + CHECK_NTTIMES_EQUAL(all_info.out.create_time); + CHECK_NTTIMES_EQUAL(all_info.out.access_time); + CHECK_NTTIMES_EQUAL(all_info.out.write_time); + CHECK_NTTIMES_EQUAL(all_info.out.change_time); + CHECK_EQUAL(all_info.out.attrib); + CHECK_EQUAL(all_info.out.alloc_size); + CHECK_EQUAL(all_info.out.size); + CHECK_EQUAL(all_info.out.nlink); + CHECK_EQUAL(all_info.out.delete_pending); + CHECK_EQUAL(all_info.out.directory); + CHECK_EQUAL(all_info.out.ea_size); + CHECK_WSTR_EQUAL(all_info.out.fname); + break; + + case RAW_FILEINFO_ALT_NAME_INFO: + case RAW_FILEINFO_ALT_NAME_INFORMATION: + CHECK_WSTR_EQUAL(alt_name_info.out.fname); + break; + + case RAW_FILEINFO_STREAM_INFO: + case RAW_FILEINFO_STREAM_INFORMATION: + CHECK_EQUAL(stream_info.out.num_streams); + for (i=0;i<parm[0].stream_info.out.num_streams;i++) { + CHECK_EQUAL(stream_info.out.streams[i].size); + CHECK_EQUAL(stream_info.out.streams[i].alloc_size); + CHECK_WSTR_EQUAL(stream_info.out.streams[i].stream_name); + } + break; + + case RAW_FILEINFO_COMPRESSION_INFO: + case RAW_FILEINFO_COMPRESSION_INFORMATION: + CHECK_EQUAL(compression_info.out.compressed_size); + CHECK_EQUAL(compression_info.out.format); + CHECK_EQUAL(compression_info.out.unit_shift); + CHECK_EQUAL(compression_info.out.chunk_shift); + CHECK_EQUAL(compression_info.out.cluster_shift); + break; + + case RAW_FILEINFO_INTERNAL_INFORMATION: + CHECK_EQUAL(internal_information.out.file_id); + break; + + case RAW_FILEINFO_ACCESS_INFORMATION: + CHECK_EQUAL(access_information.out.access_flags); + break; + + case RAW_FILEINFO_POSITION_INFORMATION: + CHECK_EQUAL(position_information.out.position); + break; + + case RAW_FILEINFO_MODE_INFORMATION: + CHECK_EQUAL(mode_information.out.mode); + break; + + case RAW_FILEINFO_ALIGNMENT_INFORMATION: + CHECK_EQUAL(alignment_information.out.alignment_requirement); + break; + + case RAW_FILEINFO_NETWORK_OPEN_INFORMATION: + CHECK_NTTIMES_EQUAL(network_open_information.out.create_time); + CHECK_NTTIMES_EQUAL(network_open_information.out.access_time); + CHECK_NTTIMES_EQUAL(network_open_information.out.write_time); + CHECK_NTTIMES_EQUAL(network_open_information.out.change_time); + CHECK_EQUAL(network_open_information.out.alloc_size); + CHECK_EQUAL(network_open_information.out.size); + CHECK_EQUAL(network_open_information.out.attrib); + break; + + case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION: + CHECK_EQUAL(attribute_tag_information.out.attrib); + CHECK_EQUAL(attribute_tag_information.out.reparse_tag); + break; + + /* Unhandled levels */ + + case RAW_FILEINFO_SEC_DESC: + case RAW_FILEINFO_EA_LIST: + case RAW_FILEINFO_UNIX_BASIC: + case RAW_FILEINFO_UNIX_LINK: + case RAW_FILEINFO_SMB2_ALL_EAS: + case RAW_FILEINFO_SMB2_ALL_INFORMATION: + case RAW_FILEINFO_UNIX_INFO2: + break; + } + + return true; +} + +/* + generate qfileinfo operations +*/ +static bool handler_qfileinfo(int instance) +{ + union smb_fileinfo parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].generic.in.file.fnum = gen_fnum(instance); + + gen_fileinfo(instance, &parm[0]); + + GEN_COPY_PARM; + GEN_SET_FNUM(generic.in.file.fnum); + GEN_CALL(smb_raw_fileinfo(tree, current_op.mem_ctx, &parm[i])); + + return cmp_fileinfo(instance, parm, status); +} + + +/* + generate a fileinfo query structure +*/ +static void gen_setfileinfo(int instance, union smb_setfileinfo *info) +{ + int i; + #undef LVL + #define LVL(v) {RAW_SFILEINFO_ ## v, "RAW_SFILEINFO_" #v} + struct { + enum smb_setfileinfo_level level; + const char *name; + } levels[] = { +#if 0 + /* disabled until win2003 can handle them ... */ + LVL(EA_SET), LVL(BASIC_INFO), LVL(DISPOSITION_INFO), + LVL(STANDARD), LVL(ALLOCATION_INFO), LVL(END_OF_FILE_INFO), +#endif + LVL(SETATTR), LVL(SETATTRE), LVL(BASIC_INFORMATION), + LVL(RENAME_INFORMATION), LVL(DISPOSITION_INFORMATION), + LVL(POSITION_INFORMATION), LVL(MODE_INFORMATION), + LVL(ALLOCATION_INFORMATION), LVL(END_OF_FILE_INFORMATION), + LVL(1023), LVL(1025), LVL(1029), LVL(1032), LVL(1039), LVL(1040) + }; + do { + i = gen_int_range(0, ARRAY_SIZE(levels)-1); + } while (ignore_pattern(levels[i].name)); + + info->generic.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 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; +} + +#endif + + +/* + wipe any relevant files +*/ +static void wipe_files(void) +{ + int i; + NTSTATUS status; + + for (i=0;i<NSERVERS;i++) { + int n = smb2_deltree(servers[i].tree[0], "gentest"); + if (n == -1) { + printf("Failed to wipe tree on server %d\n", i); + exit(1); + } + status = smb2_util_mkdir(servers[i].tree[0], "gentest"); + if (NT_STATUS_IS_ERR(status)) { + printf("Failed to create gentest on server %d - %s\n", i, nt_errstr(status)); + 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<options.numops;i++) { + fprintf(f, "%u\n", op_parms[i].seed); + } + fclose(f); + rename("seeds.tmp", options.seeds_file); +} + + + +/* + the list of top-level operations that we will generate +*/ +static struct { + const char *name; + bool (*handler)(int instance); + int count, success_count; +} gen_ops[] = { + {"NTCREATEX", handler_ntcreatex}, + {"CLOSE", handler_close}, + {"READ", handler_read}, + {"WRITE", handler_write}, +}; + + +/* + run the test with the current set of op_parms parameters + return the number of operations that completed successfully +*/ +static int run_test(struct event_context *ev, struct loadparm_context *lp_ctx) +{ + int op, i; + + if (!connect_servers(ev, lp_ctx)) { + printf("Failed to connect to servers\n"); + exit(1); + } + + dump_seeds(); + + /* wipe any leftover files from old runs */ + wipe_files(); + + /* reset the open handles array */ + memset(open_handles, 0, options.max_open_handles * sizeof(open_handles[0])); + num_open_handles = 0; + + for (i=0;i<ARRAY_SIZE(gen_ops);i++) { + gen_ops[i].count = 0; + gen_ops[i].success_count = 0; + } + + for (op=0; op<options.numops; op++) { + int instance, which_op; + bool ret; + + if (op_parms[op].disabled) continue; + + srandom(op_parms[op].seed); + + instance = gen_int_range(0, NINSTANCES-1); + + /* generate a non-ignored operation */ + do { + which_op = gen_int_range(0, ARRAY_SIZE(gen_ops)-1); + } while (ignore_pattern(gen_ops[which_op].name)); + + DEBUG(3,("Generating op %s on instance %d\n", + gen_ops[which_op].name, instance)); + + current_op.seed = op_parms[op].seed; + current_op.opnum = op; + current_op.name = gen_ops[which_op].name; + current_op.status = NT_STATUS_OK; + current_op.mem_ctx = talloc_named(NULL, 0, "%s", current_op.name); + + ret = gen_ops[which_op].handler(instance); + + talloc_free(current_op.mem_ctx); + + gen_ops[which_op].count++; + if (NT_STATUS_IS_OK(current_op.status)) { + gen_ops[which_op].success_count++; + } + + if (!ret) { + printf("Failed at operation %d - %s\n", + op, gen_ops[which_op].name); + return op; + } + + if (op % 100 == 0) { + printf("%d\n", op); + } + } + + for (i=0;i<ARRAY_SIZE(gen_ops);i++) { + printf("Op %-10s got %d/%d success\n", + gen_ops[i].name, + gen_ops[i].success_count, + gen_ops[i].count); + } + + return op; +} + +/* + perform a backtracking analysis of the minimal set of operations + to generate an error +*/ +static void backtrack_analyze(struct event_context *ev, + struct loadparm_context *lp_ctx) +{ + int chunk, ret; + + chunk = options.numops / 2; + + do { + int base; + for (base=0; + chunk > 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<max; i++) { + op_parms[i].disabled = true; + } + printf("Testing %d ops with %d-%d disabled\n", + options.numops, base, max-1); + ret = run_test(ev, lp_ctx); + printf("Completed %d of %d ops\n", ret, options.numops); + for (i=base;i<max; i++) { + op_parms[i].disabled = false; + } + if (ret == options.numops) { + /* this chunk is needed */ + base += chunk; + } else if (ret < base) { + printf("damn - inconsistent errors! found early error\n"); + options.numops = ret+1; + base = 0; + } else { + /* it failed - this chunk isn't needed for a failure */ + memmove(&op_parms[base], &op_parms[max], + sizeof(op_parms[0]) * (options.numops - max)); + options.numops = (ret+1) - (max - base); + } + } + + if (chunk == 2) { + chunk = 1; + } else { + chunk *= 0.4; + } + + if (options.analyze_continuous && chunk == 0 && options.numops != 1) { + chunk = 1; + } + } while (chunk > 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<options.numops;op++) { + if (!preset[op]) { + printf("Not enough seeds in %s\n", options.seeds_file); + exit(1); + } + op_parms[op].seed = atoi(preset[op]); + } + printf("Loaded %d seeds from %s\n", options.numops, options.seeds_file); + } else { + for (op=0; op<options.numops; op++) { + op_parms[op].seed = random(); + } + } + + ret = run_test(ev, lp_ctx); + + if (ret != options.numops && options.analyze) { + options.numops = ret+1; + backtrack_analyze(ev, lp_ctx); + } else if (options.analyze_always) { + backtrack_analyze(ev, lp_ctx); + } else if (options.analyze_continuous) { + while (run_test(ev, lp_ctx) == options.numops) ; + } + + return ret == options.numops; +} + + +static void usage(poptContext pc) +{ + printf( +"Usage:\n\ + gentest //server1/share1 //server2/share2 [options..]\n\ +"); + poptPrintUsage(pc, stdout, 0); +} + +/** + split a UNC name into server and share names +*/ +static bool split_unc_name(const char *unc, char **server, char **share) +{ + char *p = strdup(unc); + if (!p) return false; + all_string_sub(p, "\\", "/", 0); + if (strncmp(p, "//", 2) != 0) return false; + + (*server) = p+2; + p = strchr(*server, '/'); + if (!p) return false; + + *p = 0; + (*share) = p+1; + + return true; +} + + + +/**************************************************************************** + main program +****************************************************************************/ + int main(int argc, char *argv[]) +{ + int opt; + int i, username_count=0; + bool ret; + char *ignore_file=NULL; + struct event_context *ev; + struct loadparm_context *lp_ctx; + poptContext pc; + int argc_new; + char **argv_new; + enum {OPT_UNCLIST=1000}; + struct poptOption long_options[] = { + POPT_AUTOHELP + {"seed", 0, POPT_ARG_INT, &options.seed, 0, "Seed to use for randomizer", NULL}, + {"num-ops", 0, POPT_ARG_INT, &options.numops, 0, "num ops", NULL}, + {"oplocks", 0, POPT_ARG_NONE, &options.use_oplocks,0, "use oplocks", NULL}, + {"showall", 0, POPT_ARG_NONE, &options.showall, 0, "display all operations", NULL}, + {"analyse", 0, POPT_ARG_NONE, &options.analyze, 0, "do backtrack analysis", NULL}, + {"analysealways", 0, POPT_ARG_NONE, &options.analyze_always, 0, "analysis always", NULL}, + {"analysecontinuous", 0, POPT_ARG_NONE, &options.analyze_continuous, 0, "analysis continuous", NULL}, + {"ignore", 0, POPT_ARG_STRING, &ignore_file, 0, "ignore from file", NULL}, + {"preset", 0, POPT_ARG_NONE, &options.use_preset_seeds, 0, "use preset seeds", NULL}, + {"fast", 0, POPT_ARG_NONE, &options.fast_reconnect, 0, "use fast reconnect", NULL}, + {"unclist", 0, POPT_ARG_STRING, NULL, OPT_UNCLIST, "unclist", NULL}, + {"seedsfile", 0, POPT_ARG_STRING, &options.seeds_file, 0, "seed file", NULL}, + { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "[DOMAIN/]USERNAME[%PASSWORD]" }, + POPT_COMMON_SAMBA + POPT_COMMON_CONNECTION + POPT_COMMON_CREDENTIALS + POPT_COMMON_VERSION + { NULL } + }; + + memset(&bad_smb2_handle, 0xFF, sizeof(bad_smb2_handle)); + + setlinebuf(stdout); + options.seed = time(NULL); + options.numops = 1000; + options.max_open_handles = 20; + options.seeds_file = "gentest_seeds.dat"; + + pc = poptGetContext("gentest", argc, (const char **) argv, long_options, + POPT_CONTEXT_KEEP_FIRST); + + poptSetOtherOptionHelp(pc, "<unc1> <unc2>"); + + 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<argc; i++) { + if (argv_new[i] == NULL) { + argc_new = i; + break; + } + } + + if (!(argc_new >= 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<NSERVERS;i++) { + const char *share = argv[1+i]; + if (!split_unc_name(share, &servers[i].server_name, &servers[i].share_name)) { + printf("Invalid share name '%s'\n", share); + return -1; + } + } + + if (username_count == 0) { + usage(pc); + return -1; + } + if (username_count == 1) { + servers[1].credentials = servers[0].credentials; + } + + printf("seed=%u\n", options.seed); + + ev = event_context_init(talloc_autofree_context()); + + gensec_init(lp_ctx); + + ret = start_gentest(ev, lp_ctx); + + if (ret) { + printf("gentest completed - no errors\n"); + } else { + printf("gentest failed\n"); + } + + return ret?0:-1; +} diff --git a/source4/torture/raw/raw.c b/source4/torture/raw/raw.c index c6133081b0..0a7fc3ebfd 100644 --- a/source4/torture/raw/raw.c +++ b/source4/torture/raw/raw.c @@ -35,6 +35,8 @@ NTSTATUS torture_raw_init(void) torture_suite_add_simple_test(suite, "BENCH-OPEN", torture_bench_open); torture_suite_add_simple_test(suite, "BENCH-LOOKUP", torture_bench_lookup); + torture_suite_add_simple_test(suite, "BENCH-TCON", + torture_bench_treeconnect); torture_suite_add_simple_test(suite, "OFFLINE", torture_test_offline); torture_suite_add_1smb_test(suite, "QFSINFO", torture_raw_qfsinfo); torture_suite_add_1smb_test(suite, "QFILEINFO", torture_raw_qfileinfo); diff --git a/source4/torture/raw/tconrate.c b/source4/torture/raw/tconrate.c new file mode 100644 index 0000000000..6f0ba0d617 --- /dev/null +++ b/source4/torture/raw/tconrate.c @@ -0,0 +1,201 @@ +/* + SMB tree connection rate test + + Copyright (C) 2006-2007 James Peach + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "libcli/resolve/resolve.h" +#include "torture/smbtorture.h" +#include "lib/cmdline/popt_common.h" +#include "param/param.h" + +#include "system/filesys.h" +#include "system/shmem.h" + +#define TIME_LIMIT_SECS 30 +#define usec_to_sec(s) ((s) / 1000000) +#define sec_to_usec(s) ((s) * 1000000) + +/* Map a shared memory buffer of at least nelem counters. */ +static void * map_count_buffer(unsigned nelem, size_t elemsz) +{ + void * buf; + size_t bufsz; + size_t pagesz = getpagesize(); + + bufsz = nelem * elemsz; + bufsz = (bufsz + pagesz) % pagesz; /* round up to pagesz */ + +#ifdef MAP_ANON + /* BSD */ + buf = mmap(NULL, bufsz, PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, + -1 /* fd */, 0 /* offset */); +#else + buf = mmap(NULL, bufsz, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, + open("/dev/zero", O_RDWR), 0 /* offset */); +#endif + + if (buf == MAP_FAILED) { + printf("failed to map count buffer: %s\n", + strerror(errno)); + return NULL; + } + + return buf; + +} + +static int fork_tcon_client(struct torture_context *tctx, + int *tcon_count, unsigned tcon_timelimit, + const char *host, const char *share) +{ + pid_t child; + struct smbcli_state *cli; + struct timeval end; + struct timeval now; + struct smbcli_options options; + + lp_smbcli_options(tctx->lp_ctx, &options); + + child = fork(); + if (child == -1) { + printf("failed to fork child: %s\n,", strerror(errno)); + return -1; + } else if (child != 0) { + /* Parent, just return. */ + return 0; + } + + /* Child. Just make as many connections as possible within the + * time limit. Don't bother synchronising the child start times + * because it's probably not work the effort, and a bit of startup + * jitter is probably a more realistic test. + */ + + + end = timeval_current(); + now = timeval_current(); + end.tv_sec += tcon_timelimit; + *tcon_count = 0; + + while (timeval_compare(&now, &end) == -1) { + NTSTATUS status; + + status = smbcli_full_connection(NULL, &cli, + host, lp_smb_ports(tctx->lp_ctx), share, + NULL, cmdline_credentials, + lp_resolve_context(tctx->lp_ctx), + tctx->ev, &options); + + if (!NT_STATUS_IS_OK(status)) { + printf("failed to connect to //%s/%s: %s\n", + host, share, nt_errstr(status)); + goto done; + } + + smbcli_tdis(cli); + + *tcon_count = *tcon_count + 1; + now = timeval_current(); + } + +done: + exit(0); +} + +static bool children_remain(void) +{ + /* Reap as many children as possible. */ + for (;;) { + pid_t ret = waitpid(-1, NULL, WNOHANG); + if (ret == 0) { + /* no children ready */ + return true; + } + if (ret == -1) { + /* no children left. maybe */ + return errno == ECHILD ? false : true; + } + } + + /* notreached */ + return false; +} + +static double rate_convert_secs(unsigned count, + const struct timeval *start, const struct timeval *end) +{ + return (double)count / + usec_to_sec((double)usec_time_diff(end, start)); +} + +/* Test the rate at which the server will accept connections. */ +bool torture_bench_treeconnect(struct torture_context *tctx) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + + int timelimit = torture_setting_int(tctx, "timelimit", + TIME_LIMIT_SECS); + int nprocs = torture_setting_int(tctx, "nprocs", 4); + + int *curr_counts = map_count_buffer(nprocs, sizeof(int)); + int *last_counts = talloc_array(NULL, int, nprocs); + + struct timeval now, last, start; + int i, delta; + + torture_assert(tctx, nprocs > 0, "bad proc count"); + torture_assert(tctx, timelimit > 0, "bad timelimit"); + torture_assert(tctx, curr_counts, "allocation failure"); + torture_assert(tctx, last_counts, "allocation failure"); + + start = last = timeval_current(); + for (i = 0; i < nprocs; ++i) { + fork_tcon_client(tctx, &curr_counts[i], timelimit, host, share); + } + + while (children_remain()) { + + sleep(1); + now = timeval_current(); + + for (i = 0, delta = 0; i < nprocs; ++i) { + delta += curr_counts[i] - last_counts[i]; + } + + printf("%u connections/sec\n", + (unsigned)rate_convert_secs(delta, &last, &now)); + + memcpy(last_counts, curr_counts, nprocs * sizeof(int)); + last = timeval_current(); + } + + now = timeval_current(); + + for (i = 0, delta = 0; i < nprocs; ++i) { + delta += curr_counts[i]; + } + + printf("TOTAL: %u connections/sec over %u secs\n", + (unsigned)rate_convert_secs(delta, &start, &now), + timelimit); + return true; +} + +/* vim: set sts=8 sw=8 : */ diff --git a/source4/torture/rpc/schannel.c b/source4/torture/rpc/schannel.c index f0279f0d04..a8f70b2ea9 100644 --- a/source4/torture/rpc/schannel.c +++ b/source4/torture/rpc/schannel.c @@ -738,6 +738,70 @@ bool torture_rpc_schannel_bench1(struct torture_context *torture) } torture_assert_ntstatus_ok(torture, s->error, "Failed establish a connect"); + /* + * Change the workstation password after establishing the netlogon + * schannel connections to prove that existing connections are not + * affected by a wks pwchange. + */ + + { + struct netr_ServerPasswordSet pwset; + char *password = generate_random_str(s->join_ctx1, 8); + struct creds_CredentialState *creds_state; + struct dcerpc_pipe *net_pipe; + + status = dcerpc_pipe_connect_b(s, &net_pipe, s->b, + &ndr_table_netlogon, + s->wks_creds1, + torture->ev, torture->lp_ctx); + + torture_assert_ntstatus_ok(torture, status, + "dcerpc_pipe_connect_b failed"); + + pwset.in.server_name = talloc_asprintf( + net_pipe, "\\\\%s", dcerpc_server_name(net_pipe)); + pwset.in.computer_name = + cli_credentials_get_workstation(s->wks_creds1); + pwset.in.account_name = talloc_asprintf( + net_pipe, "%s$", pwset.in.computer_name); + pwset.in.secure_channel_type = SEC_CHAN_WKSTA; + E_md4hash(password, pwset.in.new_password.hash); + + creds_state = cli_credentials_get_netlogon_creds( + s->wks_creds1); + creds_des_encrypt(creds_state, &pwset.in.new_password); + creds_client_authenticator(creds_state, &pwset.in.credential); + + status = dcerpc_netr_ServerPasswordSet(net_pipe, torture, &pwset); + torture_assert_ntstatus_ok(torture, status, + "ServerPasswordSet failed"); + + if (!creds_client_check(creds_state, + &pwset.out.return_authenticator.cred)) { + printf("Credential chaining failed\n"); + } + + cli_credentials_set_password(s->wks_creds1, password, + CRED_SPECIFIED); + + talloc_free(net_pipe); + + /* Just as a test, connect with the new creds */ + + talloc_free(s->wks_creds1->netlogon_creds); + s->wks_creds1->netlogon_creds = NULL; + + status = dcerpc_pipe_connect_b(s, &net_pipe, s->b, + &ndr_table_netlogon, + s->wks_creds1, + torture->ev, torture->lp_ctx); + + torture_assert_ntstatus_ok(torture, status, + "dcerpc_pipe_connect_b failed"); + + talloc_free(net_pipe); + } + torture_comment(torture, "Start looping LogonSamLogonEx on %d connections for %d secs\n", s->nprocs, s->timelimit); for (i=0; i < s->nprocs; i++) { diff --git a/source4/torture/smb2/oplocks.c b/source4/torture/smb2/oplocks.c index b0a1b31d1f..9a06ae1f19 100644 --- a/source4/torture/smb2/oplocks.c +++ b/source4/torture/smb2/oplocks.c @@ -99,7 +99,7 @@ static bool torture_oplock_handler(struct smb2_transport *transport, req = smb2_break_send(tree, &break_info.br); req->async.fn = torture_oplock_break_callback; - req->async.private = NULL; + req->async.private_data = NULL; return true; } diff --git a/source4/torture/smb2/util.c b/source4/torture/smb2/util.c index 6ac3926c98..af4f345104 100644 --- a/source4/torture/smb2/util.c +++ b/source4/torture/smb2/util.c @@ -22,6 +22,7 @@ #include "includes.h" #include "libcli/smb2/smb2.h" #include "libcli/smb2/smb2_calls.h" +#include "libcli/smb_composite/smb_composite.h" #include "lib/cmdline/popt_common.h" #include "lib/events/events.h" #include "system/time.h" @@ -34,47 +35,6 @@ /* - close a handle with SMB2 -*/ -NTSTATUS smb2_util_close(struct smb2_tree *tree, struct smb2_handle h) -{ - struct smb2_close c; - - ZERO_STRUCT(c); - c.in.file.handle = h; - - return smb2_close(tree, &c); -} - -/* - unlink a file with SMB2 -*/ -NTSTATUS smb2_util_unlink(struct smb2_tree *tree, const char *fname) -{ - struct smb2_create io; - NTSTATUS status; - - ZERO_STRUCT(io); - io.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.in.create_disposition = NTCREATEX_DISP_OPEN; - io.in.share_access = - NTCREATEX_SHARE_ACCESS_DELETE| - NTCREATEX_SHARE_ACCESS_READ| - NTCREATEX_SHARE_ACCESS_WRITE; - io.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE; - io.in.fname = fname; - - status = smb2_create(tree, tree, &io); - if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { - return NT_STATUS_OK; - } - NT_STATUS_NOT_OK_RETURN(status); - - return smb2_util_close(tree, io.out.file.handle); -} - -/* write to a file on SMB2 */ NTSTATUS smb2_util_write(struct smb2_tree *tree, |