From b53aad11d59b32d70ac189525795446f47ad8cd2 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Mon, 8 Mar 2004 07:17:15 +0000 Subject: added the "nbench" ntvfs backend. This is used to capture NBENCH load files, and also serves as an example of how to write a NTVFS filter module for Samba4. (This used to be commit 04f8996dd32d92c5df1b6b1b27c6e71e99bc77da) --- source4/ntvfs/config.m4 | 2 + source4/ntvfs/nbench/README | 14 + source4/ntvfs/nbench/vfs_nbench.c | 700 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 716 insertions(+) create mode 100644 source4/ntvfs/nbench/README create mode 100644 source4/ntvfs/nbench/vfs_nbench.c (limited to 'source4/ntvfs') diff --git a/source4/ntvfs/config.m4 b/source4/ntvfs/config.m4 index aabaa7edfc..58d0768d7a 100644 --- a/source4/ntvfs/config.m4 +++ b/source4/ntvfs/config.m4 @@ -12,6 +12,8 @@ SMB_MODULE(ntvfs_ipc, NTVFS, STATIC, [ntvfs/ipc/vfs_ipc.o]) SMB_MODULE(ntvfs_posix, NTVFS, NOT, [ntvfs/posix/vfs_posix.o]) +SMB_MODULE(ntvfs_nbench, NTVFS, STATIC, [ntvfs/nbench/vfs_nbench.o]) + SMB_SUBSYSTEM(NTVFS,ntvfs/ntvfs_base.o, [ntvfs/ntvfs_generic.o ntvfs/ntvfs_util.o], ntvfs_public_proto.h) diff --git a/source4/ntvfs/nbench/README b/source4/ntvfs/nbench/README new file mode 100644 index 0000000000..9aaae9d7ee --- /dev/null +++ b/source4/ntvfs/nbench/README @@ -0,0 +1,14 @@ +This module provides a way to capture file sharing loads for the +NBENCH benchmark client. It also servers as an example of a NTVFS +filter module. + +Here is an example config that passes through to the CIFS NTVFS backend. + +[bench] + ntvfs handler = nbench + nbench:passthru = cifs + cifs:server = myserver + cifs:user = myuser + cifs:password = mypass + cifs:domain = MYDOMAIN + cifs:share = bench diff --git a/source4/ntvfs/nbench/vfs_nbench.c b/source4/ntvfs/nbench/vfs_nbench.c new file mode 100644 index 0000000000..34f7b784a5 --- /dev/null +++ b/source4/ntvfs/nbench/vfs_nbench.c @@ -0,0 +1,700 @@ +/* + Unix SMB/CIFS implementation. + + a pass-thru NTVFS module to record a NBENCH load file + + Copyright (C) Andrew Tridgell 2004 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + "passthru" in this module refers to the next level of NTVFS being used +*/ + +#include "includes.h" + +/* this is stored in ntvfs_private */ +struct nbench_private { + const struct ntvfs_ops *passthru_ops; + void *passthru_private; + const struct ntvfs_ops *nbench_ops; + int log_fd; +}; + + +/* + log one request to the nbench log +*/ +static void nbench_log(struct nbench_private *private, + const char *format, ...) +{ + va_list ap; + char *s = NULL; + + va_start(ap, format); + vasprintf(&s, format, ap); + va_end(ap); + + write(private->log_fd, s, strlen(s)); + free(s); +} + + +/* + when we call the next stacked level of NTVFS module we need + to give it its own private pointer, plus its own NTVFS operations structure. + Then we need to restore both of these after the call, as the next level could + modify either of these +*/ +#define PASS_THRU(conn, op, args) do { \ + conn->ntvfs_private = private->passthru_private; \ + conn->ntvfs_ops = private->passthru_ops; \ +\ + status = private->passthru_ops->op args; \ +\ + private->passthru_private = conn->ntvfs_private; \ + private->passthru_ops = conn->ntvfs_ops; \ +\ + conn->ntvfs_private = private; \ + conn->ntvfs_ops = private->nbench_ops; \ +} while (0) + +/* + this pass through macro operates on request contexts, and disables + async calls. + + async calls are a pain for the nbench module as it makes pulling the + status code and any result parameters much harder. +*/ +#define PASS_THRU_REQ(req, op, args) do { \ + void *send_fn_saved = req->async.send_fn; \ + req->async.send_fn = NULL; \ + PASS_THRU(req->conn, op, args); \ + req->async.send_fn = send_fn_saved; \ +} while (0) + + +/* + connect to a share - used when a tree_connect operation comes in. +*/ +static NTSTATUS nbench_connect(struct request_context *req, const char *sharename) +{ + struct nbench_private *private; + const char *passthru; + NTSTATUS status; + char *logname = NULL; + + private = talloc_p(req->conn->mem_ctx, struct nbench_private); + if (!private) { + return NT_STATUS_NO_MEMORY; + } + + asprintf(&logname, "/tmp/nbenchlog.%u", getpid()); + private->log_fd = open(logname, O_WRONLY|O_CREAT|O_APPEND, 0644); + free(logname); + + if (private->log_fd == -1) { + DEBUG(0,("Failed to open nbench log\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + passthru = lp_parm_string(req->conn->service, "nbench", "passthru"); + + private->passthru_private = NULL; + private->nbench_ops = req->conn->ntvfs_ops; + private->passthru_ops = ntvfs_backend_byname(passthru, NTVFS_DISK); + + if (!private->passthru_ops) { + DEBUG(0,("Unable to connect to '%s' pass through backend\n", passthru)); + return NT_STATUS_UNSUCCESSFUL; + } + + PASS_THRU(req->conn, connect, (req, sharename)); + + return status; +} + +/* + disconnect from a share +*/ +static NTSTATUS nbench_disconnect(struct tcon_context *conn) +{ + struct nbench_private *private = conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU(conn, disconnect, (conn)); + + close(private->log_fd); + + return status; +} + +/* + 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) +*/ +static NTSTATUS nbench_unlink(struct request_context *req, struct smb_unlink *unl) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, unlink, (req, unl)); + + nbench_log(private, "Unlink \"%s\" 0x%x %s\n", + unl->in.pattern, unl->in.attrib, + get_nt_error_c_code(status)); + + return status; +} + +/* + ioctl interface +*/ +static NTSTATUS nbench_ioctl(struct request_context *req, union smb_ioctl *io) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, ioctl, (req, io)); + + nbench_log(private, "Ioctl - NOT HANDLED\n"); + + return status; +} + +/* + check if a directory exists +*/ +static NTSTATUS nbench_chkpath(struct request_context *req, struct smb_chkpath *cp) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, chkpath, (req, cp)); + + nbench_log(private, "Chkpath \"%s\" %s\n", + cp->in.path, + get_nt_error_c_code(status)); + + return status; +} + +/* + return info on a pathname +*/ +static NTSTATUS nbench_qpathinfo(struct request_context *req, union smb_fileinfo *info) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, qpathinfo, (req, info)); + + nbench_log(private, "QUERY_PATH_INFORMATION \"%s\" %d %s\n", + info->generic.in.fname, + info->generic.level, + get_nt_error_c_code(status)); + + return status; +} + +/* + query info on a open file +*/ +static NTSTATUS nbench_qfileinfo(struct request_context *req, union smb_fileinfo *info) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, qfileinfo, (req, info)); + + nbench_log(private, "QUERY_FILE_INFORMATION %d %d %s\n", + info->generic.in.fnum, + info->generic.level, + get_nt_error_c_code(status)); + + return status; +} + + +/* + set info on a pathname +*/ +static NTSTATUS nbench_setpathinfo(struct request_context *req, union smb_setfileinfo *st) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, setpathinfo, (req, st)); + + nbench_log(private, "SET_PATH_INFORMATION \"%s\" %d %s\n", + st->generic.file.fname, + st->generic.level, + get_nt_error_c_code(status)); + + return status; +} + +/* + open a file +*/ +static NTSTATUS nbench_open(struct request_context *req, union smb_open *io) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, open, (req, io)); + + switch (io->generic.level) { + case RAW_OPEN_NTCREATEX: + nbench_log(private, "NTCreateX \"%s\" 0x%x 0x%x %d %s\n", + io->ntcreatex.in.fname, + io->ntcreatex.in.create_options, + io->ntcreatex.in.open_disposition, + io->ntcreatex.out.fnum, + get_nt_error_c_code(status)); + break; + + default: + nbench_log(private, "Open-%d - NOT HANDLED\n", + io->generic.level); + break; + } + + return status; +} + +/* + create a directory +*/ +static NTSTATUS nbench_mkdir(struct request_context *req, union smb_mkdir *md) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, mkdir, (req, md)); + + nbench_log(private, "Mkdir - NOT HANDLED\n"); + + return status; +} + +/* + remove a directory +*/ +static NTSTATUS nbench_rmdir(struct request_context *req, struct smb_rmdir *rd) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, rmdir, (req, rd)); + + nbench_log(private, "Rmdir \"%s\" %s\n", + rd->in.path, + get_nt_error_c_code(status)); + + return status; +} + +/* + rename a set of files +*/ +static NTSTATUS nbench_rename(struct request_context *req, union smb_rename *ren) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, rename, (req, ren)); + + switch (ren->generic.level) { + case RAW_RENAME_RENAME: + nbench_log(private, "Rename \"%s\" \"%s\" %s\n", + ren->rename.in.pattern1, + ren->rename.in.pattern2, + get_nt_error_c_code(status)); + break; + + default: + nbench_log(private, "Rename-%d - NOT HANDLED\n", + ren->generic.level); + break; + } + + return status; +} + +/* + copy a set of files +*/ +static NTSTATUS nbench_copy(struct request_context *req, struct smb_copy *cp) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, copy, (req, cp)); + + nbench_log(private, "Copy - NOT HANDLED\n"); + + return status; +} + +/* + read from a file +*/ +static NTSTATUS nbench_read(struct request_context *req, union smb_read *rd) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, read, (req, rd)); + + switch (rd->generic.level) { + case RAW_READ_READX: + nbench_log(private, "ReadX %d %d %d %d %s\n", + rd->readx.in.fnum, + (int)rd->readx.in.offset, + rd->readx.in.maxcnt, + rd->readx.out.nread, + get_nt_error_c_code(status)); + break; + default: + nbench_log(private, "Read-%d - NOT HANDLED\n", + rd->generic.level); + break; + } + + return status; +} + +/* + write to a file +*/ +static NTSTATUS nbench_write(struct request_context *req, union smb_write *wr) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, write, (req, wr)); + + switch (wr->generic.level) { + case RAW_WRITE_WRITEX: + nbench_log(private, "WriteX %d %d %d %d %s\n", + wr->writex.in.fnum, + (int)wr->writex.in.offset, + wr->writex.in.count, + wr->writex.out.nwritten, + get_nt_error_c_code(status)); + break; + + case RAW_WRITE_WRITE: + nbench_log(private, "Write %d %d %d %d %s\n", + wr->write.in.fnum, + wr->write.in.offset, + wr->write.in.count, + wr->write.out.nwritten, + get_nt_error_c_code(status)); + break; + + default: + nbench_log(private, "Write-%d - NOT HANDLED\n", + wr->generic.level); + break; + } + + return status; +} + +/* + seek in a file +*/ +static NTSTATUS nbench_seek(struct request_context *req, struct smb_seek *io) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, seek, (req, io)); + + nbench_log(private, "Seek - NOT HANDLED\n"); + + return status; +} + +/* + flush a file +*/ +static NTSTATUS nbench_flush(struct request_context *req, struct smb_flush *io) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, flush, (req, io)); + + nbench_log(private, "Flush %d %s\n", + io->in.fnum, + get_nt_error_c_code(status)); + + return status; +} + +/* + close a file +*/ +static NTSTATUS nbench_close(struct request_context *req, union smb_close *io) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, close, (req, io)); + + switch (io->generic.level) { + case RAW_CLOSE_CLOSE: + nbench_log(private, "Close %d %s\n", + io->close.in.fnum, + get_nt_error_c_code(status)); + break; + + default: + nbench_log(private, "Close-%d - NOT HANDLED\n", + io->generic.level); + break; + } + + return status; +} + +/* + exit - closing files +*/ +static NTSTATUS nbench_exit(struct request_context *req) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, exit, (req)); + + return status; +} + +/* + lock a byte range +*/ +static NTSTATUS nbench_lock(struct request_context *req, union smb_lock *lck) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, lock, (req, lck)); + + if (lck->generic.level == RAW_LOCK_LOCKX && + lck->lockx.in.lock_cnt == 1 && + lck->lockx.in.ulock_cnt == 0) { + nbench_log(private, "LockX %d %d %d %s\n", + lck->lockx.in.fnum, + (int)lck->lockx.in.locks[0].offset, + (int)lck->lockx.in.locks[0].count, + get_nt_error_c_code(status)); + } else if (lck->generic.level == RAW_LOCK_LOCKX && + lck->lockx.in.ulock_cnt == 1) { + nbench_log(private, "UnlockX %d %d %d %s\n", + lck->lockx.in.fnum, + (int)lck->lockx.in.locks[0].offset, + (int)lck->lockx.in.locks[0].count, + get_nt_error_c_code(status)); + } else { + nbench_log(private, "Lock-%d - NOT HANDLED\n", lck->generic.level); + } + + return status; +} + +/* + set info on a open file +*/ +static NTSTATUS nbench_setfileinfo(struct request_context *req, + union smb_setfileinfo *info) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, setfileinfo, (req, info)); + + nbench_log(private, "Setfileinfo %d %d %s\n", + info->generic.file.fnum, + info->generic.level, + get_nt_error_c_code(status)); + + return status; +} + + +/* + return filesystem space info +*/ +static NTSTATUS nbench_fsinfo(struct request_context *req, union smb_fsinfo *fs) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, fsinfo, (req, fs)); + + nbench_log(private, "Fsinfo %d %s", + fs->generic.level, + get_nt_error_c_code(status)); + + return status; +} + +/* + return print queue info +*/ +static NTSTATUS nbench_lpq(struct request_context *req, union smb_lpq *lpq) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, lpq, (req, lpq)); + + nbench_log(private, "Lpq-%d - NOT HANDLED\n", lpq->generic.level); + + return status; +} + +/* + list files in a directory matching a wildcard pattern +*/ +static NTSTATUS nbench_search_first(struct request_context *req, union smb_search_first *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, search_first, (req, io, search_private, callback)); + + switch (io->generic.level) { + case RAW_SEARCH_BOTH_DIRECTORY_INFO: + nbench_log(private, "Search \"%s\" %d %d %d %s\n", + io->t2ffirst.in.pattern, + io->generic.level, + io->t2ffirst.in.max_count, + io->t2ffirst.out.count, + get_nt_error_c_code(status)); + break; + + default: + nbench_log(private, "Search-%d - NOT HANDLED\n", io->generic.level); + break; + } + + return status; +} + +/* continue a search */ +static NTSTATUS nbench_search_next(struct request_context *req, union smb_search_next *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, search_next, (req, io, search_private, callback)); + + nbench_log(private, "Searchnext-%d - NOT HANDLED\n", io->generic.level); + + return status; +} + +/* close a search */ +static NTSTATUS nbench_search_close(struct request_context *req, union smb_search_close *io) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, search_close, (req, io)); + + nbench_log(private, "Searchclose-%d - NOT HANDLED\n", io->generic.level); + + return status; +} + +/* SMBtrans - not used on file shares */ +static NTSTATUS nbench_trans(struct request_context *req, struct smb_trans2 *trans2) +{ + struct nbench_private *private = req->conn->ntvfs_private; + NTSTATUS status; + + PASS_THRU_REQ(req, trans, (req,trans2)); + + nbench_log(private, "Trans - NOT HANDLED\n"); + + return status; +} + +/* + initialise the nbench backend, registering ourselves with the ntvfs subsystem + */ +NTSTATUS ntvfs_nbench_init(void) +{ + NTSTATUS ret; + struct ntvfs_ops ops; + + ZERO_STRUCT(ops); + + /* fill in the name and type */ + ops.name = "nbench"; + ops.type = NTVFS_DISK; + + /* fill in all the operations */ + ops.connect = nbench_connect; + ops.disconnect = nbench_disconnect; + ops.unlink = nbench_unlink; + ops.chkpath = nbench_chkpath; + ops.qpathinfo = nbench_qpathinfo; + ops.setpathinfo = nbench_setpathinfo; + ops.open = nbench_open; + ops.mkdir = nbench_mkdir; + ops.rmdir = nbench_rmdir; + ops.rename = nbench_rename; + ops.copy = nbench_copy; + ops.ioctl = nbench_ioctl; + ops.read = nbench_read; + ops.write = nbench_write; + ops.seek = nbench_seek; + ops.flush = nbench_flush; + ops.close = nbench_close; + ops.exit = nbench_exit; + ops.lock = nbench_lock; + ops.setfileinfo = nbench_setfileinfo; + ops.qfileinfo = nbench_qfileinfo; + ops.fsinfo = nbench_fsinfo; + ops.lpq = nbench_lpq; + ops.search_first = nbench_search_first; + ops.search_next = nbench_search_next; + ops.search_close = nbench_search_close; + ops.trans = nbench_trans; + + /* we don't register a trans2 handler as we want to be able to + log individual trans2 requests */ + ops.trans2 = NULL; + + /* register ourselves with the NTVFS subsystem. */ + ret = register_backend("ntvfs", &ops); + + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,("Failed to register nbench backend!\n")); + } + + return ret; +} -- cgit