2009-03-10 20:02:21 +03:00
/*
* Force a readahead of files by opening them and reading the first bytes
*
* Copyright ( C ) Volker Lendecke 2008
*
* 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"
2016-04-05 23:22:47 +03:00
# include "lib/util/sys_rw.h"
2015-10-12 16:57:34 +03:00
# include "lib/util/sys_rw_data.h"
2020-07-03 09:11:20 +03:00
# include "lib/util/smb_strtox.h"
2021-06-09 16:08:38 +03:00
# include "lib/util_matching.h"
2021-01-03 23:53:49 +03:00
# include "lib/global_contexts.h"
2009-03-10 20:02:21 +03:00
2021-06-08 11:56:22 +03:00
static int vfs_preopen_debug_level = DBGC_VFS ;
# undef DBGC_CLASS
# define DBGC_CLASS vfs_preopen_debug_level
2021-06-18 21:29:31 +03:00
# define PREOPEN_MAX_DIGITS 19
# define PREOPEN_MAX_NUMBER (uint64_t)9999999999999999999ULL
2009-03-10 20:02:21 +03:00
struct preopen_state ;
struct preopen_helper {
struct preopen_state * state ;
2013-02-18 13:24:12 +04:00
struct tevent_fd * fde ;
2009-03-10 20:02:21 +03:00
pid_t pid ;
int fd ;
bool busy ;
} ;
struct preopen_state {
int num_helpers ;
struct preopen_helper * helpers ;
size_t to_read ; /* How many bytes to read in children? */
int queue_max ;
char * template_fname ; /* Filename to be sent to children */
size_t number_start ; /* start offset into "template_fname" */
int num_digits ; /* How many digits is the number long? */
2021-06-18 21:29:31 +03:00
uint64_t fnum_sent ; /* last fname sent to children */
2009-03-10 20:02:21 +03:00
2021-06-18 21:29:31 +03:00
uint64_t fnum_queue_end ; /* last fname to be sent, based on
2009-03-10 20:02:21 +03:00
* last open call + preopen : queuelen
*/
2021-06-09 16:08:38 +03:00
struct samba_path_matching * preopen_names ;
2021-06-18 16:31:58 +03:00
ssize_t last_match_idx ; /* remember the last match */
2009-03-10 20:02:21 +03:00
} ;
static void preopen_helper_destroy ( struct preopen_helper * c )
{
int status ;
2019-06-26 18:43:44 +03:00
TALLOC_FREE ( c - > fde ) ;
2009-03-10 20:02:21 +03:00
close ( c - > fd ) ;
c - > fd = - 1 ;
kill ( c - > pid , SIGKILL ) ;
waitpid ( c - > pid , & status , 0 ) ;
c - > busy = true ;
}
static void preopen_queue_run ( struct preopen_state * state )
{
char * pdelimiter ;
char delimiter ;
pdelimiter = state - > template_fname + state - > number_start
+ state - > num_digits ;
delimiter = * pdelimiter ;
while ( state - > fnum_sent < state - > fnum_queue_end ) {
ssize_t written ;
size_t to_write ;
int helper ;
for ( helper = 0 ; helper < state - > num_helpers ; helper + + ) {
if ( state - > helpers [ helper ] . busy ) {
continue ;
}
break ;
}
if ( helper = = state - > num_helpers ) {
/* everyone is busy */
return ;
}
snprintf ( state - > template_fname + state - > number_start ,
state - > num_digits + 1 ,
2021-06-18 21:29:31 +03:00
" %.*llu " , state - > num_digits ,
( long long unsigned int ) ( state - > fnum_sent + 1 ) ) ;
2009-03-10 20:02:21 +03:00
* pdelimiter = delimiter ;
to_write = talloc_get_size ( state - > template_fname ) ;
written = write_data ( state - > helpers [ helper ] . fd ,
state - > template_fname , to_write ) ;
state - > helpers [ helper ] . busy = true ;
if ( written ! = to_write ) {
preopen_helper_destroy ( & state - > helpers [ helper ] ) ;
}
state - > fnum_sent + = 1 ;
}
}
2013-02-18 12:59:08 +04:00
static void preopen_helper_readable ( struct tevent_context * ev ,
2013-02-18 13:24:12 +04:00
struct tevent_fd * fde , uint16_t flags ,
2009-03-10 20:02:21 +03:00
void * priv )
{
struct preopen_helper * helper = ( struct preopen_helper * ) priv ;
struct preopen_state * state = helper - > state ;
ssize_t nread ;
char c ;
2013-02-18 13:53:02 +04:00
if ( ( flags & TEVENT_FD_READ ) = = 0 ) {
2009-03-10 20:02:21 +03:00
return ;
}
nread = read ( helper - > fd , & c , 1 ) ;
if ( nread < = 0 ) {
preopen_helper_destroy ( helper ) ;
return ;
}
helper - > busy = false ;
preopen_queue_run ( state ) ;
}
static int preopen_helpers_destructor ( struct preopen_state * c )
{
int i ;
for ( i = 0 ; i < c - > num_helpers ; i + + ) {
if ( c - > helpers [ i ] . fd = = - 1 ) {
continue ;
}
preopen_helper_destroy ( & c - > helpers [ i ] ) ;
}
return 0 ;
}
static bool preopen_helper_open_one ( int sock_fd , char * * pnamebuf ,
size_t to_read , void * filebuf )
{
char * namebuf = * pnamebuf ;
2015-04-10 12:31:31 +03:00
ssize_t nread ;
2009-03-10 20:02:21 +03:00
char c = 0 ;
int fd ;
nread = 0 ;
2017-12-07 20:44:59 +03:00
do {
2009-03-10 20:02:21 +03:00
ssize_t thistime ;
thistime = read ( sock_fd , namebuf + nread ,
talloc_get_size ( namebuf ) - nread ) ;
if ( thistime < = 0 ) {
return false ;
}
nread + = thistime ;
if ( nread = = talloc_get_size ( namebuf ) ) {
2011-06-07 05:10:15 +04:00
namebuf = talloc_realloc (
2009-03-10 20:02:21 +03:00
NULL , namebuf , char ,
talloc_get_size ( namebuf ) * 2 ) ;
if ( namebuf = = NULL ) {
return false ;
}
* pnamebuf = namebuf ;
}
2017-12-07 20:44:59 +03:00
} while ( namebuf [ nread - 1 ] ! = ' \0 ' ) ;
2009-03-10 20:02:21 +03:00
fd = open ( namebuf , O_RDONLY ) ;
if ( fd = = - 1 ) {
goto done ;
}
nread = read ( fd , filebuf , to_read ) ;
close ( fd ) ;
done :
2016-04-05 23:22:47 +03:00
sys_write_v ( sock_fd , & c , 1 ) ;
2009-03-10 20:02:21 +03:00
return true ;
}
static bool preopen_helper ( int fd , size_t to_read )
{
char * namebuf ;
void * readbuf ;
2011-06-07 05:30:12 +04:00
namebuf = talloc_array ( NULL , char , 1024 ) ;
2009-03-10 20:02:21 +03:00
if ( namebuf = = NULL ) {
return false ;
}
readbuf = talloc_size ( NULL , to_read ) ;
if ( readbuf = = NULL ) {
TALLOC_FREE ( namebuf ) ;
return false ;
}
while ( preopen_helper_open_one ( fd , & namebuf , to_read , readbuf ) ) {
;
}
TALLOC_FREE ( readbuf ) ;
TALLOC_FREE ( namebuf ) ;
return false ;
}
static NTSTATUS preopen_init_helper ( struct preopen_helper * h )
{
int fdpair [ 2 ] ;
NTSTATUS status ;
if ( socketpair ( AF_UNIX , SOCK_STREAM , 0 , fdpair ) = = - 1 ) {
status = map_nt_error_from_unix ( errno ) ;
DEBUG ( 10 , ( " socketpair() failed: %s \n " , strerror ( errno ) ) ) ;
return status ;
}
2012-03-24 23:17:08 +04:00
h - > pid = fork ( ) ;
2009-03-10 20:02:21 +03:00
if ( h - > pid = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
if ( h - > pid = = 0 ) {
close ( fdpair [ 0 ] ) ;
preopen_helper ( fdpair [ 1 ] , h - > state - > to_read ) ;
exit ( 0 ) ;
}
close ( fdpair [ 1 ] ) ;
h - > fd = fdpair [ 0 ] ;
2018-08-21 21:06:16 +03:00
h - > fde = tevent_add_fd ( global_event_context ( ) , h - > state , h - > fd ,
2013-02-18 13:53:02 +04:00
TEVENT_FD_READ , preopen_helper_readable , h ) ;
2009-03-10 20:02:21 +03:00
if ( h - > fde = = NULL ) {
close ( h - > fd ) ;
h - > fd = - 1 ;
return NT_STATUS_NO_MEMORY ;
}
h - > busy = false ;
return NT_STATUS_OK ;
}
static NTSTATUS preopen_init_helpers ( TALLOC_CTX * mem_ctx , size_t to_read ,
int num_helpers , int queue_max ,
struct preopen_state * * presult )
{
struct preopen_state * result ;
int i ;
result = talloc ( mem_ctx , struct preopen_state ) ;
if ( result = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
result - > num_helpers = num_helpers ;
2011-06-07 05:30:12 +04:00
result - > helpers = talloc_array ( result , struct preopen_helper ,
2009-03-10 20:02:21 +03:00
num_helpers ) ;
if ( result - > helpers = = NULL ) {
TALLOC_FREE ( result ) ;
return NT_STATUS_NO_MEMORY ;
}
result - > to_read = to_read ;
result - > queue_max = queue_max ;
result - > template_fname = NULL ;
result - > fnum_sent = 0 ;
2019-06-26 18:43:20 +03:00
result - > fnum_queue_end = 0 ;
2009-03-10 20:02:21 +03:00
for ( i = 0 ; i < num_helpers ; i + + ) {
result - > helpers [ i ] . state = result ;
result - > helpers [ i ] . fd = - 1 ;
}
talloc_set_destructor ( result , preopen_helpers_destructor ) ;
for ( i = 0 ; i < num_helpers ; i + + ) {
preopen_init_helper ( & result - > helpers [ i ] ) ;
}
* presult = result ;
return NT_STATUS_OK ;
}
static void preopen_free_helpers ( void * * ptr )
{
TALLOC_FREE ( * ptr ) ;
}
static struct preopen_state * preopen_state_get ( vfs_handle_struct * handle )
{
struct preopen_state * state ;
NTSTATUS status ;
const char * namelist ;
if ( SMB_VFS_HANDLE_TEST_DATA ( handle ) ) {
SMB_VFS_HANDLE_GET_DATA ( handle , state , struct preopen_state ,
return NULL ) ;
return state ;
}
namelist = lp_parm_const_string ( SNUM ( handle - > conn ) , " preopen " , " names " ,
NULL ) ;
if ( namelist = = NULL ) {
return NULL ;
}
status = preopen_init_helpers (
NULL ,
lp_parm_int ( SNUM ( handle - > conn ) , " preopen " , " num_bytes " , 1 ) ,
lp_parm_int ( SNUM ( handle - > conn ) , " preopen " , " helpers " , 1 ) ,
lp_parm_int ( SNUM ( handle - > conn ) , " preopen " , " queuelen " , 10 ) ,
& state ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return NULL ;
}
2021-06-11 22:07:03 +03:00
if ( lp_parm_bool ( SNUM ( handle - > conn ) , " preopen " , " posix-basic-regex " , false ) ) {
status = samba_path_matching_regex_sub1_create ( state ,
namelist ,
& state - > preopen_names ) ;
} else {
status = samba_path_matching_mswild_create ( state ,
true , /* case_sensitive */
namelist ,
& state - > preopen_names ) ;
}
2021-06-09 16:08:38 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-03-10 20:02:21 +03:00
TALLOC_FREE ( state ) ;
return NULL ;
}
2021-06-18 16:31:58 +03:00
state - > last_match_idx = - 1 ;
2009-03-10 20:02:21 +03:00
if ( ! SMB_VFS_HANDLE_TEST_DATA ( handle ) ) {
SMB_VFS_HANDLE_SET_DATA ( handle , state , preopen_free_helpers ,
struct preopen_state , return NULL ) ;
}
return state ;
}
2021-06-18 21:29:31 +03:00
static bool preopen_parse_fname ( const char * fname , uint64_t * pnum ,
2009-03-10 20:02:21 +03:00
size_t * pstart_idx , int * pnum_digits )
{
2021-06-12 00:08:19 +03:00
char digits [ PREOPEN_MAX_DIGITS + 1 ] = { 0 , } ;
2015-04-10 12:31:31 +03:00
const char * p ;
char * q = NULL ;
2021-06-18 21:29:31 +03:00
unsigned long long num ;
2021-06-12 00:08:19 +03:00
size_t start_idx = 0 ;
int num_digits = - 1 ;
2019-01-28 16:30:15 +03:00
int error = 0 ;
2009-03-10 20:02:21 +03:00
2021-06-12 00:08:19 +03:00
if ( * pstart_idx > 0 & & * pnum_digits > 0 ) {
/*
* If the caller knowns
* how many digits are expected
* and on what position ,
* we should copy the exact
* subset before we start
* parsing the string into a number
*/
if ( * pnum_digits < 1 ) {
/*
* We need at least one digit
*/
return false ;
}
if ( * pnum_digits > PREOPEN_MAX_DIGITS ) {
/*
* a string with as much digits as
* PREOPEN_MAX_DIGITS is the longest
* string that would make any sense for us .
*
* The rest will be checked via
* smb_strtoull ( ) .
*/
return false ;
}
p = fname + * pstart_idx ;
memcpy ( digits , p , * pnum_digits ) ;
p = digits ;
start_idx = * pstart_idx ;
goto parse ;
}
2009-03-10 20:02:21 +03:00
p = strrchr_m ( fname , ' / ' ) ;
if ( p = = NULL ) {
p = fname ;
}
p + = 1 ;
while ( p [ 0 ] ! = ' \0 ' ) {
if ( isdigit ( p [ 0 ] ) & & isdigit ( p [ 1 ] ) & & isdigit ( p [ 2 ] ) ) {
break ;
}
p + = 1 ;
}
if ( * p = = ' \0 ' ) {
/* no digits around */
return false ;
}
2021-06-12 00:08:19 +03:00
start_idx = ( p - fname ) ;
2021-06-12 00:08:19 +03:00
parse :
2021-06-18 21:29:31 +03:00
num = smb_strtoull ( p , ( char * * ) & q , 10 , & error , SMB_STR_STANDARD ) ;
2019-01-28 16:30:15 +03:00
if ( error ! = 0 ) {
return false ;
}
2009-03-10 20:02:21 +03:00
2021-06-18 21:29:31 +03:00
if ( num > = PREOPEN_MAX_NUMBER ) {
2009-03-10 20:02:21 +03:00
/* overflow */
return false ;
}
2021-06-12 00:08:19 +03:00
num_digits = ( q - p ) ;
2021-06-12 00:08:19 +03:00
if ( * pnum_digits ! = - 1 & & * pnum_digits ! = num_digits ) {
/*
* If the caller knowns how many digits
* it expects we should fail if we got something
* different .
*/
return false ;
}
2009-03-10 20:02:21 +03:00
* pnum = num ;
2021-06-12 00:08:19 +03:00
* pstart_idx = start_idx ;
* pnum_digits = num_digits ;
2009-03-10 20:02:21 +03:00
return true ;
}
2021-06-18 21:29:31 +03:00
static uint64_t num_digits_max_value ( int num_digits )
{
uint64_t num_max = 1 ;
int i ;
if ( num_digits < 1 ) {
return 0 ;
}
if ( num_digits > = PREOPEN_MAX_DIGITS ) {
return PREOPEN_MAX_NUMBER ;
}
for ( i = 0 ; i < num_digits ; i + + ) {
num_max * = 10 ;
}
/*
* We actually want
* 9 instead of 10
* 99 instead of 100
* 999 instead of 1000
*/
return num_max - 1 ;
}
2020-05-20 22:51:00 +03:00
static int preopen_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 )
{
2021-06-11 23:05:14 +03:00
const char * dirname = dirfsp - > fsp_name - > base_name ;
2020-05-20 22:51:00 +03:00
struct preopen_state * state ;
int res ;
2021-06-18 21:29:31 +03:00
uint64_t num ;
uint64_t num_max ;
2021-06-09 16:08:38 +03:00
NTSTATUS status ;
2021-06-11 23:03:49 +03:00
char * new_template = NULL ;
size_t new_start = 0 ;
int new_digits = - 1 ;
2021-06-18 16:31:58 +03:00
size_t new_end = 0 ;
2021-06-09 16:08:38 +03:00
ssize_t match_idx = - 1 ;
2021-06-12 00:08:19 +03:00
ssize_t replace_start = - 1 ;
ssize_t replace_end = - 1 ;
2021-06-18 16:31:58 +03:00
bool need_reset = false ;
2020-05-20 22:51:00 +03:00
DEBUG ( 10 , ( " preopen_open called on %s \n " , smb_fname_str_dbg ( smb_fname ) ) ) ;
state = preopen_state_get ( handle ) ;
if ( state = = NULL ) {
return SMB_VFS_NEXT_OPENAT ( handle ,
dirfsp ,
smb_fname ,
fsp ,
flags ,
mode ) ;
}
res = SMB_VFS_NEXT_OPENAT ( handle , dirfsp , smb_fname , fsp , flags , mode ) ;
if ( res = = - 1 ) {
return - 1 ;
}
if ( ( flags & O_ACCMODE ) ! = O_RDONLY ) {
return res ;
}
2021-06-11 23:05:14 +03:00
/*
* Make sure we can later contruct an absolute pathname
*/
if ( dirname [ 0 ] ! = ' / ' ) {
return res ;
}
/*
* There ' s no point in preopen the directory itself .
*/
if ( ISDOT ( smb_fname - > base_name ) ) {
return res ;
}
/*
* If we got an absolute path in
* smb_fname it ' s most likely the
* reopen via / proc / self / fd / $ fd
*/
if ( smb_fname - > base_name [ 0 ] = = ' / ' ) {
return res ;
}
2021-06-09 16:08:38 +03:00
status = samba_path_matching_check_last_component ( state - > preopen_names ,
smb_fname - > base_name ,
& match_idx ,
2021-06-12 00:08:19 +03:00
& replace_start ,
& replace_end ) ;
2021-06-09 16:08:38 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
match_idx = - 1 ;
}
if ( match_idx < 0 ) {
2020-05-20 22:51:00 +03:00
DEBUG ( 10 , ( " %s does not match the preopen:names list \n " ,
smb_fname_str_dbg ( smb_fname ) ) ) ;
return res ;
}
2021-06-11 23:03:49 +03:00
new_template = talloc_asprintf (
2020-05-20 22:51:00 +03:00
state , " %s/%s " ,
2021-06-11 23:05:14 +03:00
dirname , smb_fname - > base_name ) ;
2021-06-11 23:03:49 +03:00
if ( new_template = = NULL ) {
2020-05-20 22:51:00 +03:00
return res ;
}
2021-06-12 00:08:19 +03:00
if ( replace_start ! = - 1 & & replace_end ! = - 1 ) {
size_t dirofs = strlen ( dirname ) + 1 ;
new_start = dirofs + replace_start ;
new_digits = replace_end - replace_start ;
}
2021-06-11 23:03:49 +03:00
if ( ! preopen_parse_fname ( new_template , & num ,
& new_start , & new_digits ) ) {
TALLOC_FREE ( new_template ) ;
2020-05-20 22:51:00 +03:00
return res ;
}
2021-06-18 16:31:58 +03:00
new_end = new_start + new_digits ;
if ( state - > last_match_idx ! = match_idx ) {
/*
* If a different pattern caused the match
* we better reset the queue
*/
need_reset = true ;
} else if ( state - > number_start ! = new_start ) {
/*
* If the digits started at a different possition
* we better reset the queue
*/
need_reset = true ;
} else if ( state - > num_digits ! = new_digits ) {
/*
* If number of digits changed
* we better reset the queue
*/
need_reset = true ;
} else if ( strncmp ( state - > template_fname , new_template , new_start ) ! = 0 ) {
/*
* If name before the digits changed
* we better reset the queue
*/
need_reset = true ;
} else if ( strcmp ( state - > template_fname + new_end , new_template + new_end ) ! = 0 ) {
/*
* If name after the digits changed
* we better reset the queue
*/
need_reset = true ;
}
if ( need_reset ) {
/*
* Reset the queue
*/
state - > fnum_sent = 0 ;
state - > fnum_queue_end = 0 ;
state - > last_match_idx = match_idx ;
}
2020-05-20 22:51:00 +03:00
2021-06-11 23:03:49 +03:00
TALLOC_FREE ( state - > template_fname ) ;
state - > template_fname = new_template ;
state - > number_start = new_start ;
state - > num_digits = new_digits ;
2020-05-20 22:51:00 +03:00
if ( num > state - > fnum_sent ) {
/*
* Helpers were too slow , there ' s no point in reading
* files in helpers that we already read in the
* parent .
*/
state - > fnum_sent = num ;
}
if ( ( state - > fnum_queue_end ! = 0 ) /* Something was started earlier */
& & ( num < ( state - > fnum_queue_end - state - > queue_max ) ) ) {
/*
* " num " is before the queue we announced . This means
* a new run is started .
*/
state - > fnum_sent = num ;
}
2021-06-18 21:29:31 +03:00
num_max = num_digits_max_value ( state - > num_digits ) ;
state - > fnum_queue_end = MIN ( num_max , num + state - > queue_max ) ;
2020-05-20 22:51:00 +03:00
preopen_queue_run ( state ) ;
return res ;
}
2009-07-24 04:28:58 +04:00
static struct vfs_fn_pointers vfs_preopen_fns = {
2020-05-20 22:51:00 +03:00
. openat_fn = preopen_openat ,
2009-03-10 20:02:21 +03:00
} ;
2017-12-16 01:32:12 +03:00
static_decl_vfs ;
2017-04-20 22:24:43 +03:00
NTSTATUS vfs_preopen_init ( TALLOC_CTX * ctx )
2009-03-10 20:02:21 +03:00
{
2021-06-08 11:56:22 +03:00
NTSTATUS status ;
status = smb_register_vfs ( SMB_VFS_INTERFACE_VERSION ,
" preopen " ,
& vfs_preopen_fns ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
vfs_preopen_debug_level = debug_add_class ( " preopen " ) ;
if ( vfs_preopen_debug_level = = - 1 ) {
vfs_preopen_debug_level = DBGC_VFS ;
DBG_ERR ( " Couldn't register custom debugging class! \n " ) ;
} else {
DBG_DEBUG ( " Debug class number of 'preopen': %d \n " ,
vfs_preopen_debug_level ) ;
}
return NT_STATUS_OK ;
2009-03-10 20:02:21 +03:00
}