2010-03-23 17:37:36 +03:00
# include <linux/syscalls.h>
2011-11-17 08:57:37 +04:00
# include <linux/export.h>
2010-03-23 17:37:36 +03:00
# 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>
2017-04-09 01:08:15 +03:00
# include <linux/compat.h>
2012-01-03 07:28:36 +04:00
# include "internal.h"
2010-03-23 17:37:36 +03:00
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 ) ;
}
2012-01-03 07:28:36 +04:00
static 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 ;
}
2016-11-21 04:27:12 +03:00
int vfs_statfs ( const struct path * path , struct kstatfs * buf )
2010-07-07 20:53:11 +04:00
{
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 ) ;
2011-03-12 18:41:39 +03:00
int user_statfs ( const char __user * pathname , struct kstatfs * st )
2010-03-23 17:37:36 +03:00
{
2011-03-12 18:41:39 +03:00
struct path path ;
2012-12-11 21:10:14 +04:00
int error ;
unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT ;
retry :
error = user_path_at ( AT_FDCWD , pathname , lookup_flags , & path ) ;
2011-03-12 18:41:39 +03:00
if ( ! error ) {
error = vfs_statfs ( & path , st ) ;
path_put ( & path ) ;
2012-12-11 21:10:14 +04:00
if ( retry_estale ( error , lookup_flags ) ) {
lookup_flags | = LOOKUP_REVAL ;
goto retry ;
}
2011-03-12 18:41:39 +03:00
}
return error ;
}
2010-03-23 17:37:36 +03:00
2011-03-12 18:41:39 +03:00
int fd_statfs ( int fd , struct kstatfs * st )
{
2013-09-30 19:35:10 +04:00
struct fd f = fdget_raw ( fd ) ;
2011-03-12 18:41:39 +03:00
int error = - EBADF ;
2012-08-28 20:52:22 +04:00
if ( f . file ) {
error = vfs_statfs ( & f . file - > f_path , st ) ;
fdput ( f ) ;
2011-03-12 18:41:39 +03:00
}
return error ;
}
2010-03-23 17:37:36 +03:00
2011-03-12 18:41:39 +03:00
static int do_statfs_native ( struct kstatfs * st , struct statfs __user * p )
{
struct statfs buf ;
if ( sizeof ( buf ) = = sizeof ( * st ) )
memcpy ( & buf , st , sizeof ( * st ) ) ;
2010-03-23 17:37:36 +03:00
else {
2011-03-12 18:41:39 +03:00
if ( sizeof buf . f_blocks = = 4 ) {
if ( ( st - > f_blocks | st - > f_bfree | st - > f_bavail |
st - > f_bsize | st - > f_frsize ) &
2010-03-23 17:37:36 +03:00
0xffffffff00000000ULL )
return - EOVERFLOW ;
/*
* f_files and f_ffree may be - 1 ; it ' s okay to stuff
* that into 32 bits
*/
2011-03-12 18:41:39 +03:00
if ( st - > f_files ! = - 1 & &
( st - > f_files & 0xffffffff00000000ULL ) )
2010-03-23 17:37:36 +03:00
return - EOVERFLOW ;
2011-03-12 18:41:39 +03:00
if ( st - > f_ffree ! = - 1 & &
( st - > f_ffree & 0xffffffff00000000ULL ) )
2010-03-23 17:37:36 +03:00
return - EOVERFLOW ;
}
2011-03-12 18:41:39 +03:00
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 ;
buf . f_flags = st - > f_flags ;
memset ( buf . f_spare , 0 , sizeof ( buf . f_spare ) ) ;
2010-03-23 17:37:36 +03:00
}
2011-03-12 18:41:39 +03:00
if ( copy_to_user ( p , & buf , sizeof ( buf ) ) )
return - EFAULT ;
2010-03-23 17:37:36 +03:00
return 0 ;
}
2011-03-12 18:41:39 +03:00
static int do_statfs64 ( struct kstatfs * st , struct statfs64 __user * p )
2010-03-23 17:37:36 +03:00
{
2011-03-12 18:41:39 +03:00
struct statfs64 buf ;
if ( sizeof ( buf ) = = sizeof ( * st ) )
memcpy ( & buf , st , sizeof ( * st ) ) ;
2010-03-23 17:37:36 +03:00
else {
2011-03-12 18:41:39 +03:00
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 ;
buf . f_flags = st - > f_flags ;
memset ( buf . f_spare , 0 , sizeof ( buf . f_spare ) ) ;
2010-03-23 17:37:36 +03:00
}
2011-03-12 18:41:39 +03:00
if ( copy_to_user ( p , & buf , sizeof ( buf ) ) )
return - EFAULT ;
2010-03-23 17:37:36 +03:00
return 0 ;
}
SYSCALL_DEFINE2 ( statfs , const char __user * , pathname , struct statfs __user * , buf )
{
2011-03-12 18:41:39 +03:00
struct kstatfs st ;
int error = user_statfs ( pathname , & st ) ;
if ( ! error )
error = do_statfs_native ( & st , buf ) ;
2010-03-23 17:37:36 +03:00
return error ;
}
SYSCALL_DEFINE3 ( statfs64 , const char __user * , pathname , size_t , sz , struct statfs64 __user * , buf )
{
2011-03-12 18:41:39 +03:00
struct kstatfs st ;
int error ;
2010-03-23 17:37:36 +03:00
if ( sz ! = sizeof ( * buf ) )
return - EINVAL ;
2011-03-12 18:41:39 +03:00
error = user_statfs ( pathname , & st ) ;
if ( ! error )
error = do_statfs64 ( & st , buf ) ;
2010-03-23 17:37:36 +03:00
return error ;
}
SYSCALL_DEFINE2 ( fstatfs , unsigned int , fd , struct statfs __user * , buf )
{
2011-03-12 18:41:39 +03:00
struct kstatfs st ;
int error = fd_statfs ( fd , & st ) ;
if ( ! error )
error = do_statfs_native ( & st , buf ) ;
2010-03-23 17:37:36 +03:00
return error ;
}
SYSCALL_DEFINE3 ( fstatfs64 , unsigned int , fd , size_t , sz , struct statfs64 __user * , buf )
{
2011-03-12 18:41:39 +03:00
struct kstatfs st ;
2010-03-23 17:37:36 +03:00
int error ;
if ( sz ! = sizeof ( * buf ) )
return - EINVAL ;
2011-03-12 18:41:39 +03:00
error = fd_statfs ( fd , & st ) ;
if ( ! error )
error = do_statfs64 ( & st , buf ) ;
2010-03-23 17:37:36 +03:00
return error ;
}
2012-01-03 07:28:36 +04:00
int vfs_ustat ( dev_t dev , struct kstatfs * sbuf )
2010-03-23 17:37:36 +03:00
{
2012-01-03 07:28:36 +04:00
struct super_block * s = user_get_super ( dev ) ;
2010-03-23 17:37:36 +03:00
int err ;
if ( ! s )
return - EINVAL ;
2012-01-03 07:28:36 +04:00
err = statfs_by_dentry ( s - > s_root , sbuf ) ;
2010-03-23 17:37:36 +03:00
drop_super ( s ) ;
2012-01-03 07:28:36 +04:00
return err ;
}
SYSCALL_DEFINE2 ( ustat , unsigned , dev , struct ustat __user * , ubuf )
{
struct ustat tmp ;
struct kstatfs sbuf ;
int err = vfs_ustat ( new_decode_dev ( dev ) , & sbuf ) ;
2010-03-23 17:37:36 +03:00
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 ;
}
2017-04-09 01:08:15 +03:00
# ifdef CONFIG_COMPAT
static int put_compat_statfs ( struct compat_statfs __user * ubuf , struct kstatfs * kbuf )
{
if ( sizeof ubuf - > f_blocks = = 4 ) {
if ( ( kbuf - > f_blocks | kbuf - > f_bfree | kbuf - > f_bavail |
kbuf - > f_bsize | kbuf - > f_frsize ) & 0xffffffff00000000ULL )
return - EOVERFLOW ;
/* f_files and f_ffree may be -1; it's okay
* to stuff that into 32 bits */
if ( kbuf - > f_files ! = 0xffffffffffffffffULL
& & ( kbuf - > f_files & 0xffffffff00000000ULL ) )
return - EOVERFLOW ;
if ( kbuf - > f_ffree ! = 0xffffffffffffffffULL
& & ( kbuf - > f_ffree & 0xffffffff00000000ULL ) )
return - EOVERFLOW ;
}
if ( ! access_ok ( VERIFY_WRITE , ubuf , sizeof ( * ubuf ) ) | |
__put_user ( kbuf - > f_type , & ubuf - > f_type ) | |
__put_user ( kbuf - > f_bsize , & ubuf - > f_bsize ) | |
__put_user ( kbuf - > f_blocks , & ubuf - > f_blocks ) | |
__put_user ( kbuf - > f_bfree , & ubuf - > f_bfree ) | |
__put_user ( kbuf - > f_bavail , & ubuf - > f_bavail ) | |
__put_user ( kbuf - > f_files , & ubuf - > f_files ) | |
__put_user ( kbuf - > f_ffree , & ubuf - > f_ffree ) | |
__put_user ( kbuf - > f_namelen , & ubuf - > f_namelen ) | |
__put_user ( kbuf - > f_fsid . val [ 0 ] , & ubuf - > f_fsid . val [ 0 ] ) | |
__put_user ( kbuf - > f_fsid . val [ 1 ] , & ubuf - > f_fsid . val [ 1 ] ) | |
__put_user ( kbuf - > f_frsize , & ubuf - > f_frsize ) | |
__put_user ( kbuf - > f_flags , & ubuf - > f_flags ) | |
__clear_user ( ubuf - > f_spare , sizeof ( ubuf - > f_spare ) ) )
return - EFAULT ;
return 0 ;
}
/*
* The following statfs calls are copies of code from fs / statfs . c and
* should be checked against those from time to time
*/
COMPAT_SYSCALL_DEFINE2 ( statfs , const char __user * , pathname , struct compat_statfs __user * , buf )
{
struct kstatfs tmp ;
int error = user_statfs ( pathname , & tmp ) ;
if ( ! error )
error = put_compat_statfs ( buf , & tmp ) ;
return error ;
}
COMPAT_SYSCALL_DEFINE2 ( fstatfs , unsigned int , fd , struct compat_statfs __user * , buf )
{
struct kstatfs tmp ;
int error = fd_statfs ( fd , & tmp ) ;
if ( ! error )
error = put_compat_statfs ( buf , & tmp ) ;
return error ;
}
static int put_compat_statfs64 ( struct compat_statfs64 __user * ubuf , struct kstatfs * kbuf )
{
if ( sizeof ( ubuf - > f_bsize ) = = 4 ) {
if ( ( kbuf - > f_type | kbuf - > f_bsize | kbuf - > f_namelen |
kbuf - > f_frsize | kbuf - > f_flags ) & 0xffffffff00000000ULL )
return - EOVERFLOW ;
/* f_files and f_ffree may be -1; it's okay
* to stuff that into 32 bits */
if ( kbuf - > f_files ! = 0xffffffffffffffffULL
& & ( kbuf - > f_files & 0xffffffff00000000ULL ) )
return - EOVERFLOW ;
if ( kbuf - > f_ffree ! = 0xffffffffffffffffULL
& & ( kbuf - > f_ffree & 0xffffffff00000000ULL ) )
return - EOVERFLOW ;
}
if ( ! access_ok ( VERIFY_WRITE , ubuf , sizeof ( * ubuf ) ) | |
__put_user ( kbuf - > f_type , & ubuf - > f_type ) | |
__put_user ( kbuf - > f_bsize , & ubuf - > f_bsize ) | |
__put_user ( kbuf - > f_blocks , & ubuf - > f_blocks ) | |
__put_user ( kbuf - > f_bfree , & ubuf - > f_bfree ) | |
__put_user ( kbuf - > f_bavail , & ubuf - > f_bavail ) | |
__put_user ( kbuf - > f_files , & ubuf - > f_files ) | |
__put_user ( kbuf - > f_ffree , & ubuf - > f_ffree ) | |
__put_user ( kbuf - > f_namelen , & ubuf - > f_namelen ) | |
__put_user ( kbuf - > f_fsid . val [ 0 ] , & ubuf - > f_fsid . val [ 0 ] ) | |
__put_user ( kbuf - > f_fsid . val [ 1 ] , & ubuf - > f_fsid . val [ 1 ] ) | |
__put_user ( kbuf - > f_frsize , & ubuf - > f_frsize ) | |
__put_user ( kbuf - > f_flags , & ubuf - > f_flags ) | |
__clear_user ( ubuf - > f_spare , sizeof ( ubuf - > f_spare ) ) )
return - EFAULT ;
return 0 ;
}
COMPAT_SYSCALL_DEFINE3 ( statfs64 , const char __user * , pathname , compat_size_t , sz , struct compat_statfs64 __user * , buf )
{
struct kstatfs tmp ;
int error ;
if ( sz ! = sizeof ( * buf ) )
return - EINVAL ;
error = user_statfs ( pathname , & tmp ) ;
if ( ! error )
error = put_compat_statfs64 ( buf , & tmp ) ;
return error ;
}
COMPAT_SYSCALL_DEFINE3 ( fstatfs64 , unsigned int , fd , compat_size_t , sz , struct compat_statfs64 __user * , buf )
{
struct kstatfs tmp ;
int error ;
if ( sz ! = sizeof ( * buf ) )
return - EINVAL ;
error = fd_statfs ( fd , & tmp ) ;
if ( ! error )
error = put_compat_statfs64 ( buf , & tmp ) ;
return error ;
}
/*
* This is a copy of sys_ustat , just dealing with a structure layout .
* Given how simple this syscall is that apporach is more maintainable
* than the various conversion hacks .
*/
COMPAT_SYSCALL_DEFINE2 ( ustat , unsigned , dev , struct compat_ustat __user * , u )
{
struct compat_ustat tmp ;
struct kstatfs sbuf ;
int err = vfs_ustat ( new_decode_dev ( dev ) , & sbuf ) ;
if ( err )
return err ;
memset ( & tmp , 0 , sizeof ( struct compat_ustat ) ) ;
tmp . f_tfree = sbuf . f_bfree ;
tmp . f_tinode = sbuf . f_ffree ;
if ( copy_to_user ( u , & tmp , sizeof ( struct compat_ustat ) ) )
return - EFAULT ;
return 0 ;
}
# endif