2007-10-23 20:43:27 +10:00
/*
* ensure meta data operations are performed synchronously
*
* Copyright ( C ) Andrew Tridgell 2007
2011-02-18 13:03:52 +01:00
* Copyright ( C ) Christian Ambach , 2010 - 2011
2007-10-23 20:43:27 +10: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-25 23:20:06 +01:00
# include "system/filesys.h"
2011-03-22 22:34:22 +01:00
# include "smbd/smbd.h"
2007-10-23 20:43:27 +10: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 16:09:52 +02:00
You can disable that with
syncops : onclose = no
that can be set either globally or per share .
2011-02-18 13:03:52 +01: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 16:56:19 +02:00
syncops : disable = true
2007-10-23 20:43:27 +10:00
*/
2010-10-07 16:09:52 +02:00
struct syncops_config_data {
bool onclose ;
2011-02-18 13:03:52 +01:00
bool onmeta ;
2010-10-07 16:56:19 +02:00
bool disable ;
2010-10-07 16:09:52 +02:00
} ;
2007-10-23 20:43:27 +10: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
*/
2021-06-17 12:01:48 -07:00
static void syncops_sync_directory ( connection_struct * conn ,
char * dname )
2007-10-23 20:43:27 +10:00
{
2021-06-17 12:10:16 -07:00
struct smb_Dir * dir_hnd = NULL ;
struct files_struct * dirfsp = NULL ;
struct smb_filename smb_dname = { . base_name = dname } ;
2022-02-28 14:13:04 -08:00
NTSTATUS status ;
2022-02-28 14:34:48 -08:00
status = OpenDir ( talloc_tos ( ) ,
conn ,
& smb_dname ,
" * " ,
0 ,
& dir_hnd ) ;
2022-02-28 14:13:04 -08:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
errno = map_errno_from_nt_status ( status ) ;
2021-06-17 12:10:16 -07:00
return ;
2007-10-23 20:43:27 +10:00
}
2021-06-17 12:10:16 -07:00
dirfsp = dir_hnd_fetch_fsp ( dir_hnd ) ;
smb_vfs_fsync_sync ( dirfsp ) ;
TALLOC_FREE ( dir_hnd ) ;
2007-10-23 20:43:27 +10:00
}
/*
sync two meta data changes for 2 names
*/
2021-06-17 11:58:54 -07:00
static void syncops_two_names ( connection_struct * conn ,
const struct smb_filename * name1 ,
const struct smb_filename * name2 )
2007-10-23 20:43:27 +10:00
{
TALLOC_CTX * tmp_ctx = talloc_new ( NULL ) ;
char * parent1 , * parent2 ;
2021-06-17 11:58:54 -07:00
parent1 = parent_dir ( tmp_ctx , name1 - > base_name ) ;
parent2 = parent_dir ( tmp_ctx , name2 - > base_name ) ;
2007-10-23 20:43:27 +10:00
if ( ! parent1 | | ! parent2 ) {
talloc_free ( tmp_ctx ) ;
return ;
}
2021-06-17 12:01:48 -07:00
syncops_sync_directory ( conn , parent1 ) ;
2007-10-23 20:43:27 +10:00
if ( strcmp ( parent1 , parent2 ) ! = 0 ) {
2021-06-17 12:01:48 -07:00
syncops_sync_directory ( conn , parent2 ) ;
2007-10-23 20:43:27 +10:00
}
talloc_free ( tmp_ctx ) ;
}
2009-06-16 12:01:13 -07:00
/*
sync two meta data changes for 1 names
*/
2021-06-17 11:55:50 -07:00
static void syncops_smb_fname ( connection_struct * conn ,
const struct smb_filename * smb_fname )
2009-06-16 12:01:13 -07:00
{
2019-05-22 14:07:44 +00:00
char * parent = NULL ;
if ( smb_fname ! = NULL ) {
parent = parent_dir ( NULL , smb_fname - > base_name ) ;
if ( parent ! = NULL ) {
2021-06-17 12:01:48 -07:00
syncops_sync_directory ( conn , parent ) ;
2019-05-22 14:07:44 +00:00
talloc_free ( parent ) ;
}
2009-06-16 12:01:13 -07:00
}
}
2007-10-23 20:43:27 +10:00
/*
2019-08-09 16:12:25 -07:00
renameat needs special handling , as we may need to fsync two directories
2007-10-23 20:43:27 +10:00
*/
2019-08-09 15:33:08 -07: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 ;
2021-06-17 12:28:30 -07:00
struct smb_filename * full_fname_src = NULL ;
struct smb_filename * full_fname_dst = NULL ;
2019-08-09 15:33:08 -07:00
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 ) ;
2021-06-17 12:16:58 -07:00
if ( ret = = - 1 ) {
return ret ;
2019-08-09 15:33:08 -07:00
}
2021-06-17 12:16:58 -07:00
if ( config - > disable ) {
return ret ;
}
if ( ! config - > onmeta ) {
return ret ;
}
2021-06-17 12:28:30 -07:00
full_fname_src = full_path_from_dirfsp_atname ( talloc_tos ( ) ,
srcfsp ,
smb_fname_src ) ;
if ( full_fname_src = = NULL ) {
errno = ENOMEM ;
return ret ;
}
full_fname_dst = full_path_from_dirfsp_atname ( talloc_tos ( ) ,
dstfsp ,
smb_fname_dst ) ;
if ( full_fname_dst = = NULL ) {
TALLOC_FREE ( full_fname_src ) ;
errno = ENOMEM ;
return ret ;
}
2021-06-17 12:16:58 -07:00
syncops_two_names ( handle - > conn ,
2021-06-17 12:28:30 -07:00
full_fname_src ,
full_fname_dst ) ;
TALLOC_FREE ( full_fname_src ) ;
TALLOC_FREE ( full_fname_dst ) ;
2019-08-09 15:33:08 -07:00
return ret ;
}
2009-06-16 12:01:13 -07:00
# define SYNCOPS_NEXT_SMB_FNAME(op, fname, args) do { \
2010-10-07 16:56:19 +02:00
int ret ; \
2021-01-20 12:11:01 -08:00
struct smb_filename * full_fname = NULL ; \
2010-10-07 16:56:19 +02:00
struct syncops_config_data * config ; \
SMB_VFS_HANDLE_GET_DATA ( handle , config , \
struct syncops_config_data , \
return - 1 ) ; \
2021-06-17 12:23:46 -07:00
ret = SMB_VFS_NEXT_ # # op args ; \
if ( ret ! = 0 ) { \
return ret ; \
} \
if ( config - > disable ) { \
return ret ; \
} \
if ( ! config - > onmeta ) { \
return ret ; \
} \
2021-01-20 12:11:01 -08:00
full_fname = full_path_from_dirfsp_atname ( talloc_tos ( ) , \
dirfsp , \
smb_fname ) ; \
if ( full_fname = = NULL ) { \
2021-06-17 12:23:46 -07:00
return ret ; \
2021-01-20 12:11:01 -08:00
} \
2021-06-17 12:23:46 -07:00
syncops_smb_fname ( dirfsp - > conn , full_fname ) ; \
2021-01-20 12:11:01 -08:00
TALLOC_FREE ( full_fname ) ; \
2009-06-16 12:01:13 -07:00
return ret ; \
} while ( 0 )
2019-08-30 14:00:12 -07:00
static int syncops_symlinkat ( vfs_handle_struct * handle ,
2020-04-30 19:30:50 +02:00
const struct smb_filename * link_contents ,
2019-08-30 14:00:12 -07:00
struct files_struct * dirfsp ,
2021-01-25 12:46:16 -08:00
const struct smb_filename * smb_fname )
2019-08-30 14:00:12 -07:00
{
2021-01-25 12:46:16 -08:00
SYNCOPS_NEXT_SMB_FNAME ( SYMLINKAT ,
smb_fname ,
( handle ,
2019-08-30 14:00:12 -07:00
link_contents ,
dirfsp ,
2021-01-25 12:46:16 -08:00
smb_fname ) ) ;
2019-08-30 14:00:12 -07:00
}
2019-08-16 16:04:02 -07: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-05 14:19:21 -08:00
struct smb_filename * old_full_fname = NULL ;
struct smb_filename * new_full_fname = NULL ;
2019-08-16 16:04:02 -07:00
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct syncops_config_data ,
return - 1 ) ;
ret = SMB_VFS_NEXT_LINKAT ( handle ,
srcfsp ,
old_smb_fname ,
dstfsp ,
new_smb_fname ,
flags ) ;
2021-06-17 12:31:19 -07:00
if ( ret = = - 1 ) {
return ret ;
}
if ( config - > disable ) {
return ret ;
}
if ( ! config - > onmeta ) {
return ret ;
}
old_full_fname = full_path_from_dirfsp_atname ( talloc_tos ( ) ,
srcfsp ,
old_smb_fname ) ;
if ( old_full_fname = = NULL ) {
return ret ;
}
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 ret ;
2019-08-16 16:04:02 -07:00
}
2021-06-17 12:31:19 -07:00
syncops_two_names ( handle - > conn ,
old_full_fname ,
new_full_fname ) ;
2021-02-05 14:19:21 -08:00
TALLOC_FREE ( old_full_fname ) ;
TALLOC_FREE ( new_full_fname ) ;
2019-08-16 16:04:02 -07:00
return ret ;
}
2020-05-20 22:22:26 +02: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 ,
2022-06-03 15:53:29 +02:00
const struct vfs_open_how * how )
2020-05-20 22:22:26 +02:00
{
2022-06-03 15:53:29 +02:00
SYNCOPS_NEXT_SMB_FNAME ( OPENAT , ( how - > flags & O_CREAT ? smb_fname : NULL ) ,
( handle , dirfsp , smb_fname , fsp , how ) ) ;
2020-05-20 22:22:26 +02:00
}
2019-09-12 14:09:27 -07: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 11:06:17 -07: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 10:40:04 -07: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 15:31:37 +01:00
full_fname ,
2019-09-05 10:40:04 -07:00
( handle ,
dirfsp ,
smb_fname ,
mode ) ) ;
}
2007-10-23 20:43:27 +10:00
/* close needs to be handled specially */
2008-01-11 14:19:28 +01:00
static int syncops_close ( vfs_handle_struct * handle , files_struct * fsp )
2007-10-23 20:43:27 +10:00
{
2010-10-07 16:09:52 +02:00
struct syncops_config_data * config ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct syncops_config_data ,
return - 1 ) ;
2020-04-02 17:28:32 +02:00
if ( fsp - > fsp_flags . can_write & & config - > onclose ) {
2007-10-23 20:43:27 +10: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 21:52:52 +02:00
fsync ( fsp_get_io_fd ( fsp ) ) ;
2007-10-23 20:43:27 +10:00
}
2008-01-11 14:19:28 +01:00
return SMB_VFS_NEXT_CLOSE ( handle , fsp ) ;
2007-10-23 20:43:27 +10:00
}
2011-02-24 23:04:40 +01:00
static int syncops_connect ( struct vfs_handle_struct * handle , const char * service ,
const char * user )
2010-10-07 16:09:52 +02: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 13:03:52 +01:00
config - > onmeta = lp_parm_bool ( SNUM ( handle - > conn ) , " syncops " ,
" onmeta " , true ) ;
2010-10-07 16:56:19 +02:00
config - > disable = lp_parm_bool ( SNUM ( handle - > conn ) , " syncops " ,
" disable " , false ) ;
2010-10-07 16:09:52 +02:00
SMB_VFS_HANDLE_SET_DATA ( handle , config ,
NULL , struct syncops_config_data ,
return - 1 ) ;
return 0 ;
}
2007-10-23 20:43:27 +10:00
2009-07-23 20:28:58 -04:00
static struct vfs_fn_pointers vfs_syncops_fns = {
2010-10-07 16:09:52 +02:00
. connect_fn = syncops_connect ,
2019-09-05 10:40:04 -07:00
. mkdirat_fn = syncops_mkdirat ,
2020-05-20 22:22:26 +02:00
. openat_fn = syncops_openat ,
2019-08-09 15:33:08 -07:00
. renameat_fn = syncops_renameat ,
2019-09-12 14:09:27 -07:00
. unlinkat_fn = syncops_unlinkat ,
2019-08-30 14:00:12 -07:00
. symlinkat_fn = syncops_symlinkat ,
2019-08-16 16:04:02 -07:00
. linkat_fn = syncops_linkat ,
2019-08-21 11:06:17 -07:00
. mknodat_fn = syncops_mknodat ,
2009-07-23 20:28:58 -04:00
. close_fn = syncops_close ,
2007-10-23 20:43:27 +10:00
} ;
2015-08-13 18:16:20 +02:00
static_decl_vfs ;
2017-04-20 12:24:43 -07:00
NTSTATUS vfs_syncops_init ( TALLOC_CTX * ctx )
2007-10-23 20:43:27 +10:00
{
NTSTATUS ret ;
2009-07-23 20:28:58 -04:00
ret = smb_register_vfs ( SMB_VFS_INTERFACE_VERSION , " syncops " ,
& vfs_syncops_fns ) ;
2007-10-23 20:43:27 +10:00
if ( ! NT_STATUS_IS_OK ( ret ) )
return ret ;
return ret ;
}