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
2008-01-16 12:19:51 +03:00
# ifdef HAVE_GPFS
# include "gpfs_gpl.h"
# endif
2006-07-11 22:01:26 +04:00
# 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 ;
2008-01-16 12:19:51 +03:00
# ifndef HAVE_GPFS
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
2008-01-16 12:19:51 +03:00
# else /* GPFS uses completely different interface */
err = gpfs_prealloc ( fd , ( gpfs_off64_t ) 0 , ( gpfs_off64_t ) size ) ;
# endif
2006-07-11 22:01:26 +04:00
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
}
static int prealloc_open ( vfs_handle_struct * handle ,
2009-06-16 23:01:13 +04:00
struct smb_filename * smb_fname ,
2006-07-11 22:01:26 +04:00
files_struct * fsp ,
int flags ,
mode_t mode )
{
int fd ;
2012-03-28 03:03:24 +04:00
off_t size = 0 ;
2006-07-11 22:01:26 +04:00
const char * dot ;
char fext [ 10 ] ;
if ( ! ( flags & ( O_CREAT | O_TRUNC ) ) ) {
/* Caller is not intending to rewrite the file. Let's not mess
* with the allocation in this case .
*/
goto normal_open ;
}
* fext = ' \0 ' ;
2009-06-16 23:01:13 +04:00
dot = strrchr ( smb_fname - > base_name , ' . ' ) ;
2006-07-11 22:01:26 +04:00
if ( dot & & * + + dot ) {
if ( strlen ( dot ) < sizeof ( fext ) ) {
strncpy ( fext , dot , sizeof ( fext ) ) ;
2012-08-09 02:49:34 +04:00
if ( ! strnorm ( fext , CASE_LOWER ) ) {
goto normal_open ;
}
2006-07-11 22:01:26 +04:00
}
}
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 ;
}
2009-06-16 23:01:13 +04:00
fd = SMB_VFS_NEXT_OPEN ( handle , smb_fname , fsp , flags , mode ) ;
2006-07-11 22:01:26 +04:00
if ( fd < 0 ) {
return fd ;
}
/* Prellocate only if the file is being created or replaced. Note that
* Samba won ' t ever pass down O_TRUNC , which is why we have to handle
* truncate calls specially .
*/
if ( ( flags & O_CREAT ) | | ( flags & O_TRUNC ) ) {
2012-04-05 08:53:08 +04:00
off_t * psize ;
2006-07-11 22:01:26 +04:00
2012-04-05 08:53:08 +04:00
psize = VFS_ADD_FSP_EXTENSION ( handle , fsp , off_t , NULL ) ;
2006-07-11 22:01:26 +04:00
if ( psize = = NULL | | * psize = = - 1 ) {
return fd ;
}
DEBUG ( module_debug ,
( " %s: preallocating %s (fd=%d) to %lld bytes \n " ,
2009-06-16 23:01:13 +04:00
MODULE , smb_fname_str_dbg ( smb_fname ) , fd ,
( long long ) size ) ) ;
2006-07-11 22:01:26 +04:00
* 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 " ,
2009-06-16 23:01:13 +04:00
MODULE , smb_fname_str_dbg ( smb_fname ) ) ) ;
return SMB_VFS_NEXT_OPEN ( handle , smb_fname , fsp , flags , mode ) ;
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 ) ) ) {
2008-02-21 09:10:54 +03:00
preallocate_space ( fsp - > fh - > fd , * 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 = {
2011-04-21 00:55:25 +04:00
. open_fn = prealloc_open ,
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
} ;
2006-12-19 23:16:52 +03:00
NTSTATUS vfs_prealloc_init ( void ) ;
2006-07-11 22:01:26 +04:00
NTSTATUS vfs_prealloc_init ( void )
{
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
}