2005-04-17 02:20:36 +04:00
/*
* Quota code necessary even when VFS quota support is not compiled
* into the kernel . The interesting stuff is over in dquot . c , here
* we have symbols for initial quotactl ( 2 ) handling , the sysctl ( 2 )
* variables , etc - things needed even when quota support disabled .
*/
# include <linux/fs.h>
# include <linux/namei.h>
# include <linux/slab.h>
# include <asm/current.h>
# include <asm/uaccess.h>
2007-07-16 10:41:12 +04:00
# include <linux/compat.h>
2005-04-17 02:20:36 +04:00
# include <linux/kernel.h>
# include <linux/security.h>
# include <linux/syscalls.h>
# include <linux/buffer_head.h>
2006-01-11 23:17:46 +03:00
# include <linux/capability.h>
2005-11-07 11:59:35 +03:00
# include <linux/quotaops.h>
2007-07-16 10:41:12 +04:00
# include <linux/types.h>
2005-04-17 02:20:36 +04:00
/* Check validity of generic quotactl commands */
static int generic_quotactl_valid ( struct super_block * sb , int type , int cmd , qid_t id )
{
if ( type > = MAXQUOTAS )
return - EINVAL ;
if ( ! sb & & cmd ! = Q_SYNC )
return - ENODEV ;
/* Is operation supported? */
if ( sb & & ! sb - > s_qcop )
return - ENOSYS ;
switch ( cmd ) {
case Q_GETFMT :
break ;
case Q_QUOTAON :
if ( ! sb - > s_qcop - > quota_on )
return - ENOSYS ;
break ;
case Q_QUOTAOFF :
if ( ! sb - > s_qcop - > quota_off )
return - ENOSYS ;
break ;
case Q_SETINFO :
if ( ! sb - > s_qcop - > set_info )
return - ENOSYS ;
break ;
case Q_GETINFO :
if ( ! sb - > s_qcop - > get_info )
return - ENOSYS ;
break ;
case Q_SETQUOTA :
if ( ! sb - > s_qcop - > set_dqblk )
return - ENOSYS ;
break ;
case Q_GETQUOTA :
if ( ! sb - > s_qcop - > get_dqblk )
return - ENOSYS ;
break ;
case Q_SYNC :
if ( sb & & ! sb - > s_qcop - > quota_sync )
return - ENOSYS ;
break ;
default :
return - EINVAL ;
}
/* Is quota turned on for commands which need it? */
switch ( cmd ) {
case Q_GETFMT :
case Q_GETINFO :
case Q_QUOTAOFF :
case Q_SETINFO :
case Q_SETQUOTA :
case Q_GETQUOTA :
/* This is just informative test so we are satisfied without a lock */
if ( ! sb_has_quota_enabled ( sb , type ) )
return - ESRCH ;
}
/* Check privileges */
if ( cmd = = Q_GETQUOTA ) {
if ( ( ( type = = USRQUOTA & & current - > euid ! = id ) | |
( type = = GRPQUOTA & & ! in_egroup_p ( id ) ) ) & &
! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
}
else if ( cmd ! = Q_GETFMT & & cmd ! = Q_SYNC & & cmd ! = Q_GETINFO )
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
return 0 ;
}
/* Check validity of XFS Quota Manager commands */
static int xqm_quotactl_valid ( struct super_block * sb , int type , int cmd , qid_t id )
{
if ( type > = XQM_MAXQUOTAS )
return - EINVAL ;
if ( ! sb )
return - ENODEV ;
if ( ! sb - > s_qcop )
return - ENOSYS ;
switch ( cmd ) {
case Q_XQUOTAON :
case Q_XQUOTAOFF :
case Q_XQUOTARM :
if ( ! sb - > s_qcop - > set_xstate )
return - ENOSYS ;
break ;
case Q_XGETQSTAT :
if ( ! sb - > s_qcop - > get_xstate )
return - ENOSYS ;
break ;
case Q_XSETQLIM :
if ( ! sb - > s_qcop - > set_xquota )
return - ENOSYS ;
break ;
case Q_XGETQUOTA :
if ( ! sb - > s_qcop - > get_xquota )
return - ENOSYS ;
break ;
2005-11-03 05:53:34 +03:00
case Q_XQUOTASYNC :
if ( ! sb - > s_qcop - > quota_sync )
return - ENOSYS ;
break ;
2005-04-17 02:20:36 +04:00
default :
return - EINVAL ;
}
/* Check privileges */
if ( cmd = = Q_XGETQUOTA ) {
if ( ( ( type = = XQM_USRQUOTA & & current - > euid ! = id ) | |
( type = = XQM_GRPQUOTA & & ! in_egroup_p ( id ) ) ) & &
! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
2005-11-03 05:53:34 +03:00
} else if ( cmd ! = Q_XGETQSTAT & & cmd ! = Q_XQUOTASYNC ) {
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
}
return 0 ;
}
static int check_quotactl_valid ( struct super_block * sb , int type , int cmd , qid_t id )
{
int error ;
if ( XQM_COMMAND ( cmd ) )
error = xqm_quotactl_valid ( sb , type , cmd , id ) ;
else
error = generic_quotactl_valid ( sb , type , cmd , id ) ;
if ( ! error )
error = security_quotactl ( cmd , type , id , sb ) ;
return error ;
}
static void quota_sync_sb ( struct super_block * sb , int type )
{
int cnt ;
sb - > s_qcop - > quota_sync ( sb , type ) ;
/* This is not very clever (and fast) but currently I don't know about
* any other simple way of getting quota data to disk and we must get
* them there for userspace to be visible . . . */
if ( sb - > s_op - > sync_fs )
sb - > s_op - > sync_fs ( sb , 1 ) ;
sync_blockdev ( sb - > s_bdev ) ;
2007-05-17 09:11:19 +04:00
/*
* Now when everything is written we can discard the pagecache so
* that userspace sees the changes .
*/
2006-03-23 14:00:29 +03:00
mutex_lock ( & sb_dqopt ( sb ) - > dqonoff_mutex ) ;
2005-04-17 02:20:36 +04:00
for ( cnt = 0 ; cnt < MAXQUOTAS ; cnt + + ) {
if ( type ! = - 1 & & cnt ! = type )
continue ;
if ( ! sb_has_quota_enabled ( sb , cnt ) )
continue ;
2007-05-17 09:11:19 +04:00
mutex_lock_nested ( & sb_dqopt ( sb ) - > files [ cnt ] - > i_mutex , I_MUTEX_QUOTA ) ;
truncate_inode_pages ( & sb_dqopt ( sb ) - > files [ cnt ] - > i_data , 0 ) ;
mutex_unlock ( & sb_dqopt ( sb ) - > files [ cnt ] - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-23 14:00:29 +03:00
mutex_unlock ( & sb_dqopt ( sb ) - > dqonoff_mutex ) ;
2005-04-17 02:20:36 +04:00
}
void sync_dquots ( struct super_block * sb , int type )
{
2005-06-23 11:09:54 +04:00
int cnt , dirty ;
2005-04-17 02:20:36 +04:00
if ( sb ) {
if ( sb - > s_qcop - > quota_sync )
quota_sync_sb ( sb , type ) ;
2005-06-23 11:09:54 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
2005-06-23 11:09:54 +04:00
spin_lock ( & sb_lock ) ;
restart :
list_for_each_entry ( sb , & super_blocks , s_list ) {
/* This test just improves performance so it needn't be reliable... */
for ( cnt = 0 , dirty = 0 ; cnt < MAXQUOTAS ; cnt + + )
if ( ( type = = cnt | | type = = - 1 ) & & sb_has_quota_enabled ( sb , cnt )
& & info_any_dirty ( & sb_dqopt ( sb ) - > info [ cnt ] ) )
dirty = 1 ;
if ( ! dirty )
continue ;
sb - > s_count + + ;
spin_unlock ( & sb_lock ) ;
down_read ( & sb - > s_umount ) ;
if ( sb - > s_root & & sb - > s_qcop - > quota_sync )
quota_sync_sb ( sb , type ) ;
up_read ( & sb - > s_umount ) ;
spin_lock ( & sb_lock ) ;
if ( __put_super_and_need_restart ( sb ) )
goto restart ;
2005-04-17 02:20:36 +04:00
}
2005-06-23 11:09:54 +04:00
spin_unlock ( & sb_lock ) ;
2005-04-17 02:20:36 +04:00
}
/* Copy parameters and call proper function */
static int do_quotactl ( struct super_block * sb , int type , int cmd , qid_t id , void __user * addr )
{
int ret ;
switch ( cmd ) {
case Q_QUOTAON : {
char * pathname ;
if ( IS_ERR ( pathname = getname ( addr ) ) )
return PTR_ERR ( pathname ) ;
ret = sb - > s_qcop - > quota_on ( sb , type , id , pathname ) ;
putname ( pathname ) ;
return ret ;
}
case Q_QUOTAOFF :
return sb - > s_qcop - > quota_off ( sb , type ) ;
case Q_GETFMT : {
__u32 fmt ;
down_read ( & sb_dqopt ( sb ) - > dqptr_sem ) ;
if ( ! sb_has_quota_enabled ( sb , type ) ) {
up_read ( & sb_dqopt ( sb ) - > dqptr_sem ) ;
return - ESRCH ;
}
fmt = sb_dqopt ( sb ) - > info [ type ] . dqi_format - > qf_fmt_id ;
up_read ( & sb_dqopt ( sb ) - > dqptr_sem ) ;
if ( copy_to_user ( addr , & fmt , sizeof ( fmt ) ) )
return - EFAULT ;
return 0 ;
}
case Q_GETINFO : {
struct if_dqinfo info ;
if ( ( ret = sb - > s_qcop - > get_info ( sb , type , & info ) ) )
return ret ;
if ( copy_to_user ( addr , & info , sizeof ( info ) ) )
return - EFAULT ;
return 0 ;
}
case Q_SETINFO : {
struct if_dqinfo info ;
if ( copy_from_user ( & info , addr , sizeof ( info ) ) )
return - EFAULT ;
return sb - > s_qcop - > set_info ( sb , type , & info ) ;
}
case Q_GETQUOTA : {
struct if_dqblk idq ;
if ( ( ret = sb - > s_qcop - > get_dqblk ( sb , type , id , & idq ) ) )
return ret ;
if ( copy_to_user ( addr , & idq , sizeof ( idq ) ) )
return - EFAULT ;
return 0 ;
}
case Q_SETQUOTA : {
struct if_dqblk idq ;
if ( copy_from_user ( & idq , addr , sizeof ( idq ) ) )
return - EFAULT ;
return sb - > s_qcop - > set_dqblk ( sb , type , id , & idq ) ;
}
case Q_SYNC :
sync_dquots ( sb , type ) ;
return 0 ;
case Q_XQUOTAON :
case Q_XQUOTAOFF :
case Q_XQUOTARM : {
__u32 flags ;
if ( copy_from_user ( & flags , addr , sizeof ( flags ) ) )
return - EFAULT ;
return sb - > s_qcop - > set_xstate ( sb , flags , cmd ) ;
}
case Q_XGETQSTAT : {
struct fs_quota_stat fqs ;
if ( ( ret = sb - > s_qcop - > get_xstate ( sb , & fqs ) ) )
return ret ;
if ( copy_to_user ( addr , & fqs , sizeof ( fqs ) ) )
return - EFAULT ;
return 0 ;
}
case Q_XSETQLIM : {
struct fs_disk_quota fdq ;
if ( copy_from_user ( & fdq , addr , sizeof ( fdq ) ) )
return - EFAULT ;
return sb - > s_qcop - > set_xquota ( sb , type , id , & fdq ) ;
}
case Q_XGETQUOTA : {
struct fs_disk_quota fdq ;
if ( ( ret = sb - > s_qcop - > get_xquota ( sb , type , id , & fdq ) ) )
return ret ;
if ( copy_to_user ( addr , & fdq , sizeof ( fdq ) ) )
return - EFAULT ;
return 0 ;
}
2005-11-03 05:53:34 +03:00
case Q_XQUOTASYNC :
return sb - > s_qcop - > quota_sync ( sb , type ) ;
2005-04-17 02:20:36 +04:00
/* We never reach here unless validity check is broken */
default :
BUG ( ) ;
}
return 0 ;
}
[PATCH] BLOCK: Make it possible to disable the block layer [try #6]
Make it possible to disable the block layer. Not all embedded devices require
it, some can make do with just JFFS2, NFS, ramfs, etc - none of which require
the block layer to be present.
This patch does the following:
(*) Introduces CONFIG_BLOCK to disable the block layer, buffering and blockdev
support.
(*) Adds dependencies on CONFIG_BLOCK to any configuration item that controls
an item that uses the block layer. This includes:
(*) Block I/O tracing.
(*) Disk partition code.
(*) All filesystems that are block based, eg: Ext3, ReiserFS, ISOFS.
(*) The SCSI layer. As far as I can tell, even SCSI chardevs use the
block layer to do scheduling. Some drivers that use SCSI facilities -
such as USB storage - end up disabled indirectly from this.
(*) Various block-based device drivers, such as IDE and the old CDROM
drivers.
(*) MTD blockdev handling and FTL.
(*) JFFS - which uses set_bdev_super(), something it could avoid doing by
taking a leaf out of JFFS2's book.
(*) Makes most of the contents of linux/blkdev.h, linux/buffer_head.h and
linux/elevator.h contingent on CONFIG_BLOCK being set. sector_div() is,
however, still used in places, and so is still available.
(*) Also made contingent are the contents of linux/mpage.h, linux/genhd.h and
parts of linux/fs.h.
(*) Makes a number of files in fs/ contingent on CONFIG_BLOCK.
(*) Makes mm/bounce.c (bounce buffering) contingent on CONFIG_BLOCK.
(*) set_page_dirty() doesn't call __set_page_dirty_buffers() if CONFIG_BLOCK
is not enabled.
(*) fs/no-block.c is created to hold out-of-line stubs and things that are
required when CONFIG_BLOCK is not set:
(*) Default blockdev file operations (to give error ENODEV on opening).
(*) Makes some /proc changes:
(*) /proc/devices does not list any blockdevs.
(*) /proc/diskstats and /proc/partitions are contingent on CONFIG_BLOCK.
(*) Makes some compat ioctl handling contingent on CONFIG_BLOCK.
(*) If CONFIG_BLOCK is not defined, makes sys_quotactl() return -ENODEV if
given command other than Q_SYNC or if a special device is specified.
(*) In init/do_mounts.c, no reference is made to the blockdev routines if
CONFIG_BLOCK is not defined. This does not prohibit NFS roots or JFFS2.
(*) The bdflush, ioprio_set and ioprio_get syscalls can now be absent (return
error ENOSYS by way of cond_syscall if so).
(*) The seclvl_bd_claim() and seclvl_bd_release() security calls do nothing if
CONFIG_BLOCK is not set, since they can't then happen.
Signed-Off-By: David Howells <dhowells@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
2006-09-30 22:45:40 +04:00
/*
* look up a superblock on which quota ops will be performed
* - use the name of a block device to find the superblock thereon
*/
static inline struct super_block * quotactl_block ( const char __user * special )
{
# ifdef CONFIG_BLOCK
struct block_device * bdev ;
struct super_block * sb ;
char * tmp = getname ( special ) ;
if ( IS_ERR ( tmp ) )
return ERR_PTR ( PTR_ERR ( tmp ) ) ;
bdev = lookup_bdev ( tmp ) ;
putname ( tmp ) ;
if ( IS_ERR ( bdev ) )
return ERR_PTR ( PTR_ERR ( bdev ) ) ;
sb = get_super ( bdev ) ;
bdput ( bdev ) ;
if ( ! sb )
return ERR_PTR ( - ENODEV ) ;
return sb ;
# else
return ERR_PTR ( - ENODEV ) ;
# endif
}
2005-04-17 02:20:36 +04:00
/*
* This is the system call interface . This communicates with
* the user - level programs . Currently this only supports diskquota
* calls . Maybe we need to add the process quotas etc . in the future ,
* but we probably should use rlimits for that .
*/
asmlinkage long sys_quotactl ( unsigned int cmd , const char __user * special , qid_t id , void __user * addr )
{
uint cmds , type ;
struct super_block * sb = NULL ;
int ret ;
cmds = cmd > > SUBCMDSHIFT ;
type = cmd & SUBCMDMASK ;
if ( cmds ! = Q_SYNC | | special ) {
[PATCH] BLOCK: Make it possible to disable the block layer [try #6]
Make it possible to disable the block layer. Not all embedded devices require
it, some can make do with just JFFS2, NFS, ramfs, etc - none of which require
the block layer to be present.
This patch does the following:
(*) Introduces CONFIG_BLOCK to disable the block layer, buffering and blockdev
support.
(*) Adds dependencies on CONFIG_BLOCK to any configuration item that controls
an item that uses the block layer. This includes:
(*) Block I/O tracing.
(*) Disk partition code.
(*) All filesystems that are block based, eg: Ext3, ReiserFS, ISOFS.
(*) The SCSI layer. As far as I can tell, even SCSI chardevs use the
block layer to do scheduling. Some drivers that use SCSI facilities -
such as USB storage - end up disabled indirectly from this.
(*) Various block-based device drivers, such as IDE and the old CDROM
drivers.
(*) MTD blockdev handling and FTL.
(*) JFFS - which uses set_bdev_super(), something it could avoid doing by
taking a leaf out of JFFS2's book.
(*) Makes most of the contents of linux/blkdev.h, linux/buffer_head.h and
linux/elevator.h contingent on CONFIG_BLOCK being set. sector_div() is,
however, still used in places, and so is still available.
(*) Also made contingent are the contents of linux/mpage.h, linux/genhd.h and
parts of linux/fs.h.
(*) Makes a number of files in fs/ contingent on CONFIG_BLOCK.
(*) Makes mm/bounce.c (bounce buffering) contingent on CONFIG_BLOCK.
(*) set_page_dirty() doesn't call __set_page_dirty_buffers() if CONFIG_BLOCK
is not enabled.
(*) fs/no-block.c is created to hold out-of-line stubs and things that are
required when CONFIG_BLOCK is not set:
(*) Default blockdev file operations (to give error ENODEV on opening).
(*) Makes some /proc changes:
(*) /proc/devices does not list any blockdevs.
(*) /proc/diskstats and /proc/partitions are contingent on CONFIG_BLOCK.
(*) Makes some compat ioctl handling contingent on CONFIG_BLOCK.
(*) If CONFIG_BLOCK is not defined, makes sys_quotactl() return -ENODEV if
given command other than Q_SYNC or if a special device is specified.
(*) In init/do_mounts.c, no reference is made to the blockdev routines if
CONFIG_BLOCK is not defined. This does not prohibit NFS roots or JFFS2.
(*) The bdflush, ioprio_set and ioprio_get syscalls can now be absent (return
error ENOSYS by way of cond_syscall if so).
(*) The seclvl_bd_claim() and seclvl_bd_release() security calls do nothing if
CONFIG_BLOCK is not set, since they can't then happen.
Signed-Off-By: David Howells <dhowells@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
2006-09-30 22:45:40 +04:00
sb = quotactl_block ( special ) ;
if ( IS_ERR ( sb ) )
return PTR_ERR ( sb ) ;
2005-04-17 02:20:36 +04:00
}
ret = check_quotactl_valid ( sb , type , cmds , id ) ;
if ( ret > = 0 )
ret = do_quotactl ( sb , type , cmds , id , addr ) ;
if ( sb )
drop_super ( sb ) ;
return ret ;
}
2007-07-16 10:41:12 +04:00
# if defined(CONFIG_X86_64) || defined(CONFIG_IA64)
/*
* This code works only for 32 bit quota tools over 64 bit OS ( x86_64 , ia64 )
* and is necessary due to alignment problems .
*/
struct compat_if_dqblk {
compat_u64 dqb_bhardlimit ;
compat_u64 dqb_bsoftlimit ;
compat_u64 dqb_curspace ;
compat_u64 dqb_ihardlimit ;
compat_u64 dqb_isoftlimit ;
compat_u64 dqb_curinodes ;
compat_u64 dqb_btime ;
compat_u64 dqb_itime ;
compat_uint_t dqb_valid ;
} ;
/* XFS structures */
struct compat_fs_qfilestat {
compat_u64 dqb_bhardlimit ;
compat_u64 qfs_nblks ;
compat_uint_t qfs_nextents ;
} ;
struct compat_fs_quota_stat {
__s8 qs_version ;
__u16 qs_flags ;
__s8 qs_pad ;
struct compat_fs_qfilestat qs_uquota ;
struct compat_fs_qfilestat qs_gquota ;
compat_uint_t qs_incoredqs ;
compat_int_t qs_btimelimit ;
compat_int_t qs_itimelimit ;
compat_int_t qs_rtbtimelimit ;
__u16 qs_bwarnlimit ;
__u16 qs_iwarnlimit ;
} ;
asmlinkage long sys32_quotactl ( unsigned int cmd , const char __user * special ,
qid_t id , void __user * addr )
{
unsigned int cmds ;
struct if_dqblk __user * dqblk ;
struct compat_if_dqblk __user * compat_dqblk ;
struct fs_quota_stat __user * fsqstat ;
struct compat_fs_quota_stat __user * compat_fsqstat ;
compat_uint_t data ;
u16 xdata ;
long ret ;
cmds = cmd > > SUBCMDSHIFT ;
switch ( cmds ) {
case Q_GETQUOTA :
dqblk = compat_alloc_user_space ( sizeof ( struct if_dqblk ) ) ;
compat_dqblk = addr ;
ret = sys_quotactl ( cmd , special , id , dqblk ) ;
if ( ret )
break ;
if ( copy_in_user ( compat_dqblk , dqblk , sizeof ( * compat_dqblk ) ) | |
get_user ( data , & dqblk - > dqb_valid ) | |
put_user ( data , & compat_dqblk - > dqb_valid ) )
ret = - EFAULT ;
break ;
case Q_SETQUOTA :
dqblk = compat_alloc_user_space ( sizeof ( struct if_dqblk ) ) ;
compat_dqblk = addr ;
ret = - EFAULT ;
if ( copy_in_user ( dqblk , compat_dqblk , sizeof ( * compat_dqblk ) ) | |
get_user ( data , & compat_dqblk - > dqb_valid ) | |
put_user ( data , & dqblk - > dqb_valid ) )
break ;
ret = sys_quotactl ( cmd , special , id , dqblk ) ;
break ;
case Q_XGETQSTAT :
fsqstat = compat_alloc_user_space ( sizeof ( struct fs_quota_stat ) ) ;
compat_fsqstat = addr ;
ret = sys_quotactl ( cmd , special , id , fsqstat ) ;
if ( ret )
break ;
ret = - EFAULT ;
/* Copying qs_version, qs_flags, qs_pad */
if ( copy_in_user ( compat_fsqstat , fsqstat ,
offsetof ( struct compat_fs_quota_stat , qs_uquota ) ) )
break ;
/* Copying qs_uquota */
if ( copy_in_user ( & compat_fsqstat - > qs_uquota ,
& fsqstat - > qs_uquota ,
sizeof ( compat_fsqstat - > qs_uquota ) ) | |
get_user ( data , & fsqstat - > qs_uquota . qfs_nextents ) | |
put_user ( data , & compat_fsqstat - > qs_uquota . qfs_nextents ) )
break ;
/* Copying qs_gquota */
if ( copy_in_user ( & compat_fsqstat - > qs_gquota ,
& fsqstat - > qs_gquota ,
sizeof ( compat_fsqstat - > qs_gquota ) ) | |
get_user ( data , & fsqstat - > qs_gquota . qfs_nextents ) | |
put_user ( data , & compat_fsqstat - > qs_gquota . qfs_nextents ) )
break ;
/* Copying the rest */
if ( copy_in_user ( & compat_fsqstat - > qs_incoredqs ,
& fsqstat - > qs_incoredqs ,
sizeof ( struct compat_fs_quota_stat ) -
offsetof ( struct compat_fs_quota_stat , qs_incoredqs ) ) | |
get_user ( xdata , & fsqstat - > qs_iwarnlimit ) | |
put_user ( xdata , & compat_fsqstat - > qs_iwarnlimit ) )
break ;
ret = 0 ;
break ;
default :
ret = sys_quotactl ( cmd , special , id , addr ) ;
}
return ret ;
}
# endif