2008-01-16 12:26:35 +03:00
/*
Unix SMB / CIFS implementation .
Samba VFS module for handling offline files
with Tivoli Storage Manager Space Management
2008-01-18 17:34:21 +03:00
( c ) Alexander Bokovoy , 2007 , 2008
( c ) Andrew Tridgell , 2007 , 2008
2008-01-16 12:26:35 +03: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 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/>.
*/
/*
This VFS module accepts following options :
2008-01-18 17:34:21 +03:00
tsmsm : hsm script = < path to hsm script > ( default does nothing )
2008-01-16 12:26:35 +03:00
hsm script should point to a shell script which accepts two arguments :
< operation > < filepath >
where < operation > is currently ' offline ' to set offline status of the < filepath >
tsmsm : online ratio = ratio to check reported size against actual file size ( 0.5 by default )
2008-01-18 17:34:21 +03:00
tsmsm : attribute name = name of DMAPI attribute that is present when a file is offline .
Default is " IBMobj " ( which is what GPFS uses )
2008-01-16 12:26:35 +03:00
The TSMSM VFS module tries to avoid calling expensive DMAPI calls with some heuristics
based on the fact that number of blocks reported of a file multiplied by 512 will be
bigger than ' online ratio ' of actual size for online ( non - migrated ) files .
2008-01-18 17:34:21 +03:00
If checks fail , we call DMAPI and ask for specific attribute which present for
2008-01-16 12:26:35 +03:00
offline ( migrated ) files . If this attribute presents , we consider file offline .
*/
# include "includes.h"
# ifndef USE_DMAPI
# error "This module requires DMAPI support!"
# endif
# ifdef HAVE_XFS_DMAPI_H
# include <xfs/dmapi.h>
# elif defined(HAVE_SYS_DMI_H)
# include <sys/dmi.h>
# elif defined(HAVE_SYS_JFSDMAPI_H)
# include <sys/jfsdmapi.h>
# elif defined(HAVE_SYS_DMAPI_H)
# include <sys/dmapi.h>
# elif defined(HAVE_DMAPI_H)
# include <dmapi.h>
# endif
# ifndef _ISOC99_SOURCE
# define _ISOC99_SOURCE
# endif
# include <math.h>
/* optimisation tunables - used to avoid the DMAPI slow path */
# define FILE_IS_ONLINE_RATIO 0.5
2008-01-18 17:34:21 +03:00
/* default attribute name to look for */
2008-01-16 12:26:35 +03:00
# define DM_ATTRIB_OBJECT "IBMObj"
struct tsmsm_struct {
float online_ratio ;
char * hsmscript ;
2008-01-18 17:34:21 +03:00
const char * attrib_name ;
2008-04-08 15:17:53 +04:00
const char * attrib_value ;
2008-01-16 12:26:35 +03:00
} ;
static void tsmsm_free_data ( void * * pptr ) {
struct tsmsm_struct * * tsmd = ( struct tsmsm_struct * * ) pptr ;
if ( ! tsmd ) return ;
TALLOC_FREE ( * tsmd ) ;
}
2008-01-18 17:34:21 +03:00
/*
called when a client connects to a share
*/
2008-01-16 12:26:35 +03:00
static int tsmsm_connect ( struct vfs_handle_struct * handle ,
const char * service ,
const char * user ) {
struct tsmsm_struct * tsmd = TALLOC_ZERO_P ( handle , struct tsmsm_struct ) ;
const char * fres ;
2008-01-18 17:34:21 +03:00
const char * tsmname ;
2008-01-16 12:26:35 +03:00
if ( ! tsmd ) {
DEBUG ( 0 , ( " tsmsm_connect: out of memory! \n " ) ) ;
return - 1 ;
}
2008-01-18 17:34:21 +03:00
if ( ! dmapi_have_session ( ) ) {
2008-01-16 12:26:35 +03:00
DEBUG ( 0 , ( " tsmsm_connect: no DMAPI session for Samba is available! \n " ) ) ;
TALLOC_FREE ( tsmd ) ;
return - 1 ;
}
tsmname = ( handle - > param ? handle - > param : " tsmsm " ) ;
2008-01-18 17:34:21 +03:00
/* Get 'hsm script' and 'dmapi attribute' parameters to tsmd context */
tsmd - > hsmscript = lp_parm_talloc_string ( SNUM ( handle - > conn ) , tsmname ,
" hsm script " , NULL ) ;
talloc_steal ( tsmd , tsmd - > hsmscript ) ;
tsmd - > attrib_name = lp_parm_talloc_string ( SNUM ( handle - > conn ) , tsmname ,
" dmapi attribute " , DM_ATTRIB_OBJECT ) ;
talloc_steal ( tsmd , tsmd - > attrib_name ) ;
2008-04-08 15:17:53 +04:00
tsmd - > attrib_value = lp_parm_talloc_string ( SNUM ( handle - > conn ) , " tsmsm " ,
" dmapi value " , NULL ) ;
talloc_steal ( tsmd , tsmd - > attrib_value ) ;
2008-01-18 17:34:21 +03:00
/* retrieve 'online ratio'. In case of error default to FILE_IS_ONLINE_RATIO */
2008-01-16 12:26:35 +03:00
fres = lp_parm_const_string ( SNUM ( handle - > conn ) , tsmname ,
2008-01-18 17:34:21 +03:00
" online ratio " , NULL ) ;
if ( fres = = NULL ) {
2008-01-16 12:26:35 +03:00
tsmd - > online_ratio = FILE_IS_ONLINE_RATIO ;
2008-01-18 17:34:21 +03:00
} else {
tsmd - > online_ratio = strtof ( fres , NULL ) ;
2008-01-29 18:33:22 +03:00
if ( tsmd - > online_ratio > 1.0 | |
tsmd - > online_ratio < = 0.0 ) {
2008-01-18 17:34:21 +03:00
DEBUG ( 1 , ( " tsmsm_connect: invalid online ration %f - using %f. \n " ,
2008-01-29 18:33:22 +03:00
tsmd - > online_ratio , ( float ) FILE_IS_ONLINE_RATIO ) ) ;
2008-01-18 17:34:21 +03:00
}
2008-01-16 12:26:35 +03:00
}
/* Store the private data. */
SMB_VFS_HANDLE_SET_DATA ( handle , tsmd , tsmsm_free_data ,
struct tsmsm_struct , return - 1 ) ;
return SMB_VFS_NEXT_CONNECT ( handle , service , user ) ;
}
2008-01-17 14:57:35 +03:00
static bool tsmsm_is_offline ( struct vfs_handle_struct * handle ,
2008-01-16 12:26:35 +03:00
const char * path ,
2008-01-17 14:57:35 +03:00
SMB_STRUCT_STAT * stbuf ) {
2008-01-16 12:26:35 +03:00
struct tsmsm_struct * tsmd = ( struct tsmsm_struct * ) handle - > data ;
2008-01-18 17:34:21 +03:00
const dm_sessid_t * dmsession_id ;
2008-01-16 12:26:35 +03:00
void * dmhandle = NULL ;
size_t dmhandle_len = 0 ;
size_t rlen ;
dm_attrname_t dmname ;
2008-01-29 17:43:49 +03:00
int ret , lerrno ;
2008-01-17 14:57:35 +03:00
bool offline ;
2008-04-08 15:17:53 +04:00
char * buf ;
2008-04-08 18:43:30 +04:00
size_t buflen ;
2008-01-16 12:26:35 +03:00
/* if the file has more than FILE_IS_ONLINE_RATIO of blocks available,
then assume it is not offline ( it may not be 100 % , as it could be sparse ) */
if ( 512 * ( off_t ) stbuf - > st_blocks > = stbuf - > st_size * tsmd - > online_ratio ) {
2008-03-20 14:49:13 +03:00
DEBUG ( 10 , ( " %s not offline: st_blocks=%ld st_size=%ld "
" online_ratio=%.2f \n " , path , ( long ) stbuf - > st_blocks ,
( long ) stbuf - > st_size , tsmd - > online_ratio ) ) ;
2008-01-17 14:57:35 +03:00
return false ;
2008-01-16 12:26:35 +03:00
}
2008-01-18 17:34:21 +03:00
dmsession_id = dmapi_get_current_session ( ) ;
if ( dmsession_id = = NULL ) {
DEBUG ( 2 , ( " tsmsm_is_offline: no DMAPI session available? "
" Assume file is online. \n " ) ) ;
return false ;
}
2008-01-16 12:26:35 +03:00
/* using POSIX capabilities does not work here. It's a slow path, so
* become_root ( ) is just as good anyway ( tridge )
*/
/* Also, AIX has DMAPI but no POSIX capablities support. In this case,
* we need to be root to do DMAPI manipulations .
*/
become_root ( ) ;
/* go the slow DMAPI route */
if ( dm_path_to_handle ( ( char * ) path , & dmhandle , & dmhandle_len ) ! = 0 ) {
DEBUG ( 2 , ( " dm_path_to_handle failed - assuming offline (%s) - %s \n " ,
path , strerror ( errno ) ) ) ;
2008-01-17 14:57:35 +03:00
offline = true ;
2008-01-16 12:26:35 +03:00
goto done ;
}
memset ( & dmname , 0 , sizeof ( dmname ) ) ;
2008-01-18 17:34:21 +03:00
strlcpy ( ( char * ) & dmname . an_chars [ 0 ] , tsmd - > attrib_name , sizeof ( dmname . an_chars ) ) ;
2008-01-16 12:26:35 +03:00
2008-04-08 15:17:53 +04:00
if ( tsmd - > attrib_value ! = NULL ) {
buflen = strlen ( tsmd - > attrib_value ) ;
} else {
buflen = 1 ;
}
buf = talloc_zero_size ( tsmd , buflen ) ;
if ( buf = = NULL ) {
DEBUG ( 0 , ( " out of memory in tsmsm_is_offline -- assuming online (%s) \n " , path ) ) ;
errno = ENOMEM ;
offline = false ;
goto done ;
}
2008-01-29 17:43:49 +03:00
lerrno = 0 ;
do {
ret = dm_get_dmattr ( * dmsession_id , dmhandle , dmhandle_len ,
2008-04-08 15:17:53 +04:00
DM_NO_TOKEN , & dmname , buflen , buf , & rlen ) ;
2008-01-29 17:43:49 +03:00
if ( ret = = - 1 & & errno = = EINVAL ) {
DEBUG ( 0 , ( " Stale DMAPI session, re-creating it. \n " ) ) ;
lerrno = EINVAL ;
if ( dmapi_new_session ( ) ) {
2008-01-29 18:33:22 +03:00
dmsession_id = dmapi_get_current_session ( ) ;
2008-01-29 17:43:49 +03:00
} else {
DEBUG ( 0 ,
( " Unable to re-create DMAPI session, assuming offline (%s) - %s \n " ,
path , strerror ( errno ) ) ) ;
offline = true ;
dm_handle_free ( dmhandle , dmhandle_len ) ;
goto done ;
}
}
} while ( ret = = - 1 & & lerrno = = EINVAL ) ;
2008-01-16 12:26:35 +03:00
2008-04-08 15:17:53 +04:00
/* check if we need a specific attribute value */
if ( tsmd - > attrib_value ! = NULL ) {
offline = ( ret = = 0 & & rlen = = buflen & &
memcmp ( buf , tsmd - > attrib_value , buflen ) = = 0 ) ;
} else {
/* its offline if the specified DMAPI attribute exists */
offline = ( ret = = 0 | | ( ret = = - 1 & & errno = = E2BIG ) ) ;
}
2008-01-16 12:26:35 +03:00
DEBUG ( 10 , ( " dm_get_dmattr %s ret=%d (%s) \n " , path , ret , strerror ( errno ) ) ) ;
ret = 0 ;
dm_handle_free ( dmhandle , dmhandle_len ) ;
done :
2008-04-08 15:17:53 +04:00
talloc_free ( buf ) ;
2008-01-16 12:26:35 +03:00
unbecome_root ( ) ;
2008-01-17 14:57:35 +03:00
return offline ;
2008-01-16 12:26:35 +03:00
}
static bool tsmsm_aio_force ( struct vfs_handle_struct * handle , struct files_struct * fsp )
{
SMB_STRUCT_STAT sbuf ;
struct tsmsm_struct * tsmd = ( struct tsmsm_struct * ) handle - > data ;
/* see if the file might be offline. This is called before each IO
to ensure we use AIO if the file is offline . We don ' t do the full dmapi
call as that would be too slow , instead we err on the side of using AIO
if the file might be offline
*/
if ( SMB_VFS_FSTAT ( fsp , & sbuf ) = = 0 ) {
2008-03-20 14:49:13 +03:00
DEBUG ( 10 , ( " tsmsm_aio_force st_blocks=%ld st_size=%ld "
" online_ratio=%.2f \n " , ( long ) sbuf . st_blocks ,
( long ) sbuf . st_size , tsmd - > online_ratio ) ) ;
2008-01-16 12:26:35 +03:00
return ! ( 512 * ( off_t ) sbuf . st_blocks > = sbuf . st_size * tsmd - > online_ratio ) ;
}
2008-01-17 07:34:33 +03:00
return false ;
2008-01-16 12:26:35 +03:00
}
static ssize_t tsmsm_aio_return ( struct vfs_handle_struct * handle , struct files_struct * fsp ,
SMB_STRUCT_AIOCB * aiocb )
{
ssize_t result ;
result = SMB_VFS_NEXT_AIO_RETURN ( handle , fsp , aiocb ) ;
if ( result > = 0 ) {
notify_fname ( handle - > conn , NOTIFY_ACTION_MODIFIED ,
FILE_NOTIFY_CHANGE_ATTRIBUTES ,
fsp - > fsp_name ) ;
}
return result ;
}
static ssize_t tsmsm_sendfile ( vfs_handle_struct * handle , int tofd , files_struct * fsp , const DATA_BLOB * hdr ,
SMB_OFF_T offset , size_t n )
{
bool file_online = tsmsm_aio_force ( handle , fsp ) ;
if ( ! file_online )
return ENOSYS ;
return SMB_VFS_NEXT_SENDFILE ( handle , tofd , fsp , hdr , offset , n ) ;
}
/* We do overload pread to allow notification when file becomes online after offline status */
/* We don't intercept SMB_VFS_READ here because all file I/O now goes through SMB_VFS_PREAD instead */
static ssize_t tsmsm_pread ( struct vfs_handle_struct * handle , struct files_struct * fsp ,
void * data , size_t n , SMB_OFF_T offset ) {
ssize_t result ;
bool notify_online = tsmsm_aio_force ( handle , fsp ) ;
result = SMB_VFS_NEXT_PREAD ( handle , fsp , data , n , offset ) ;
if ( ( result ! = - 1 ) & & notify_online ) {
/* We can't actually force AIO at this point (came here not from reply_read_and_X)
what we can do is to send notification that file became online
*/
notify_fname ( handle - > conn , NOTIFY_ACTION_MODIFIED ,
FILE_NOTIFY_CHANGE_ATTRIBUTES ,
fsp - > fsp_name ) ;
}
return result ;
}
static ssize_t tsmsm_pwrite ( struct vfs_handle_struct * handle , struct files_struct * fsp ,
void * data , size_t n , SMB_OFF_T offset ) {
ssize_t result ;
bool notify_online = tsmsm_aio_force ( handle , fsp ) ;
result = SMB_VFS_NEXT_PWRITE ( handle , fsp , data , n , offset ) ;
if ( ( result ! = - 1 ) & & notify_online ) {
/* We can't actually force AIO at this point (came here not from reply_read_and_X)
what we can do is to send notification that file became online
*/
notify_fname ( handle - > conn , NOTIFY_ACTION_MODIFIED ,
FILE_NOTIFY_CHANGE_ATTRIBUTES ,
fsp - > fsp_name ) ;
}
return result ;
}
2008-01-17 07:34:33 +03:00
static int tsmsm_set_offline ( struct vfs_handle_struct * handle ,
2008-01-16 12:26:35 +03:00
const char * path ) {
struct tsmsm_struct * tsmd = ( struct tsmsm_struct * ) handle - > data ;
int result = 0 ;
char * command ;
2008-01-18 17:34:21 +03:00
if ( tsmd - > hsmscript = = NULL ) {
/* no script enabled */
DEBUG ( 1 , ( " tsmsm_set_offline: No tsmsm:hsmscript configured \n " ) ) ;
return 0 ;
}
2008-01-16 12:26:35 +03:00
/* Now, call the script */
command = talloc_asprintf ( tsmd , " %s offline \" %s \" " , tsmd - > hsmscript , path ) ;
if ( ! command ) {
DEBUG ( 1 , ( " tsmsm_set_offline: can't allocate memory to run hsm script " ) ) ;
return - 1 ;
}
DEBUG ( 10 , ( " tsmsm_set_offline: Running [%s] \n " , command ) ) ;
if ( ( result = smbrun ( command , NULL ) ) ! = 0 ) {
DEBUG ( 1 , ( " tsmsm_set_offline: Running [%s] returned %d \n " , command , result ) ) ;
}
TALLOC_FREE ( command ) ;
return result ;
}
2008-01-29 18:33:22 +03:00
static uint32_t tsmsm_fs_capabilities ( struct vfs_handle_struct * handle )
2008-01-17 16:51:14 +03:00
{
2008-01-29 18:33:22 +03:00
return SMB_VFS_NEXT_FS_CAPABILITIES ( handle ) | FILE_SUPPORTS_REMOTE_STORAGE | FILE_SUPPORTS_REPARSE_POINTS ;
2008-01-16 12:26:35 +03:00
}
static vfs_op_tuple vfs_tsmsm_ops [ ] = {
/* Disk operations */
{ SMB_VFS_OP ( tsmsm_connect ) , SMB_VFS_OP_CONNECT ,
SMB_VFS_LAYER_TRANSPARENT } ,
2008-01-29 18:33:22 +03:00
{ SMB_VFS_OP ( tsmsm_fs_capabilities ) , SMB_VFS_OP_FS_CAPABILITIES ,
2008-01-17 16:51:14 +03:00
SMB_VFS_LAYER_TRANSPARENT } ,
2008-01-16 12:26:35 +03:00
{ SMB_VFS_OP ( tsmsm_aio_force ) , SMB_VFS_OP_AIO_FORCE ,
SMB_VFS_LAYER_TRANSPARENT } ,
{ SMB_VFS_OP ( tsmsm_aio_return ) , SMB_VFS_OP_AIO_RETURN ,
SMB_VFS_LAYER_TRANSPARENT } ,
{ SMB_VFS_OP ( tsmsm_pread ) , SMB_VFS_OP_PREAD ,
SMB_VFS_LAYER_TRANSPARENT } ,
{ SMB_VFS_OP ( tsmsm_pwrite ) , SMB_VFS_OP_PWRITE ,
SMB_VFS_LAYER_TRANSPARENT } ,
{ SMB_VFS_OP ( tsmsm_sendfile ) , SMB_VFS_OP_SENDFILE ,
SMB_VFS_LAYER_TRANSPARENT } ,
2008-01-17 16:51:14 +03:00
{ SMB_VFS_OP ( tsmsm_is_offline ) , SMB_VFS_OP_IS_OFFLINE ,
2008-01-16 12:26:35 +03:00
SMB_VFS_LAYER_OPAQUE } ,
2008-01-17 16:51:14 +03:00
{ SMB_VFS_OP ( tsmsm_set_offline ) , SMB_VFS_OP_SET_OFFLINE ,
2008-01-16 12:26:35 +03:00
SMB_VFS_LAYER_OPAQUE } ,
/* Finish VFS operations definition */
{ SMB_VFS_OP ( NULL ) , SMB_VFS_OP_NOOP ,
SMB_VFS_LAYER_NOOP }
} ;
NTSTATUS vfs_tsmsm_init ( void ) ;
NTSTATUS vfs_tsmsm_init ( void )
{
return smb_register_vfs ( SMB_VFS_INTERFACE_VERSION ,
" tsmsm " , vfs_tsmsm_ops ) ;
}