2005-04-16 15:20:36 -07:00
/*
* Copyright 2000 by Hans Reiser , licensing governed by reiserfs / README
*/
# include <linux/fs.h>
# include <linux/reiserfs_fs.h>
# include <linux/time.h>
# include <asm/uaccess.h>
# include <linux/pagemap.h>
# include <linux/smp_lock.h>
static int reiserfs_unpack ( struct inode * inode , struct file * filp ) ;
/*
* * reiserfs_ioctl - handler for ioctl for inode
* * supported commands :
* * 1 ) REISERFS_IOC_UNPACK - try to unpack tail from direct item into indirect
* * and prevent packing file ( argument arg has to be non - zero )
* * 2 ) REISERFS_IOC_ [ GS ] ETFLAGS , REISERFS_IOC_ [ GS ] ETVERSION
* * 3 ) That ' s all for a while . . .
*/
int reiserfs_ioctl ( struct inode * inode , struct file * filp , unsigned int cmd ,
unsigned long arg )
{
unsigned int flags ;
switch ( cmd ) {
case REISERFS_IOC_UNPACK :
if ( S_ISREG ( inode - > i_mode ) ) {
if ( arg )
return reiserfs_unpack ( inode , filp ) ;
else
return 0 ;
} else
return - ENOTTY ;
/* following two cases are taken from fs/ext2/ioctl.c by Remy
Card ( card @ masi . ibp . fr ) */
case REISERFS_IOC_GETFLAGS :
2005-06-29 18:52:28 -04:00
if ( ! reiserfs_attrs ( inode - > i_sb ) )
return - ENOTTY ;
2005-04-16 15:20:36 -07:00
flags = REISERFS_I ( inode ) - > i_attrs ;
i_attrs_to_sd_attrs ( inode , ( __u16 * ) & flags ) ;
return put_user ( flags , ( int __user * ) arg ) ;
case REISERFS_IOC_SETFLAGS : {
2005-06-29 18:52:28 -04:00
if ( ! reiserfs_attrs ( inode - > i_sb ) )
return - ENOTTY ;
2005-04-16 15:20:36 -07:00
if ( IS_RDONLY ( inode ) )
return - EROFS ;
if ( ( current - > fsuid ! = inode - > i_uid ) & & ! capable ( CAP_FOWNER ) )
return - EPERM ;
if ( get_user ( flags , ( int __user * ) arg ) )
return - EFAULT ;
if ( ( ( flags ^ REISERFS_I ( inode ) - > i_attrs ) & ( REISERFS_IMMUTABLE_FL | REISERFS_APPEND_FL ) ) & &
! capable ( CAP_LINUX_IMMUTABLE ) )
return - EPERM ;
if ( ( flags & REISERFS_NOTAIL_FL ) & &
S_ISREG ( inode - > i_mode ) ) {
int result ;
result = reiserfs_unpack ( inode , filp ) ;
if ( result )
return result ;
}
sd_attrs_to_i_attrs ( flags , inode ) ;
REISERFS_I ( inode ) - > i_attrs = flags ;
inode - > i_ctime = CURRENT_TIME_SEC ;
mark_inode_dirty ( inode ) ;
return 0 ;
}
case REISERFS_IOC_GETVERSION :
return put_user ( inode - > i_generation , ( int __user * ) arg ) ;
case REISERFS_IOC_SETVERSION :
if ( ( current - > fsuid ! = inode - > i_uid ) & & ! capable ( CAP_FOWNER ) )
return - EPERM ;
if ( IS_RDONLY ( inode ) )
return - EROFS ;
if ( get_user ( inode - > i_generation , ( int __user * ) arg ) )
return - EFAULT ;
inode - > i_ctime = CURRENT_TIME_SEC ;
mark_inode_dirty ( inode ) ;
return 0 ;
default :
return - ENOTTY ;
}
}
/*
* * reiserfs_unpack
* * Function try to convert tail from direct item into indirect .
* * It set up nopack attribute in the REISERFS_I ( inode ) - > nopack
*/
static int reiserfs_unpack ( struct inode * inode , struct file * filp )
{
int retval = 0 ;
int index ;
struct page * page ;
struct address_space * mapping ;
unsigned long write_from ;
unsigned long blocksize = inode - > i_sb - > s_blocksize ;
if ( inode - > i_size = = 0 ) {
REISERFS_I ( inode ) - > i_flags | = i_nopack_mask ;
return 0 ;
}
/* ioctl already done */
if ( REISERFS_I ( inode ) - > i_flags & i_nopack_mask ) {
return 0 ;
}
reiserfs_write_lock ( inode - > i_sb ) ;
/* we need to make sure nobody is changing the file size beneath
* * us
*/
down ( & inode - > i_sem ) ;
write_from = inode - > i_size & ( blocksize - 1 ) ;
/* if we are on a block boundary, we are already unpacked. */
if ( write_from = = 0 ) {
REISERFS_I ( inode ) - > i_flags | = i_nopack_mask ;
goto out ;
}
/* we unpack by finding the page with the tail, and calling
* * reiserfs_prepare_write on that page . This will force a
* * reiserfs_get_block to unpack the tail for us .
*/
index = inode - > i_size > > PAGE_CACHE_SHIFT ;
mapping = inode - > i_mapping ;
page = grab_cache_page ( mapping , index ) ;
retval = - ENOMEM ;
if ( ! page ) {
goto out ;
}
retval = mapping - > a_ops - > prepare_write ( NULL , page , write_from , write_from ) ;
if ( retval )
goto out_unlock ;
/* conversion can change page contents, must flush */
flush_dcache_page ( page ) ;
retval = mapping - > a_ops - > commit_write ( NULL , page , write_from , write_from ) ;
REISERFS_I ( inode ) - > i_flags | = i_nopack_mask ;
out_unlock :
unlock_page ( page ) ;
page_cache_release ( page ) ;
out :
up ( & inode - > i_sem ) ;
reiserfs_write_unlock ( inode - > i_sb ) ;
return retval ;
}