2006-07-11 22:01:26 +04:00
/*
2007-11-21 03:45:33 +03:00
* Copyright ( c ) James Peach 2006 , 2007
* Copyright ( c ) David Losada Carballo 2007
2006-07-11 22:01:26 +04:00
*
* 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
2007-07-09 23:25:36 +04:00
* the Free Software Foundation ; either version 3 of the License , or
2006-07-11 22:01:26 +04:00
* ( 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
2007-07-10 09:23:25 +04:00
* along with this program ; if not , see < http : //www.gnu.org/licenses/>.
2006-07-11 22:01:26 +04:00
*/
# 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
2007-11-21 03:45:33 +03:00
* before we commit ( sync ) it .
2006-07-11 22:01:26 +04:00
*
* commit : debug Debug level at which to emit messages .
*
2007-11-21 03:45:33 +03:00
* commit : eof mode String . Tunes how the module tries to guess when
* the client has written the last bytes of the file .
* Possible values ( default = hinted ) :
*
* ( * ) = hinted Some clients ( i . e . Windows Explorer ) declare the
* size of the file before transferring it . With this
* option , we remember that hint , and commit after
* writing in that file position . If the client
* doesn ' t declare the size of file , commiting on EOF
* is not triggered .
*
* = growth Commits after a write operation has made the file
* size grow . If the client declares a file size , it
* refrains to commit until the file has reached it .
* Useful for defeating writeback on NFS shares .
*
2006-07-11 22:01:26 +04:00
*/
# define MODULE "commit"
static int module_debug ;
2007-11-21 03:45:33 +03:00
enum eof_mode
{
EOF_NONE = 0x0000 ,
EOF_HINTED = 0x0001 ,
EOF_GROWTH = 0x0002
} ;
2006-07-11 22:01:26 +04:00
struct commit_info
{
2007-11-21 03:45:33 +03:00
/* For chunk-based commits */
2006-07-11 22:01:26 +04:00
SMB_OFF_T dbytes ; /* Dirty (uncommitted) bytes */
SMB_OFF_T dthresh ; /* Dirty data threshold */
2007-11-21 03:45:33 +03:00
/* For commits on EOF */
enum eof_mode on_eof ;
SMB_OFF_T eof ; /* Expected file size */
2006-07-11 22:01:26 +04:00
} ;
2007-11-21 03:45:33 +03:00
static int commit_do (
struct commit_info * c ,
int fd )
{
int result ;
DEBUG ( module_debug ,
( " %s: flushing %lu dirty bytes \n " ,
MODULE , ( unsigned long ) c - > dbytes ) ) ;
# if HAVE_FDATASYNC
result = fdatasync ( fd ) ;
# elif HAVE_FSYNC
result = fsync ( fd ) ;
# else
result = 0
# endif
if ( result = = 0 ) {
c - > dbytes = 0 ; /* on success, no dirty bytes */
}
return result ;
}
static int commit_all (
2006-07-11 22:01:26 +04:00
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 ) ) ;
2007-11-21 03:45:33 +03:00
return commit_do ( c , fsp - > fh - > fd ) ;
2006-07-11 22:01:26 +04:00
}
}
2007-11-21 03:45:33 +03:00
return 0 ;
2006-07-11 22:01:26 +04:00
}
2007-11-21 03:45:33 +03:00
static int commit (
2006-07-11 22:01:26 +04:00
struct vfs_handle_struct * handle ,
files_struct * fsp ,
2007-11-21 03:45:33 +03:00
SMB_OFF_T offset ,
2006-07-11 22:01:26 +04:00
ssize_t last_write )
{
struct commit_info * c ;
2007-11-21 03:45:33 +03:00
if ( ( c = VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ) = = NULL ) {
return 0 ;
}
2006-07-11 22:01:26 +04:00
2007-11-21 03:45:33 +03:00
c - > dbytes + = last_write ; /* dirty bytes always counted */
2006-07-11 22:01:26 +04:00
2007-11-21 03:45:33 +03:00
if ( c - > dthresh & & ( c - > dbytes > c - > dthresh ) ) {
return commit_do ( c , fsp - > fh - > fd ) ;
}
2006-07-11 22:01:26 +04:00
2007-11-21 03:45:33 +03:00
/* Return if we are not in EOF mode or if we have temporarily opted
* out of it .
*/
if ( c - > on_eof = = EOF_NONE | | c - > eof < 0 ) {
return 0 ;
}
/* This write hit or went past our cache the file size. */
if ( ( offset + last_write ) > = c - > eof ) {
if ( commit_do ( c , fsp - > fh - > fd ) = = - 1 ) {
return - 1 ;
}
/* Hinted mode only commits the first time we hit EOF. */
if ( c - > on_eof = = EOF_HINTED ) {
c - > eof = - 1 ;
} else if ( c - > on_eof = = EOF_GROWTH ) {
c - > eof = offset + last_write ;
}
}
return 0 ;
2006-07-11 22:01:26 +04:00
}
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 ;
2007-11-21 03:45:33 +03:00
const char * eof_mode ;
struct commit_info * c = NULL ;
int fd ;
2006-07-11 22:01:26 +04:00
/* Don't bother with read-only files. */
if ( ( flags & O_ACCMODE ) = = O_RDONLY ) {
return SMB_VFS_NEXT_OPEN ( handle , fname , fsp , flags , mode ) ;
}
2007-11-21 03:45:33 +03:00
/* Read and check module configuration */
2006-07-11 22:01:26 +04:00
dthresh = conv_str_size ( lp_parm_const_string ( SNUM ( handle - > conn ) ,
MODULE , " dthresh " , NULL ) ) ;
2007-11-21 03:45:33 +03:00
eof_mode = lp_parm_const_string ( SNUM ( handle - > conn ) ,
MODULE , " eof mode " , " none " ) ;
if ( dthresh > 0 | | ! strequal ( eof_mode , " none " ) ) {
2006-07-11 22:01:26 +04:00
c = VFS_ADD_FSP_EXTENSION ( handle , fsp , struct commit_info ) ;
2007-11-21 03:45:33 +03:00
/* Process main tunables */
2006-07-11 22:01:26 +04:00
if ( c ) {
c - > dthresh = dthresh ;
c - > dbytes = 0 ;
2007-11-21 03:45:33 +03:00
c - > on_eof = EOF_NONE ;
c - > eof = 0 ;
}
}
/* Process eof_mode tunable */
if ( c ) {
if ( strequal ( eof_mode , " hinted " ) ) {
c - > on_eof = EOF_HINTED ;
} else if ( strequal ( eof_mode , " growth " ) ) {
c - > on_eof = EOF_GROWTH ;
}
}
fd = SMB_VFS_NEXT_OPEN ( handle , fname , fsp , flags , mode ) ;
if ( fd = = - 1 ) {
VFS_REMOVE_FSP_EXTENSION ( handle , fsp ) ;
return fd ;
}
/* EOF commit modes require us to know the initial file size. */
if ( c & & ( c - > on_eof ! = EOF_NONE ) ) {
SMB_STRUCT_STAT st ;
2008-01-07 15:21:26 +03:00
if ( SMB_VFS_FSTAT ( fsp , & st ) = = - 1 ) {
2007-11-21 03:45:33 +03:00
return - 1 ;
2006-07-11 22:01:26 +04:00
}
2007-11-21 03:45:33 +03:00
c - > eof = st . st_size ;
2006-07-11 22:01:26 +04:00
}
2007-11-21 03:45:33 +03:00
return 0 ;
2006-07-11 22:01:26 +04:00
}
static ssize_t commit_write (
vfs_handle_struct * handle ,
files_struct * fsp ,
void * data ,
size_t count )
{
ssize_t ret ;
2008-01-10 17:49:35 +03:00
ret = SMB_VFS_NEXT_WRITE ( handle , fsp , data , count ) ;
2007-11-21 03:45:33 +03:00
if ( ret > 0 ) {
if ( commit ( handle , fsp , fsp - > fh - > pos , ret ) = = - 1 ) {
return - 1 ;
}
}
2006-07-11 22:01:26 +04:00
return ret ;
}
static ssize_t commit_pwrite (
vfs_handle_struct * handle ,
files_struct * fsp ,
void * data ,
size_t count ,
SMB_OFF_T offset )
{
ssize_t ret ;
2008-01-07 11:23:04 +03:00
ret = SMB_VFS_NEXT_PWRITE ( handle , fsp , data , count , offset ) ;
2007-11-21 03:45:33 +03:00
if ( ret > 0 ) {
if ( commit ( handle , fsp , offset , ret ) = = - 1 ) {
return - 1 ;
}
}
2006-07-11 22:01:26 +04:00
return ret ;
}
2007-11-21 03:45:33 +03:00
static int commit_close (
2006-07-11 22:01:26 +04:00
vfs_handle_struct * handle ,
2008-01-11 16:19:28 +03:00
files_struct * fsp )
2006-07-11 22:01:26 +04:00
{
2007-11-21 03:45:33 +03:00
/* Commit errors not checked, close() will find them again */
2006-07-11 22:01:26 +04:00
commit_all ( handle , fsp ) ;
2008-01-11 16:19:28 +03:00
return SMB_VFS_NEXT_CLOSE ( handle , fsp ) ;
2006-07-11 22:01:26 +04:00
}
2007-11-21 03:45:33 +03:00
static int commit_ftruncate (
vfs_handle_struct * handle ,
files_struct * fsp ,
SMB_OFF_T len )
{
int result ;
2008-01-07 17:55:09 +03:00
result = SMB_VFS_NEXT_FTRUNCATE ( handle , fsp , len ) ;
2007-11-21 03:45:33 +03:00
if ( result = = 0 ) {
struct commit_info * c ;
if ( ( c = VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ) ) {
commit ( handle , fsp , len , 0 ) ;
c - > eof = len ;
}
}
return result ;
}
2006-07-11 22:01:26 +04:00
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 } ,
2007-11-21 03:45:33 +03:00
{ SMB_VFS_OP ( commit_ftruncate ) ,
SMB_VFS_OP_FTRUNCATE , SMB_VFS_LAYER_TRANSPARENT } ,
2006-07-11 22:01:26 +04:00
{ SMB_VFS_OP ( NULL ) , SMB_VFS_OP_NOOP , SMB_VFS_LAYER_NOOP }
} ;
2006-12-19 23:16:52 +03:00
NTSTATUS vfs_commit_init ( void ) ;
2006-07-11 22:01:26 +04:00
NTSTATUS vfs_commit_init ( void )
{
return smb_register_vfs ( SMB_VFS_INTERFACE_VERSION , MODULE , commit_ops ) ;
}
2007-11-21 03:45:33 +03:00