2005-04-17 02:20:36 +04:00
/*
2006-06-09 09:29:58 +04:00
* Copyright ( c ) 2000 - 2006 Silicon Graphics , Inc .
2005-11-02 06:59:41 +03:00
* All Rights Reserved .
2005-04-17 02:20:36 +04:00
*
2005-11-02 06:59:41 +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:59:41 +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:59:41 +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_clnt.h"
# 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_quota.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"
# include "xfs_dir2_sf.h"
2005-11-02 06:38:42 +03:00
# include "xfs_attr_sf.h"
2005-04-17 02:20:36 +04:00
# include "xfs_dinode.h"
# include "xfs_inode.h"
2005-11-02 06:38:42 +03:00
# include "xfs_ialloc.h"
# include "xfs_itable.h"
# include "xfs_btree.h"
2005-04-17 02:20:36 +04:00
# include "xfs_bmap.h"
# include "xfs_rtalloc.h"
# include "xfs_error.h"
# include "xfs_rw.h"
# include "xfs_acl.h"
# include "xfs_cap.h"
# include "xfs_mac.h"
# include "xfs_attr.h"
# include "xfs_buf_item.h"
# include "xfs_qm.h"
# define MNTOPT_QUOTA "quota" /* disk quotas (user) */
# define MNTOPT_NOQUOTA "noquota" /* no quotas */
# define MNTOPT_USRQUOTA "usrquota" /* user quota enabled */
# define MNTOPT_GRPQUOTA "grpquota" /* group quota enabled */
2005-06-21 09:38:48 +04:00
# define MNTOPT_PRJQUOTA "prjquota" /* project quota enabled */
2005-04-17 02:20:36 +04:00
# define MNTOPT_UQUOTA "uquota" /* user quota (IRIX variant) */
# define MNTOPT_GQUOTA "gquota" /* group quota (IRIX variant) */
2005-06-21 09:38:48 +04:00
# define MNTOPT_PQUOTA "pquota" /* project quota (IRIX variant) */
2005-04-17 02:20:36 +04:00
# define MNTOPT_UQUOTANOENF "uqnoenforce" /* user quota limit enforcement */
# define MNTOPT_GQUOTANOENF "gqnoenforce" /* group quota limit enforcement */
2005-06-21 09:38:48 +04:00
# define MNTOPT_PQUOTANOENF "pqnoenforce" /* project quota limit enforcement */
2005-04-17 02:20:36 +04:00
# define MNTOPT_QUOTANOENF "qnoenforce" /* same as uqnoenforce */
STATIC int
xfs_qm_parseargs (
struct bhv_desc * bhv ,
char * options ,
struct xfs_mount_args * args ,
int update )
{
size_t length ;
char * local_options = options ;
char * this_char ;
int error ;
int referenced = update ;
while ( ( this_char = strsep ( & local_options , " , " ) ) ! = NULL ) {
length = strlen ( this_char ) ;
if ( local_options )
length + + ;
if ( ! strcmp ( this_char , MNTOPT_NOQUOTA ) ) {
args - > flags & = ~ ( XFSMNT_UQUOTAENF | XFSMNT_UQUOTA ) ;
args - > flags & = ~ ( XFSMNT_GQUOTAENF | XFSMNT_GQUOTA ) ;
referenced = update ;
} else if ( ! strcmp ( this_char , MNTOPT_QUOTA ) | |
! strcmp ( this_char , MNTOPT_UQUOTA ) | |
! strcmp ( this_char , MNTOPT_USRQUOTA ) ) {
args - > flags | = XFSMNT_UQUOTA | XFSMNT_UQUOTAENF ;
referenced = 1 ;
} else if ( ! strcmp ( this_char , MNTOPT_QUOTANOENF ) | |
! strcmp ( this_char , MNTOPT_UQUOTANOENF ) ) {
args - > flags | = XFSMNT_UQUOTA ;
args - > flags & = ~ XFSMNT_UQUOTAENF ;
referenced = 1 ;
2005-06-21 09:38:48 +04:00
} else if ( ! strcmp ( this_char , MNTOPT_PQUOTA ) | |
! strcmp ( this_char , MNTOPT_PRJQUOTA ) ) {
args - > flags | = XFSMNT_PQUOTA | XFSMNT_PQUOTAENF ;
referenced = 1 ;
} else if ( ! strcmp ( this_char , MNTOPT_PQUOTANOENF ) ) {
args - > flags | = XFSMNT_PQUOTA ;
args - > flags & = ~ XFSMNT_PQUOTAENF ;
referenced = 1 ;
2005-04-17 02:20:36 +04:00
} else if ( ! strcmp ( this_char , MNTOPT_GQUOTA ) | |
! strcmp ( this_char , MNTOPT_GRPQUOTA ) ) {
args - > flags | = XFSMNT_GQUOTA | XFSMNT_GQUOTAENF ;
referenced = 1 ;
} else if ( ! strcmp ( this_char , MNTOPT_GQUOTANOENF ) ) {
args - > flags | = XFSMNT_GQUOTA ;
args - > flags & = ~ XFSMNT_GQUOTAENF ;
referenced = 1 ;
} else {
if ( local_options )
* ( local_options - 1 ) = ' , ' ;
continue ;
}
while ( length - - )
* this_char + + = ' , ' ;
}
2005-06-21 09:38:48 +04:00
if ( ( args - > flags & XFSMNT_GQUOTA ) & & ( args - > flags & XFSMNT_PQUOTA ) ) {
cmn_err ( CE_WARN ,
" XFS: cannot mount with both project and group quota " ) ;
return XFS_ERROR ( EINVAL ) ;
}
2006-06-09 10:48:30 +04:00
error = bhv_next_vfs_parseargs ( BHV_NEXT ( bhv ) , options , args , update ) ;
2005-04-17 02:20:36 +04:00
if ( ! error & & ! referenced )
bhv_remove_vfsops ( bhvtovfs ( bhv ) , VFS_POSITION_QM ) ;
return error ;
}
STATIC int
xfs_qm_showargs (
struct bhv_desc * bhv ,
struct seq_file * m )
{
2006-06-09 10:48:30 +04:00
struct bhv_vfs * vfsp = bhvtovfs ( bhv ) ;
2005-04-17 02:20:36 +04:00
struct xfs_mount * mp = XFS_VFSTOM ( vfsp ) ;
if ( mp - > m_qflags & XFS_UQUOTA_ACCT ) {
( mp - > m_qflags & XFS_UQUOTA_ENFD ) ?
seq_puts ( m , " , " MNTOPT_USRQUOTA ) :
seq_puts ( m , " , " MNTOPT_UQUOTANOENF ) ;
}
2005-06-21 09:38:48 +04:00
if ( mp - > m_qflags & XFS_PQUOTA_ACCT ) {
( mp - > m_qflags & XFS_OQUOTA_ENFD ) ?
seq_puts ( m , " , " MNTOPT_PRJQUOTA ) :
seq_puts ( m , " , " MNTOPT_PQUOTANOENF ) ;
}
2005-04-17 02:20:36 +04:00
if ( mp - > m_qflags & XFS_GQUOTA_ACCT ) {
2005-06-21 09:38:48 +04:00
( mp - > m_qflags & XFS_OQUOTA_ENFD ) ?
2005-04-17 02:20:36 +04:00
seq_puts ( m , " , " MNTOPT_GRPQUOTA ) :
seq_puts ( m , " , " MNTOPT_GQUOTANOENF ) ;
}
2005-06-21 09:38:48 +04:00
if ( ! ( mp - > m_qflags & XFS_ALL_QUOTA_ACCT ) )
2005-04-17 02:20:36 +04:00
seq_puts ( m , " , " MNTOPT_NOQUOTA ) ;
2006-06-09 10:48:30 +04:00
return bhv_next_vfs_showargs ( BHV_NEXT ( bhv ) , m ) ;
2005-04-17 02:20:36 +04:00
}
STATIC int
xfs_qm_mount (
struct bhv_desc * bhv ,
struct xfs_mount_args * args ,
struct cred * cr )
{
2006-06-09 10:48:30 +04:00
struct bhv_vfs * vfsp = bhvtovfs ( bhv ) ;
2005-04-17 02:20:36 +04:00
struct xfs_mount * mp = XFS_VFSTOM ( vfsp ) ;
2005-06-21 09:38:48 +04:00
if ( args - > flags & ( XFSMNT_UQUOTA | XFSMNT_GQUOTA | XFSMNT_PQUOTA ) )
2005-04-17 02:20:36 +04:00
xfs_qm_mount_quotainit ( mp , args - > flags ) ;
2006-06-09 10:48:30 +04:00
return bhv_next_vfs_mount ( BHV_NEXT ( bhv ) , args , cr ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-09 09:29:58 +04:00
/*
* Directory tree accounting is implemented using project quotas , where
* the project identifier is inherited from parent directories .
* A statvfs ( df , etc . ) of a directory that is using project quota should
* return a statvfs of the project , not the entire filesystem .
* This makes such trees appear as if they are filesystems in themselves .
*/
STATIC int
xfs_qm_statvfs (
struct bhv_desc * bhv ,
2006-06-09 11:07:12 +04:00
bhv_statvfs_t * statp ,
2006-06-09 11:00:52 +04:00
struct bhv_vnode * vnode )
2006-06-09 09:29:58 +04:00
{
xfs_mount_t * mp ;
xfs_inode_t * ip ;
xfs_dquot_t * dqp ;
xfs_disk_dquot_t * dp ;
__uint64_t limit ;
int error ;
2006-06-09 10:48:30 +04:00
error = bhv_next_vfs_statvfs ( BHV_NEXT ( bhv ) , statp , vnode ) ;
2006-06-09 09:29:58 +04:00
if ( error | | ! vnode )
return error ;
mp = XFS_BHVTOM ( bhv ) ;
ip = xfs_vtoi ( vnode ) ;
if ( ! ( ip - > i_d . di_flags & XFS_DIFLAG_PROJINHERIT ) )
return 0 ;
if ( ! ( mp - > m_qflags & XFS_PQUOTA_ACCT ) )
return 0 ;
if ( ! ( mp - > m_qflags & XFS_OQUOTA_ENFD ) )
return 0 ;
if ( xfs_qm_dqget ( mp , NULL , ip - > i_d . di_projid , XFS_DQ_PROJ , 0 , & dqp ) )
return 0 ;
dp = & dqp - > q_core ;
limit = dp - > d_blk_softlimit ? dp - > d_blk_softlimit : dp - > d_blk_hardlimit ;
if ( limit & & statp - > f_blocks > limit ) {
statp - > f_blocks = limit ;
statp - > f_bfree = ( statp - > f_blocks > dp - > d_bcount ) ?
( statp - > f_blocks - dp - > d_bcount ) : 0 ;
}
limit = dp - > d_ino_softlimit ? dp - > d_ino_softlimit : dp - > d_ino_hardlimit ;
if ( limit & & statp - > f_files > limit ) {
statp - > f_files = limit ;
statp - > f_ffree = ( statp - > f_files > dp - > d_icount ) ?
( statp - > f_ffree - dp - > d_icount ) : 0 ;
}
xfs_qm_dqput ( dqp ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
STATIC int
xfs_qm_syncall (
struct bhv_desc * bhv ,
int flags ,
cred_t * credp )
{
2006-06-09 10:48:30 +04:00
struct bhv_vfs * vfsp = bhvtovfs ( bhv ) ;
2005-04-17 02:20:36 +04:00
struct xfs_mount * mp = XFS_VFSTOM ( vfsp ) ;
int error ;
/*
* Get the Quota Manager to flush the dquots .
*/
if ( XFS_IS_QUOTA_ON ( mp ) ) {
if ( ( error = xfs_qm_sync ( mp , flags ) ) ) {
/*
* If we got an IO error , we will be shutting down .
* So , there ' s nothing more for us to do here .
*/
ASSERT ( error ! = EIO | | XFS_FORCED_SHUTDOWN ( mp ) ) ;
if ( XFS_FORCED_SHUTDOWN ( mp ) ) {
return XFS_ERROR ( error ) ;
}
}
}
2006-06-09 10:48:30 +04:00
return bhv_next_vfs_sync ( BHV_NEXT ( bhv ) , flags , credp ) ;
2005-04-17 02:20:36 +04:00
}
STATIC int
xfs_qm_newmount (
xfs_mount_t * mp ,
uint * needquotamount ,
uint * quotaflags )
{
uint quotaondisk ;
2005-06-21 09:38:48 +04:00
uint uquotaondisk = 0 , gquotaondisk = 0 , pquotaondisk = 0 ;
2005-04-17 02:20:36 +04:00
* quotaflags = 0 ;
* needquotamount = B_FALSE ;
quotaondisk = XFS_SB_VERSION_HASQUOTA ( & mp - > m_sb ) & &
2005-06-21 09:38:48 +04:00
( mp - > m_sb . sb_qflags & XFS_ALL_QUOTA_ACCT ) ;
2005-04-17 02:20:36 +04:00
if ( quotaondisk ) {
uquotaondisk = mp - > m_sb . sb_qflags & XFS_UQUOTA_ACCT ;
2005-06-21 09:38:48 +04:00
pquotaondisk = mp - > m_sb . sb_qflags & XFS_PQUOTA_ACCT ;
2005-04-17 02:20:36 +04:00
gquotaondisk = mp - > m_sb . sb_qflags & XFS_GQUOTA_ACCT ;
}
/*
* If the device itself is read - only , we can ' t allow
* the user to change the state of quota on the mount -
* this would generate a transaction on the ro device ,
* which would lead to an I / O error and shutdown
*/
if ( ( ( uquotaondisk & & ! XFS_IS_UQUOTA_ON ( mp ) ) | |
( ! uquotaondisk & & XFS_IS_UQUOTA_ON ( mp ) ) | |
2005-06-21 09:38:48 +04:00
( pquotaondisk & & ! XFS_IS_PQUOTA_ON ( mp ) ) | |
( ! pquotaondisk & & XFS_IS_PQUOTA_ON ( mp ) ) | |
2005-04-17 02:20:36 +04:00
( gquotaondisk & & ! XFS_IS_GQUOTA_ON ( mp ) ) | |
2005-06-21 09:38:48 +04:00
( ! gquotaondisk & & XFS_IS_OQUOTA_ON ( mp ) ) ) & &
2005-04-17 02:20:36 +04:00
xfs_dev_is_read_only ( mp , " changing quota state " ) ) {
cmn_err ( CE_WARN ,
2005-06-21 09:38:48 +04:00
" XFS: please mount with%s%s%s%s. " ,
2005-04-17 02:20:36 +04:00
( ! quotaondisk ? " out quota " : " " ) ,
( uquotaondisk ? " usrquota " : " " ) ,
2005-06-21 09:38:48 +04:00
( pquotaondisk ? " prjquota " : " " ) ,
2005-04-17 02:20:36 +04:00
( gquotaondisk ? " grpquota " : " " ) ) ;
return XFS_ERROR ( EPERM ) ;
}
if ( XFS_IS_QUOTA_ON ( mp ) | | quotaondisk ) {
/*
* Call mount_quotas at this point only if we won ' t have to do
* a quotacheck .
*/
if ( quotaondisk & & ! XFS_QM_NEED_QUOTACHECK ( mp ) ) {
/*
* If an error occured , qm_mount_quotas code
* has already disabled quotas . So , just finish
* mounting , and get on with the boring life
* without disk quotas .
*/
xfs_qm_mount_quotas ( mp , 0 ) ;
} else {
/*
* Clear the quota flags , but remember them . This
* is so that the quota code doesn ' t get invoked
* before we ' re ready . This can happen when an
* inode goes inactive and wants to free blocks ,
* or via xfs_log_mount_finish .
*/
* needquotamount = B_TRUE ;
* quotaflags = mp - > m_qflags ;
mp - > m_qflags = 0 ;
}
}
return 0 ;
}
STATIC int
xfs_qm_endmount (
xfs_mount_t * mp ,
uint needquotamount ,
uint quotaflags ,
int mfsi_flags )
{
if ( needquotamount ) {
ASSERT ( mp - > m_qflags = = 0 ) ;
mp - > m_qflags = quotaflags ;
xfs_qm_mount_quotas ( mp , mfsi_flags ) ;
}
# if defined(DEBUG) && defined(XFS_LOUD_RECOVERY)
if ( ! ( XFS_IS_QUOTA_ON ( mp ) ) )
xfs_fs_cmn_err ( CE_NOTE , mp , " Disk quotas not turned on " ) ;
else
xfs_fs_cmn_err ( CE_NOTE , mp , " Disk quotas turned on " ) ;
# endif
# ifdef QUOTADEBUG
if ( XFS_IS_QUOTA_ON ( mp ) & & xfs_qm_internalqcheck ( mp ) )
cmn_err ( CE_WARN , " XFS: mount internalqcheck failed " ) ;
# endif
return 0 ;
}
STATIC void
xfs_qm_dqrele_null (
xfs_dquot_t * dq )
{
/*
* Called from XFS , where we always check first for a NULL dquot .
*/
if ( ! dq )
return ;
xfs_qm_dqrele ( dq ) ;
}
2005-06-21 09:36:52 +04:00
STATIC struct xfs_qmops xfs_qmcore_xfs = {
2005-04-17 02:20:36 +04:00
. xfs_qminit = xfs_qm_newmount ,
. xfs_qmdone = xfs_qm_unmount_quotadestroy ,
. xfs_qmmount = xfs_qm_endmount ,
. xfs_qmunmount = xfs_qm_unmount_quotas ,
. xfs_dqrele = xfs_qm_dqrele_null ,
. xfs_dqattach = xfs_qm_dqattach ,
. xfs_dqdetach = xfs_qm_dqdetach ,
. xfs_dqpurgeall = xfs_qm_dqpurge_all ,
. xfs_dqvopalloc = xfs_qm_vop_dqalloc ,
. xfs_dqvopcreate = xfs_qm_vop_dqattach_and_dqmod_newinode ,
. xfs_dqvoprename = xfs_qm_vop_rename_dqattach ,
. xfs_dqvopchown = xfs_qm_vop_chown ,
. xfs_dqvopchownresv = xfs_qm_vop_chown_reserve ,
. xfs_dqtrxops = & xfs_trans_dquot_ops ,
} ;
2006-06-09 10:48:30 +04:00
struct bhv_module_vfsops xfs_qmops = { {
2005-04-17 02:20:36 +04:00
BHV_IDENTITY_INIT ( VFS_BHV_QM , VFS_POSITION_QM ) ,
. vfs_parseargs = xfs_qm_parseargs ,
. vfs_showargs = xfs_qm_showargs ,
. vfs_mount = xfs_qm_mount ,
2006-06-09 09:29:58 +04:00
. vfs_statvfs = xfs_qm_statvfs ,
2005-04-17 02:20:36 +04:00
. vfs_sync = xfs_qm_syncall ,
. vfs_quotactl = xfs_qm_quotactl , } ,
} ;
void __init
xfs_qm_init ( void )
{
static char message [ ] __initdata =
KERN_INFO " SGI XFS Quota Management subsystem \n " ;
printk ( message ) ;
2006-01-10 02:59:21 +03:00
mutex_init ( & xfs_Gqm_lock ) ;
2005-04-17 02:20:36 +04:00
vfs_bhv_set_custom ( & xfs_qmops , & xfs_qmcore_xfs ) ;
xfs_qm_init_procfs ( ) ;
}
void __exit
xfs_qm_exit ( void )
{
vfs_bhv_clr_custom ( & xfs_qmops ) ;
xfs_qm_cleanup_procfs ( ) ;
if ( qm_dqzone )
2006-03-22 04:47:28 +03:00
kmem_zone_destroy ( qm_dqzone ) ;
2005-04-17 02:20:36 +04:00
if ( qm_dqtrxzone )
2006-03-22 04:47:28 +03:00
kmem_zone_destroy ( qm_dqtrxzone ) ;
2005-04-17 02:20:36 +04:00
}