2005-04-17 02:20:36 +04:00
/*
2005-11-02 06:58:39 +03:00
* Copyright ( c ) 2000 - 2005 Silicon Graphics , Inc .
* All Rights Reserved .
2005-04-17 02:20:36 +04:00
*
2005-11-02 06:58:39 +03:00
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
2005-04-17 02:20:36 +04:00
* published by the Free Software Foundation .
*
2005-11-02 06:58:39 +03:00
* This program is distributed in the hope that it would be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2005-04-17 02:20:36 +04:00
*
2005-11-02 06:58:39 +03:00
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write the Free Software Foundation ,
* Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
2005-04-17 02:20:36 +04:00
*/
# include "xfs.h"
# include "xfs_fs.h"
2005-11-02 06:38:42 +03:00
# include "xfs_bit.h"
2005-04-17 02:20:36 +04:00
# include "xfs_log.h"
2005-11-02 06:38:42 +03:00
# include "xfs_inum.h"
2005-04-17 02:20:36 +04:00
# include "xfs_trans.h"
# include "xfs_sb.h"
2005-11-02 06:38:42 +03:00
# include "xfs_ag.h"
2005-04-17 02:20:36 +04:00
# include "xfs_dir2.h"
# include "xfs_alloc.h"
# include "xfs_dmapi.h"
# include "xfs_mount.h"
# include "xfs_bmap_btree.h"
2005-11-02 06:38:42 +03:00
# include "xfs_alloc_btree.h"
2005-04-17 02:20:36 +04:00
# include "xfs_ialloc_btree.h"
2005-11-02 06:38:42 +03:00
# include "xfs_attr_sf.h"
2005-04-17 02:20:36 +04:00
# include "xfs_dir2_sf.h"
# include "xfs_dinode.h"
# include "xfs_inode.h"
2009-03-05 17:20:25 +03:00
# include "xfs_ioctl.h"
2005-11-02 06:38:42 +03:00
# include "xfs_btree.h"
# include "xfs_ialloc.h"
2005-04-17 02:20:36 +04:00
# include "xfs_rtalloc.h"
# include "xfs_itable.h"
2005-11-02 06:38:42 +03:00
# include "xfs_error.h"
2005-04-17 02:20:36 +04:00
# include "xfs_rw.h"
# include "xfs_attr.h"
2005-11-02 06:38:42 +03:00
# include "xfs_bmap.h"
2005-04-17 02:20:36 +04:00
# include "xfs_buf_item.h"
# include "xfs_utils.h"
# include "xfs_dfrag.h"
# include "xfs_fsops.h"
2007-08-28 10:12:30 +04:00
# include "xfs_vnodeops.h"
2008-07-18 11:13:20 +04:00
# include "xfs_quota.h"
# include "xfs_inode_item.h"
2009-01-19 04:02:57 +03:00
# include "xfs_export.h"
2005-04-17 02:20:36 +04:00
2006-01-11 23:17:46 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/dcache.h>
# include <linux/mount.h>
# include <linux/namei.h>
# include <linux/pagemap.h>
2009-01-19 04:02:57 +03:00
# include <linux/exportfs.h>
2005-04-17 02:20:36 +04:00
/*
* xfs_find_handle maps from userspace xfs_fsop_handlereq structure to
* a file or fs handle .
*
* XFS_IOC_PATH_TO_FSHANDLE
* returns fs handle for a mount point or path within that mount point
* XFS_IOC_FD_TO_HANDLE
* returns full handle for a FD opened in user space
* XFS_IOC_PATH_TO_HANDLE
* returns full handle for a path
*/
2008-11-26 06:20:08 +03:00
int
2005-04-17 02:20:36 +04:00
xfs_find_handle (
unsigned int cmd ,
2008-11-26 06:20:06 +03:00
xfs_fsop_handlereq_t * hreq )
2005-04-17 02:20:36 +04:00
{
int hsize ;
xfs_handle_t handle ;
struct inode * inode ;
2009-02-08 23:51:14 +03:00
struct file * file = NULL ;
struct path path ;
int error ;
struct xfs_inode * ip ;
2005-04-17 02:20:36 +04:00
2009-02-08 23:51:14 +03:00
if ( cmd = = XFS_IOC_FD_TO_HANDLE ) {
2008-11-26 06:20:06 +03:00
file = fget ( hreq - > fd ) ;
2005-04-17 02:20:36 +04:00
if ( ! file )
2009-02-08 23:51:14 +03:00
return - EBADF ;
inode = file - > f_path . dentry - > d_inode ;
} else {
error = user_lpath ( ( const char __user * ) hreq - > path , & path ) ;
if ( error )
return error ;
inode = path . dentry - > d_inode ;
2005-04-17 02:20:36 +04:00
}
2009-02-08 23:51:14 +03:00
ip = XFS_I ( inode ) ;
/*
* We can only generate handles for inodes residing on a XFS filesystem ,
* and only for regular files , directories or symbolic links .
*/
error = - EINVAL ;
if ( inode - > i_sb - > s_magic ! = XFS_SB_MAGIC )
goto out_put ;
error = - EBADF ;
if ( ! S_ISREG ( inode - > i_mode ) & &
! S_ISDIR ( inode - > i_mode ) & &
! S_ISLNK ( inode - > i_mode ) )
goto out_put ;
memcpy ( & handle . ha_fsid , ip - > i_mount - > m_fixedfsid , sizeof ( xfs_fsid_t ) ) ;
if ( cmd = = XFS_IOC_PATH_TO_FSHANDLE ) {
/*
* This handle only contains an fsid , zero the rest .
*/
memset ( & handle . ha_fid , 0 , sizeof ( handle . ha_fid ) ) ;
hsize = sizeof ( xfs_fsid_t ) ;
} else {
2005-04-17 02:20:36 +04:00
int lock_mode ;
lock_mode = xfs_ilock_map_shared ( ip ) ;
2007-09-14 09:22:37 +04:00
handle . ha_fid . fid_len = sizeof ( xfs_fid_t ) -
sizeof ( handle . ha_fid . fid_len ) ;
handle . ha_fid . fid_pad = 0 ;
handle . ha_fid . fid_gen = ip - > i_d . di_gen ;
handle . ha_fid . fid_ino = ip - > i_ino ;
2005-04-17 02:20:36 +04:00
xfs_iunlock_map_shared ( ip , lock_mode ) ;
hsize = XFS_HSIZE ( handle ) ;
}
2009-02-08 23:51:14 +03:00
error = - EFAULT ;
2008-11-26 06:20:06 +03:00
if ( copy_to_user ( hreq - > ohandle , & handle , hsize ) | |
2009-02-08 23:51:14 +03:00
copy_to_user ( hreq - > ohandlen , & hsize , sizeof ( __s32 ) ) )
goto out_put ;
2005-04-17 02:20:36 +04:00
2009-02-08 23:51:14 +03:00
error = 0 ;
out_put :
if ( cmd = = XFS_IOC_FD_TO_HANDLE )
fput ( file ) ;
else
path_put ( & path ) ;
return error ;
2005-04-17 02:20:36 +04:00
}
/*
2009-01-19 04:02:57 +03:00
* No need to do permission checks on the various pathname components
* as the handle operations are privileged .
2005-04-17 02:20:36 +04:00
*/
STATIC int
2009-01-19 04:02:57 +03:00
xfs_handle_acceptable (
void * context ,
struct dentry * dentry )
{
return 1 ;
}
/*
* Convert userspace handle data into a dentry .
*/
struct dentry *
xfs_handle_to_dentry (
struct file * parfilp ,
void __user * uhandle ,
u32 hlen )
2005-04-17 02:20:36 +04:00
{
xfs_handle_t handle ;
2009-01-19 04:02:57 +03:00
struct xfs_fid64 fid ;
2005-04-17 02:20:36 +04:00
/*
* Only allow handle opens under a directory .
*/
2009-01-19 04:02:57 +03:00
if ( ! S_ISDIR ( parfilp - > f_path . dentry - > d_inode - > i_mode ) )
return ERR_PTR ( - ENOTDIR ) ;
if ( hlen ! = sizeof ( xfs_handle_t ) )
return ERR_PTR ( - EINVAL ) ;
if ( copy_from_user ( & handle , uhandle , hlen ) )
return ERR_PTR ( - EFAULT ) ;
if ( handle . ha_fid . fid_len ! =
sizeof ( handle . ha_fid ) - sizeof ( handle . ha_fid . fid_len ) )
return ERR_PTR ( - EINVAL ) ;
memset ( & fid , 0 , sizeof ( struct fid ) ) ;
fid . ino = handle . ha_fid . fid_ino ;
fid . gen = handle . ha_fid . fid_gen ;
return exportfs_decode_fh ( parfilp - > f_path . mnt , ( struct fid * ) & fid , 3 ,
FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG ,
xfs_handle_acceptable , NULL ) ;
}
2005-04-17 02:20:36 +04:00
2009-01-19 04:02:57 +03:00
STATIC struct dentry *
xfs_handlereq_to_dentry (
struct file * parfilp ,
xfs_fsop_handlereq_t * hreq )
{
return xfs_handle_to_dentry ( parfilp , hreq - > ihandle , hreq - > ihandlen ) ;
2005-04-17 02:20:36 +04:00
}
2008-11-26 06:20:08 +03:00
int
2005-04-17 02:20:36 +04:00
xfs_open_by_handle (
struct file * parfilp ,
2009-01-19 04:02:57 +03:00
xfs_fsop_handlereq_t * hreq )
2005-04-17 02:20:36 +04:00
{
2008-11-14 02:39:22 +03:00
const struct cred * cred = current_cred ( ) ;
2005-04-17 02:20:36 +04:00
int error ;
2009-01-19 04:02:57 +03:00
int fd ;
2005-04-17 02:20:36 +04:00
int permflag ;
struct file * filp ;
struct inode * inode ;
struct dentry * dentry ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - XFS_ERROR ( EPERM ) ;
2009-01-19 04:02:57 +03:00
dentry = xfs_handlereq_to_dentry ( parfilp , hreq ) ;
if ( IS_ERR ( dentry ) )
return PTR_ERR ( dentry ) ;
inode = dentry - > d_inode ;
2005-04-17 02:20:36 +04:00
/* Restrict xfs_open_by_handle to directories & regular files. */
if ( ! ( S_ISREG ( inode - > i_mode ) | | S_ISDIR ( inode - > i_mode ) ) ) {
2009-01-19 04:02:57 +03:00
error = - XFS_ERROR ( EPERM ) ;
goto out_dput ;
2005-04-17 02:20:36 +04:00
}
# if BITS_PER_LONG != 32
2008-11-26 06:20:06 +03:00
hreq - > oflags | = O_LARGEFILE ;
2005-04-17 02:20:36 +04:00
# endif
2009-01-19 04:02:57 +03:00
2005-04-17 02:20:36 +04:00
/* Put open permission in namei format. */
2008-11-26 06:20:06 +03:00
permflag = hreq - > oflags ;
2005-04-17 02:20:36 +04:00
if ( ( permflag + 1 ) & O_ACCMODE )
permflag + + ;
if ( permflag & O_TRUNC )
permflag | = 2 ;
if ( ( ! ( permflag & O_APPEND ) | | ( permflag & O_TRUNC ) ) & &
( permflag & FMODE_WRITE ) & & IS_APPEND ( inode ) ) {
2009-01-19 04:02:57 +03:00
error = - XFS_ERROR ( EPERM ) ;
goto out_dput ;
2005-04-17 02:20:36 +04:00
}
if ( ( permflag & FMODE_WRITE ) & & IS_IMMUTABLE ( inode ) ) {
2009-01-19 04:02:57 +03:00
error = - XFS_ERROR ( EACCES ) ;
goto out_dput ;
2005-04-17 02:20:36 +04:00
}
/* Can't write directories. */
2009-01-19 04:02:57 +03:00
if ( S_ISDIR ( inode - > i_mode ) & & ( permflag & FMODE_WRITE ) ) {
error = - XFS_ERROR ( EISDIR ) ;
goto out_dput ;
2005-04-17 02:20:36 +04:00
}
2009-01-19 04:02:57 +03:00
fd = get_unused_fd ( ) ;
if ( fd < 0 ) {
error = fd ;
goto out_dput ;
2005-04-17 02:20:36 +04:00
}
2009-01-19 04:02:57 +03:00
filp = dentry_open ( dentry , mntget ( parfilp - > f_path . mnt ) ,
hreq - > oflags , cred ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( filp ) ) {
2009-01-19 04:02:57 +03:00
put_unused_fd ( fd ) ;
return PTR_ERR ( filp ) ;
2005-04-17 02:20:36 +04:00
}
2008-12-09 12:47:33 +03:00
2006-11-11 10:04:47 +03:00
if ( inode - > i_mode & S_IFREG ) {
filp - > f_flags | = O_NOATIME ;
2008-12-09 12:47:33 +03:00
filp - > f_mode | = FMODE_NOCMTIME ;
2006-11-11 10:04:47 +03:00
}
2005-04-17 02:20:36 +04:00
2009-01-19 04:02:57 +03:00
fd_install ( fd , filp ) ;
return fd ;
out_dput :
dput ( dentry ) ;
return error ;
2005-04-17 02:20:36 +04:00
}
2007-08-28 07:59:03 +04:00
/*
* This is a copy from fs / namei . c : vfs_readlink ( ) , except for removing it ' s
* unused first argument .
*/
STATIC int
do_readlink (
char __user * buffer ,
int buflen ,
const char * link )
{
int len ;
len = PTR_ERR ( link ) ;
if ( IS_ERR ( link ) )
goto out ;
len = strlen ( link ) ;
if ( len > ( unsigned ) buflen )
len = buflen ;
if ( copy_to_user ( buffer , link , len ) )
len = - EFAULT ;
out :
return len ;
}
2008-11-26 06:20:08 +03:00
int
2005-04-17 02:20:36 +04:00
xfs_readlink_by_handle (
2009-01-19 04:02:57 +03:00
struct file * parfilp ,
xfs_fsop_handlereq_t * hreq )
2005-04-17 02:20:36 +04:00
{
2009-01-19 04:02:57 +03:00
struct dentry * dentry ;
2005-04-17 02:20:36 +04:00
__u32 olen ;
2007-08-28 07:59:03 +04:00
void * link ;
int error ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_SYS_ADMIN ) )
return - XFS_ERROR ( EPERM ) ;
2009-01-19 04:02:57 +03:00
dentry = xfs_handlereq_to_dentry ( parfilp , hreq ) ;
if ( IS_ERR ( dentry ) )
return PTR_ERR ( dentry ) ;
2005-04-17 02:20:36 +04:00
/* Restrict this handle operation to symlinks only. */
2009-01-19 04:02:57 +03:00
if ( ! S_ISLNK ( dentry - > d_inode - > i_mode ) ) {
2007-08-28 07:59:03 +04:00
error = - XFS_ERROR ( EINVAL ) ;
2009-01-19 04:02:57 +03:00
goto out_dput ;
2005-04-17 02:20:36 +04:00
}
2008-11-26 06:20:06 +03:00
if ( copy_from_user ( & olen , hreq - > ohandlen , sizeof ( __u32 ) ) ) {
2007-08-28 07:59:03 +04:00
error = - XFS_ERROR ( EFAULT ) ;
2009-01-19 04:02:57 +03:00
goto out_dput ;
2005-04-17 02:20:36 +04:00
}
2007-08-28 07:59:03 +04:00
link = kmalloc ( MAXPATHLEN + 1 , GFP_KERNEL ) ;
2009-01-19 04:02:57 +03:00
if ( ! link ) {
error = - XFS_ERROR ( ENOMEM ) ;
goto out_dput ;
}
2005-04-17 02:20:36 +04:00
2009-01-19 04:02:57 +03:00
error = - xfs_readlink ( XFS_I ( dentry - > d_inode ) , link ) ;
2006-06-09 11:00:52 +04:00
if ( error )
2007-08-28 07:59:03 +04:00
goto out_kfree ;
2008-11-26 06:20:06 +03:00
error = do_readlink ( hreq - > ohandle , olen , link ) ;
2007-08-28 07:59:03 +04:00
if ( error )
goto out_kfree ;
2006-06-09 11:00:52 +04:00
2007-08-28 07:59:03 +04:00
out_kfree :
kfree ( link ) ;
2009-01-19 04:02:57 +03:00
out_dput :
dput ( dentry ) ;
2007-08-28 07:59:03 +04:00
return error ;
2005-04-17 02:20:36 +04:00
}
STATIC int
xfs_fssetdm_by_handle (
2009-01-19 04:02:57 +03:00
struct file * parfilp ,
void __user * arg )
2005-04-17 02:20:36 +04:00
{
int error ;
struct fsdmidata fsd ;
xfs_fsop_setdm_handlereq_t dmhreq ;
2009-01-19 04:02:57 +03:00
struct dentry * dentry ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_MKNOD ) )
return - XFS_ERROR ( EPERM ) ;
if ( copy_from_user ( & dmhreq , arg , sizeof ( xfs_fsop_setdm_handlereq_t ) ) )
return - XFS_ERROR ( EFAULT ) ;
2009-01-19 04:02:57 +03:00
dentry = xfs_handlereq_to_dentry ( parfilp , & dmhreq . hreq ) ;
if ( IS_ERR ( dentry ) )
return PTR_ERR ( dentry ) ;
2005-04-17 02:20:36 +04:00
2009-01-19 04:02:57 +03:00
if ( IS_IMMUTABLE ( dentry - > d_inode ) | | IS_APPEND ( dentry - > d_inode ) ) {
2007-10-11 12:09:50 +04:00
error = - XFS_ERROR ( EPERM ) ;
goto out ;
2005-04-17 02:20:36 +04:00
}
if ( copy_from_user ( & fsd , dmhreq . data , sizeof ( fsd ) ) ) {
2007-10-11 12:09:50 +04:00
error = - XFS_ERROR ( EFAULT ) ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2009-01-19 04:02:57 +03:00
error = - xfs_set_dmattrs ( XFS_I ( dentry - > d_inode ) , fsd . fsd_dmevmask ,
2007-10-11 12:09:50 +04:00
fsd . fsd_dmstate ) ;
2005-04-17 02:20:36 +04:00
2007-10-11 12:09:50 +04:00
out :
2009-01-19 04:02:57 +03:00
dput ( dentry ) ;
2007-10-11 12:09:50 +04:00
return error ;
2005-04-17 02:20:36 +04:00
}
STATIC int
xfs_attrlist_by_handle (
2009-01-19 04:02:57 +03:00
struct file * parfilp ,
void __user * arg )
2005-04-17 02:20:36 +04:00
{
2009-01-19 04:02:57 +03:00
int error = - ENOMEM ;
2005-04-17 02:20:36 +04:00
attrlist_cursor_kern_t * cursor ;
xfs_fsop_attrlist_handlereq_t al_hreq ;
2009-01-19 04:02:57 +03:00
struct dentry * dentry ;
2005-04-17 02:20:36 +04:00
char * kbuf ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - XFS_ERROR ( EPERM ) ;
if ( copy_from_user ( & al_hreq , arg , sizeof ( xfs_fsop_attrlist_handlereq_t ) ) )
return - XFS_ERROR ( EFAULT ) ;
if ( al_hreq . buflen > XATTR_LIST_MAX )
return - XFS_ERROR ( EINVAL ) ;
2008-06-27 07:32:19 +04:00
/*
* Reject flags , only allow namespaces .
*/
if ( al_hreq . flags & ~ ( ATTR_ROOT | ATTR_SECURE ) )
return - XFS_ERROR ( EINVAL ) ;
2009-01-19 04:02:57 +03:00
dentry = xfs_handlereq_to_dentry ( parfilp , & al_hreq . hreq ) ;
if ( IS_ERR ( dentry ) )
return PTR_ERR ( dentry ) ;
2005-04-17 02:20:36 +04:00
kbuf = kmalloc ( al_hreq . buflen , GFP_KERNEL ) ;
if ( ! kbuf )
2009-01-19 04:02:57 +03:00
goto out_dput ;
2005-04-17 02:20:36 +04:00
cursor = ( attrlist_cursor_kern_t * ) & al_hreq . pos ;
2009-01-19 04:02:57 +03:00
error = - xfs_attr_list ( XFS_I ( dentry - > d_inode ) , kbuf , al_hreq . buflen ,
2007-08-29 04:58:01 +04:00
al_hreq . flags , cursor ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto out_kfree ;
if ( copy_to_user ( al_hreq . buffer , kbuf , al_hreq . buflen ) )
error = - EFAULT ;
out_kfree :
kfree ( kbuf ) ;
2009-01-19 04:02:57 +03:00
out_dput :
dput ( dentry ) ;
return error ;
2005-04-17 02:20:36 +04:00
}
2008-11-26 06:20:15 +03:00
int
2005-04-17 02:20:36 +04:00
xfs_attrmulti_attr_get (
2007-08-29 04:58:01 +04:00
struct inode * inode ,
2005-04-17 02:20:36 +04:00
char * name ,
char __user * ubuf ,
__uint32_t * len ,
__uint32_t flags )
{
char * kbuf ;
int error = EFAULT ;
2008-04-22 11:34:31 +04:00
2005-04-17 02:20:36 +04:00
if ( * len > XATTR_SIZE_MAX )
return EINVAL ;
kbuf = kmalloc ( * len , GFP_KERNEL ) ;
if ( ! kbuf )
return ENOMEM ;
2008-04-22 11:34:31 +04:00
error = xfs_attr_get ( XFS_I ( inode ) , name , kbuf , ( int * ) len , flags ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto out_kfree ;
if ( copy_to_user ( ubuf , kbuf , * len ) )
error = EFAULT ;
out_kfree :
kfree ( kbuf ) ;
return error ;
}
2008-11-26 06:20:15 +03:00
int
2005-04-17 02:20:36 +04:00
xfs_attrmulti_attr_set (
2007-08-29 04:58:01 +04:00
struct inode * inode ,
2005-04-17 02:20:36 +04:00
char * name ,
const char __user * ubuf ,
__uint32_t len ,
__uint32_t flags )
{
char * kbuf ;
int error = EFAULT ;
2007-08-29 04:58:01 +04:00
if ( IS_IMMUTABLE ( inode ) | | IS_APPEND ( inode ) )
2005-04-17 02:20:36 +04:00
return EPERM ;
if ( len > XATTR_SIZE_MAX )
return EINVAL ;
2009-04-08 11:08:04 +04:00
kbuf = memdup_user ( ubuf , len ) ;
if ( IS_ERR ( kbuf ) )
return PTR_ERR ( kbuf ) ;
2008-04-22 11:34:31 +04:00
2007-08-29 04:58:01 +04:00
error = xfs_attr_set ( XFS_I ( inode ) , name , kbuf , len , flags ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2008-11-26 06:20:15 +03:00
int
2005-04-17 02:20:36 +04:00
xfs_attrmulti_attr_remove (
2007-08-29 04:58:01 +04:00
struct inode * inode ,
2005-04-17 02:20:36 +04:00
char * name ,
__uint32_t flags )
{
2007-08-29 04:58:01 +04:00
if ( IS_IMMUTABLE ( inode ) | | IS_APPEND ( inode ) )
2005-04-17 02:20:36 +04:00
return EPERM ;
2007-08-29 04:58:01 +04:00
return xfs_attr_remove ( XFS_I ( inode ) , name , flags ) ;
2005-04-17 02:20:36 +04:00
}
STATIC int
xfs_attrmulti_by_handle (
2008-02-16 01:37:46 +03:00
struct file * parfilp ,
2009-01-19 04:02:57 +03:00
void __user * arg )
2005-04-17 02:20:36 +04:00
{
int error ;
xfs_attr_multiop_t * ops ;
xfs_fsop_attrmulti_handlereq_t am_hreq ;
2009-01-19 04:02:57 +03:00
struct dentry * dentry ;
2005-04-17 02:20:36 +04:00
unsigned int i , size ;
char * attr_name ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - XFS_ERROR ( EPERM ) ;
if ( copy_from_user ( & am_hreq , arg , sizeof ( xfs_fsop_attrmulti_handlereq_t ) ) )
return - XFS_ERROR ( EFAULT ) ;
2009-01-19 04:02:57 +03:00
dentry = xfs_handlereq_to_dentry ( parfilp , & am_hreq . hreq ) ;
if ( IS_ERR ( dentry ) )
return PTR_ERR ( dentry ) ;
2005-04-17 02:20:36 +04:00
error = E2BIG ;
2008-06-27 07:32:31 +04:00
size = am_hreq . opcount * sizeof ( xfs_attr_multiop_t ) ;
2005-04-17 02:20:36 +04:00
if ( ! size | | size > 16 * PAGE_SIZE )
2009-01-19 04:02:57 +03:00
goto out_dput ;
2005-04-17 02:20:36 +04:00
2009-04-08 11:08:04 +04:00
ops = memdup_user ( am_hreq . ops , size ) ;
if ( IS_ERR ( ops ) ) {
error = PTR_ERR ( ops ) ;
2009-01-19 04:02:57 +03:00
goto out_dput ;
2009-04-08 11:08:04 +04:00
}
2005-04-17 02:20:36 +04:00
attr_name = kmalloc ( MAXNAMELEN , GFP_KERNEL ) ;
if ( ! attr_name )
goto out_kfree_ops ;
error = 0 ;
for ( i = 0 ; i < am_hreq . opcount ; i + + ) {
ops [ i ] . am_error = strncpy_from_user ( attr_name ,
ops [ i ] . am_attrname , MAXNAMELEN ) ;
if ( ops [ i ] . am_error = = 0 | | ops [ i ] . am_error = = MAXNAMELEN )
error = - ERANGE ;
if ( ops [ i ] . am_error < 0 )
break ;
switch ( ops [ i ] . am_opcode ) {
case ATTR_OP_GET :
2009-01-19 04:02:57 +03:00
ops [ i ] . am_error = xfs_attrmulti_attr_get (
dentry - > d_inode , attr_name ,
ops [ i ] . am_attrvalue , & ops [ i ] . am_length ,
ops [ i ] . am_flags ) ;
2005-04-17 02:20:36 +04:00
break ;
case ATTR_OP_SET :
2008-02-16 01:37:46 +03:00
ops [ i ] . am_error = mnt_want_write ( parfilp - > f_path . mnt ) ;
if ( ops [ i ] . am_error )
break ;
2009-01-19 04:02:57 +03:00
ops [ i ] . am_error = xfs_attrmulti_attr_set (
dentry - > d_inode , attr_name ,
ops [ i ] . am_attrvalue , ops [ i ] . am_length ,
ops [ i ] . am_flags ) ;
2008-02-16 01:37:46 +03:00
mnt_drop_write ( parfilp - > f_path . mnt ) ;
2005-04-17 02:20:36 +04:00
break ;
case ATTR_OP_REMOVE :
2008-02-16 01:37:46 +03:00
ops [ i ] . am_error = mnt_want_write ( parfilp - > f_path . mnt ) ;
if ( ops [ i ] . am_error )
break ;
2009-01-19 04:02:57 +03:00
ops [ i ] . am_error = xfs_attrmulti_attr_remove (
dentry - > d_inode , attr_name ,
ops [ i ] . am_flags ) ;
2008-02-16 01:37:46 +03:00
mnt_drop_write ( parfilp - > f_path . mnt ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
ops [ i ] . am_error = EINVAL ;
}
}
if ( copy_to_user ( am_hreq . ops , ops , size ) )
error = XFS_ERROR ( EFAULT ) ;
kfree ( attr_name ) ;
out_kfree_ops :
kfree ( ops ) ;
2009-01-19 04:02:57 +03:00
out_dput :
dput ( dentry ) ;
2005-04-17 02:20:36 +04:00
return - error ;
}
2008-11-26 06:20:08 +03:00
int
2005-04-17 02:20:36 +04:00
xfs_ioc_space (
2007-08-28 10:12:30 +04:00
struct xfs_inode * ip ,
2006-09-28 04:52:04 +04:00
struct inode * inode ,
2005-04-17 02:20:36 +04:00
struct file * filp ,
int ioflags ,
unsigned int cmd ,
2008-11-26 06:20:06 +03:00
xfs_flock64_t * bf )
2005-04-17 02:20:36 +04:00
{
int attr_flags = 0 ;
int error ;
2008-11-26 06:20:06 +03:00
/*
* Only allow the sys admin to reserve space unless
* unwritten extents are enabled .
*/
if ( ! xfs_sb_version_hasextflgbit ( & ip - > i_mount - > m_sb ) & &
! capable ( CAP_SYS_ADMIN ) )
return - XFS_ERROR ( EPERM ) ;
2006-09-28 04:52:04 +04:00
if ( inode - > i_flags & ( S_IMMUTABLE | S_APPEND ) )
2005-04-17 02:20:36 +04:00
return - XFS_ERROR ( EPERM ) ;
2005-09-02 10:41:16 +04:00
if ( ! ( filp - > f_mode & FMODE_WRITE ) )
2005-04-17 02:20:36 +04:00
return - XFS_ERROR ( EBADF ) ;
2006-09-28 04:52:04 +04:00
if ( ! S_ISREG ( inode - > i_mode ) )
2005-04-17 02:20:36 +04:00
return - XFS_ERROR ( EINVAL ) ;
if ( filp - > f_flags & ( O_NDELAY | O_NONBLOCK ) )
2008-07-18 11:13:28 +04:00
attr_flags | = XFS_ATTR_NONBLOCK ;
2005-04-17 02:20:36 +04:00
if ( ioflags & IO_INVIS )
2008-07-18 11:13:28 +04:00
attr_flags | = XFS_ATTR_DMI ;
2005-04-17 02:20:36 +04:00
2008-11-26 06:20:06 +03:00
error = xfs_change_file_space ( ip , cmd , bf , filp - > f_pos , attr_flags ) ;
2005-04-17 02:20:36 +04:00
return - error ;
}
STATIC int
xfs_ioc_bulkstat (
xfs_mount_t * mp ,
unsigned int cmd ,
void __user * arg )
{
xfs_fsop_bulkreq_t bulkreq ;
int count ; /* # of records returned */
xfs_ino_t inlast ; /* last inode number */
int done ;
int error ;
/* done = 1 if there are more stats to get and if bulkstat */
/* should be called again (unused here, but used in dmapi) */
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
if ( XFS_FORCED_SHUTDOWN ( mp ) )
return - XFS_ERROR ( EIO ) ;
if ( copy_from_user ( & bulkreq , arg , sizeof ( xfs_fsop_bulkreq_t ) ) )
return - XFS_ERROR ( EFAULT ) ;
if ( copy_from_user ( & inlast , bulkreq . lastip , sizeof ( __s64 ) ) )
return - XFS_ERROR ( EFAULT ) ;
if ( ( count = bulkreq . icount ) < = 0 )
return - XFS_ERROR ( EINVAL ) ;
2007-11-23 08:30:32 +03:00
if ( bulkreq . ubuffer = = NULL )
return - XFS_ERROR ( EINVAL ) ;
2005-04-17 02:20:36 +04:00
if ( cmd = = XFS_IOC_FSINUMBERS )
error = xfs_inumbers ( mp , & inlast , & count ,
2007-07-11 05:10:19 +04:00
bulkreq . ubuffer , xfs_inumbers_fmt ) ;
2005-04-17 02:20:36 +04:00
else if ( cmd = = XFS_IOC_FSBULKSTAT_SINGLE )
error = xfs_bulkstat_single ( mp , & inlast ,
bulkreq . ubuffer , & done ) ;
2007-11-23 08:30:32 +03:00
else /* XFS_IOC_FSBULKSTAT */
error = xfs_bulkstat ( mp , & inlast , & count ,
( bulkstat_one_pf ) xfs_bulkstat_one , NULL ,
sizeof ( xfs_bstat_t ) , bulkreq . ubuffer ,
BULKSTAT_FG_QUICK , & done ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return - error ;
if ( bulkreq . ocount ! = NULL ) {
if ( copy_to_user ( bulkreq . lastip , & inlast ,
sizeof ( xfs_ino_t ) ) )
return - XFS_ERROR ( EFAULT ) ;
if ( copy_to_user ( bulkreq . ocount , & count , sizeof ( count ) ) )
return - XFS_ERROR ( EFAULT ) ;
}
return 0 ;
}
STATIC int
xfs_ioc_fsgeometry_v1 (
xfs_mount_t * mp ,
void __user * arg )
{
xfs_fsop_geom_v1_t fsgeo ;
int error ;
error = xfs_fs_geometry ( mp , ( xfs_fsop_geom_t * ) & fsgeo , 3 ) ;
if ( error )
return - error ;
if ( copy_to_user ( arg , & fsgeo , sizeof ( fsgeo ) ) )
return - XFS_ERROR ( EFAULT ) ;
return 0 ;
}
STATIC int
xfs_ioc_fsgeometry (
xfs_mount_t * mp ,
void __user * arg )
{
xfs_fsop_geom_t fsgeo ;
int error ;
error = xfs_fs_geometry ( mp , & fsgeo , 4 ) ;
if ( error )
return - error ;
if ( copy_to_user ( arg , & fsgeo , sizeof ( fsgeo ) ) )
return - XFS_ERROR ( EFAULT ) ;
return 0 ;
}
/*
* Linux extended inode flags interface .
*/
STATIC unsigned int
xfs_merge_ioc_xflags (
unsigned int flags ,
unsigned int start )
{
unsigned int xflags = start ;
2007-02-10 10:37:10 +03:00
if ( flags & FS_IMMUTABLE_FL )
2005-04-17 02:20:36 +04:00
xflags | = XFS_XFLAG_IMMUTABLE ;
else
xflags & = ~ XFS_XFLAG_IMMUTABLE ;
2007-02-10 10:37:10 +03:00
if ( flags & FS_APPEND_FL )
2005-04-17 02:20:36 +04:00
xflags | = XFS_XFLAG_APPEND ;
else
xflags & = ~ XFS_XFLAG_APPEND ;
2007-02-10 10:37:10 +03:00
if ( flags & FS_SYNC_FL )
2005-04-17 02:20:36 +04:00
xflags | = XFS_XFLAG_SYNC ;
else
xflags & = ~ XFS_XFLAG_SYNC ;
2007-02-10 10:37:10 +03:00
if ( flags & FS_NOATIME_FL )
2005-04-17 02:20:36 +04:00
xflags | = XFS_XFLAG_NOATIME ;
else
xflags & = ~ XFS_XFLAG_NOATIME ;
2007-02-10 10:37:10 +03:00
if ( flags & FS_NODUMP_FL )
2005-04-17 02:20:36 +04:00
xflags | = XFS_XFLAG_NODUMP ;
else
xflags & = ~ XFS_XFLAG_NODUMP ;
return xflags ;
}
STATIC unsigned int
xfs_di2lxflags (
__uint16_t di_flags )
{
unsigned int flags = 0 ;
if ( di_flags & XFS_DIFLAG_IMMUTABLE )
2007-02-10 10:37:10 +03:00
flags | = FS_IMMUTABLE_FL ;
2005-04-17 02:20:36 +04:00
if ( di_flags & XFS_DIFLAG_APPEND )
2007-02-10 10:37:10 +03:00
flags | = FS_APPEND_FL ;
2005-04-17 02:20:36 +04:00
if ( di_flags & XFS_DIFLAG_SYNC )
2007-02-10 10:37:10 +03:00
flags | = FS_SYNC_FL ;
2005-04-17 02:20:36 +04:00
if ( di_flags & XFS_DIFLAG_NOATIME )
2007-02-10 10:37:10 +03:00
flags | = FS_NOATIME_FL ;
2005-04-17 02:20:36 +04:00
if ( di_flags & XFS_DIFLAG_NODUMP )
2007-02-10 10:37:10 +03:00
flags | = FS_NODUMP_FL ;
2005-04-17 02:20:36 +04:00
return flags ;
}
2007-10-11 11:47:00 +04:00
STATIC int
xfs_ioc_fsgetxattr (
xfs_inode_t * ip ,
int attr ,
void __user * arg )
{
struct fsxattr fa ;
xfs_ilock ( ip , XFS_ILOCK_SHARED ) ;
fa . fsx_xflags = xfs_ip2xflags ( ip ) ;
fa . fsx_extsize = ip - > i_d . di_extsize < < ip - > i_mount - > m_sb . sb_blocklog ;
fa . fsx_projid = ip - > i_d . di_projid ;
if ( attr ) {
if ( ip - > i_afp ) {
if ( ip - > i_afp - > if_flags & XFS_IFEXTENTS )
fa . fsx_nextents = ip - > i_afp - > if_bytes /
sizeof ( xfs_bmbt_rec_t ) ;
else
fa . fsx_nextents = ip - > i_d . di_anextents ;
} else
fa . fsx_nextents = 0 ;
} else {
if ( ip - > i_df . if_flags & XFS_IFEXTENTS )
fa . fsx_nextents = ip - > i_df . if_bytes /
sizeof ( xfs_bmbt_rec_t ) ;
else
fa . fsx_nextents = ip - > i_d . di_nextents ;
}
xfs_iunlock ( ip , XFS_ILOCK_SHARED ) ;
if ( copy_to_user ( arg , & fa , sizeof ( fa ) ) )
return - EFAULT ;
return 0 ;
}
2008-07-18 11:13:20 +04:00
STATIC void
xfs_set_diflags (
struct xfs_inode * ip ,
unsigned int xflags )
{
unsigned int di_flags ;
/* can't set PREALLOC this way, just preserve it */
di_flags = ( ip - > i_d . di_flags & XFS_DIFLAG_PREALLOC ) ;
if ( xflags & XFS_XFLAG_IMMUTABLE )
di_flags | = XFS_DIFLAG_IMMUTABLE ;
if ( xflags & XFS_XFLAG_APPEND )
di_flags | = XFS_DIFLAG_APPEND ;
if ( xflags & XFS_XFLAG_SYNC )
di_flags | = XFS_DIFLAG_SYNC ;
if ( xflags & XFS_XFLAG_NOATIME )
di_flags | = XFS_DIFLAG_NOATIME ;
if ( xflags & XFS_XFLAG_NODUMP )
di_flags | = XFS_DIFLAG_NODUMP ;
if ( xflags & XFS_XFLAG_PROJINHERIT )
di_flags | = XFS_DIFLAG_PROJINHERIT ;
if ( xflags & XFS_XFLAG_NODEFRAG )
di_flags | = XFS_DIFLAG_NODEFRAG ;
if ( xflags & XFS_XFLAG_FILESTREAM )
di_flags | = XFS_DIFLAG_FILESTREAM ;
if ( ( ip - > i_d . di_mode & S_IFMT ) = = S_IFDIR ) {
if ( xflags & XFS_XFLAG_RTINHERIT )
di_flags | = XFS_DIFLAG_RTINHERIT ;
if ( xflags & XFS_XFLAG_NOSYMLINKS )
di_flags | = XFS_DIFLAG_NOSYMLINKS ;
if ( xflags & XFS_XFLAG_EXTSZINHERIT )
di_flags | = XFS_DIFLAG_EXTSZINHERIT ;
} else if ( ( ip - > i_d . di_mode & S_IFMT ) = = S_IFREG ) {
if ( xflags & XFS_XFLAG_REALTIME )
di_flags | = XFS_DIFLAG_REALTIME ;
if ( xflags & XFS_XFLAG_EXTSIZE )
di_flags | = XFS_DIFLAG_EXTSIZE ;
}
ip - > i_d . di_flags = di_flags ;
}
2008-07-21 10:16:15 +04:00
STATIC void
xfs_diflags_to_linux (
struct xfs_inode * ip )
{
2008-08-13 10:00:45 +04:00
struct inode * inode = VFS_I ( ip ) ;
2008-07-21 10:16:15 +04:00
unsigned int xflags = xfs_ip2xflags ( ip ) ;
if ( xflags & XFS_XFLAG_IMMUTABLE )
inode - > i_flags | = S_IMMUTABLE ;
else
inode - > i_flags & = ~ S_IMMUTABLE ;
if ( xflags & XFS_XFLAG_APPEND )
inode - > i_flags | = S_APPEND ;
else
inode - > i_flags & = ~ S_APPEND ;
if ( xflags & XFS_XFLAG_SYNC )
inode - > i_flags | = S_SYNC ;
else
inode - > i_flags & = ~ S_SYNC ;
if ( xflags & XFS_XFLAG_NOATIME )
inode - > i_flags | = S_NOATIME ;
else
inode - > i_flags & = ~ S_NOATIME ;
}
2008-07-18 11:13:20 +04:00
# define FSX_PROJID 1
# define FSX_EXTSIZE 2
# define FSX_XFLAGS 4
# define FSX_NONBLOCK 8
STATIC int
xfs_ioctl_setattr (
xfs_inode_t * ip ,
struct fsxattr * fa ,
int mask )
{
struct xfs_mount * mp = ip - > i_mount ;
struct xfs_trans * tp ;
unsigned int lock_flags = 0 ;
2009-06-08 17:33:32 +04:00
struct xfs_dquot * udqp = NULL ;
struct xfs_dquot * gdqp = NULL ;
2008-07-18 11:13:20 +04:00
struct xfs_dquot * olddquot = NULL ;
int code ;
xfs_itrace_entry ( ip ) ;
if ( mp - > m_flags & XFS_MOUNT_RDONLY )
return XFS_ERROR ( EROFS ) ;
if ( XFS_FORCED_SHUTDOWN ( mp ) )
return XFS_ERROR ( EIO ) ;
/*
* If disk quotas is on , we make sure that the dquots do exist on disk ,
* before we start any other transactions . Trying to do this later
* is messy . We don ' t care to take a readlock to look at the ids
* in inode here , because we can ' t hold it across the trans_reserve .
* If the IDs do change before we take the ilock , we ' re covered
* because the i_ * dquot fields will get updated anyway .
*/
if ( XFS_IS_QUOTA_ON ( mp ) & & ( mask & FSX_PROJID ) ) {
2009-06-08 17:33:32 +04:00
code = xfs_qm_vop_dqalloc ( ip , ip - > i_d . di_uid ,
2008-07-18 11:13:20 +04:00
ip - > i_d . di_gid , fa - > fsx_projid ,
XFS_QMOPT_PQUOTA , & udqp , & gdqp ) ;
if ( code )
return code ;
}
/*
* For the other attributes , we acquire the inode lock and
* first do an error checking pass .
*/
tp = xfs_trans_alloc ( mp , XFS_TRANS_SETATTR_NOT_SIZE ) ;
code = xfs_trans_reserve ( tp , 0 , XFS_ICHANGE_LOG_RES ( mp ) , 0 , 0 , 0 ) ;
if ( code )
goto error_return ;
lock_flags = XFS_ILOCK_EXCL ;
xfs_ilock ( ip , lock_flags ) ;
/*
* CAP_FOWNER overrides the following restrictions :
*
* The user ID of the calling process must be equal
* to the file owner ID , except in cases where the
* CAP_FSETID capability is applicable .
*/
2008-10-31 07:50:04 +03:00
if ( current_fsuid ( ) ! = ip - > i_d . di_uid & & ! capable ( CAP_FOWNER ) ) {
2008-07-18 11:13:20 +04:00
code = XFS_ERROR ( EPERM ) ;
goto error_return ;
}
/*
* Do a quota reservation only if projid is actually going to change .
*/
if ( mask & FSX_PROJID ) {
2009-06-08 17:33:32 +04:00
if ( XFS_IS_QUOTA_RUNNING ( mp ) & &
XFS_IS_PQUOTA_ON ( mp ) & &
2008-07-18 11:13:20 +04:00
ip - > i_d . di_projid ! = fa - > fsx_projid ) {
ASSERT ( tp ) ;
2009-06-08 17:33:32 +04:00
code = xfs_qm_vop_chown_reserve ( tp , ip , udqp , gdqp ,
2008-07-18 11:13:20 +04:00
capable ( CAP_FOWNER ) ?
XFS_QMOPT_FORCE_RES : 0 ) ;
if ( code ) /* out of quota */
goto error_return ;
}
}
if ( mask & FSX_EXTSIZE ) {
/*
* Can ' t change extent size if any extents are allocated .
*/
if ( ip - > i_d . di_nextents & &
( ( ip - > i_d . di_extsize < < mp - > m_sb . sb_blocklog ) ! =
fa - > fsx_extsize ) ) {
code = XFS_ERROR ( EINVAL ) ; /* EFBIG? */
goto error_return ;
}
/*
* Extent size must be a multiple of the appropriate block
* size , if set at all .
*/
if ( fa - > fsx_extsize ! = 0 ) {
xfs_extlen_t size ;
if ( XFS_IS_REALTIME_INODE ( ip ) | |
( ( mask & FSX_XFLAGS ) & &
( fa - > fsx_xflags & XFS_XFLAG_REALTIME ) ) ) {
size = mp - > m_sb . sb_rextsize < <
mp - > m_sb . sb_blocklog ;
} else {
size = mp - > m_sb . sb_blocksize ;
}
if ( fa - > fsx_extsize % size ) {
code = XFS_ERROR ( EINVAL ) ;
goto error_return ;
}
}
}
if ( mask & FSX_XFLAGS ) {
/*
* Can ' t change realtime flag if any extents are allocated .
*/
if ( ( ip - > i_d . di_nextents | | ip - > i_delayed_blks ) & &
( XFS_IS_REALTIME_INODE ( ip ) ) ! =
( fa - > fsx_xflags & XFS_XFLAG_REALTIME ) ) {
code = XFS_ERROR ( EINVAL ) ; /* EFBIG? */
goto error_return ;
}
/*
* If realtime flag is set then must have realtime data .
*/
if ( ( fa - > fsx_xflags & XFS_XFLAG_REALTIME ) ) {
if ( ( mp - > m_sb . sb_rblocks = = 0 ) | |
( mp - > m_sb . sb_rextsize = = 0 ) | |
( ip - > i_d . di_extsize % mp - > m_sb . sb_rextsize ) ) {
code = XFS_ERROR ( EINVAL ) ;
goto error_return ;
}
}
/*
* Can ' t modify an immutable / append - only file unless
* we have appropriate permission .
*/
if ( ( ip - > i_d . di_flags &
( XFS_DIFLAG_IMMUTABLE | XFS_DIFLAG_APPEND ) | |
( fa - > fsx_xflags &
( XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND ) ) ) & &
! capable ( CAP_LINUX_IMMUTABLE ) ) {
code = XFS_ERROR ( EPERM ) ;
goto error_return ;
}
}
xfs_trans_ijoin ( tp , ip , lock_flags ) ;
xfs_trans_ihold ( tp , ip ) ;
/*
* Change file ownership . Must be the owner or privileged .
*/
if ( mask & FSX_PROJID ) {
/*
* CAP_FSETID overrides the following restrictions :
*
* The set - user - ID and set - group - ID bits of a file will be
* cleared upon successful return from chown ( )
*/
if ( ( ip - > i_d . di_mode & ( S_ISUID | S_ISGID ) ) & &
! capable ( CAP_FSETID ) )
ip - > i_d . di_mode & = ~ ( S_ISUID | S_ISGID ) ;
/*
* Change the ownerships and register quota modifications
* in the transaction .
*/
if ( ip - > i_d . di_projid ! = fa - > fsx_projid ) {
2009-06-08 17:33:32 +04:00
if ( XFS_IS_QUOTA_RUNNING ( mp ) & & XFS_IS_PQUOTA_ON ( mp ) ) {
olddquot = xfs_qm_vop_chown ( tp , ip ,
2008-07-18 11:13:20 +04:00
& ip - > i_gdquot , gdqp ) ;
}
ip - > i_d . di_projid = fa - > fsx_projid ;
/*
* We may have to rev the inode as well as
* the superblock version number since projids didn ' t
* exist before DINODE_VERSION_2 and SB_VERSION_NLINK .
*/
2008-11-28 06:23:39 +03:00
if ( ip - > i_d . di_version = = 1 )
2008-07-18 11:13:20 +04:00
xfs_bump_ino_vers2 ( tp , ip ) ;
}
}
if ( mask & FSX_EXTSIZE )
ip - > i_d . di_extsize = fa - > fsx_extsize > > mp - > m_sb . sb_blocklog ;
2008-07-21 10:16:15 +04:00
if ( mask & FSX_XFLAGS ) {
2008-07-18 11:13:20 +04:00
xfs_set_diflags ( ip , fa - > fsx_xflags ) ;
2008-07-21 10:16:15 +04:00
xfs_diflags_to_linux ( ip ) ;
}
2008-07-18 11:13:20 +04:00
xfs_trans_log_inode ( tp , ip , XFS_ILOG_CORE ) ;
xfs_ichgtime ( ip , XFS_ICHGTIME_CHG ) ;
XFS_STATS_INC ( xs_ig_attrchg ) ;
/*
* If this is a synchronous mount , make sure that the
* transaction goes to disk before returning to the user .
* This is slightly sub - optimal in that truncates require
* two sync transactions instead of one for wsync filesystems .
* One for the truncate and one for the timestamps since we
* don ' t want to change the timestamps unless we ' re sure the
* truncate worked . Truncates are less than 1 % of the laddis
* mix so this probably isn ' t worth the trouble to optimize .
*/
if ( mp - > m_flags & XFS_MOUNT_WSYNC )
xfs_trans_set_sync ( tp ) ;
code = xfs_trans_commit ( tp , 0 ) ;
xfs_iunlock ( ip , lock_flags ) ;
/*
* Release any dquot ( s ) the inode had kept before chown .
*/
2009-06-08 17:33:32 +04:00
xfs_qm_dqrele ( olddquot ) ;
xfs_qm_dqrele ( udqp ) ;
xfs_qm_dqrele ( gdqp ) ;
2008-07-18 11:13:20 +04:00
if ( code )
return code ;
if ( DM_EVENT_ENABLED ( ip , DM_EVENT_ATTRIBUTE ) ) {
XFS_SEND_NAMESP ( mp , DM_EVENT_ATTRIBUTE , ip , DM_RIGHT_NULL ,
NULL , DM_RIGHT_NULL , NULL , NULL , 0 , 0 ,
( mask & FSX_NONBLOCK ) ? DM_FLAGS_NDELAY : 0 ) ;
}
return 0 ;
error_return :
2009-06-08 17:33:32 +04:00
xfs_qm_dqrele ( udqp ) ;
xfs_qm_dqrele ( gdqp ) ;
2008-07-18 11:13:20 +04:00
xfs_trans_cancel ( tp , 0 ) ;
if ( lock_flags )
xfs_iunlock ( ip , lock_flags ) ;
return code ;
}
2005-04-17 02:20:36 +04:00
STATIC int
2008-04-18 05:44:03 +04:00
xfs_ioc_fssetxattr (
2005-04-17 02:20:36 +04:00
xfs_inode_t * ip ,
struct file * filp ,
void __user * arg )
{
struct fsxattr fa ;
2008-07-18 11:13:20 +04:00
unsigned int mask ;
2008-04-18 05:44:03 +04:00
if ( copy_from_user ( & fa , arg , sizeof ( fa ) ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2008-07-18 11:13:20 +04:00
mask = FSX_XFLAGS | FSX_EXTSIZE | FSX_PROJID ;
2008-04-18 05:44:03 +04:00
if ( filp - > f_flags & ( O_NDELAY | O_NONBLOCK ) )
2008-07-18 11:13:20 +04:00
mask | = FSX_NONBLOCK ;
2005-04-17 02:20:36 +04:00
2008-07-18 11:13:20 +04:00
return - xfs_ioctl_setattr ( ip , & fa , mask ) ;
2008-04-18 05:44:03 +04:00
}
2005-04-17 02:20:36 +04:00
2008-04-18 05:44:03 +04:00
STATIC int
xfs_ioc_getxflags (
xfs_inode_t * ip ,
void __user * arg )
{
unsigned int flags ;
2005-04-17 02:20:36 +04:00
2008-04-18 05:44:03 +04:00
flags = xfs_di2lxflags ( ip - > i_d . di_flags ) ;
if ( copy_to_user ( arg , & flags , sizeof ( flags ) ) )
return - EFAULT ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
2008-04-18 05:44:03 +04:00
STATIC int
xfs_ioc_setxflags (
xfs_inode_t * ip ,
struct file * filp ,
void __user * arg )
{
2008-07-18 11:13:20 +04:00
struct fsxattr fa ;
2008-04-18 05:44:03 +04:00
unsigned int flags ;
2008-07-18 11:13:20 +04:00
unsigned int mask ;
2005-04-17 02:20:36 +04:00
2008-04-18 05:44:03 +04:00
if ( copy_from_user ( & flags , arg , sizeof ( flags ) ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2008-04-18 05:44:03 +04:00
if ( flags & ~ ( FS_IMMUTABLE_FL | FS_APPEND_FL | \
FS_NOATIME_FL | FS_NODUMP_FL | \
FS_SYNC_FL ) )
return - EOPNOTSUPP ;
2005-04-17 02:20:36 +04:00
2008-07-18 11:13:20 +04:00
mask = FSX_XFLAGS ;
2008-04-18 05:44:03 +04:00
if ( filp - > f_flags & ( O_NDELAY | O_NONBLOCK ) )
2008-07-18 11:13:20 +04:00
mask | = FSX_NONBLOCK ;
fa . fsx_xflags = xfs_merge_ioc_xflags ( flags , xfs_ip2xflags ( ip ) ) ;
2005-04-17 02:20:36 +04:00
2008-07-18 11:13:20 +04:00
return - xfs_ioctl_setattr ( ip , & fa , mask ) ;
2005-04-17 02:20:36 +04:00
}
2008-11-28 06:23:35 +03:00
STATIC int
xfs_getbmap_format ( void * * ap , struct getbmapx * bmv , int * full )
{
struct getbmap __user * base = * ap ;
/* copy only getbmap portion (not getbmapx) */
if ( copy_to_user ( base , bmv , sizeof ( struct getbmap ) ) )
return XFS_ERROR ( EFAULT ) ;
* ap + = sizeof ( struct getbmap ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
STATIC int
xfs_ioc_getbmap (
2007-08-28 10:12:30 +04:00
struct xfs_inode * ip ,
2005-04-17 02:20:36 +04:00
int ioflags ,
unsigned int cmd ,
void __user * arg )
{
2008-11-28 06:23:35 +03:00
struct getbmapx bmx ;
2005-04-17 02:20:36 +04:00
int error ;
2008-11-28 06:23:35 +03:00
if ( copy_from_user ( & bmx , arg , sizeof ( struct getbmapx ) ) )
2005-04-17 02:20:36 +04:00
return - XFS_ERROR ( EFAULT ) ;
2008-11-28 06:23:35 +03:00
if ( bmx . bmv_count < 2 )
2005-04-17 02:20:36 +04:00
return - XFS_ERROR ( EINVAL ) ;
2008-11-28 06:23:35 +03:00
bmx . bmv_iflags = ( cmd = = XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0 ) ;
2005-04-17 02:20:36 +04:00
if ( ioflags & IO_INVIS )
2008-11-28 06:23:35 +03:00
bmx . bmv_iflags | = BMV_IF_NO_DMAPI_READ ;
2005-04-17 02:20:36 +04:00
2008-11-28 06:23:35 +03:00
error = xfs_getbmap ( ip , & bmx , xfs_getbmap_format ,
( struct getbmap * ) arg + 1 ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return - error ;
2008-11-28 06:23:35 +03:00
/* copy back header - only size of getbmap */
if ( copy_to_user ( arg , & bmx , sizeof ( struct getbmap ) ) )
2005-04-17 02:20:36 +04:00
return - XFS_ERROR ( EFAULT ) ;
return 0 ;
}
2008-11-28 06:23:35 +03:00
STATIC int
xfs_getbmapx_format ( void * * ap , struct getbmapx * bmv , int * full )
{
struct getbmapx __user * base = * ap ;
if ( copy_to_user ( base , bmv , sizeof ( struct getbmapx ) ) )
return XFS_ERROR ( EFAULT ) ;
* ap + = sizeof ( struct getbmapx ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
STATIC int
xfs_ioc_getbmapx (
2007-08-28 10:12:30 +04:00
struct xfs_inode * ip ,
2005-04-17 02:20:36 +04:00
void __user * arg )
{
struct getbmapx bmx ;
int error ;
if ( copy_from_user ( & bmx , arg , sizeof ( bmx ) ) )
return - XFS_ERROR ( EFAULT ) ;
if ( bmx . bmv_count < 2 )
return - XFS_ERROR ( EINVAL ) ;
2008-11-28 06:23:35 +03:00
if ( bmx . bmv_iflags & ( ~ BMV_IF_VALID ) )
2005-04-17 02:20:36 +04:00
return - XFS_ERROR ( EINVAL ) ;
2008-11-28 06:23:35 +03:00
error = xfs_getbmap ( ip , & bmx , xfs_getbmapx_format ,
( struct getbmapx * ) arg + 1 ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return - error ;
2008-11-28 06:23:35 +03:00
/* copy back header */
if ( copy_to_user ( arg , & bmx , sizeof ( struct getbmapx ) ) )
2005-04-17 02:20:36 +04:00
return - XFS_ERROR ( EFAULT ) ;
return 0 ;
}
2008-04-18 05:44:03 +04:00
2008-12-09 12:47:33 +03:00
/*
* Note : some of the ioctl ' s return positive numbers as a
* byte count indicating success , such as readlink_by_handle .
* So we don ' t " sign flip " like most other routines . This means
* true errors need to be returned as a negative value .
*/
long
xfs_file_ioctl (
2008-04-18 05:44:03 +04:00
struct file * filp ,
unsigned int cmd ,
2008-12-09 12:47:33 +03:00
unsigned long p )
2008-04-18 05:44:03 +04:00
{
struct inode * inode = filp - > f_path . dentry - > d_inode ;
2008-12-09 12:47:33 +03:00
struct xfs_inode * ip = XFS_I ( inode ) ;
struct xfs_mount * mp = ip - > i_mount ;
void __user * arg = ( void __user * ) p ;
int ioflags = 0 ;
2008-04-18 05:44:03 +04:00
int error ;
2008-12-09 12:47:33 +03:00
if ( filp - > f_mode & FMODE_NOCMTIME )
ioflags | = IO_INVIS ;
2008-04-18 05:44:03 +04:00
2008-12-09 12:47:33 +03:00
xfs_itrace_entry ( ip ) ;
switch ( cmd ) {
2008-04-18 05:44:03 +04:00
case XFS_IOC_ALLOCSP :
case XFS_IOC_FREESP :
case XFS_IOC_RESVSP :
case XFS_IOC_UNRESVSP :
case XFS_IOC_ALLOCSP64 :
case XFS_IOC_FREESP64 :
case XFS_IOC_RESVSP64 :
2008-11-26 06:20:06 +03:00
case XFS_IOC_UNRESVSP64 : {
xfs_flock64_t bf ;
2008-04-18 05:44:03 +04:00
2008-11-26 06:20:06 +03:00
if ( copy_from_user ( & bf , arg , sizeof ( bf ) ) )
return - XFS_ERROR ( EFAULT ) ;
return xfs_ioc_space ( ip , inode , filp , ioflags , cmd , & bf ) ;
}
2008-04-18 05:44:03 +04:00
case XFS_IOC_DIOINFO : {
struct dioattr da ;
xfs_buftarg_t * target =
XFS_IS_REALTIME_INODE ( ip ) ?
mp - > m_rtdev_targp : mp - > m_ddev_targp ;
da . d_mem = da . d_miniosz = 1 < < target - > bt_sshift ;
da . d_maxiosz = INT_MAX & ~ ( da . d_miniosz - 1 ) ;
if ( copy_to_user ( arg , & da , sizeof ( da ) ) )
return - XFS_ERROR ( EFAULT ) ;
return 0 ;
}
case XFS_IOC_FSBULKSTAT_SINGLE :
case XFS_IOC_FSBULKSTAT :
case XFS_IOC_FSINUMBERS :
return xfs_ioc_bulkstat ( mp , cmd , arg ) ;
case XFS_IOC_FSGEOMETRY_V1 :
return xfs_ioc_fsgeometry_v1 ( mp , arg ) ;
case XFS_IOC_FSGEOMETRY :
return xfs_ioc_fsgeometry ( mp , arg ) ;
case XFS_IOC_GETVERSION :
return put_user ( inode - > i_generation , ( int __user * ) arg ) ;
case XFS_IOC_FSGETXATTR :
return xfs_ioc_fsgetxattr ( ip , 0 , arg ) ;
case XFS_IOC_FSGETXATTRA :
return xfs_ioc_fsgetxattr ( ip , 1 , arg ) ;
2008-04-18 06:59:45 +04:00
case XFS_IOC_FSSETXATTR :
return xfs_ioc_fssetxattr ( ip , filp , arg ) ;
2008-04-18 05:44:03 +04:00
case XFS_IOC_GETXFLAGS :
2008-04-18 06:59:45 +04:00
return xfs_ioc_getxflags ( ip , arg ) ;
2008-04-18 05:44:03 +04:00
case XFS_IOC_SETXFLAGS :
2008-04-18 06:59:45 +04:00
return xfs_ioc_setxflags ( ip , filp , arg ) ;
2008-04-18 05:44:03 +04:00
case XFS_IOC_FSSETDM : {
struct fsdmidata dmi ;
if ( copy_from_user ( & dmi , arg , sizeof ( dmi ) ) )
return - XFS_ERROR ( EFAULT ) ;
error = xfs_set_dmattrs ( ip , dmi . fsd_dmevmask ,
dmi . fsd_dmstate ) ;
return - error ;
}
case XFS_IOC_GETBMAP :
case XFS_IOC_GETBMAPA :
return xfs_ioc_getbmap ( ip , ioflags , cmd , arg ) ;
case XFS_IOC_GETBMAPX :
return xfs_ioc_getbmapx ( ip , arg ) ;
case XFS_IOC_FD_TO_HANDLE :
case XFS_IOC_PATH_TO_HANDLE :
2008-11-26 06:20:06 +03:00
case XFS_IOC_PATH_TO_FSHANDLE : {
xfs_fsop_handlereq_t hreq ;
2008-04-18 05:44:03 +04:00
2008-11-26 06:20:06 +03:00
if ( copy_from_user ( & hreq , arg , sizeof ( hreq ) ) )
return - XFS_ERROR ( EFAULT ) ;
return xfs_find_handle ( cmd , & hreq ) ;
}
case XFS_IOC_OPEN_BY_HANDLE : {
xfs_fsop_handlereq_t hreq ;
2008-04-18 05:44:03 +04:00
2008-11-26 06:20:06 +03:00
if ( copy_from_user ( & hreq , arg , sizeof ( xfs_fsop_handlereq_t ) ) )
return - XFS_ERROR ( EFAULT ) ;
2009-01-19 04:02:57 +03:00
return xfs_open_by_handle ( filp , & hreq ) ;
2008-11-26 06:20:06 +03:00
}
2008-04-18 05:44:03 +04:00
case XFS_IOC_FSSETDM_BY_HANDLE :
2009-01-19 04:02:57 +03:00
return xfs_fssetdm_by_handle ( filp , arg ) ;
2008-04-18 05:44:03 +04:00
2008-11-26 06:20:06 +03:00
case XFS_IOC_READLINK_BY_HANDLE : {
xfs_fsop_handlereq_t hreq ;
2008-04-18 05:44:03 +04:00
2008-11-26 06:20:06 +03:00
if ( copy_from_user ( & hreq , arg , sizeof ( xfs_fsop_handlereq_t ) ) )
return - XFS_ERROR ( EFAULT ) ;
2009-01-19 04:02:57 +03:00
return xfs_readlink_by_handle ( filp , & hreq ) ;
2008-11-26 06:20:06 +03:00
}
2008-04-18 05:44:03 +04:00
case XFS_IOC_ATTRLIST_BY_HANDLE :
2009-01-19 04:02:57 +03:00
return xfs_attrlist_by_handle ( filp , arg ) ;
2008-04-18 05:44:03 +04:00
case XFS_IOC_ATTRMULTI_BY_HANDLE :
2009-01-19 04:02:57 +03:00
return xfs_attrmulti_by_handle ( filp , arg ) ;
2008-04-18 05:44:03 +04:00
case XFS_IOC_SWAPEXT : {
2008-11-26 06:20:06 +03:00
struct xfs_swapext sxp ;
if ( copy_from_user ( & sxp , arg , sizeof ( xfs_swapext_t ) ) )
return - XFS_ERROR ( EFAULT ) ;
error = xfs_swapext ( & sxp ) ;
2008-04-18 05:44:03 +04:00
return - error ;
}
case XFS_IOC_FSCOUNTS : {
xfs_fsop_counts_t out ;
error = xfs_fs_counts ( mp , & out ) ;
if ( error )
return - error ;
if ( copy_to_user ( arg , & out , sizeof ( out ) ) )
return - XFS_ERROR ( EFAULT ) ;
return 0 ;
}
case XFS_IOC_SET_RESBLKS : {
xfs_fsop_resblks_t inout ;
__uint64_t in ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
if ( copy_from_user ( & inout , arg , sizeof ( inout ) ) )
return - XFS_ERROR ( EFAULT ) ;
/* input parameter is passed in resblks field of structure */
in = inout . resblks ;
error = xfs_reserve_blocks ( mp , & in , & inout ) ;
if ( error )
return - error ;
if ( copy_to_user ( arg , & inout , sizeof ( inout ) ) )
return - XFS_ERROR ( EFAULT ) ;
return 0 ;
}
case XFS_IOC_GET_RESBLKS : {
xfs_fsop_resblks_t out ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
error = xfs_reserve_blocks ( mp , NULL , & out ) ;
if ( error )
return - error ;
if ( copy_to_user ( arg , & out , sizeof ( out ) ) )
return - XFS_ERROR ( EFAULT ) ;
return 0 ;
}
case XFS_IOC_FSGROWFSDATA : {
xfs_growfs_data_t in ;
if ( copy_from_user ( & in , arg , sizeof ( in ) ) )
return - XFS_ERROR ( EFAULT ) ;
error = xfs_growfs_data ( mp , & in ) ;
return - error ;
}
case XFS_IOC_FSGROWFSLOG : {
xfs_growfs_log_t in ;
if ( copy_from_user ( & in , arg , sizeof ( in ) ) )
return - XFS_ERROR ( EFAULT ) ;
error = xfs_growfs_log ( mp , & in ) ;
return - error ;
}
case XFS_IOC_FSGROWFSRT : {
xfs_growfs_rt_t in ;
if ( copy_from_user ( & in , arg , sizeof ( in ) ) )
return - XFS_ERROR ( EFAULT ) ;
error = xfs_growfs_rt ( mp , & in ) ;
return - error ;
}
case XFS_IOC_GOINGDOWN : {
__uint32_t in ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
if ( get_user ( in , ( __uint32_t __user * ) arg ) )
return - XFS_ERROR ( EFAULT ) ;
error = xfs_fs_goingdown ( mp , in ) ;
return - error ;
}
case XFS_IOC_ERROR_INJECTION : {
xfs_error_injection_t in ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
if ( copy_from_user ( & in , arg , sizeof ( in ) ) )
return - XFS_ERROR ( EFAULT ) ;
error = xfs_errortag_add ( in . errtag , mp ) ;
return - error ;
}
case XFS_IOC_ERROR_CLEARALL :
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
error = xfs_errortag_clearall ( mp , 1 ) ;
return - error ;
default :
return - ENOTTY ;
}
}