/* 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 { int log_fd; }; /* log one request to the nbench log */ static void nbench_log(struct nbench_private *private, const char *format, ...) PRINTF_ATTRIBUTE(2, 3); 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); } /* 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(ntvfs, req, op, args) do { \ void *send_fn_saved = req->async.send_fn; \ req->async.send_fn = NULL; \ status = ntvfs_next_##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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, const char *sharename) { struct nbench_private *private; NTSTATUS status; char *logname = NULL; private = talloc_p(req->tcon, struct nbench_private); if (!private) { return NT_STATUS_NO_MEMORY; } asprintf(&logname, "/tmp/nbenchlog%d.%u", ntvfs->depth, getpid()); private->log_fd = sys_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; } ntvfs->private_data = private; status = ntvfs_next_connect(ntvfs, req, sharename); return status; } /* disconnect from a share */ static NTSTATUS nbench_disconnect(struct ntvfs_module_context *ntvfs, struct smbsrv_tcon *tcon) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; close(private->log_fd); status = ntvfs_next_disconnect(ntvfs, tcon); 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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, struct smb_unlink *unl) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, unlink, (ntvfs, 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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, union smb_ioctl *io) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, ioctl, (ntvfs, req, io)); nbench_log(private, "Ioctl - NOT HANDLED\n"); return status; } /* check if a directory exists */ static NTSTATUS nbench_chkpath(struct ntvfs_module_context *ntvfs, struct smbsrv_request *req, struct smb_chkpath *cp) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, chkpath, (ntvfs, 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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, union smb_fileinfo *info) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, qpathinfo, (ntvfs, 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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, union smb_fileinfo *info) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, qfileinfo, (ntvfs, 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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, union smb_setfileinfo *st) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, setpathinfo, (ntvfs, 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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, union smb_open *io) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, open, (ntvfs, req, io)); DEBUG(0,("%d: %s\n", ntvfs->depth, get_nt_error_c_code(status))); 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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, union smb_mkdir *md) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, mkdir, (ntvfs, req, md)); nbench_log(private, "Mkdir - NOT HANDLED\n"); return status; } /* remove a directory */ static NTSTATUS nbench_rmdir(struct ntvfs_module_context *ntvfs, struct smbsrv_request *req, struct smb_rmdir *rd) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, rmdir, (ntvfs, 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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, union smb_rename *ren) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, rename, (ntvfs, 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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, struct smb_copy *cp) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, copy, (ntvfs, req, cp)); nbench_log(private, "Copy - NOT HANDLED\n"); return status; } /* read from a file */ static NTSTATUS nbench_read(struct ntvfs_module_context *ntvfs, struct smbsrv_request *req, union smb_read *rd) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, read, (ntvfs, 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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, union smb_write *wr) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, write, (ntvfs, 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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, struct smb_seek *io) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, seek, (ntvfs, req, io)); nbench_log(private, "Seek - NOT HANDLED\n"); return status; } /* flush a file */ static NTSTATUS nbench_flush(struct ntvfs_module_context *ntvfs, struct smbsrv_request *req, struct smb_flush *io) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, flush, (ntvfs, 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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, union smb_close *io) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, close, (ntvfs, 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 ntvfs_module_context *ntvfs, struct smbsrv_request *req) { NTSTATUS status; PASS_THRU_REQ(ntvfs, req, exit, (ntvfs, req)); return status; } /* logoff - closing files */ static NTSTATUS nbench_logoff(struct ntvfs_module_context *ntvfs, struct smbsrv_request *req) { NTSTATUS status; PASS_THRU_REQ(ntvfs, req, logoff, (ntvfs, req)); return status; } /* lock a byte range */ static NTSTATUS nbench_lock(struct ntvfs_module_context *ntvfs, struct smbsrv_request *req, union smb_lock *lck) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, lock, (ntvfs, 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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, union smb_setfileinfo *info) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, setfileinfo, (ntvfs, req, info)); nbench_log(private, "SET_FILE_INFORMATION %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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, union smb_fsinfo *fs) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, fsinfo, (ntvfs, req, fs)); nbench_log(private, "QUERY_FS_INFORMATION %d %s\n", fs->generic.level, get_nt_error_c_code(status)); return status; } /* return print queue info */ static NTSTATUS nbench_lpq(struct ntvfs_module_context *ntvfs, struct smbsrv_request *req, union smb_lpq *lpq) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, lpq, (ntvfs, 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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, union smb_search_first *io, void *search_private, BOOL (*callback)(void *, union smb_search_data *)) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, search_first, (ntvfs, req, io, search_private, callback)); switch (io->generic.level) { case RAW_SEARCH_BOTH_DIRECTORY_INFO: nbench_log(private, "FIND_FIRST \"%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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, union smb_search_next *io, void *search_private, BOOL (*callback)(void *, union smb_search_data *)) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, search_next, (ntvfs, 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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, union smb_search_close *io) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, search_close, (ntvfs, 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 ntvfs_module_context *ntvfs, struct smbsrv_request *req, struct smb_trans2 *trans2) { struct nbench_private *private = ntvfs->private_data; NTSTATUS status; PASS_THRU_REQ(ntvfs, req, trans, (ntvfs, 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; ops.logoff = nbench_logoff; /* 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; }