2005-04-17 02:20:36 +04:00
/*
* Copyright 2000 by Hans Reiser , licensing governed by reiserfs / README
*/
2006-01-11 23:17:46 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# 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>
2005-07-13 07:21:28 +04:00
static int reiserfs_unpack ( struct inode * inode , struct file * filp ) ;
2005-04-17 02:20:36 +04:00
/*
* * 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 . . .
*/
2005-07-13 07:21:28 +04:00
int reiserfs_ioctl ( struct inode * inode , struct file * filp , unsigned int cmd ,
unsigned long arg )
2005-04-17 02:20:36 +04:00
{
unsigned int flags ;
switch ( cmd ) {
2005-07-13 07:21:28 +04:00
case REISERFS_IOC_UNPACK :
if ( S_ISREG ( inode - > i_mode ) ) {
if ( arg )
return reiserfs_unpack ( inode , filp ) ;
2005-04-17 02:20:36 +04:00
else
return 0 ;
} else
return - ENOTTY ;
2005-07-13 07:21:28 +04:00
/* following two cases are taken from fs/ext2/ioctl.c by Remy
Card ( card @ masi . ibp . fr ) */
2005-04-17 02:20:36 +04:00
case REISERFS_IOC_GETFLAGS :
2005-07-13 07:21:28 +04:00
if ( ! reiserfs_attrs ( inode - > i_sb ) )
2005-06-30 02:52:28 +04:00
return - ENOTTY ;
2005-07-13 07:21:28 +04: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 : {
if ( ! reiserfs_attrs ( inode - > i_sb ) )
return - ENOTTY ;
2005-06-30 02:52:28 +04:00
2005-07-13 07:21:28 +04:00
if ( IS_RDONLY ( inode ) )
return - EROFS ;
2005-04-17 02:20:36 +04:00
2005-07-13 07:21:28 +04:00
if ( ( current - > fsuid ! = inode - > i_uid )
& & ! capable ( CAP_FOWNER ) )
return - EPERM ;
2005-04-17 02:20:36 +04:00
2005-07-13 07:21:28 +04:00
if ( get_user ( flags , ( int __user * ) arg ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2005-07-13 07:21:28 +04:00
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 ) ) {
2005-04-17 02:20:36 +04:00
int result ;
2005-07-13 07:21:28 +04:00
result = reiserfs_unpack ( inode , filp ) ;
if ( result )
2005-04-17 02:20:36 +04:00
return result ;
2005-07-13 07:21:28 +04:00
}
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 ;
2005-04-17 02:20:36 +04:00
}
case REISERFS_IOC_GETVERSION :
2005-07-13 07:21:28 +04:00
return put_user ( inode - > i_generation , ( int __user * ) arg ) ;
2005-04-17 02:20:36 +04:00
case REISERFS_IOC_SETVERSION :
if ( ( current - > fsuid ! = inode - > i_uid ) & & ! capable ( CAP_FOWNER ) )
return - EPERM ;
if ( IS_RDONLY ( inode ) )
return - EROFS ;
2005-07-13 07:21:28 +04:00
if ( get_user ( inode - > i_generation , ( int __user * ) arg ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
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
*/
2005-07-13 07:21:28 +04:00
static int reiserfs_unpack ( struct inode * inode , struct file * filp )
2005-04-17 02:20:36 +04:00
{
2005-07-13 07:21:28 +04:00
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 ;
}
/* we need to make sure nobody is changing the file size beneath
* * us
*/
2006-01-10 02:59:24 +03:00
mutex_lock ( & inode - > i_mutex ) ;
2006-08-05 23:15:08 +04:00
reiserfs_write_lock ( inode - > i_sb ) ;
2005-07-13 07:21:28 +04:00
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 ) ;
2005-04-17 02:20:36 +04:00
REISERFS_I ( inode ) - > i_flags | = i_nopack_mask ;
2005-07-13 07:21:28 +04:00
out_unlock :
unlock_page ( page ) ;
page_cache_release ( page ) ;
out :
2006-01-10 02:59:24 +03:00
mutex_unlock ( & inode - > i_mutex ) ;
2005-07-13 07:21:28 +04:00
reiserfs_write_unlock ( inode - > i_sb ) ;
return retval ;
2005-04-17 02:20:36 +04:00
}