2006-10-11 12:20:50 +04:00
/*
2006-10-11 12:20:53 +04:00
* linux / fs / ext4 / ioctl . c
2006-10-11 12:20:50 +04:00
*
* Copyright ( C ) 1993 , 1994 , 1995
* Remy Card ( card @ masi . ibp . fr )
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie ( Paris VI )
*/
# include <linux/fs.h>
2006-10-11 12:21:01 +04:00
# include <linux/jbd2.h>
2006-10-11 12:20:50 +04:00
# include <linux/capability.h>
# include <linux/time.h>
# include <linux/compat.h>
2008-02-16 01:37:46 +03:00
# include <linux/mount.h>
2009-06-18 03:24:03 +04:00
# include <linux/file.h>
2006-10-11 12:20:50 +04:00
# include <asm/uaccess.h>
2008-04-30 02:13:32 +04:00
# include "ext4_jbd2.h"
# include "ext4.h"
2006-10-11 12:20:50 +04:00
2012-01-05 02:09:44 +04:00
# define MAX_32_NUM ((((unsigned long long) 1) << 32) - 1)
2013-04-08 20:54:05 +04:00
/**
* Swap memory between @ a and @ b for @ len bytes .
*
* @ a : pointer to first memory area
* @ b : pointer to second memory area
* @ len : number of bytes to swap
*
*/
static void memswap ( void * a , void * b , size_t len )
{
unsigned char * ap , * bp ;
unsigned char tmp ;
ap = ( unsigned char * ) a ;
bp = ( unsigned char * ) b ;
while ( len - - > 0 ) {
tmp = * ap ;
* ap = * bp ;
* bp = tmp ;
ap + + ;
bp + + ;
}
}
/**
* Swap i_data and associated attributes between @ inode1 and @ inode2 .
* This function is used for the primary swap between inode1 and inode2
* and also to revert this primary swap in case of errors .
*
* Therefore you have to make sure , that calling this method twice
* will revert all changes .
*
* @ inode1 : pointer to first inode
* @ inode2 : pointer to second inode
*/
static void swap_inode_data ( struct inode * inode1 , struct inode * inode2 )
{
loff_t isize ;
struct ext4_inode_info * ei1 ;
struct ext4_inode_info * ei2 ;
ei1 = EXT4_I ( inode1 ) ;
ei2 = EXT4_I ( inode2 ) ;
memswap ( & inode1 - > i_flags , & inode2 - > i_flags , sizeof ( inode1 - > i_flags ) ) ;
memswap ( & inode1 - > i_version , & inode2 - > i_version ,
sizeof ( inode1 - > i_version ) ) ;
memswap ( & inode1 - > i_blocks , & inode2 - > i_blocks ,
sizeof ( inode1 - > i_blocks ) ) ;
memswap ( & inode1 - > i_bytes , & inode2 - > i_bytes , sizeof ( inode1 - > i_bytes ) ) ;
memswap ( & inode1 - > i_atime , & inode2 - > i_atime , sizeof ( inode1 - > i_atime ) ) ;
memswap ( & inode1 - > i_mtime , & inode2 - > i_mtime , sizeof ( inode1 - > i_mtime ) ) ;
memswap ( ei1 - > i_data , ei2 - > i_data , sizeof ( ei1 - > i_data ) ) ;
memswap ( & ei1 - > i_flags , & ei2 - > i_flags , sizeof ( ei1 - > i_flags ) ) ;
memswap ( & ei1 - > i_disksize , & ei2 - > i_disksize , sizeof ( ei1 - > i_disksize ) ) ;
2013-08-12 17:29:30 +04:00
ext4_es_remove_extent ( inode1 , 0 , EXT_MAX_BLOCKS ) ;
ext4_es_remove_extent ( inode2 , 0 , EXT_MAX_BLOCKS ) ;
ext4_es_lru_del ( inode1 ) ;
ext4_es_lru_del ( inode2 ) ;
2013-04-08 20:54:05 +04:00
isize = i_size_read ( inode1 ) ;
i_size_write ( inode1 , i_size_read ( inode2 ) ) ;
i_size_write ( inode2 , isize ) ;
}
/**
* Swap the information from the given @ inode and the inode
* EXT4_BOOT_LOADER_INO . It will basically swap i_data and all other
* important fields of the inodes .
*
* @ sb : the super block of the filesystem
* @ inode : the inode to swap with EXT4_BOOT_LOADER_INO
*
*/
static long swap_inode_boot_loader ( struct super_block * sb ,
struct inode * inode )
{
handle_t * handle ;
int err ;
struct inode * inode_bl ;
struct ext4_inode_info * ei ;
struct ext4_inode_info * ei_bl ;
struct ext4_sb_info * sbi ;
if ( inode - > i_nlink ! = 1 | | ! S_ISREG ( inode - > i_mode ) ) {
err = - EINVAL ;
goto swap_boot_out ;
}
if ( ! inode_owner_or_capable ( inode ) | | ! capable ( CAP_SYS_ADMIN ) ) {
err = - EPERM ;
goto swap_boot_out ;
}
sbi = EXT4_SB ( sb ) ;
ei = EXT4_I ( inode ) ;
inode_bl = ext4_iget ( sb , EXT4_BOOT_LOADER_INO ) ;
if ( IS_ERR ( inode_bl ) ) {
err = PTR_ERR ( inode_bl ) ;
goto swap_boot_out ;
}
ei_bl = EXT4_I ( inode_bl ) ;
filemap_flush ( inode - > i_mapping ) ;
filemap_flush ( inode_bl - > i_mapping ) ;
/* Protect orig inodes against a truncate and make sure,
* that only 1 swap_inode_boot_loader is running . */
2012-04-18 23:16:33 +04:00
lock_two_nondirectories ( inode , inode_bl ) ;
2013-04-08 20:54:05 +04:00
truncate_inode_pages ( & inode - > i_data , 0 ) ;
truncate_inode_pages ( & inode_bl - > i_data , 0 ) ;
/* Wait for all existing dio workers */
ext4_inode_block_unlocked_dio ( inode ) ;
ext4_inode_block_unlocked_dio ( inode_bl ) ;
inode_dio_wait ( inode ) ;
inode_dio_wait ( inode_bl ) ;
handle = ext4_journal_start ( inode_bl , EXT4_HT_MOVE_EXTENTS , 2 ) ;
if ( IS_ERR ( handle ) ) {
err = - EINVAL ;
goto swap_boot_out ;
}
/* Protect extent tree against block allocations via delalloc */
ext4_double_down_write_data_sem ( inode , inode_bl ) ;
if ( inode_bl - > i_nlink = = 0 ) {
/* this inode has never been used as a BOOT_LOADER */
set_nlink ( inode_bl , 1 ) ;
i_uid_write ( inode_bl , 0 ) ;
i_gid_write ( inode_bl , 0 ) ;
inode_bl - > i_flags = 0 ;
ei_bl - > i_flags = 0 ;
inode_bl - > i_version = 1 ;
i_size_write ( inode_bl , 0 ) ;
inode_bl - > i_mode = S_IFREG ;
if ( EXT4_HAS_INCOMPAT_FEATURE ( sb ,
EXT4_FEATURE_INCOMPAT_EXTENTS ) ) {
ext4_set_inode_flag ( inode_bl , EXT4_INODE_EXTENTS ) ;
ext4_ext_tree_init ( handle , inode_bl ) ;
} else
memset ( ei_bl - > i_data , 0 , sizeof ( ei_bl - > i_data ) ) ;
}
swap_inode_data ( inode , inode_bl ) ;
inode - > i_ctime = inode_bl - > i_ctime = ext4_current_time ( inode ) ;
spin_lock ( & sbi - > s_next_gen_lock ) ;
inode - > i_generation = sbi - > s_next_generation + + ;
inode_bl - > i_generation = sbi - > s_next_generation + + ;
spin_unlock ( & sbi - > s_next_gen_lock ) ;
ext4_discard_preallocations ( inode ) ;
err = ext4_mark_inode_dirty ( handle , inode ) ;
if ( err < 0 ) {
ext4_warning ( inode - > i_sb ,
" couldn't mark inode #%lu dirty (err %d) " ,
inode - > i_ino , err ) ;
/* Revert all changes: */
swap_inode_data ( inode , inode_bl ) ;
} else {
err = ext4_mark_inode_dirty ( handle , inode_bl ) ;
if ( err < 0 ) {
ext4_warning ( inode_bl - > i_sb ,
" couldn't mark inode #%lu dirty (err %d) " ,
inode_bl - > i_ino , err ) ;
/* Revert all changes: */
swap_inode_data ( inode , inode_bl ) ;
ext4_mark_inode_dirty ( handle , inode ) ;
}
}
ext4_journal_stop ( handle ) ;
ext4_double_up_write_data_sem ( inode , inode_bl ) ;
ext4_inode_resume_unlocked_dio ( inode ) ;
ext4_inode_resume_unlocked_dio ( inode_bl ) ;
2012-04-18 23:16:33 +04:00
unlock_two_nondirectories ( inode , inode_bl ) ;
2013-04-08 20:54:05 +04:00
iput ( inode_bl ) ;
swap_boot_out :
return err ;
}
2008-04-30 06:03:54 +04:00
long ext4_ioctl ( struct file * filp , unsigned int cmd , unsigned long arg )
2006-10-11 12:20:50 +04:00
{
2013-01-24 02:07:38 +04:00
struct inode * inode = file_inode ( filp ) ;
2011-09-10 02:36:51 +04:00
struct super_block * sb = inode - > i_sb ;
2006-10-11 12:20:53 +04:00
struct ext4_inode_info * ei = EXT4_I ( inode ) ;
2006-10-11 12:20:50 +04:00
unsigned int flags ;
2008-09-09 06:25:24 +04:00
ext4_debug ( " cmd = %u, arg = %lu \n " , cmd , arg ) ;
2006-10-11 12:20:50 +04:00
switch ( cmd ) {
2006-10-11 12:20:53 +04:00
case EXT4_IOC_GETFLAGS :
2007-07-18 17:24:20 +04:00
ext4_get_inode_flags ( ei ) ;
2006-10-11 12:20:53 +04:00
flags = ei - > i_flags & EXT4_FL_USER_VISIBLE ;
2006-10-11 12:20:50 +04:00
return put_user ( flags , ( int __user * ) arg ) ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC_SETFLAGS : {
2006-10-11 12:20:50 +04:00
handle_t * handle = NULL ;
2008-10-09 07:34:06 +04:00
int err , migrate = 0 ;
2006-10-11 12:20:53 +04:00
struct ext4_iloc iloc ;
2012-06-01 07:46:01 +04:00
unsigned int oldflags , mask , i ;
2006-10-11 12:20:50 +04:00
unsigned int jflag ;
2011-03-24 02:43:26 +03:00
if ( ! inode_owner_or_capable ( inode ) )
2006-10-11 12:20:50 +04:00
return - EACCES ;
if ( get_user ( flags , ( int __user * ) arg ) )
return - EFAULT ;
2011-11-23 20:57:51 +04:00
err = mnt_want_write_file ( filp ) ;
2008-02-16 01:37:46 +03:00
if ( err )
return err ;
2009-02-16 02:09:20 +03:00
flags = ext4_mask_flags ( inode - > i_mode , flags ) ;
2006-10-11 12:20:50 +04:00
2008-02-16 01:37:46 +03:00
err = - EPERM ;
2006-10-11 12:20:50 +04:00
mutex_lock ( & inode - > i_mutex ) ;
2007-11-15 03:58:56 +03:00
/* Is it quota file? Do not allow user to mess with it */
2008-02-16 01:37:46 +03:00
if ( IS_NOQUOTA ( inode ) )
goto flags_out ;
2006-10-11 12:20:50 +04:00
oldflags = ei - > i_flags ;
/* The JOURNAL_DATA flag is modifiable only by root */
2006-10-11 12:20:53 +04:00
jflag = flags & EXT4_JOURNAL_DATA_FL ;
2006-10-11 12:20:50 +04:00
/*
* The IMMUTABLE and APPEND_ONLY flags can only be changed by
* the relevant capability .
*
* This test looks nicer . Thanks to Pauline Middelink
*/
2006-10-11 12:20:53 +04:00
if ( ( flags ^ oldflags ) & ( EXT4_APPEND_FL | EXT4_IMMUTABLE_FL ) ) {
2008-02-16 01:37:46 +03:00
if ( ! capable ( CAP_LINUX_IMMUTABLE ) )
goto flags_out ;
2006-10-11 12:20:50 +04:00
}
/*
* The JOURNAL_DATA flag can only be changed by
* the relevant capability .
*/
2006-10-11 12:20:53 +04:00
if ( ( jflag ^ oldflags ) & ( EXT4_JOURNAL_DATA_FL ) ) {
2008-02-16 01:37:46 +03:00
if ( ! capable ( CAP_SYS_RESOURCE ) )
goto flags_out ;
2006-10-11 12:20:50 +04:00
}
2013-04-04 06:04:52 +04:00
if ( ( flags ^ oldflags ) & EXT4_EXTENTS_FL )
2008-10-09 07:34:06 +04:00
migrate = 1 ;
2006-10-11 12:20:50 +04:00
2010-02-24 17:52:53 +03:00
if ( flags & EXT4_EOFBLOCKS_FL ) {
/* we don't support adding EOFBLOCKS flag */
if ( ! ( oldflags & EXT4_EOFBLOCKS_FL ) ) {
err = - EOPNOTSUPP ;
goto flags_out ;
}
} else if ( oldflags & EXT4_EOFBLOCKS_FL )
ext4_truncate ( inode ) ;
2013-02-09 06:59:22 +04:00
handle = ext4_journal_start ( inode , EXT4_HT_INODE , 1 ) ;
2006-10-11 12:20:50 +04:00
if ( IS_ERR ( handle ) ) {
2008-02-16 01:37:46 +03:00
err = PTR_ERR ( handle ) ;
goto flags_out ;
2006-10-11 12:20:50 +04:00
}
if ( IS_SYNC ( inode ) )
2009-01-07 08:06:22 +03:00
ext4_handle_sync ( handle ) ;
2006-10-11 12:20:53 +04:00
err = ext4_reserve_inode_write ( handle , inode , & iloc ) ;
2006-10-11 12:20:50 +04:00
if ( err )
goto flags_err ;
2012-06-01 07:46:01 +04:00
for ( i = 0 , mask = 1 ; i < 32 ; i + + , mask < < = 1 ) {
if ( ! ( mask & EXT4_FL_USER_MODIFIABLE ) )
continue ;
if ( mask & flags )
ext4_set_inode_flag ( inode , i ) ;
else
ext4_clear_inode_flag ( inode , i ) ;
}
2006-10-11 12:20:50 +04:00
2006-10-11 12:20:53 +04:00
ext4_set_inode_flags ( inode ) ;
2007-07-18 17:15:20 +04:00
inode - > i_ctime = ext4_current_time ( inode ) ;
2006-10-11 12:20:50 +04:00
2006-10-11 12:20:53 +04:00
err = ext4_mark_iloc_dirty ( handle , inode , & iloc ) ;
2006-10-11 12:20:50 +04:00
flags_err :
2006-10-11 12:20:53 +04:00
ext4_journal_stop ( handle ) ;
2008-02-16 01:37:46 +03:00
if ( err )
goto flags_out ;
2006-10-11 12:20:50 +04:00
2006-10-11 12:20:53 +04:00
if ( ( jflag ^ oldflags ) & ( EXT4_JOURNAL_DATA_FL ) )
err = ext4_change_inode_journal_flag ( inode , jflag ) ;
2008-10-09 07:34:06 +04:00
if ( err )
goto flags_out ;
2013-04-04 06:04:52 +04:00
if ( migrate ) {
if ( flags & EXT4_EXTENTS_FL )
err = ext4_ext_migrate ( inode ) ;
else
err = ext4_ind_migrate ( inode ) ;
}
2008-02-16 01:37:46 +03:00
flags_out :
2006-10-11 12:20:50 +04:00
mutex_unlock ( & inode - > i_mutex ) ;
2011-12-09 17:06:57 +04:00
mnt_drop_write_file ( filp ) ;
2006-10-11 12:20:50 +04:00
return err ;
}
2006-10-11 12:20:53 +04:00
case EXT4_IOC_GETVERSION :
case EXT4_IOC_GETVERSION_OLD :
2006-10-11 12:20:50 +04:00
return put_user ( inode - > i_generation , ( int __user * ) arg ) ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC_SETVERSION :
case EXT4_IOC_SETVERSION_OLD : {
2006-10-11 12:20:50 +04:00
handle_t * handle ;
2006-10-11 12:20:53 +04:00
struct ext4_iloc iloc ;
2006-10-11 12:20:50 +04:00
__u32 generation ;
int err ;
2011-03-24 02:43:26 +03:00
if ( ! inode_owner_or_capable ( inode ) )
2006-10-11 12:20:50 +04:00
return - EPERM ;
2008-02-16 01:37:46 +03:00
2012-04-30 02:31:10 +04:00
if ( EXT4_HAS_RO_COMPAT_FEATURE ( inode - > i_sb ,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM ) ) {
ext4_warning ( sb , " Setting inode version is not "
" supported with metadata_csum enabled. " ) ;
return - ENOTTY ;
}
2011-11-23 20:57:51 +04:00
err = mnt_want_write_file ( filp ) ;
2008-02-16 01:37:46 +03:00
if ( err )
return err ;
if ( get_user ( generation , ( int __user * ) arg ) ) {
err = - EFAULT ;
goto setversion_out ;
}
2006-10-11 12:20:50 +04:00
2012-01-03 05:31:52 +04:00
mutex_lock ( & inode - > i_mutex ) ;
2013-02-09 06:59:22 +04:00
handle = ext4_journal_start ( inode , EXT4_HT_INODE , 1 ) ;
2008-02-16 01:37:46 +03:00
if ( IS_ERR ( handle ) ) {
err = PTR_ERR ( handle ) ;
2012-01-03 05:31:52 +04:00
goto unlock_out ;
2008-02-16 01:37:46 +03:00
}
2006-10-11 12:20:53 +04:00
err = ext4_reserve_inode_write ( handle , inode , & iloc ) ;
2006-10-11 12:20:50 +04:00
if ( err = = 0 ) {
2007-07-18 17:15:20 +04:00
inode - > i_ctime = ext4_current_time ( inode ) ;
2006-10-11 12:20:50 +04:00
inode - > i_generation = generation ;
2006-10-11 12:20:53 +04:00
err = ext4_mark_iloc_dirty ( handle , inode , & iloc ) ;
2006-10-11 12:20:50 +04:00
}
2006-10-11 12:20:53 +04:00
ext4_journal_stop ( handle ) ;
2012-01-03 05:31:52 +04:00
unlock_out :
mutex_unlock ( & inode - > i_mutex ) ;
2008-02-16 01:37:46 +03:00
setversion_out :
2011-12-09 17:06:57 +04:00
mnt_drop_write_file ( filp ) ;
2006-10-11 12:20:50 +04:00
return err ;
}
2006-10-11 12:20:53 +04:00
case EXT4_IOC_GROUP_EXTEND : {
ext4_fsblk_t n_blocks_count ;
2009-07-13 17:30:17 +04:00
int err , err2 = 0 ;
2006-10-11 12:20:50 +04:00
2011-07-27 05:35:44 +04:00
err = ext4_resize_begin ( sb ) ;
if ( err )
return err ;
2006-10-11 12:20:50 +04:00
2012-01-05 02:09:52 +04:00
if ( get_user ( n_blocks_count , ( __u32 __user * ) arg ) ) {
err = - EFAULT ;
goto group_extend_out ;
}
2006-10-11 12:20:50 +04:00
2011-09-10 02:36:51 +04:00
if ( EXT4_HAS_RO_COMPAT_FEATURE ( sb ,
EXT4_FEATURE_RO_COMPAT_BIGALLOC ) ) {
ext4_msg ( sb , KERN_ERR ,
" Online resizing not supported with bigalloc " ) ;
2012-01-05 02:09:52 +04:00
err = - EOPNOTSUPP ;
goto group_extend_out ;
2011-09-10 02:36:51 +04:00
}
2011-11-23 20:57:51 +04:00
err = mnt_want_write_file ( filp ) ;
2008-02-16 01:37:46 +03:00
if ( err )
2012-01-05 02:09:52 +04:00
goto group_extend_out ;
2008-02-16 01:37:46 +03:00
2006-10-11 12:20:53 +04:00
err = ext4_group_extend ( sb , EXT4_SB ( sb ) - > s_es , n_blocks_count ) ;
2009-07-13 17:30:17 +04:00
if ( EXT4_SB ( sb ) - > s_journal ) {
jbd2_journal_lock_updates ( EXT4_SB ( sb ) - > s_journal ) ;
err2 = jbd2_journal_flush ( EXT4_SB ( sb ) - > s_journal ) ;
jbd2_journal_unlock_updates ( EXT4_SB ( sb ) - > s_journal ) ;
}
2008-10-11 04:29:21 +04:00
if ( err = = 0 )
err = err2 ;
2011-12-09 17:06:57 +04:00
mnt_drop_write_file ( filp ) ;
2012-01-05 02:09:52 +04:00
group_extend_out :
2011-07-27 05:35:44 +04:00
ext4_resize_end ( sb ) ;
2006-10-11 12:20:50 +04:00
return err ;
}
2009-06-18 03:24:03 +04:00
case EXT4_IOC_MOVE_EXT : {
struct move_extent me ;
2012-08-28 20:52:22 +04:00
struct fd donor ;
int err ;
2009-06-18 03:24:03 +04:00
2009-12-07 07:38:31 +03:00
if ( ! ( filp - > f_mode & FMODE_READ ) | |
! ( filp - > f_mode & FMODE_WRITE ) )
return - EBADF ;
2009-06-18 03:24:03 +04:00
if ( copy_from_user ( & me ,
( struct move_extent __user * ) arg , sizeof ( me ) ) )
return - EFAULT ;
2009-12-07 07:38:31 +03:00
me . moved_len = 0 ;
2009-06-18 03:24:03 +04:00
2012-08-28 20:52:22 +04:00
donor = fdget ( me . donor_fd ) ;
if ( ! donor . file )
2009-06-18 03:24:03 +04:00
return - EBADF ;
2012-08-28 20:52:22 +04:00
if ( ! ( donor . file - > f_mode & FMODE_WRITE ) ) {
2009-12-07 07:38:31 +03:00
err = - EBADF ;
goto mext_out ;
2009-06-18 03:24:03 +04:00
}
2011-09-10 02:36:51 +04:00
if ( EXT4_HAS_RO_COMPAT_FEATURE ( sb ,
EXT4_FEATURE_RO_COMPAT_BIGALLOC ) ) {
ext4_msg ( sb , KERN_ERR ,
" Online defrag not supported with bigalloc " ) ;
2012-08-27 05:00:03 +04:00
err = - EOPNOTSUPP ;
goto mext_out ;
2011-09-10 02:36:51 +04:00
}
2011-11-23 20:57:51 +04:00
err = mnt_want_write_file ( filp ) ;
2009-12-07 07:38:31 +03:00
if ( err )
goto mext_out ;
2012-08-28 20:52:22 +04:00
err = ext4_move_extents ( filp , donor . file , me . orig_start ,
2009-06-18 03:24:03 +04:00
me . donor_start , me . len , & me . moved_len ) ;
2011-12-09 17:06:57 +04:00
mnt_drop_write_file ( filp ) ;
2009-06-18 03:24:03 +04:00
2010-05-17 15:00:00 +04:00
if ( copy_to_user ( ( struct move_extent __user * ) arg ,
2010-03-04 08:39:24 +03:00
& me , sizeof ( me ) ) )
2009-12-07 07:38:31 +03:00
err = - EFAULT ;
mext_out :
2012-08-28 20:52:22 +04:00
fdput ( donor ) ;
2009-06-18 03:24:03 +04:00
return err ;
}
2006-10-11 12:20:53 +04:00
case EXT4_IOC_GROUP_ADD : {
struct ext4_new_group_data input ;
2009-07-13 17:30:17 +04:00
int err , err2 = 0 ;
2006-10-11 12:20:50 +04:00
2011-07-27 05:35:44 +04:00
err = ext4_resize_begin ( sb ) ;
if ( err )
return err ;
2006-10-11 12:20:50 +04:00
2006-10-11 12:20:53 +04:00
if ( copy_from_user ( & input , ( struct ext4_new_group_input __user * ) arg ,
2012-01-05 02:09:52 +04:00
sizeof ( input ) ) ) {
err = - EFAULT ;
goto group_add_out ;
}
2006-10-11 12:20:50 +04:00
2011-09-10 02:36:51 +04:00
if ( EXT4_HAS_RO_COMPAT_FEATURE ( sb ,
EXT4_FEATURE_RO_COMPAT_BIGALLOC ) ) {
ext4_msg ( sb , KERN_ERR ,
" Online resizing not supported with bigalloc " ) ;
2012-01-05 02:09:52 +04:00
err = - EOPNOTSUPP ;
goto group_add_out ;
2011-09-10 02:36:51 +04:00
}
2011-11-23 20:57:51 +04:00
err = mnt_want_write_file ( filp ) ;
2008-02-16 01:37:46 +03:00
if ( err )
2012-01-05 02:09:52 +04:00
goto group_add_out ;
2008-02-16 01:37:46 +03:00
2006-10-11 12:20:53 +04:00
err = ext4_group_add ( sb , & input ) ;
2009-07-13 17:30:17 +04:00
if ( EXT4_SB ( sb ) - > s_journal ) {
jbd2_journal_lock_updates ( EXT4_SB ( sb ) - > s_journal ) ;
err2 = jbd2_journal_flush ( EXT4_SB ( sb ) - > s_journal ) ;
jbd2_journal_unlock_updates ( EXT4_SB ( sb ) - > s_journal ) ;
}
2008-10-11 04:29:21 +04:00
if ( err = = 0 )
err = err2 ;
2011-12-09 17:06:57 +04:00
mnt_drop_write_file ( filp ) ;
2013-01-13 17:41:45 +04:00
if ( ! err & & ext4_has_group_desc_csum ( sb ) & &
test_opt ( sb , INIT_INODE_TABLE ) )
err = ext4_register_li_request ( sb , input . group ) ;
2012-01-05 02:09:52 +04:00
group_add_out :
2011-07-27 05:35:44 +04:00
ext4_resize_end ( sb ) ;
2006-10-11 12:20:50 +04:00
return err ;
}
2008-01-29 07:58:26 +03:00
case EXT4_IOC_MIGRATE :
2008-09-13 20:52:26 +04:00
{
int err ;
2011-03-24 02:43:26 +03:00
if ( ! inode_owner_or_capable ( inode ) )
2008-09-13 20:52:26 +04:00
return - EACCES ;
2011-11-23 20:57:51 +04:00
err = mnt_want_write_file ( filp ) ;
2008-09-13 20:52:26 +04:00
if ( err )
return err ;
/*
* inode_mutex prevent write and truncate on the file .
* Read still goes through . We take i_data_sem in
* ext4_ext_swap_inode_data before we switch the
* inode format to prevent read .
*/
mutex_lock ( & ( inode - > i_mutex ) ) ;
err = ext4_ext_migrate ( inode ) ;
mutex_unlock ( & ( inode - > i_mutex ) ) ;
2011-12-09 17:06:57 +04:00
mnt_drop_write_file ( filp ) ;
2008-09-13 20:52:26 +04:00
return err ;
}
2008-01-29 07:58:26 +03:00
2009-02-26 09:04:07 +03:00
case EXT4_IOC_ALLOC_DA_BLKS :
{
int err ;
2011-03-24 02:43:26 +03:00
if ( ! inode_owner_or_capable ( inode ) )
2009-02-26 09:04:07 +03:00
return - EACCES ;
2011-11-23 20:57:51 +04:00
err = mnt_want_write_file ( filp ) ;
2009-02-26 09:04:07 +03:00
if ( err )
return err ;
err = ext4_alloc_da_blocks ( inode ) ;
2011-12-09 17:06:57 +04:00
mnt_drop_write_file ( filp ) ;
2009-02-26 09:04:07 +03:00
return err ;
}
2013-04-08 20:54:05 +04:00
case EXT4_IOC_SWAP_BOOT :
if ( ! ( filp - > f_mode & FMODE_WRITE ) )
return - EBADF ;
return swap_inode_boot_loader ( sb , inode ) ;
2012-01-05 02:09:44 +04:00
case EXT4_IOC_RESIZE_FS : {
ext4_fsblk_t n_blocks_count ;
int err = 0 , err2 = 0 ;
2013-01-13 17:41:45 +04:00
ext4_group_t o_group = EXT4_SB ( sb ) - > s_groups_count ;
2012-01-05 02:09:44 +04:00
if ( EXT4_HAS_RO_COMPAT_FEATURE ( sb ,
EXT4_FEATURE_RO_COMPAT_BIGALLOC ) ) {
ext4_msg ( sb , KERN_ERR ,
" Online resizing not (yet) supported with bigalloc " ) ;
return - EOPNOTSUPP ;
}
if ( copy_from_user ( & n_blocks_count , ( __u64 __user * ) arg ,
sizeof ( __u64 ) ) ) {
return - EFAULT ;
}
err = ext4_resize_begin ( sb ) ;
if ( err )
return err ;
2012-07-19 11:19:07 +04:00
err = mnt_want_write_file ( filp ) ;
2012-01-05 02:09:44 +04:00
if ( err )
goto resizefs_out ;
err = ext4_resize_fs ( sb , n_blocks_count ) ;
if ( EXT4_SB ( sb ) - > s_journal ) {
jbd2_journal_lock_updates ( EXT4_SB ( sb ) - > s_journal ) ;
err2 = jbd2_journal_flush ( EXT4_SB ( sb ) - > s_journal ) ;
jbd2_journal_unlock_updates ( EXT4_SB ( sb ) - > s_journal ) ;
}
if ( err = = 0 )
err = err2 ;
2012-07-19 11:19:07 +04:00
mnt_drop_write_file ( filp ) ;
2013-01-13 17:41:45 +04:00
if ( ! err & & ( o_group > EXT4_SB ( sb ) - > s_groups_count ) & &
ext4_has_group_desc_csum ( sb ) & &
test_opt ( sb , INIT_INODE_TABLE ) )
err = ext4_register_li_request ( sb , o_group ) ;
2012-01-05 02:09:44 +04:00
resizefs_out :
ext4_resize_end ( sb ) ;
return err ;
}
2010-11-20 05:47:07 +03:00
case FITRIM :
{
2011-02-23 20:42:32 +03:00
struct request_queue * q = bdev_get_queue ( sb - > s_bdev ) ;
2010-11-20 05:47:07 +03:00
struct fstrim_range range ;
int ret = 0 ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
2011-02-23 20:42:32 +03:00
if ( ! blk_queue_discard ( q ) )
return - EOPNOTSUPP ;
2011-10-18 18:59:51 +04:00
if ( copy_from_user ( & range , ( struct fstrim_range __user * ) arg ,
2010-11-20 05:47:07 +03:00
sizeof ( range ) ) )
return - EFAULT ;
2011-02-24 01:49:51 +03:00
range . minlen = max ( ( unsigned int ) range . minlen ,
q - > limits . discard_granularity ) ;
2010-11-20 05:47:07 +03:00
ret = ext4_trim_fs ( sb , & range ) ;
if ( ret < 0 )
return ret ;
2011-10-18 18:59:51 +04:00
if ( copy_to_user ( ( struct fstrim_range __user * ) arg , & range ,
2010-11-20 05:47:07 +03:00
sizeof ( range ) ) )
return - EFAULT ;
return 0 ;
}
2013-08-17 06:05:14 +04:00
case EXT4_IOC_PRECACHE_EXTENTS :
return ext4_ext_precache ( inode ) ;
2010-11-20 05:47:07 +03:00
2006-10-11 12:20:50 +04:00
default :
return - ENOTTY ;
}
}
# ifdef CONFIG_COMPAT
2006-10-11 12:20:53 +04:00
long ext4_compat_ioctl ( struct file * file , unsigned int cmd , unsigned long arg )
2006-10-11 12:20:50 +04:00
{
/* These are just misnamed, they actually get/put from/to user an int */
switch ( cmd ) {
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_GETFLAGS :
cmd = EXT4_IOC_GETFLAGS ;
2006-10-11 12:20:50 +04:00
break ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_SETFLAGS :
cmd = EXT4_IOC_SETFLAGS ;
2006-10-11 12:20:50 +04:00
break ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_GETVERSION :
cmd = EXT4_IOC_GETVERSION ;
2006-10-11 12:20:50 +04:00
break ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_SETVERSION :
cmd = EXT4_IOC_SETVERSION ;
2006-10-11 12:20:50 +04:00
break ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_GROUP_EXTEND :
cmd = EXT4_IOC_GROUP_EXTEND ;
2006-10-11 12:20:50 +04:00
break ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_GETVERSION_OLD :
cmd = EXT4_IOC_GETVERSION_OLD ;
2006-10-11 12:20:50 +04:00
break ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_SETVERSION_OLD :
cmd = EXT4_IOC_SETVERSION_OLD ;
2006-10-11 12:20:50 +04:00
break ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_GETRSVSZ :
cmd = EXT4_IOC_GETRSVSZ ;
2006-10-11 12:20:50 +04:00
break ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_SETRSVSZ :
cmd = EXT4_IOC_SETRSVSZ ;
2006-10-11 12:20:50 +04:00
break ;
2010-05-17 14:00:00 +04:00
case EXT4_IOC32_GROUP_ADD : {
struct compat_ext4_new_group_input __user * uinput ;
struct ext4_new_group_input input ;
mm_segment_t old_fs ;
int err ;
uinput = compat_ptr ( arg ) ;
err = get_user ( input . group , & uinput - > group ) ;
err | = get_user ( input . block_bitmap , & uinput - > block_bitmap ) ;
err | = get_user ( input . inode_bitmap , & uinput - > inode_bitmap ) ;
err | = get_user ( input . inode_table , & uinput - > inode_table ) ;
err | = get_user ( input . blocks_count , & uinput - > blocks_count ) ;
err | = get_user ( input . reserved_blocks ,
& uinput - > reserved_blocks ) ;
if ( err )
return - EFAULT ;
old_fs = get_fs ( ) ;
set_fs ( KERNEL_DS ) ;
err = ext4_ioctl ( file , EXT4_IOC_GROUP_ADD ,
( unsigned long ) & input ) ;
set_fs ( old_fs ) ;
return err ;
}
2010-05-15 08:00:00 +04:00
case EXT4_IOC_MOVE_EXT :
2011-03-21 06:16:58 +03:00
case FITRIM :
2012-01-05 02:09:44 +04:00
case EXT4_IOC_RESIZE_FS :
2013-08-17 06:05:14 +04:00
case EXT4_IOC_PRECACHE_EXTENTS :
2010-05-15 08:00:00 +04:00
break ;
2006-10-11 12:20:50 +04:00
default :
return - ENOIOCTLCMD ;
}
2008-04-30 06:03:54 +04:00
return ext4_ioctl ( file , cmd , ( unsigned long ) compat_ptr ( arg ) ) ;
2006-10-11 12:20:50 +04:00
}
# endif