2007-10-23 14:43:27 +04:00
/*
* ensure meta data operations are performed synchronously
*
* Copyright ( C ) Andrew Tridgell 2007
2011-02-18 15:03:52 +03:00
* Copyright ( C ) Christian Ambach , 2010 - 2011
2007-10-23 14:43:27 +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
* 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 0213 9 , USA .
*/
# include "includes.h"
2011-02-26 01:20:06 +03:00
# include "system/filesys.h"
2011-03-23 00:34:22 +03:00
# include "smbd/smbd.h"
2007-10-23 14:43:27 +04:00
/*
Some filesystems ( even some journaled filesystems ) require that a
fsync ( ) be performed on many meta data operations to ensure that the
operation is guaranteed to remain in the filesystem after a power
failure . This is particularly important for some cluster filesystems
which are participating in a node failover system with clustered
Samba
On those filesystems this module provides a way to perform those
operations safely .
most of the performance loss with this module is in fsync on close ( ) .
2010-10-07 18:09:52 +04:00
You can disable that with
syncops : onclose = no
that can be set either globally or per share .
2011-02-18 15:03:52 +03:00
On certain filesystems that only require the last data written to be
fsync ( ) ' ed , you can disable the metadata synchronization of this module with
syncops : onmeta = no
This option can be set either globally or per share .
you can also disable the module completely for a share with
2010-10-07 18:56:19 +04:00
syncops : disable = true
2007-10-23 14:43:27 +04:00
*/
2010-10-07 18:09:52 +04:00
struct syncops_config_data {
bool onclose ;
2011-02-18 15:03:52 +03:00
bool onmeta ;
2010-10-07 18:56:19 +04:00
bool disable ;
2010-10-07 18:09:52 +04:00
} ;
2007-10-23 14:43:27 +04:00
/*
given a filename , find the parent directory
*/
static char * parent_dir ( TALLOC_CTX * mem_ctx , const char * name )
{
const char * p = strrchr ( name , ' / ' ) ;
if ( p = = NULL ) {
return talloc_strdup ( mem_ctx , " . " ) ;
}
return talloc_strndup ( mem_ctx , name , ( p + 1 ) - name ) ;
}
/*
fsync a directory by name
*/
static void syncops_sync_directory ( const char * dname )
{
# ifdef O_DIRECTORY
int fd = open ( dname , O_DIRECTORY | O_RDONLY ) ;
if ( fd ! = - 1 ) {
fsync ( fd ) ;
close ( fd ) ;
}
# else
DIR * d = opendir ( dname ) ;
if ( d ! = NULL ) {
fsync ( dirfd ( d ) ) ;
closedir ( d ) ;
}
# endif
}
/*
sync two meta data changes for 2 names
*/
static void syncops_two_names ( const char * name1 , const char * name2 )
{
TALLOC_CTX * tmp_ctx = talloc_new ( NULL ) ;
char * parent1 , * parent2 ;
parent1 = parent_dir ( tmp_ctx , name1 ) ;
parent2 = parent_dir ( tmp_ctx , name2 ) ;
if ( ! parent1 | | ! parent2 ) {
talloc_free ( tmp_ctx ) ;
return ;
}
syncops_sync_directory ( parent1 ) ;
if ( strcmp ( parent1 , parent2 ) ! = 0 ) {
syncops_sync_directory ( parent2 ) ;
}
talloc_free ( tmp_ctx ) ;
}
/*
sync two meta data changes for 1 names
*/
static void syncops_name ( const char * name )
{
char * parent ;
parent = parent_dir ( NULL , name ) ;
if ( parent ) {
syncops_sync_directory ( parent ) ;
talloc_free ( parent ) ;
}
}
2009-06-16 23:01:13 +04:00
/*
sync two meta data changes for 1 names
*/
2009-07-02 20:27:44 +04:00
static void syncops_smb_fname ( const struct smb_filename * smb_fname )
2009-06-16 23:01:13 +04:00
{
char * parent ;
parent = parent_dir ( NULL , smb_fname - > base_name ) ;
if ( parent ) {
syncops_sync_directory ( parent ) ;
talloc_free ( parent ) ;
}
}
2007-10-23 14:43:27 +04:00
/*
rename needs special handling , as we may need to fsync two directories
*/
static int syncops_rename ( vfs_handle_struct * handle ,
2009-07-01 04:04:38 +04:00
const struct smb_filename * smb_fname_src ,
const struct smb_filename * smb_fname_dst )
2007-10-23 14:43:27 +04:00
{
2010-10-07 18:56:19 +04:00
int ret ;
struct syncops_config_data * config ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct syncops_config_data ,
return - 1 ) ;
ret = SMB_VFS_NEXT_RENAME ( handle , smb_fname_src , smb_fname_dst ) ;
2011-02-18 15:03:52 +03:00
if ( ret = = 0 & & config - > onmeta & & ! config - > disable ) {
2009-07-01 04:04:38 +04:00
syncops_two_names ( smb_fname_src - > base_name ,
smb_fname_dst - > base_name ) ;
2007-10-23 14:43:27 +04:00
}
return ret ;
}
/* handle the rest with a macro */
# define SYNCOPS_NEXT(op, fname, args) do { \
2010-10-07 18:56:19 +04:00
int ret ; \
struct syncops_config_data * config ; \
SMB_VFS_HANDLE_GET_DATA ( handle , config , \
struct syncops_config_data , \
return - 1 ) ; \
ret = SMB_VFS_NEXT_ # # op args ; \
if ( ret = = 0 \
2011-02-18 15:03:52 +03:00
& & config - > onmeta & & ! config - > disable \
2010-10-07 18:56:19 +04:00
& & fname ) syncops_name ( fname ) ; \
2007-10-23 14:43:27 +04:00
return ret ; \
} while ( 0 )
2009-06-16 23:01:13 +04:00
# define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do { \
2010-10-07 18:56:19 +04:00
int ret ; \
struct syncops_config_data * config ; \
SMB_VFS_HANDLE_GET_DATA ( handle , config , \
struct syncops_config_data , \
return - 1 ) ; \
ret = SMB_VFS_NEXT_ # # op args ; \
if ( ret = = 0 \
2011-02-18 15:03:52 +03:00
& & config - > onmeta & & ! config - > disable \
2010-10-07 18:56:19 +04:00
& & fname ) syncops_smb_fname ( fname ) ; \
2009-06-16 23:01:13 +04:00
return ret ; \
} while ( 0 )
2007-10-23 14:43:27 +04:00
static int syncops_symlink ( vfs_handle_struct * handle ,
const char * oldname , const char * newname )
{
SYNCOPS_NEXT ( SYMLINK , newname , ( handle , oldname , newname ) ) ;
}
static int syncops_link ( vfs_handle_struct * handle ,
const char * oldname , const char * newname )
{
SYNCOPS_NEXT ( LINK , newname , ( handle , oldname , newname ) ) ;
}
static int syncops_open ( vfs_handle_struct * handle ,
2009-06-16 23:01:13 +04:00
struct smb_filename * smb_fname , files_struct * fsp ,
int flags , mode_t mode )
2007-10-23 14:43:27 +04:00
{
2009-06-16 23:01:13 +04:00
SYNCOPS_NEXT_SMB_FNAME ( OPEN , ( flags & O_CREAT ? smb_fname : NULL ) ,
( handle , smb_fname , fsp , flags , mode ) ) ;
2007-10-23 14:43:27 +04:00
}
2009-07-02 20:27:44 +04:00
static int syncops_unlink ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname )
2007-10-23 14:43:27 +04:00
{
2009-07-02 20:27:44 +04:00
SYNCOPS_NEXT_SMB_FNAME ( UNLINK , smb_fname , ( handle , smb_fname ) ) ;
2007-10-23 14:43:27 +04:00
}
static int syncops_mknod ( vfs_handle_struct * handle ,
const char * fname , mode_t mode , SMB_DEV_T dev )
{
SYNCOPS_NEXT ( MKNOD , fname , ( handle , fname , mode , dev ) ) ;
}
static int syncops_mkdir ( vfs_handle_struct * handle , const char * fname , mode_t mode )
{
SYNCOPS_NEXT ( MKDIR , fname , ( handle , fname , mode ) ) ;
}
static int syncops_rmdir ( vfs_handle_struct * handle , const char * fname )
{
SYNCOPS_NEXT ( RMDIR , fname , ( handle , fname ) ) ;
}
/* close needs to be handled specially */
2008-01-11 16:19:28 +03:00
static int syncops_close ( vfs_handle_struct * handle , files_struct * fsp )
2007-10-23 14:43:27 +04:00
{
2010-10-07 18:09:52 +04:00
struct syncops_config_data * config ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct syncops_config_data ,
return - 1 ) ;
if ( fsp - > can_write & & config - > onclose ) {
2007-10-23 14:43:27 +04:00
/* ideally we'd only do this if we have written some
data , but there is no flag for that in fsp yet . */
2008-01-11 16:19:28 +03:00
fsync ( fsp - > fh - > fd ) ;
2007-10-23 14:43:27 +04:00
}
2008-01-11 16:19:28 +03:00
return SMB_VFS_NEXT_CLOSE ( handle , fsp ) ;
2007-10-23 14:43:27 +04:00
}
2011-02-25 01:04:40 +03:00
static int syncops_connect ( struct vfs_handle_struct * handle , const char * service ,
const char * user )
2010-10-07 18:09:52 +04:00
{
struct syncops_config_data * config ;
int ret = SMB_VFS_NEXT_CONNECT ( handle , service , user ) ;
if ( ret < 0 ) {
return ret ;
}
config = talloc_zero ( handle - > conn , struct syncops_config_data ) ;
if ( ! config ) {
SMB_VFS_NEXT_DISCONNECT ( handle ) ;
DEBUG ( 0 , ( " talloc_zero() failed \n " ) ) ;
return - 1 ;
}
config - > onclose = lp_parm_bool ( SNUM ( handle - > conn ) , " syncops " ,
" onclose " , true ) ;
2011-02-18 15:03:52 +03:00
config - > onmeta = lp_parm_bool ( SNUM ( handle - > conn ) , " syncops " ,
" onmeta " , true ) ;
2010-10-07 18:56:19 +04:00
config - > disable = lp_parm_bool ( SNUM ( handle - > conn ) , " syncops " ,
" disable " , false ) ;
2010-10-07 18:09:52 +04:00
SMB_VFS_HANDLE_SET_DATA ( handle , config ,
NULL , struct syncops_config_data ,
return - 1 ) ;
return 0 ;
}
2007-10-23 14:43:27 +04:00
2009-07-24 04:28:58 +04:00
static struct vfs_fn_pointers vfs_syncops_fns = {
2010-10-07 18:09:52 +04:00
. connect_fn = syncops_connect ,
2011-12-04 08:45:04 +04:00
. mkdir_fn = syncops_mkdir ,
. rmdir_fn = syncops_rmdir ,
. open_fn = syncops_open ,
. rename_fn = syncops_rename ,
. unlink_fn = syncops_unlink ,
. symlink_fn = syncops_symlink ,
. link_fn = syncops_link ,
. mknod_fn = syncops_mknod ,
2009-07-24 04:28:58 +04:00
. close_fn = syncops_close ,
2007-10-23 14:43:27 +04:00
} ;
NTSTATUS vfs_syncops_init ( void )
{
NTSTATUS ret ;
2009-07-24 04:28:58 +04:00
ret = smb_register_vfs ( SMB_VFS_INTERFACE_VERSION , " syncops " ,
& vfs_syncops_fns ) ;
2007-10-23 14:43:27 +04:00
if ( ! NT_STATUS_IS_OK ( ret ) )
return ret ;
return ret ;
}