2007-08-10 13:05:18 +04:00
/*
* VFS module to alter the algorithm to calculate
* the struct file_id used as key for the share mode
* and byte range locking db ' s .
*
* Copyright ( C ) 2007 , Stefan Metzmacher
*
* 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 < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
2011-03-23 00:34:22 +03:00
# include "smbd/smbd.h"
2011-02-26 01:20:06 +03:00
# include "system/filesys.h"
2007-08-10 13:05:18 +04:00
static int vfs_fileid_debug_level = DBGC_VFS ;
# undef DBGC_CLASS
# define DBGC_CLASS vfs_fileid_debug_level
struct fileid_mount_entry {
SMB_DEV_T device ;
const char * mnt_fsname ;
fsid_t fsid ;
uint64_t devid ;
} ;
struct fileid_handle_data {
uint64_t ( * device_mapping_fn ) ( struct fileid_handle_data * data ,
2018-01-04 18:35:38 +03:00
const SMB_STRUCT_STAT * sbuf ) ;
2016-01-12 18:00:24 +03:00
char * * fstype_deny_list ;
char * * fstype_allow_list ;
char * * mntdir_deny_list ;
char * * mntdir_allow_list ;
2007-08-10 13:05:18 +04:00
unsigned num_mount_entries ;
struct fileid_mount_entry * mount_entries ;
2018-01-04 19:09:21 +03:00
ino_t nolockinode ;
2007-08-10 13:05:18 +04:00
} ;
2016-01-12 18:00:24 +03:00
/* check if a mount entry is allowed based on fstype and mount directory */
static bool fileid_mount_entry_allowed ( struct fileid_handle_data * data ,
struct mntent * m )
{
int i ;
char * * fstype_deny = data - > fstype_deny_list ;
char * * fstype_allow = data - > fstype_allow_list ;
char * * mntdir_deny = data - > mntdir_deny_list ;
char * * mntdir_allow = data - > mntdir_allow_list ;
if ( fstype_deny ! = NULL ) {
for ( i = 0 ; fstype_deny [ i ] ! = NULL ; i + + ) {
if ( strcmp ( m - > mnt_type , fstype_deny [ i ] ) = = 0 ) {
return false ;
}
}
}
if ( fstype_allow ! = NULL ) {
for ( i = 0 ; fstype_allow [ i ] ! = NULL ; i + + ) {
if ( strcmp ( m - > mnt_type , fstype_allow [ i ] ) = = 0 ) {
break ;
}
}
if ( fstype_allow [ i ] = = NULL ) {
return false ;
}
}
if ( mntdir_deny ! = NULL ) {
for ( i = 0 ; mntdir_deny [ i ] ! = NULL ; i + + ) {
if ( strcmp ( m - > mnt_dir , mntdir_deny [ i ] ) = = 0 ) {
return false ;
}
}
}
if ( mntdir_allow ! = NULL ) {
for ( i = 0 ; mntdir_allow [ i ] ! = NULL ; i + + ) {
if ( strcmp ( m - > mnt_dir , mntdir_allow [ i ] ) = = 0 ) {
break ;
}
}
if ( mntdir_allow [ i ] = = NULL ) {
return false ;
}
}
return true ;
}
2007-08-10 13:05:18 +04:00
/* load all the mount entries from the mtab */
static void fileid_load_mount_entries ( struct fileid_handle_data * data )
{
FILE * f ;
struct mntent * m ;
data - > num_mount_entries = 0 ;
TALLOC_FREE ( data - > mount_entries ) ;
f = setmntent ( " /etc/mtab " , " r " ) ;
if ( ! f ) return ;
while ( ( m = getmntent ( f ) ) ) {
struct stat st ;
struct statfs sfs ;
struct fileid_mount_entry * cur ;
2016-01-12 18:00:24 +03:00
bool allowed ;
2007-08-10 13:05:18 +04:00
2016-01-12 18:00:24 +03:00
allowed = fileid_mount_entry_allowed ( data , m ) ;
if ( ! allowed ) {
DBG_DEBUG ( " skipping mount entry %s \n " , m - > mnt_dir ) ;
continue ;
}
2007-08-10 13:05:18 +04:00
if ( stat ( m - > mnt_dir , & st ) ! = 0 ) continue ;
if ( statfs ( m - > mnt_dir , & sfs ) ! = 0 ) continue ;
if ( strncmp ( m - > mnt_fsname , " /dev/ " , 5 ) = = 0 ) {
m - > mnt_fsname + = 5 ;
}
2011-06-07 05:10:15 +04:00
data - > mount_entries = talloc_realloc ( data ,
2007-08-10 13:05:18 +04:00
data - > mount_entries ,
struct fileid_mount_entry ,
data - > num_mount_entries + 1 ) ;
if ( data - > mount_entries = = NULL ) {
goto nomem ;
}
cur = & data - > mount_entries [ data - > num_mount_entries ] ;
cur - > device = st . st_dev ;
cur - > mnt_fsname = talloc_strdup ( data - > mount_entries ,
m - > mnt_fsname ) ;
if ( ! cur - > mnt_fsname ) goto nomem ;
cur - > fsid = sfs . f_fsid ;
cur - > devid = ( uint64_t ) - 1 ;
data - > num_mount_entries + + ;
}
endmntent ( f ) ;
return ;
nomem :
if ( f ) endmntent ( f ) ;
data - > num_mount_entries = 0 ;
TALLOC_FREE ( data - > mount_entries ) ;
return ;
}
/* find a mount entry given a dev_t */
static struct fileid_mount_entry * fileid_find_mount_entry ( struct fileid_handle_data * data ,
SMB_DEV_T dev )
{
2016-05-19 15:55:10 +03:00
unsigned i ;
2007-08-10 13:05:18 +04:00
if ( data - > num_mount_entries = = 0 ) {
fileid_load_mount_entries ( data ) ;
}
for ( i = 0 ; i < data - > num_mount_entries ; i + + ) {
if ( data - > mount_entries [ i ] . device = = dev ) {
return & data - > mount_entries [ i ] ;
}
}
/* 2nd pass after reloading */
fileid_load_mount_entries ( data ) ;
for ( i = 0 ; i < data - > num_mount_entries ; i + + ) {
if ( data - > mount_entries [ i ] . device = = dev ) {
return & data - > mount_entries [ i ] ;
}
}
return NULL ;
}
/* a 64 bit hash, based on the one in tdb */
static uint64_t fileid_uint64_hash ( const uint8_t * s , size_t len )
{
uint64_t value ; /* Used to compute the hash value. */
uint32_t i ; /* Used to cycle through random values. */
/* Set the initial value from the key size. */
for ( value = 0x238F13AFLL * len , i = 0 ; i < len ; i + + )
2017-08-25 15:11:02 +03:00
value = ( value + ( ( ( uint64_t ) s [ i ] ) < < ( i * 5 % 24 ) ) ) ;
2007-08-10 13:05:18 +04:00
return ( 1103515243LL * value + 12345LL ) ;
}
/* a device mapping using a fsname */
static uint64_t fileid_device_mapping_fsname ( struct fileid_handle_data * data ,
2018-01-04 18:35:38 +03:00
const SMB_STRUCT_STAT * sbuf )
2007-08-10 13:05:18 +04:00
{
struct fileid_mount_entry * m ;
2018-01-04 18:35:38 +03:00
m = fileid_find_mount_entry ( data , sbuf - > st_ex_dev ) ;
if ( ! m ) return sbuf - > st_ex_dev ;
2007-08-10 13:05:18 +04:00
if ( m - > devid = = ( uint64_t ) - 1 ) {
2011-05-06 03:19:49 +04:00
m - > devid = fileid_uint64_hash ( ( const uint8_t * ) m - > mnt_fsname ,
2007-08-10 13:05:18 +04:00
strlen ( m - > mnt_fsname ) ) ;
}
return m - > devid ;
}
2018-01-04 18:59:54 +03:00
/* a device mapping using a hostname */
static uint64_t fileid_device_mapping_hostname ( struct fileid_handle_data * data ,
const SMB_STRUCT_STAT * sbuf )
{
char hostname [ HOST_NAME_MAX + 1 ] ;
char * devname = NULL ;
uint64_t id ;
size_t devname_len ;
int rc ;
rc = gethostname ( hostname , HOST_NAME_MAX + 1 ) ;
if ( rc ! = 0 ) {
DBG_ERR ( " gethostname failed \n " ) ;
return UINT64_MAX ;
}
2018-01-19 15:15:58 +03:00
devname = talloc_asprintf ( talloc_tos ( ) , " %s%ju " ,
hostname , ( uintmax_t ) sbuf - > st_ex_dev ) ;
2018-01-04 18:59:54 +03:00
if ( devname = = NULL ) {
DBG_ERR ( " talloc_asprintf failed \n " ) ;
return UINT64_MAX ;
}
devname_len = talloc_array_length ( devname ) - 1 ;
id = fileid_uint64_hash ( ( uint8_t * ) devname , devname_len ) ;
2018-01-06 18:13:52 +03:00
TALLOC_FREE ( devname ) ;
2018-01-04 18:59:54 +03:00
return id ;
}
2018-01-04 19:02:53 +03:00
/* a device mapping using a fsname for files and hostname for dirs */
static uint64_t fileid_device_mapping_fsname_nodirs (
struct fileid_handle_data * data ,
const SMB_STRUCT_STAT * sbuf )
{
if ( S_ISDIR ( sbuf - > st_ex_mode ) ) {
return fileid_device_mapping_hostname ( data , sbuf ) ;
}
return fileid_device_mapping_fsname ( data , sbuf ) ;
}
2007-08-10 13:05:18 +04:00
/* device mapping functions using a fsid */
static uint64_t fileid_device_mapping_fsid ( struct fileid_handle_data * data ,
2018-01-04 18:35:38 +03:00
const SMB_STRUCT_STAT * sbuf )
2007-08-10 13:05:18 +04:00
{
struct fileid_mount_entry * m ;
2018-01-04 18:35:38 +03:00
m = fileid_find_mount_entry ( data , sbuf - > st_ex_dev ) ;
if ( ! m ) return sbuf - > st_ex_dev ;
2007-08-10 13:05:18 +04:00
if ( m - > devid = = ( uint64_t ) - 1 ) {
if ( sizeof ( fsid_t ) > sizeof ( uint64_t ) ) {
m - > devid = fileid_uint64_hash ( ( uint8_t * ) & m - > fsid ,
sizeof ( m - > fsid ) ) ;
} else {
union {
uint64_t ret ;
fsid_t fsid ;
} u ;
ZERO_STRUCT ( u ) ;
u . fsid = m - > fsid ;
m - > devid = u . ret ;
}
}
return m - > devid ;
}
2018-01-04 19:22:16 +03:00
static int get_connectpath_ino ( struct vfs_handle_struct * handle ,
ino_t * ino )
{
struct smb_filename * fname = NULL ;
int ret ;
fname = synthetic_smb_fname ( talloc_tos ( ) ,
handle - > conn - > connectpath ,
NULL ,
NULL ,
0 ) ;
if ( fname = = NULL ) {
DBG_ERR ( " synthetic_smb_fname failed \n " ) ;
return - 1 ;
}
ret = SMB_VFS_NEXT_STAT ( handle , fname ) ;
TALLOC_FREE ( fname ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " stat failed for %s with %s \n " ,
handle - > conn - > connectpath , strerror ( errno ) ) ;
return - 1 ;
}
return 0 ;
}
2007-08-10 13:05:18 +04:00
static int fileid_connect ( struct vfs_handle_struct * handle ,
const char * service , const char * user )
{
struct fileid_handle_data * data ;
const char * algorithm ;
2016-01-12 18:00:24 +03:00
const char * * fstype_deny_list = NULL ;
const char * * fstype_allow_list = NULL ;
const char * * mntdir_deny_list = NULL ;
const char * * mntdir_allow_list = NULL ;
2018-01-05 12:23:30 +03:00
int saved_errno ;
2009-12-01 02:53:04 +03:00
int ret = SMB_VFS_NEXT_CONNECT ( handle , service , user ) ;
if ( ret < 0 ) {
return ret ;
}
2007-08-10 13:05:18 +04:00
2008-04-28 12:31:49 +04:00
data = talloc_zero ( handle - > conn , struct fileid_handle_data ) ;
2007-08-10 13:05:18 +04:00
if ( ! data ) {
2018-01-05 12:23:30 +03:00
saved_errno = errno ;
2009-12-01 02:53:04 +03:00
SMB_VFS_NEXT_DISCONNECT ( handle ) ;
2007-08-10 13:05:18 +04:00
DEBUG ( 0 , ( " talloc_zero() failed \n " ) ) ;
2018-01-05 12:23:30 +03:00
errno = saved_errno ;
2007-08-10 13:05:18 +04:00
return - 1 ;
}
2018-01-04 19:22:16 +03:00
data - > nolockinode = 0 ;
2009-01-14 15:59:09 +03:00
/*
* " fileid:mapping " is only here as fallback for old setups
* " fileid:algorithm " is the option new setups should use
*/
2007-08-10 13:05:18 +04:00
algorithm = lp_parm_const_string ( SNUM ( handle - > conn ) ,
2008-04-21 20:40:45 +04:00
" fileid " , " mapping " ,
2007-08-10 13:05:18 +04:00
" fsname " ) ;
2009-01-14 15:59:09 +03:00
algorithm = lp_parm_const_string ( SNUM ( handle - > conn ) ,
" fileid " , " algorithm " ,
algorithm ) ;
2007-08-10 13:05:18 +04:00
if ( strcmp ( " fsname " , algorithm ) = = 0 ) {
data - > device_mapping_fn = fileid_device_mapping_fsname ;
2018-01-04 19:02:53 +03:00
} else if ( strcmp ( " fsname_nodirs " , algorithm ) = = 0 ) {
data - > device_mapping_fn = fileid_device_mapping_fsname_nodirs ;
2007-08-10 13:05:18 +04:00
} else if ( strcmp ( " fsid " , algorithm ) = = 0 ) {
data - > device_mapping_fn = fileid_device_mapping_fsid ;
2018-01-04 18:59:54 +03:00
} else if ( strcmp ( " hostname " , algorithm ) = = 0 ) {
data - > device_mapping_fn = fileid_device_mapping_hostname ;
2018-01-04 19:22:16 +03:00
} else if ( strcmp ( " fsname_norootdir " , algorithm ) = = 0 ) {
data - > device_mapping_fn = fileid_device_mapping_fsname ;
ret = get_connectpath_ino ( handle , & data - > nolockinode ) ;
if ( ret ! = 0 ) {
saved_errno = errno ;
SMB_VFS_NEXT_DISCONNECT ( handle ) ;
errno = saved_errno ;
return - 1 ;
}
2007-08-10 13:05:18 +04:00
} else {
2009-12-01 02:53:04 +03:00
SMB_VFS_NEXT_DISCONNECT ( handle ) ;
2007-08-10 13:05:18 +04:00
DEBUG ( 0 , ( " fileid_connect(): unknown algorithm[%s] \n " , algorithm ) ) ;
return - 1 ;
}
2016-01-12 18:00:24 +03:00
fstype_deny_list = lp_parm_string_list ( SNUM ( handle - > conn ) , " fileid " ,
" fstype deny " , NULL ) ;
if ( fstype_deny_list ! = NULL ) {
data - > fstype_deny_list = str_list_copy ( data , fstype_deny_list ) ;
if ( data - > fstype_deny_list = = NULL ) {
saved_errno = errno ;
DBG_ERR ( " str_list_copy failed \n " ) ;
SMB_VFS_NEXT_DISCONNECT ( handle ) ;
errno = saved_errno ;
return - 1 ;
}
}
fstype_allow_list = lp_parm_string_list ( SNUM ( handle - > conn ) , " fileid " ,
" fstype allow " , NULL ) ;
if ( fstype_allow_list ! = NULL ) {
data - > fstype_allow_list = str_list_copy ( data , fstype_allow_list ) ;
if ( data - > fstype_allow_list = = NULL ) {
saved_errno = errno ;
DBG_ERR ( " str_list_copy failed \n " ) ;
SMB_VFS_NEXT_DISCONNECT ( handle ) ;
errno = saved_errno ;
return - 1 ;
}
}
mntdir_deny_list = lp_parm_string_list ( SNUM ( handle - > conn ) , " fileid " ,
" mntdir deny " , NULL ) ;
if ( mntdir_deny_list ! = NULL ) {
data - > mntdir_deny_list = str_list_copy ( data , mntdir_deny_list ) ;
if ( data - > mntdir_deny_list = = NULL ) {
saved_errno = errno ;
DBG_ERR ( " str_list_copy failed \n " ) ;
SMB_VFS_NEXT_DISCONNECT ( handle ) ;
errno = saved_errno ;
return - 1 ;
}
}
mntdir_allow_list = lp_parm_string_list ( SNUM ( handle - > conn ) , " fileid " ,
" mntdir allow " , NULL ) ;
if ( mntdir_allow_list ! = NULL ) {
data - > mntdir_allow_list = str_list_copy ( data , mntdir_allow_list ) ;
if ( data - > mntdir_allow_list = = NULL ) {
saved_errno = errno ;
DBG_ERR ( " str_list_copy failed \n " ) ;
SMB_VFS_NEXT_DISCONNECT ( handle ) ;
errno = saved_errno ;
return - 1 ;
}
}
2018-01-04 19:09:21 +03:00
data - > nolockinode = lp_parm_ulong ( SNUM ( handle - > conn ) , " fileid " ,
2018-01-04 19:22:16 +03:00
" nolockinode " , data - > nolockinode ) ;
2018-01-04 19:09:21 +03:00
2007-08-10 13:05:18 +04:00
SMB_VFS_HANDLE_SET_DATA ( handle , data , NULL ,
struct fileid_handle_data ,
return - 1 ) ;
2018-01-04 19:22:16 +03:00
DBG_DEBUG ( " connect to service[%s] with algorithm[%s] nolockinode %lli \n " ,
service , algorithm , ( long long ) data - > nolockinode ) ;
2007-08-10 13:05:18 +04:00
2009-12-01 02:53:04 +03:00
return 0 ;
2007-08-10 13:05:18 +04:00
}
static void fileid_disconnect ( struct vfs_handle_struct * handle )
{
DEBUG ( 10 , ( " fileid_disconnect() connect to service[%s]. \n " ,
2012-07-18 09:37:23 +04:00
lp_servicename ( talloc_tos ( ) , SNUM ( handle - > conn ) ) ) ) ;
2007-08-10 13:05:18 +04:00
SMB_VFS_NEXT_DISCONNECT ( handle ) ;
}
static struct file_id fileid_file_id_create ( struct vfs_handle_struct * handle ,
2009-02-16 10:38:53 +03:00
const SMB_STRUCT_STAT * sbuf )
2007-08-10 13:05:18 +04:00
{
struct fileid_handle_data * data ;
struct file_id id ;
2018-01-04 19:09:21 +03:00
uint64_t devid ;
2007-08-10 13:05:18 +04:00
ZERO_STRUCT ( id ) ;
SMB_VFS_HANDLE_GET_DATA ( handle , data ,
struct fileid_handle_data ,
return id ) ;
2018-01-04 19:09:21 +03:00
if ( ( data - > nolockinode ! = 0 ) & & ( id . inode = = data - > nolockinode ) ) {
devid = fileid_device_mapping_hostname ( data , sbuf ) ;
} else {
devid = data - > device_mapping_fn ( data , sbuf ) ;
}
2009-05-14 17:34:42 +04:00
id . inode = sbuf - > st_ex_ino ;
2018-01-04 19:09:21 +03:00
id . devid = devid ;
2007-08-10 13:05:18 +04:00
2018-01-04 19:25:07 +03:00
DBG_DEBUG ( " Returning dev [%jx] inode [%jx] \n " ,
( uintmax_t ) id . devid , ( uintmax_t ) id . inode ) ;
2007-08-10 13:05:18 +04:00
return id ;
}
2009-07-24 04:28:58 +04:00
static struct vfs_fn_pointers vfs_fileid_fns = {
. connect_fn = fileid_connect ,
2011-12-04 08:45:04 +04:00
. disconnect_fn = fileid_disconnect ,
. file_id_create_fn = fileid_file_id_create
2007-08-10 13:05:18 +04:00
} ;
2017-12-16 01:32:12 +03:00
static_decl_vfs ;
2017-04-20 22:24:43 +03:00
NTSTATUS vfs_fileid_init ( TALLOC_CTX * ctx )
2007-08-10 13:05:18 +04:00
{
NTSTATUS ret ;
2009-07-24 04:28:58 +04:00
ret = smb_register_vfs ( SMB_VFS_INTERFACE_VERSION , " fileid " ,
& vfs_fileid_fns ) ;
2007-08-10 13:05:18 +04:00
if ( ! NT_STATUS_IS_OK ( ret ) ) {
return ret ;
}
vfs_fileid_debug_level = debug_add_class ( " fileid " ) ;
if ( vfs_fileid_debug_level = = - 1 ) {
vfs_fileid_debug_level = DBGC_VFS ;
DEBUG ( 0 , ( " vfs_fileid: Couldn't register custom debugging class! \n " ) ) ;
} else {
DEBUG ( 10 , ( " vfs_fileid: Debug class number of 'fileid': %d \n " , vfs_fileid_debug_level ) ) ;
}
return ret ;
}