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 ) ;
}
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
{
2019-05-22 17:07:44 +03:00
char * parent = NULL ;
if ( smb_fname ! = NULL ) {
parent = parent_dir ( NULL , smb_fname - > base_name ) ;
if ( parent ! = NULL ) {
syncops_sync_directory ( parent ) ;
talloc_free ( parent ) ;
}
2009-06-16 23:01:13 +04:00
}
}
2007-10-23 14:43:27 +04:00
/*
2019-08-10 02:12:25 +03:00
renameat needs special handling , as we may need to fsync two directories
2007-10-23 14:43:27 +04:00
*/
2019-08-10 01:33:08 +03:00
static int syncops_renameat ( vfs_handle_struct * handle ,
files_struct * srcfsp ,
const struct smb_filename * smb_fname_src ,
files_struct * dstfsp ,
const struct smb_filename * smb_fname_dst )
{
int ret ;
struct syncops_config_data * config ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct syncops_config_data ,
return - 1 ) ;
ret = SMB_VFS_NEXT_RENAMEAT ( handle ,
srcfsp ,
smb_fname_src ,
dstfsp ,
smb_fname_dst ) ;
if ( ret = = 0 & & config - > onmeta & & ! config - > disable ) {
syncops_two_names ( smb_fname_src - > base_name ,
smb_fname_dst - > base_name ) ;
}
return ret ;
}
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 ; \
2021-01-20 23:11:01 +03:00
struct smb_filename * full_fname = NULL ; \
2010-10-07 18:56:19 +04:00
struct syncops_config_data * config ; \
SMB_VFS_HANDLE_GET_DATA ( handle , config , \
struct syncops_config_data , \
return - 1 ) ; \
2021-01-20 23:11:01 +03:00
full_fname = full_path_from_dirfsp_atname ( talloc_tos ( ) , \
dirfsp , \
smb_fname ) ; \
if ( full_fname = = NULL ) { \
return - 1 ; \
} \
2010-10-07 18:56:19 +04:00
ret = SMB_VFS_NEXT_ # # op args ; \
if ( ret = = 0 \
2011-02-18 15:03:52 +03:00
& & config - > onmeta & & ! config - > disable \
2021-01-20 23:11:01 +03:00
& & fname ) syncops_smb_fname ( full_fname ) ; \
TALLOC_FREE ( full_fname ) ; \
2009-06-16 23:01:13 +04:00
return ret ; \
} while ( 0 )
2019-08-31 00:00:12 +03:00
static int syncops_symlinkat ( vfs_handle_struct * handle ,
2020-04-30 20:30:50 +03:00
const struct smb_filename * link_contents ,
2019-08-31 00:00:12 +03:00
struct files_struct * dirfsp ,
2021-01-25 23:46:16 +03:00
const struct smb_filename * smb_fname )
2019-08-31 00:00:12 +03:00
{
2021-01-25 23:46:16 +03:00
SYNCOPS_NEXT_SMB_FNAME ( SYMLINKAT ,
smb_fname ,
( handle ,
2019-08-31 00:00:12 +03:00
link_contents ,
dirfsp ,
2021-01-25 23:46:16 +03:00
smb_fname ) ) ;
2019-08-31 00:00:12 +03:00
}
2019-08-17 02:04:02 +03:00
static int syncops_linkat ( vfs_handle_struct * handle ,
files_struct * srcfsp ,
const struct smb_filename * old_smb_fname ,
files_struct * dstfsp ,
const struct smb_filename * new_smb_fname ,
int flags )
{
int ret ;
struct syncops_config_data * config ;
2021-02-06 01:19:21 +03:00
struct smb_filename * old_full_fname = NULL ;
struct smb_filename * new_full_fname = NULL ;
2019-08-17 02:04:02 +03:00
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct syncops_config_data ,
return - 1 ) ;
2021-02-06 01:19:21 +03:00
old_full_fname = full_path_from_dirfsp_atname ( talloc_tos ( ) ,
srcfsp ,
old_smb_fname ) ;
if ( old_full_fname = = NULL ) {
return - 1 ;
}
new_full_fname = full_path_from_dirfsp_atname ( talloc_tos ( ) ,
dstfsp ,
new_smb_fname ) ;
if ( new_full_fname = = NULL ) {
TALLOC_FREE ( old_full_fname ) ;
return - 1 ;
}
2019-08-17 02:04:02 +03:00
ret = SMB_VFS_NEXT_LINKAT ( handle ,
srcfsp ,
old_smb_fname ,
dstfsp ,
new_smb_fname ,
flags ) ;
if ( ret = = 0 & & config - > onmeta & & ! config - > disable ) {
2021-02-06 01:19:21 +03:00
syncops_two_names ( old_full_fname - > base_name ,
new_full_fname - > base_name ) ;
2019-08-17 02:04:02 +03:00
}
2021-02-06 01:19:21 +03:00
TALLOC_FREE ( old_full_fname ) ;
TALLOC_FREE ( new_full_fname ) ;
2019-08-17 02:04:02 +03:00
return ret ;
}
2020-05-20 23:22:26 +03:00
static int syncops_openat ( struct vfs_handle_struct * handle ,
const struct files_struct * dirfsp ,
const struct smb_filename * smb_fname ,
struct files_struct * fsp ,
int flags ,
mode_t mode )
{
SYNCOPS_NEXT_SMB_FNAME ( OPENAT , ( flags & O_CREAT ? smb_fname : NULL ) ,
( handle , dirfsp , smb_fname , fsp , flags , mode ) ) ;
}
2019-09-13 00:09:27 +03:00
static int syncops_unlinkat ( vfs_handle_struct * handle ,
files_struct * dirfsp ,
const struct smb_filename * smb_fname ,
int flags )
{
SYNCOPS_NEXT_SMB_FNAME ( UNLINKAT ,
smb_fname ,
( handle ,
dirfsp ,
smb_fname ,
flags ) ) ;
}
2019-08-21 21:06:17 +03:00
static int syncops_mknodat ( vfs_handle_struct * handle ,
files_struct * dirfsp ,
const struct smb_filename * smb_fname ,
mode_t mode ,
SMB_DEV_T dev )
{
SYNCOPS_NEXT_SMB_FNAME ( MKNODAT ,
smb_fname ,
( handle ,
dirfsp ,
smb_fname ,
mode ,
dev ) ) ;
}
2019-09-05 20:40:04 +03:00
static int syncops_mkdirat ( vfs_handle_struct * handle ,
struct files_struct * dirfsp ,
const struct smb_filename * smb_fname ,
mode_t mode )
{
SYNCOPS_NEXT_SMB_FNAME ( MKDIRAT ,
2020-12-14 17:31:37 +03:00
full_fname ,
2019-09-05 20:40:04 +03:00
( handle ,
dirfsp ,
smb_fname ,
mode ) ) ;
}
2007-10-23 14:43:27 +04:00
/* 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 ) ;
2020-04-02 18:28:32 +03:00
if ( fsp - > fsp_flags . 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 . */
2020-09-26 22:52:52 +03:00
fsync ( fsp_get_io_fd ( fsp ) ) ;
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 ,
2019-09-05 20:40:04 +03:00
. mkdirat_fn = syncops_mkdirat ,
2020-05-20 23:22:26 +03:00
. openat_fn = syncops_openat ,
2019-08-10 01:33:08 +03:00
. renameat_fn = syncops_renameat ,
2019-09-13 00:09:27 +03:00
. unlinkat_fn = syncops_unlinkat ,
2019-08-31 00:00:12 +03:00
. symlinkat_fn = syncops_symlinkat ,
2019-08-17 02:04:02 +03:00
. linkat_fn = syncops_linkat ,
2019-08-21 21:06:17 +03:00
. mknodat_fn = syncops_mknodat ,
2009-07-24 04:28:58 +04:00
. close_fn = syncops_close ,
2007-10-23 14:43:27 +04:00
} ;
2015-08-13 19:16:20 +03:00
static_decl_vfs ;
2017-04-20 22:24:43 +03:00
NTSTATUS vfs_syncops_init ( TALLOC_CTX * ctx )
2007-10-23 14:43:27 +04:00
{
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 ;
}