mirror of
https://github.com/samba-team/samba.git
synced 2024-12-25 23:21:54 +03:00
Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into 4-0-local
(This used to be commit 7594f79db6
)
This commit is contained in:
commit
fdec7fdaf5
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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__ */
|
||||
|
@ -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)))
|
||||
|
||||
|
@ -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; */
|
||||
|
@ -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)))
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
200
source4/libcli/smb2/util.c
Normal file
200
source4/libcli/smb2/util.c
Normal file
@ -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;
|
||||
}
|
371
source4/libcli/smb_composite/smb2.c
Normal file
371
source4/libcli/smb_composite/smb2.c
Normal file
@ -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);
|
||||
}
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include "libcli/raw/signing.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
|
||||
|
||||
/*
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
|
@ -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)) {
|
||||
|
844
source4/ntvfs/smb2/vfs_smb2.c
Normal file
844
source4/ntvfs/smb2/vfs_smb2.c
Normal file
@ -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;
|
||||
}
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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 \
|
||||
@ -261,6 +262,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]
|
||||
|
1952
source4/torture/gentest_smb2.c
Normal file
1952
source4/torture/gentest_smb2.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
|
201
source4/torture/raw/tconrate.c
Normal file
201
source4/torture/raw/tconrate.c
Normal file
@ -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 : */
|
@ -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++) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"
|
||||
@ -33,47 +34,6 @@
|
||||
#include "torture/smb2/proto.h"
|
||||
|
||||
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user