2006-03-31 14:30:42 +04:00
/*
* High - level sync ( ) - related operations
*/
# include <linux/kernel.h>
# include <linux/file.h>
# include <linux/fs.h>
# include <linux/module.h>
# include <linux/writeback.h>
# include <linux/syscalls.h>
# include <linux/linkage.h>
# include <linux/pagemap.h>
# define VALID_FLAGS (SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE| \
SYNC_FILE_RANGE_WAIT_AFTER )
/*
* sys_sync_file_range ( ) permits finely controlled syncing over a segment of
* a file in the range offset . . ( offset + nbytes - 1 ) inclusive . If nbytes is
* zero then sys_sync_file_range ( ) will operate from offset out to EOF .
*
* The flag bits are :
*
* SYNC_FILE_RANGE_WAIT_BEFORE : wait upon writeout of all pages in the range
* before performing the write .
*
* SYNC_FILE_RANGE_WRITE : initiate writeout of all those dirty pages in the
* range which are not presently under writeback .
*
* SYNC_FILE_RANGE_WAIT_AFTER : wait upon writeout of all pages in the range
* after performing the write .
*
* Useful combinations of the flag bits are :
*
* SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE : ensures that all pages
* in the range which were dirty on entry to sys_sync_file_range ( ) are placed
* under writeout . This is a start - write - for - data - integrity operation .
*
* SYNC_FILE_RANGE_WRITE : start writeout of all dirty pages in the range which
* are not presently under writeout . This is an asynchronous flush - to - disk
* operation . Not suitable for data integrity operations .
*
* SYNC_FILE_RANGE_WAIT_BEFORE ( or SYNC_FILE_RANGE_WAIT_AFTER ) : wait for
* completion of writeout of all pages in the range . This will be used after an
* earlier SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE operation to wait
* for that operation to complete and to return the result .
*
* SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE | SYNC_FILE_RANGE_WAIT_AFTER :
* a traditional sync ( ) operation . This is a write - for - data - integrity operation
* which will ensure that all pages in the range which were dirty on entry to
* sys_sync_file_range ( ) are committed to disk .
*
*
* SYNC_FILE_RANGE_WAIT_BEFORE and SYNC_FILE_RANGE_WAIT_AFTER will detect any
* I / O errors or ENOSPC conditions and will return those to the caller , after
* clearing the EIO and ENOSPC flags in the address_space .
*
* It should be noted that none of these operations write out the file ' s
* metadata . So unless the application is strictly performing overwrites of
* already - instantiated disk blocks , there are no guarantees here that the data
* will be available after a crash .
*/
asmlinkage long sys_sync_file_range ( int fd , loff_t offset , loff_t nbytes ,
2006-04-11 09:53:57 +04:00
unsigned int flags )
2006-03-31 14:30:42 +04:00
{
int ret ;
struct file * file ;
loff_t endbyte ; /* inclusive */
int fput_needed ;
umode_t i_mode ;
ret = - EINVAL ;
if ( flags & ~ VALID_FLAGS )
goto out ;
endbyte = offset + nbytes ;
if ( ( s64 ) offset < 0 )
goto out ;
if ( ( s64 ) endbyte < 0 )
goto out ;
if ( endbyte < offset )
goto out ;
if ( sizeof ( pgoff_t ) = = 4 ) {
if ( offset > = ( 0x100000000ULL < < PAGE_CACHE_SHIFT ) ) {
/*
* The range starts outside a 32 bit machine ' s
* pagecache addressing capabilities . Let it " succeed "
*/
ret = 0 ;
goto out ;
}
if ( endbyte > = ( 0x100000000ULL < < PAGE_CACHE_SHIFT ) ) {
/*
* Out to EOF
*/
nbytes = 0 ;
}
}
if ( nbytes = = 0 )
endbyte = - 1 ;
else
endbyte - - ; /* inclusive */
ret = - EBADF ;
file = fget_light ( fd , & fput_needed ) ;
if ( ! file )
goto out ;
i_mode = file - > f_dentry - > d_inode - > i_mode ;
ret = - ESPIPE ;
if ( ! S_ISREG ( i_mode ) & & ! S_ISBLK ( i_mode ) & & ! S_ISDIR ( i_mode ) & &
! S_ISLNK ( i_mode ) )
goto out_put ;
ret = do_sync_file_range ( file , offset , endbyte , flags ) ;
out_put :
fput_light ( file , fput_needed ) ;
out :
return ret ;
}
/*
* ` endbyte ' is inclusive
*/
int do_sync_file_range ( struct file * file , loff_t offset , loff_t endbyte ,
2006-04-11 09:53:57 +04:00
unsigned int flags )
2006-03-31 14:30:42 +04:00
{
int ret ;
struct address_space * mapping ;
mapping = file - > f_mapping ;
if ( ! mapping ) {
ret = - EINVAL ;
goto out ;
}
ret = 0 ;
if ( flags & SYNC_FILE_RANGE_WAIT_BEFORE ) {
ret = wait_on_page_writeback_range ( mapping ,
offset > > PAGE_CACHE_SHIFT ,
endbyte > > PAGE_CACHE_SHIFT ) ;
if ( ret < 0 )
goto out ;
}
if ( flags & SYNC_FILE_RANGE_WRITE ) {
ret = __filemap_fdatawrite_range ( mapping , offset , endbyte ,
WB_SYNC_NONE ) ;
if ( ret < 0 )
goto out ;
}
if ( flags & SYNC_FILE_RANGE_WAIT_AFTER ) {
ret = wait_on_page_writeback_range ( mapping ,
offset > > PAGE_CACHE_SHIFT ,
endbyte > > PAGE_CACHE_SHIFT ) ;
}
out :
return ret ;
}
EXPORT_SYMBOL_GPL ( do_sync_file_range ) ;