2009-11-20 22:13:39 +03:00
/*
* fs / logfs / file . c - prepare_write , commit_write and friends
*
* As should be obvious for Linux kernel code , license is GPLv2
*
* Copyright ( c ) 2005 - 2008 Joern Engel < joern @ logfs . org >
*/
# include "logfs.h"
# include <linux/sched.h>
# include <linux/writeback.h>
static int logfs_write_begin ( struct file * file , struct address_space * mapping ,
loff_t pos , unsigned len , unsigned flags ,
struct page * * pagep , void * * fsdata )
{
struct inode * inode = mapping - > host ;
struct page * page ;
pgoff_t index = pos > > PAGE_CACHE_SHIFT ;
page = grab_cache_page_write_begin ( mapping , index , flags ) ;
if ( ! page )
return - ENOMEM ;
* pagep = page ;
if ( ( len = = PAGE_CACHE_SIZE ) | | PageUptodate ( page ) )
return 0 ;
if ( ( pos & PAGE_CACHE_MASK ) > = i_size_read ( inode ) ) {
unsigned start = pos & ( PAGE_CACHE_SIZE - 1 ) ;
unsigned end = start + len ;
/* Reading beyond i_size is simple: memset to zero */
zero_user_segments ( page , 0 , start , end , PAGE_CACHE_SIZE ) ;
return 0 ;
}
return logfs_readpage_nolock ( page ) ;
}
static int logfs_write_end ( struct file * file , struct address_space * mapping ,
loff_t pos , unsigned len , unsigned copied , struct page * page ,
void * fsdata )
{
struct inode * inode = mapping - > host ;
pgoff_t index = page - > index ;
unsigned start = pos & ( PAGE_CACHE_SIZE - 1 ) ;
unsigned end = start + copied ;
int ret = 0 ;
BUG_ON ( PAGE_CACHE_SIZE ! = inode - > i_sb - > s_blocksize ) ;
BUG_ON ( page - > index > I3_BLOCKS ) ;
if ( copied < len ) {
/*
* Short write of a non - initialized paged . Just tell userspace
* to retry the entire page .
*/
if ( ! PageUptodate ( page ) ) {
copied = 0 ;
goto out ;
}
}
if ( copied = = 0 )
goto out ; /* FIXME: do we need to update inode? */
if ( i_size_read ( inode ) < ( index < < PAGE_CACHE_SHIFT ) + end ) {
i_size_write ( inode , ( index < < PAGE_CACHE_SHIFT ) + end ) ;
mark_inode_dirty_sync ( inode ) ;
}
SetPageUptodate ( page ) ;
if ( ! PageDirty ( page ) ) {
if ( ! get_page_reserve ( inode , page ) )
__set_page_dirty_nobuffers ( page ) ;
else
ret = logfs_write_buf ( inode , page , WF_LOCK ) ;
}
out :
unlock_page ( page ) ;
page_cache_release ( page ) ;
return ret ? ret : copied ;
}
int logfs_readpage ( struct file * file , struct page * page )
{
int ret ;
ret = logfs_readpage_nolock ( page ) ;
unlock_page ( page ) ;
return ret ;
}
/* Clear the page's dirty flag in the radix tree. */
/* TODO: mucking with PageWriteback is silly. Add a generic function to clear
* the dirty bit from the radix tree for filesystems that don ' t have to wait
* for page writeback to finish ( i . e . any compressing filesystem ) .
*/
static void clear_radix_tree_dirty ( struct page * page )
{
BUG_ON ( PagePrivate ( page ) | | page - > private ) ;
set_page_writeback ( page ) ;
end_page_writeback ( page ) ;
}
static int __logfs_writepage ( struct page * page )
{
struct inode * inode = page - > mapping - > host ;
int err ;
err = logfs_write_buf ( inode , page , WF_LOCK ) ;
if ( err )
set_page_dirty ( page ) ;
else
clear_radix_tree_dirty ( page ) ;
unlock_page ( page ) ;
return err ;
}
static int logfs_writepage ( struct page * page , struct writeback_control * wbc )
{
struct inode * inode = page - > mapping - > host ;
loff_t i_size = i_size_read ( inode ) ;
pgoff_t end_index = i_size > > PAGE_CACHE_SHIFT ;
unsigned offset ;
u64 bix ;
level_t level ;
log_file ( " logfs_writepage(%lx, %lx, %p) \n " , inode - > i_ino , page - > index ,
page ) ;
logfs_unpack_index ( page - > index , & bix , & level ) ;
/* Indirect blocks are never truncated */
if ( level ! = 0 )
return __logfs_writepage ( page ) ;
/*
* TODO : everything below is a near - verbatim copy of nobh_writepage ( ) .
* The relevant bits should be factored out after logfs is merged .
*/
/* Is the page fully inside i_size? */
if ( bix < end_index )
return __logfs_writepage ( page ) ;
/* Is the page fully outside i_size? (truncate in progress) */
offset = i_size & ( PAGE_CACHE_SIZE - 1 ) ;
if ( bix > end_index | | offset = = 0 ) {
unlock_page ( page ) ;
return 0 ; /* don't care */
}
/*
* The page straddles i_size . It must be zeroed out on each and every
* writepage invokation because it may be mmapped . " A file is mapped
* in multiples of the page size . For a file that is not a multiple of
* the page size , the remaining memory is zeroed when mapped , and
* writes to that region are not written out to the file . "
*/
zero_user_segment ( page , offset , PAGE_CACHE_SIZE ) ;
return __logfs_writepage ( page ) ;
}
2013-05-22 07:17:23 +04:00
static void logfs_invalidatepage ( struct page * page , unsigned int offset ,
unsigned int length )
2009-11-20 22:13:39 +03:00
{
2010-05-04 21:41:09 +04:00
struct logfs_block * block = logfs_block ( page ) ;
if ( block - > reserved_bytes ) {
struct super_block * sb = page - > mapping - > host - > i_sb ;
struct logfs_super * super = logfs_super ( sb ) ;
super - > s_dirty_pages - = block - > reserved_bytes ;
block - > ops - > free_block ( sb , block ) ;
BUG_ON ( bitmap_weight ( block - > alias_map , LOGFS_BLOCK_FACTOR ) ) ;
} else
move_page_to_btree ( page ) ;
2009-11-20 22:13:39 +03:00
BUG_ON ( PagePrivate ( page ) | | page - > private ) ;
}
static int logfs_releasepage ( struct page * page , gfp_t only_xfs_uses_this )
{
return 0 ; /* None of these are easy to release */
}
2010-04-28 00:30:06 +04:00
long logfs_ioctl ( struct file * file , unsigned int cmd , unsigned long arg )
2009-11-20 22:13:39 +03:00
{
2013-01-24 02:07:38 +04:00
struct inode * inode = file_inode ( file ) ;
2009-11-20 22:13:39 +03:00
struct logfs_inode * li = logfs_inode ( inode ) ;
unsigned int oldflags , flags ;
int err ;
switch ( cmd ) {
case FS_IOC_GETFLAGS :
flags = li - > li_flags & LOGFS_FL_USER_VISIBLE ;
return put_user ( flags , ( int __user * ) arg ) ;
case FS_IOC_SETFLAGS :
if ( IS_RDONLY ( inode ) )
return - EROFS ;
2011-03-24 02:43:26 +03:00
if ( ! inode_owner_or_capable ( inode ) )
2009-11-20 22:13:39 +03:00
return - EACCES ;
err = get_user ( flags , ( int __user * ) arg ) ;
if ( err )
return err ;
mutex_lock ( & inode - > i_mutex ) ;
oldflags = li - > li_flags ;
flags & = LOGFS_FL_USER_MODIFIABLE ;
flags | = oldflags & ~ LOGFS_FL_USER_MODIFIABLE ;
li - > li_flags = flags ;
mutex_unlock ( & inode - > i_mutex ) ;
inode - > i_ctime = CURRENT_TIME ;
mark_inode_dirty_sync ( inode ) ;
return 0 ;
default :
return - ENOTTY ;
}
}
2011-07-17 04:44:56 +04:00
int logfs_fsync ( struct file * file , loff_t start , loff_t end , int datasync )
2009-11-20 22:13:39 +03:00
{
2010-05-26 19:53:25 +04:00
struct super_block * sb = file - > f_mapping - > host - > i_sb ;
2011-07-17 04:44:56 +04:00
struct inode * inode = file - > f_mapping - > host ;
int ret ;
ret = filemap_write_and_wait_range ( inode - > i_mapping , start , end ) ;
if ( ret )
return ret ;
2009-11-20 22:13:39 +03:00
2011-07-17 04:44:56 +04:00
mutex_lock ( & inode - > i_mutex ) ;
2012-01-28 10:06:06 +04:00
logfs_get_wblocks ( sb , NULL , WF_LOCK ) ;
2010-05-06 00:33:36 +04:00
logfs_write_anchor ( sb ) ;
2012-01-28 10:06:06 +04:00
logfs_put_wblocks ( sb , NULL , WF_LOCK ) ;
2011-07-17 04:44:56 +04:00
mutex_unlock ( & inode - > i_mutex ) ;
2009-11-20 22:13:39 +03:00
return 0 ;
}
static int logfs_setattr ( struct dentry * dentry , struct iattr * attr )
{
struct inode * inode = dentry - > d_inode ;
int err = 0 ;
2010-06-04 13:30:03 +04:00
err = inode_change_ok ( inode , attr ) ;
if ( err )
return err ;
2010-06-04 13:30:02 +04:00
if ( attr - > ia_valid & ATTR_SIZE ) {
2009-11-20 22:13:39 +03:00
err = logfs_truncate ( inode , attr - > ia_size ) ;
2010-06-04 13:30:02 +04:00
if ( err )
return err ;
}
2009-11-20 22:13:39 +03:00
2010-06-04 13:30:02 +04:00
setattr_copy ( inode , attr ) ;
mark_inode_dirty ( inode ) ;
return 0 ;
2009-11-20 22:13:39 +03:00
}
const struct inode_operations logfs_reg_iops = {
. setattr = logfs_setattr ,
} ;
const struct file_operations logfs_reg_fops = {
2014-04-02 22:33:16 +04:00
. read_iter = generic_file_read_iter ,
2014-04-03 11:17:43 +04:00
. write_iter = generic_file_write_iter ,
2009-11-20 22:13:39 +03:00
. fsync = logfs_fsync ,
2010-04-28 00:30:06 +04:00
. unlocked_ioctl = logfs_ioctl ,
2009-11-20 22:13:39 +03:00
. llseek = generic_file_llseek ,
. mmap = generic_file_readonly_mmap ,
. open = generic_file_open ,
2014-04-02 22:33:16 +04:00
. read = new_sync_read ,
2014-04-03 11:17:43 +04:00
. write = new_sync_write ,
2009-11-20 22:13:39 +03:00
} ;
const struct address_space_operations logfs_reg_aops = {
. invalidatepage = logfs_invalidatepage ,
. readpage = logfs_readpage ,
. releasepage = logfs_releasepage ,
. set_page_dirty = __set_page_dirty_nobuffers ,
. writepage = logfs_writepage ,
. writepages = generic_writepages ,
. write_begin = logfs_write_begin ,
. write_end = logfs_write_end ,
} ;