2010-03-23 17:37:36 +03:00
# include <linux/syscalls.h>
# include <linux/module.h>
# include <linux/fs.h>
# include <linux/file.h>
2010-07-07 20:53:25 +04:00
# include <linux/mount.h>
2010-03-23 17:37:36 +03:00
# include <linux/namei.h>
# include <linux/statfs.h>
# include <linux/security.h>
# include <linux/uaccess.h>
2010-07-07 20:53:25 +04:00
static int flags_by_mnt ( int mnt_flags )
{
int flags = 0 ;
if ( mnt_flags & MNT_READONLY )
flags | = ST_RDONLY ;
if ( mnt_flags & MNT_NOSUID )
flags | = ST_NOSUID ;
if ( mnt_flags & MNT_NODEV )
flags | = ST_NODEV ;
if ( mnt_flags & MNT_NOEXEC )
flags | = ST_NOEXEC ;
if ( mnt_flags & MNT_NOATIME )
flags | = ST_NOATIME ;
if ( mnt_flags & MNT_NODIRATIME )
flags | = ST_NODIRATIME ;
if ( mnt_flags & MNT_RELATIME )
flags | = ST_RELATIME ;
return flags ;
}
static int flags_by_sb ( int s_flags )
{
int flags = 0 ;
if ( s_flags & MS_SYNCHRONOUS )
flags | = ST_SYNCHRONOUS ;
if ( s_flags & MS_MANDLOCK )
flags | = ST_MANDLOCK ;
return flags ;
}
static int calculate_f_flags ( struct vfsmount * mnt )
{
return ST_VALID | flags_by_mnt ( mnt - > mnt_flags ) |
flags_by_sb ( mnt - > mnt_sb - > s_flags ) ;
}
2010-07-07 20:53:11 +04:00
int statfs_by_dentry ( struct dentry * dentry , struct kstatfs * buf )
2010-03-23 17:37:36 +03:00
{
2010-07-07 20:53:11 +04:00
int retval ;
if ( ! dentry - > d_sb - > s_op - > statfs )
return - ENOSYS ;
memset ( buf , 0 , sizeof ( * buf ) ) ;
retval = security_sb_statfs ( dentry ) ;
if ( retval )
return retval ;
retval = dentry - > d_sb - > s_op - > statfs ( dentry , buf ) ;
if ( retval = = 0 & & buf - > f_frsize = = 0 )
buf - > f_frsize = buf - > f_bsize ;
2010-03-23 17:37:36 +03:00
return retval ;
}
2010-07-07 20:53:11 +04:00
int vfs_statfs ( struct path * path , struct kstatfs * buf )
{
2010-07-07 20:53:25 +04:00
int error ;
error = statfs_by_dentry ( path - > dentry , buf ) ;
if ( ! error )
buf - > f_flags = calculate_f_flags ( path - > mnt ) ;
return error ;
2010-07-07 20:53:11 +04:00
}
2010-03-23 17:37:36 +03:00
EXPORT_SYMBOL ( vfs_statfs ) ;
2010-07-07 20:53:11 +04:00
static int do_statfs_native ( struct path * path , struct statfs * buf )
2010-03-23 17:37:36 +03:00
{
struct kstatfs st ;
int retval ;
2010-07-07 20:53:11 +04:00
retval = vfs_statfs ( path , & st ) ;
2010-03-23 17:37:36 +03:00
if ( retval )
return retval ;
if ( sizeof ( * buf ) = = sizeof ( st ) )
memcpy ( buf , & st , sizeof ( st ) ) ;
else {
if ( sizeof buf - > f_blocks = = 4 ) {
if ( ( st . f_blocks | st . f_bfree | st . f_bavail |
st . f_bsize | st . f_frsize ) &
0xffffffff00000000ULL )
return - EOVERFLOW ;
/*
* f_files and f_ffree may be - 1 ; it ' s okay to stuff
* that into 32 bits
*/
if ( st . f_files ! = - 1 & &
( st . f_files & 0xffffffff00000000ULL ) )
return - EOVERFLOW ;
if ( st . f_ffree ! = - 1 & &
( st . f_ffree & 0xffffffff00000000ULL ) )
return - EOVERFLOW ;
}
buf - > f_type = st . f_type ;
buf - > f_bsize = st . f_bsize ;
buf - > f_blocks = st . f_blocks ;
buf - > f_bfree = st . f_bfree ;
buf - > f_bavail = st . f_bavail ;
buf - > f_files = st . f_files ;
buf - > f_ffree = st . f_ffree ;
buf - > f_fsid = st . f_fsid ;
buf - > f_namelen = st . f_namelen ;
buf - > f_frsize = st . f_frsize ;
2010-07-07 20:53:25 +04:00
buf - > f_flags = st . f_flags ;
2010-03-23 17:37:36 +03:00
memset ( buf - > f_spare , 0 , sizeof ( buf - > f_spare ) ) ;
}
return 0 ;
}
2010-07-07 20:53:11 +04:00
static int do_statfs64 ( struct path * path , struct statfs64 * buf )
2010-03-23 17:37:36 +03:00
{
struct kstatfs st ;
int retval ;
2010-07-07 20:53:11 +04:00
retval = vfs_statfs ( path , & st ) ;
2010-03-23 17:37:36 +03:00
if ( retval )
return retval ;
if ( sizeof ( * buf ) = = sizeof ( st ) )
memcpy ( buf , & st , sizeof ( st ) ) ;
else {
buf - > f_type = st . f_type ;
buf - > f_bsize = st . f_bsize ;
buf - > f_blocks = st . f_blocks ;
buf - > f_bfree = st . f_bfree ;
buf - > f_bavail = st . f_bavail ;
buf - > f_files = st . f_files ;
buf - > f_ffree = st . f_ffree ;
buf - > f_fsid = st . f_fsid ;
buf - > f_namelen = st . f_namelen ;
buf - > f_frsize = st . f_frsize ;
2010-07-07 20:53:25 +04:00
buf - > f_flags = st . f_flags ;
2010-03-23 17:37:36 +03:00
memset ( buf - > f_spare , 0 , sizeof ( buf - > f_spare ) ) ;
}
return 0 ;
}
SYSCALL_DEFINE2 ( statfs , const char __user * , pathname , struct statfs __user * , buf )
{
struct path path ;
int error ;
error = user_path ( pathname , & path ) ;
if ( ! error ) {
struct statfs tmp ;
2010-07-07 20:53:11 +04:00
error = do_statfs_native ( & path , & tmp ) ;
2010-03-23 17:37:36 +03:00
if ( ! error & & copy_to_user ( buf , & tmp , sizeof ( tmp ) ) )
error = - EFAULT ;
path_put ( & path ) ;
}
return error ;
}
SYSCALL_DEFINE3 ( statfs64 , const char __user * , pathname , size_t , sz , struct statfs64 __user * , buf )
{
struct path path ;
long error ;
if ( sz ! = sizeof ( * buf ) )
return - EINVAL ;
error = user_path ( pathname , & path ) ;
if ( ! error ) {
struct statfs64 tmp ;
2010-07-07 20:53:11 +04:00
error = do_statfs64 ( & path , & tmp ) ;
2010-03-23 17:37:36 +03:00
if ( ! error & & copy_to_user ( buf , & tmp , sizeof ( tmp ) ) )
error = - EFAULT ;
path_put ( & path ) ;
}
return error ;
}
SYSCALL_DEFINE2 ( fstatfs , unsigned int , fd , struct statfs __user * , buf )
{
struct file * file ;
struct statfs tmp ;
int error ;
error = - EBADF ;
file = fget ( fd ) ;
if ( ! file )
goto out ;
2010-07-07 20:53:11 +04:00
error = do_statfs_native ( & file - > f_path , & tmp ) ;
2010-03-23 17:37:36 +03:00
if ( ! error & & copy_to_user ( buf , & tmp , sizeof ( tmp ) ) )
error = - EFAULT ;
fput ( file ) ;
out :
return error ;
}
SYSCALL_DEFINE3 ( fstatfs64 , unsigned int , fd , size_t , sz , struct statfs64 __user * , buf )
{
struct file * file ;
struct statfs64 tmp ;
int error ;
if ( sz ! = sizeof ( * buf ) )
return - EINVAL ;
error = - EBADF ;
file = fget ( fd ) ;
if ( ! file )
goto out ;
2010-07-07 20:53:11 +04:00
error = do_statfs64 ( & file - > f_path , & tmp ) ;
2010-03-23 17:37:36 +03:00
if ( ! error & & copy_to_user ( buf , & tmp , sizeof ( tmp ) ) )
error = - EFAULT ;
fput ( file ) ;
out :
return error ;
}
SYSCALL_DEFINE2 ( ustat , unsigned , dev , struct ustat __user * , ubuf )
{
struct super_block * s ;
struct ustat tmp ;
struct kstatfs sbuf ;
int err ;
s = user_get_super ( new_decode_dev ( dev ) ) ;
if ( ! s )
return - EINVAL ;
2010-07-07 20:53:11 +04:00
err = statfs_by_dentry ( s - > s_root , & sbuf ) ;
2010-03-23 17:37:36 +03:00
drop_super ( s ) ;
if ( err )
return err ;
memset ( & tmp , 0 , sizeof ( struct ustat ) ) ;
tmp . f_tfree = sbuf . f_bfree ;
tmp . f_tinode = sbuf . f_ffree ;
return copy_to_user ( ubuf , & tmp , sizeof ( struct ustat ) ) ? - EFAULT : 0 ;
}