/* * Copyright (c) James Peach 2006 * * 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 . */ #include "includes.h" /* Commit data module. * * The purpose of this module is to flush data to disk at regular intervals, * just like the NFS commit operation. There's two rationales for this. First, * it minimises the data loss in case of a power outage without incurring * the poor performance of synchronous I/O. Second, a steady flush rate * can produce better throughput than suddenly dumping massive amounts of * writes onto a disk. * * Tunables: * * commit: dthresh Amount of dirty data that can accumulate * before we commit (sync) it. * * commit: debug Debug level at which to emit messages. * */ #define MODULE "commit" static int module_debug; struct commit_info { SMB_OFF_T dbytes; /* Dirty (uncommitted) bytes */ SMB_OFF_T dthresh; /* Dirty data threshold */ }; static void commit_all( struct vfs_handle_struct * handle, files_struct * fsp) { struct commit_info *c; if ((c = VFS_FETCH_FSP_EXTENSION(handle, fsp))) { if (c->dbytes) { DEBUG(module_debug, ("%s: flushing %lu dirty bytes\n", MODULE, (unsigned long)c->dbytes)); fdatasync(fsp->fh->fd); c->dbytes = 0; } } } static void commit( struct vfs_handle_struct * handle, files_struct * fsp, ssize_t last_write) { struct commit_info *c; if ((c = VFS_FETCH_FSP_EXTENSION(handle, fsp))) { if (last_write > 0) { c->dbytes += last_write; } if (c->dbytes > c->dthresh) { DEBUG(module_debug, ("%s: flushing %lu dirty bytes\n", MODULE, (unsigned long)c->dbytes)); fdatasync(fsp->fh->fd); c->dbytes = 0; } } } static int commit_connect( struct vfs_handle_struct * handle, const char * service, const char * user) { module_debug = lp_parm_int(SNUM(handle->conn), MODULE, "debug", 100); return SMB_VFS_NEXT_CONNECT(handle, service, user); } static int commit_open( vfs_handle_struct * handle, const char * fname, files_struct * fsp, int flags, mode_t mode) { SMB_OFF_T dthresh; /* Don't bother with read-only files. */ if ((flags & O_ACCMODE) == O_RDONLY) { return SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode); } dthresh = conv_str_size(lp_parm_const_string(SNUM(handle->conn), MODULE, "dthresh", NULL)); if (dthresh > 0) { struct commit_info * c; c = VFS_ADD_FSP_EXTENSION(handle, fsp, struct commit_info); if (c) { c->dthresh = dthresh; c->dbytes = 0; } } return SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode); } static ssize_t commit_write( vfs_handle_struct * handle, files_struct * fsp, int fd, void * data, size_t count) { ssize_t ret; ret = SMB_VFS_NEXT_WRITE(handle, fsp, fd, data, count); commit(handle, fsp, ret); return ret; } static ssize_t commit_pwrite( vfs_handle_struct * handle, files_struct * fsp, int fd, void * data, size_t count, SMB_OFF_T offset) { ssize_t ret; ret = SMB_VFS_NEXT_PWRITE(handle, fsp, fd, data, count, offset); commit(handle, fsp, ret); return ret; } static ssize_t commit_close( vfs_handle_struct * handle, files_struct * fsp, int fd) { commit_all(handle, fsp); return SMB_VFS_NEXT_CLOSE(handle, fsp, fd); } static vfs_op_tuple commit_ops [] = { {SMB_VFS_OP(commit_open), SMB_VFS_OP_OPEN, SMB_VFS_LAYER_TRANSPARENT}, {SMB_VFS_OP(commit_close), SMB_VFS_OP_CLOSE, SMB_VFS_LAYER_TRANSPARENT}, {SMB_VFS_OP(commit_write), SMB_VFS_OP_WRITE, SMB_VFS_LAYER_TRANSPARENT}, {SMB_VFS_OP(commit_pwrite), SMB_VFS_OP_PWRITE, SMB_VFS_LAYER_TRANSPARENT}, {SMB_VFS_OP(commit_connect), SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_TRANSPARENT}, {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP} }; NTSTATUS vfs_commit_init(void); NTSTATUS vfs_commit_init(void) { return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, MODULE, commit_ops); }