2006-07-11 22:01:26 +04:00
/*
* XFS preallocation support module .
*
* Copyright ( c ) James Peach 2006
*
* 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
2007-07-09 23:25:36 +04:00
* the Free Software Foundation ; either version 3 of the License , or
2006-07-11 22:01:26 +04:00
* ( 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
2007-07-10 09:23:25 +04:00
* along with this program ; if not , see < http : //www.gnu.org/licenses/>.
2006-07-11 22:01:26 +04:00
*/
# include "includes.h"
2011-08-09 19:02:29 +04:00
# include "system/filesys.h"
2011-03-23 00:34:22 +03:00
# include "smbd/smbd.h"
2006-07-11 22:01:26 +04:00
/* Extent preallocation module.
*
* The purpose of this module is to preallocate space on the filesystem when
* we have a good idea of how large files are supposed to be . This lets writes
* proceed without having to allocate new extents and results in better file
* layouts on disk .
*
* Currently only implemented for XFS . This module is based on an original idea
* and implementation by Sebastian Brings .
*
* Tunables .
*
* prealloc : < ext > Number of bytes to preallocate for a file with
* the matching extension .
* prealloc : debug Debug level at which to emit messages .
*
* Example .
*
* prealloc : mpeg = 500 M # Preallocate * . mpeg to 500 MiB .
*/
# ifdef HAVE_XFS_LIBXFS_H
# include <xfs/libxfs.h>
# define lock_type xfs_flock64_t
# else
# define lock_type struct flock64
# endif
# define MODULE "prealloc"
static int module_debug ;
2012-04-05 08:53:08 +04:00
static int preallocate_space ( int fd , off_t size )
2006-07-11 22:01:26 +04:00
{
2008-12-05 00:55:12 +03:00
int err ;
2006-07-11 22:01:26 +04:00
lock_type fl = { 0 } ;
if ( size < = 0 ) {
return 0 ;
}
fl . l_whence = SEEK_SET ;
fl . l_start = 0 ;
fl . l_len = size ;
/* IMPORTANT: We use RESVSP because we want the extents to be
* allocated , but we don ' t want the allocation to show up in
* st_size or persist after the close ( 2 ) .
*/
# if defined(XFS_IOC_RESVSP64)
/* On Linux this comes in via libxfs.h. */
err = xfsctl ( NULL , fd , XFS_IOC_RESVSP64 , & fl ) ;
# elif defined(F_RESVSP64)
/* On IRIX, this comes from fcntl.h. */
err = fcntl ( fd , F_RESVSP64 , & fl ) ;
# else
err = - 1 ;
errno = ENOSYS ;
# endif
if ( err ) {
DEBUG ( module_debug ,
( " %s: preallocate failed on fd=%d size=%lld: %s \n " ,
MODULE , fd , ( long long ) size , strerror ( errno ) ) ) ;
}
return err ;
}
static int prealloc_connect (
struct vfs_handle_struct * handle ,
const char * service ,
const char * user )
{
2009-12-01 02:53:04 +03:00
int ret = SMB_VFS_NEXT_CONNECT ( handle , service , user ) ;
if ( ret < 0 ) {
return ret ;
}
module_debug = lp_parm_int ( SNUM ( handle - > conn ) ,
2006-07-11 22:01:26 +04:00
MODULE , " debug " , 100 ) ;
2009-12-01 02:53:04 +03:00
return 0 ;
2006-07-11 22:01:26 +04:00
}
2020-05-20 22:46:41 +03:00
static int prealloc_openat ( struct vfs_handle_struct * handle ,
const struct files_struct * dirfsp ,
const struct smb_filename * smb_fname ,
files_struct * fsp ,
2022-06-03 16:53:29 +03:00
const struct vfs_open_how * how )
2020-05-20 22:46:41 +03:00
{
int fd ;
off_t size = 0 ;
const char * dot ;
char fext [ 10 ] ;
2022-06-03 16:53:29 +03:00
if ( ! ( how - > flags & ( O_CREAT | O_TRUNC ) ) ) {
2020-05-20 22:46:41 +03:00
/* Caller is not intending to rewrite the file. Let's not mess
* with the allocation in this case .
*/
goto normal_open ;
}
* fext = ' \0 ' ;
dot = strrchr ( smb_fname - > base_name , ' . ' ) ;
if ( dot & & * + + dot ) {
if ( strlen ( dot ) < sizeof ( fext ) ) {
2022-02-17 13:19:50 +03:00
bool ok ;
2020-05-20 22:46:41 +03:00
strncpy ( fext , dot , sizeof ( fext ) ) ;
2022-02-17 13:19:50 +03:00
ok = strlower_m ( fext ) ;
if ( ! ok ) ;
2020-05-20 22:46:41 +03:00
goto normal_open ;
}
}
}
if ( * fext = = ' \0 ' ) {
goto normal_open ;
}
/* Syntax for specifying preallocation size is:
* MODULE : < extension > = < size >
* where
* < extension > is the file extension in lower case
* < size > is a size like 10 , 10 K , 10 M
*/
size = conv_str_size ( lp_parm_const_string ( SNUM ( handle - > conn ) , MODULE ,
fext , NULL ) ) ;
if ( size < = 0 ) {
/* No need to preallocate this file. */
goto normal_open ;
}
2022-06-03 16:53:29 +03:00
fd = SMB_VFS_NEXT_OPENAT ( handle , dirfsp , smb_fname , fsp , how ) ;
2020-05-20 22:46:41 +03:00
if ( fd < 0 ) {
return fd ;
}
2022-02-17 13:19:42 +03:00
/* Preallocate only if the file is being created or replaced. Note that
2020-05-20 22:46:41 +03:00
* Samba won ' t ever pass down O_TRUNC , which is why we have to handle
* truncate calls specially .
*/
2022-06-03 16:53:29 +03:00
if ( ( how - > flags & O_CREAT ) | | ( how - > flags & O_TRUNC ) ) {
2020-05-20 22:46:41 +03:00
off_t * psize ;
psize = VFS_ADD_FSP_EXTENSION ( handle , fsp , off_t , NULL ) ;
if ( psize = = NULL | | * psize = = - 1 ) {
return fd ;
}
DEBUG ( module_debug ,
( " %s: preallocating %s (fd=%d) to %lld bytes \n " ,
MODULE , smb_fname_str_dbg ( smb_fname ) , fd ,
( long long ) size ) ) ;
* psize = size ;
if ( preallocate_space ( fd , * psize ) < 0 ) {
VFS_REMOVE_FSP_EXTENSION ( handle , fsp ) ;
}
}
return fd ;
normal_open :
/* We are not creating or replacing a file. Skip the
* preallocation .
*/
DEBUG ( module_debug , ( " %s: skipping preallocation for %s \n " ,
MODULE , smb_fname_str_dbg ( smb_fname ) ) ) ;
2022-06-03 16:53:29 +03:00
return SMB_VFS_NEXT_OPENAT ( handle , dirfsp , smb_fname , fsp , how ) ;
2020-05-20 22:46:41 +03:00
}
2006-07-11 22:01:26 +04:00
static int prealloc_ftruncate ( vfs_handle_struct * handle ,
files_struct * fsp ,
2012-04-05 08:53:08 +04:00
off_t offset )
2006-07-11 22:01:26 +04:00
{
2012-04-05 08:53:08 +04:00
off_t * psize ;
2008-01-07 17:55:09 +03:00
int ret = SMB_VFS_NEXT_FTRUNCATE ( handle , fsp , offset ) ;
2006-07-11 22:01:26 +04:00
/* Maintain the allocated space even in the face of truncates. */
if ( ( psize = VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ) ) {
2020-09-26 22:52:52 +03:00
preallocate_space ( fsp_get_io_fd ( fsp ) , * psize ) ;
2006-07-11 22:01:26 +04:00
}
return ret ;
}
2009-07-24 04:28:58 +04:00
static struct vfs_fn_pointers prealloc_fns = {
2020-05-20 22:46:41 +03:00
. openat_fn = prealloc_openat ,
2011-12-04 08:45:04 +04:00
. ftruncate_fn = prealloc_ftruncate ,
2009-08-02 18:42:49 +04:00
. connect_fn = prealloc_connect ,
2006-07-11 22:01:26 +04:00
} ;
2017-12-16 01:32:12 +03:00
static_decl_vfs ;
2017-04-20 22:24:43 +03:00
NTSTATUS vfs_prealloc_init ( TALLOC_CTX * ctx )
2006-07-11 22:01:26 +04:00
{
return smb_register_vfs ( SMB_VFS_INTERFACE_VERSION ,
2009-07-24 04:28:58 +04:00
MODULE , & prealloc_fns ) ;
2006-07-11 22:01:26 +04:00
}