/* 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; 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 is used to call the next module in the ntvfs chain */ #define PASS_THRU(tcon, op, args) private->passthru_ops->op args; /* 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; \ req->ntvfs_depth++; \ status = PASS_THRU(req->tcon, op, args); \ req->ntvfs_depth--; \ 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 smbsrv_request *req, const char *sharename, int depth) { struct nbench_private *private; const char *passthru; NTSTATUS status; char *logname = NULL; const char **handlers = lp_ntvfs_handler(req->tcon->service); private = talloc_p(req->tcon, struct nbench_private); if (!private) { return NT_STATUS_NO_MEMORY; } asprintf(&logname, "/tmp/nbenchlog%d.%u", depth, 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; } private->passthru_ops = ntvfs_backend_byname(handlers[depth+1], NTVFS_DISK); if (!private->passthru_ops) { DEBUG(0,("Unable to connect to '%s' pass through backend\n", passthru)); return NT_STATUS_UNSUCCESSFUL; } ntvfs_set_private(req->tcon, depth, private); status = PASS_THRU(req->tcon, connect, (req, sharename, depth+1)); return status; } /* disconnect from a share */ static NTSTATUS nbench_disconnect(struct smbsrv_tcon *tcon, int depth) { struct nbench_private *private = tcon->ntvfs_private_list[depth]; NTSTATUS status; close(private->log_fd); status = PASS_THRU(tcon, disconnect, (tcon, depth+1)); 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 smbsrv_request *req, struct smb_unlink *unl) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, union smb_ioctl *io) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, struct smb_chkpath *cp) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, union smb_fileinfo *info) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, union smb_fileinfo *info) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, union smb_setfileinfo *st) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, union smb_open *io) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, union smb_mkdir *md) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, struct smb_rmdir *rd) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, union smb_rename *ren) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, struct smb_copy *cp) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, union smb_read *rd) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, union smb_write *wr) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, struct smb_seek *io) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, struct smb_flush *io) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, union smb_close *io) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req) { NTVFS_GET_PRIVATE(nbench_private, private, req); NTSTATUS status; PASS_THRU_REQ(req, exit, (req)); return status; } /* logoff - closing files */ static NTSTATUS nbench_logoff(struct smbsrv_request *req) { NTVFS_GET_PRIVATE(nbench_private, private, req); NTSTATUS status; PASS_THRU_REQ(req, logoff, (req)); return status; } /* lock a byte range */ static NTSTATUS nbench_lock(struct smbsrv_request *req, union smb_lock *lck) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, union smb_setfileinfo *info) { NTVFS_GET_PRIVATE(nbench_private, private, req); NTSTATUS status; PASS_THRU_REQ(req, setfileinfo, (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 smbsrv_request *req, union smb_fsinfo *fs) { NTVFS_GET_PRIVATE(nbench_private, private, req); NTSTATUS status; PASS_THRU_REQ(req, fsinfo, (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 smbsrv_request *req, union smb_lpq *lpq) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, union smb_search_first *io, void *search_private, BOOL (*callback)(void *, union smb_search_data *)) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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, "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 smbsrv_request *req, union smb_search_next *io, void *search_private, BOOL (*callback)(void *, union smb_search_data *)) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, union smb_search_close *io) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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 smbsrv_request *req, struct smb_trans2 *trans2) { NTVFS_GET_PRIVATE(nbench_private, private, req); 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; 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; }