2005-04-17 02:20:36 +04:00
/*
2005-11-02 06:59:41 +03:00
* Copyright ( c ) 2000 - 2005 Silicon Graphics , Inc .
* 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
*/
2006-01-11 23:17:46 +03:00
# include <linux/capability.h>
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_alloc.h"
# include "xfs_quota.h"
# include "xfs_mount.h"
# include "xfs_bmap_btree.h"
# include "xfs_inode.h"
2005-11-02 06:38:42 +03:00
# include "xfs_itable.h"
2005-04-17 02:20:36 +04:00
# include "xfs_bmap.h"
# include "xfs_rtalloc.h"
# include "xfs_error.h"
# include "xfs_attr.h"
# include "xfs_buf_item.h"
# include "xfs_utils.h"
# include "xfs_qm.h"
2009-12-15 02:14:59 +03:00
# include "xfs_trace.h"
2005-04-17 02:20:36 +04:00
STATIC int xfs_qm_log_quotaoff ( xfs_mount_t * , xfs_qoff_logitem_t * * , uint ) ;
STATIC int xfs_qm_log_quotaoff_end ( xfs_mount_t * , xfs_qoff_logitem_t * ,
uint ) ;
STATIC uint xfs_qm_export_flags ( uint ) ;
STATIC uint xfs_qm_export_qtype_flags ( uint ) ;
STATIC void xfs_qm_export_dquot ( xfs_mount_t * , xfs_disk_dquot_t * ,
fs_disk_quota_t * ) ;
/*
* Turn off quota accounting and / or enforcement for all udquots and / or
* gdquots . Called only at unmount time .
*
* This assumes that there are no dquots of this file system cached
* incore , and modifies the ondisk dquot directly . Therefore , for example ,
* it is an error to call this twice , without purging the cache .
*/
2009-02-09 10:47:34 +03:00
int
2005-04-17 02:20:36 +04:00
xfs_qm_scall_quotaoff (
xfs_mount_t * mp ,
2009-02-09 10:47:34 +03:00
uint flags )
2005-04-17 02:20:36 +04:00
{
2010-04-20 11:01:30 +04:00
struct xfs_quotainfo * q = mp - > m_quotainfo ;
2005-04-17 02:20:36 +04:00
uint dqtype ;
int error ;
uint inactivate_flags ;
xfs_qoff_logitem_t * qoffstart ;
int nculprits ;
/*
* No file system can have quotas enabled on disk but not in core .
* Note that quota utilities ( like quotaoff ) _expect_
* errno = = EEXIST here .
*/
if ( ( mp - > m_qflags & flags ) = = 0 )
return XFS_ERROR ( EEXIST ) ;
error = 0 ;
flags & = ( XFS_ALL_QUOTA_ACCT | XFS_ALL_QUOTA_ENFD ) ;
/*
* We don ' t want to deal with two quotaoffs messing up each other ,
* so we ' re going to serialize it . quotaoff isn ' t exactly a performance
* critical thing .
* If quotaoff , then we must be dealing with the root filesystem .
*/
2010-04-20 11:01:30 +04:00
ASSERT ( q ) ;
mutex_lock ( & q - > qi_quotaofflock ) ;
2005-04-17 02:20:36 +04:00
/*
* If we ' re just turning off quota enforcement , change mp and go .
*/
if ( ( flags & XFS_ALL_QUOTA_ACCT ) = = 0 ) {
mp - > m_qflags & = ~ ( flags ) ;
2007-10-11 11:42:32 +04:00
spin_lock ( & mp - > m_sb_lock ) ;
2005-04-17 02:20:36 +04:00
mp - > m_sb . sb_qflags = mp - > m_qflags ;
2007-10-11 11:42:32 +04:00
spin_unlock ( & mp - > m_sb_lock ) ;
2010-04-20 11:01:30 +04:00
mutex_unlock ( & q - > qi_quotaofflock ) ;
2005-04-17 02:20:36 +04:00
/* XXX what to do if error ? Revert back to old vals incore ? */
error = xfs_qm_write_sb_changes ( mp , XFS_SB_QFLAGS ) ;
return ( error ) ;
}
dqtype = 0 ;
inactivate_flags = 0 ;
/*
* If accounting is off , we must turn enforcement off , clear the
* quota ' CHKD ' certificate to make it known that we have to
* do a quotacheck the next time this quota is turned on .
*/
if ( flags & XFS_UQUOTA_ACCT ) {
dqtype | = XFS_QMOPT_UQUOTA ;
flags | = ( XFS_UQUOTA_CHKD | XFS_UQUOTA_ENFD ) ;
inactivate_flags | = XFS_UQUOTA_ACTIVE ;
}
if ( flags & XFS_GQUOTA_ACCT ) {
dqtype | = XFS_QMOPT_GQUOTA ;
2005-06-21 09:38:48 +04:00
flags | = ( XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD ) ;
2005-04-17 02:20:36 +04:00
inactivate_flags | = XFS_GQUOTA_ACTIVE ;
2005-06-21 09:38:48 +04:00
} else if ( flags & XFS_PQUOTA_ACCT ) {
dqtype | = XFS_QMOPT_PQUOTA ;
flags | = ( XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD ) ;
inactivate_flags | = XFS_PQUOTA_ACTIVE ;
2005-04-17 02:20:36 +04:00
}
/*
* Nothing to do ? Don ' t complain . This happens when we ' re just
* turning off quota enforcement .
*/
2010-04-20 11:01:30 +04:00
if ( ( mp - > m_qflags & flags ) = = 0 )
goto out_unlock ;
2005-04-17 02:20:36 +04:00
/*
* Write the LI_QUOTAOFF log record , and do SB changes atomically ,
2008-04-10 06:20:45 +04:00
* and synchronously . If we fail to write , we should abort the
* operation as it cannot be recovered safely if we crash .
2005-04-17 02:20:36 +04:00
*/
2008-04-10 06:20:45 +04:00
error = xfs_qm_log_quotaoff ( mp , & qoffstart , flags ) ;
if ( error )
2010-04-20 11:01:30 +04:00
goto out_unlock ;
2005-04-17 02:20:36 +04:00
/*
* Next we clear the XFS_MOUNT_ * DQ_ACTIVE bit ( s ) in the mount struct
* to take care of the race between dqget and quotaoff . We don ' t take
* any special locks to reset these bits . All processes need to check
* these bits * after * taking inode lock ( s ) to see if the particular
* quota type is in the process of being turned off . If * ACTIVE , it is
* guaranteed that all dquot structures and all quotainode ptrs will all
* stay valid as long as that inode is kept locked .
*
* There is no turning back after this .
*/
mp - > m_qflags & = ~ inactivate_flags ;
/*
* Give back all the dquot reference ( s ) held by inodes .
* Here we go thru every single incore inode in this file system , and
* do a dqrele on the i_udquot / i_gdquot that it may have .
* Essentially , as long as somebody has an inode locked , this guarantees
* that quotas will not be turned off . This is handy because in a
* transaction once we lock the inode ( s ) and check for quotaon , we can
* depend on the quota inodes ( and other things ) being valid as long as
* we keep the lock ( s ) .
*/
xfs_qm_dqrele_all_inodes ( mp , flags ) ;
/*
* Next we make the changes in the quota flag in the mount struct .
* This isn ' t protected by a particular lock directly , because we
* don ' t want to take a mrlock everytime we depend on quotas being on .
*/
mp - > m_qflags & = ~ ( flags ) ;
/*
* Go through all the dquots of this file system and purge them ,
* according to what was turned off . We may not be able to get rid
* of all dquots , because dquots can have temporary references that
* are not attached to inodes . eg . xfs_setattr , xfs_create .
* So , if we couldn ' t purge all the dquots from the filesystem ,
* we can ' t get rid of the incore data structures .
*/
2010-04-20 11:02:29 +04:00
while ( ( nculprits = xfs_qm_dqpurge_all ( mp , dqtype ) ) )
2005-04-17 02:20:36 +04:00
delay ( 10 * nculprits ) ;
/*
* Transactions that had started before ACTIVE state bit was cleared
* could have logged many dquots , so they ' d have higher LSNs than
* the first QUOTAOFF log record does . If we happen to crash when
* the tail of the log has gone past the QUOTAOFF record , but
* before the last dquot modification , those dquots __will__
* recover , and that ' s not good .
*
* So , we have QUOTAOFF start and end logitems ; the start
* logitem won ' t get overwritten until the end logitem appears . . .
*/
2008-04-10 06:20:45 +04:00
error = xfs_qm_log_quotaoff_end ( mp , qoffstart , flags ) ;
if ( error ) {
/* We're screwed now. Shutdown is the only option. */
xfs_force_shutdown ( mp , SHUTDOWN_CORRUPT_INCORE ) ;
2010-04-20 11:01:30 +04:00
goto out_unlock ;
2008-04-10 06:20:45 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* If quotas is completely disabled , close shop .
*/
2005-06-21 09:38:48 +04:00
if ( ( ( flags & XFS_MOUNT_QUOTA_ALL ) = = XFS_MOUNT_QUOTA_SET1 ) | |
( ( flags & XFS_MOUNT_QUOTA_ALL ) = = XFS_MOUNT_QUOTA_SET2 ) ) {
2010-04-20 11:01:30 +04:00
mutex_unlock ( & q - > qi_quotaofflock ) ;
2005-04-17 02:20:36 +04:00
xfs_qm_destroy_quotainfo ( mp ) ;
return ( 0 ) ;
}
/*
2010-04-20 11:01:30 +04:00
* Release our quotainode references if we don ' t need them anymore .
2005-04-17 02:20:36 +04:00
*/
2010-04-20 11:01:30 +04:00
if ( ( dqtype & XFS_QMOPT_UQUOTA ) & & q - > qi_uquotaip ) {
IRELE ( q - > qi_uquotaip ) ;
q - > qi_uquotaip = NULL ;
2005-04-17 02:20:36 +04:00
}
2010-04-20 11:01:30 +04:00
if ( ( dqtype & ( XFS_QMOPT_GQUOTA | XFS_QMOPT_PQUOTA ) ) & & q - > qi_gquotaip ) {
IRELE ( q - > qi_gquotaip ) ;
q - > qi_gquotaip = NULL ;
2005-04-17 02:20:36 +04:00
}
2010-04-20 11:01:30 +04:00
out_unlock :
mutex_unlock ( & q - > qi_quotaofflock ) ;
return error ;
2005-04-17 02:20:36 +04:00
}
2010-07-20 11:51:31 +04:00
STATIC int
xfs_qm_scall_trunc_qfile (
struct xfs_mount * mp ,
xfs_ino_t ino )
{
struct xfs_inode * ip ;
struct xfs_trans * tp ;
int error ;
if ( ino = = NULLFSINO )
return 0 ;
error = xfs_iget ( mp , NULL , ino , 0 , 0 , & ip ) ;
if ( error )
return error ;
xfs_ilock ( ip , XFS_IOLOCK_EXCL ) ;
tp = xfs_trans_alloc ( mp , XFS_TRANS_TRUNCATE_FILE ) ;
error = xfs_trans_reserve ( tp , 0 , XFS_ITRUNCATE_LOG_RES ( mp ) , 0 ,
XFS_TRANS_PERM_LOG_RES ,
XFS_ITRUNCATE_LOG_COUNT ) ;
if ( error ) {
xfs_trans_cancel ( tp , 0 ) ;
xfs_iunlock ( ip , XFS_IOLOCK_EXCL ) ;
goto out_put ;
}
xfs_ilock ( ip , XFS_ILOCK_EXCL ) ;
xfs_trans_ijoin ( tp , ip ) ;
error = xfs_itruncate_finish ( & tp , ip , 0 , XFS_DATA_FORK , 1 ) ;
if ( error ) {
xfs_trans_cancel ( tp , XFS_TRANS_RELEASE_LOG_RES |
XFS_TRANS_ABORT ) ;
goto out_unlock ;
}
2010-09-28 06:27:25 +04:00
xfs_trans_ichgtime ( tp , ip , XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG ) ;
2010-07-20 11:51:31 +04:00
error = xfs_trans_commit ( tp , XFS_TRANS_RELEASE_LOG_RES ) ;
out_unlock :
xfs_iunlock ( ip , XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL ) ;
out_put :
IRELE ( ip ) ;
return error ;
}
2009-02-09 10:47:34 +03:00
int
2005-04-17 02:20:36 +04:00
xfs_qm_scall_trunc_qfiles (
xfs_mount_t * mp ,
uint flags )
{
2008-04-10 06:20:51 +04:00
int error = 0 , error2 = 0 ;
2005-04-17 02:20:36 +04:00
2008-03-06 05:44:28 +03:00
if ( ! xfs_sb_version_hasquota ( & mp - > m_sb ) | | flags = = 0 ) {
2011-03-07 02:07:35 +03:00
xfs_debug ( mp , " %s: flags=%x m_qflags=%x \n " ,
__func__ , flags , mp - > m_qflags ) ;
2005-04-17 02:20:36 +04:00
return XFS_ERROR ( EINVAL ) ;
}
2010-07-20 11:51:31 +04:00
if ( flags & XFS_DQ_USER )
error = xfs_qm_scall_trunc_qfile ( mp , mp - > m_sb . sb_uquotino ) ;
if ( flags & ( XFS_DQ_GROUP | XFS_DQ_PROJ ) )
error2 = xfs_qm_scall_trunc_qfile ( mp , mp - > m_sb . sb_gquotino ) ;
2005-04-17 02:20:36 +04:00
2008-04-10 06:20:51 +04:00
return error ? error : error2 ;
2005-04-17 02:20:36 +04:00
}
/*
* Switch on ( a given ) quota enforcement for a filesystem . This takes
* effect immediately .
* ( Switching on quota accounting must be done at mount time . )
*/
2009-02-09 10:47:34 +03:00
int
2005-04-17 02:20:36 +04:00
xfs_qm_scall_quotaon (
xfs_mount_t * mp ,
uint flags )
{
int error ;
uint qf ;
uint accflags ;
__int64_t sbflags ;
flags & = ( XFS_ALL_QUOTA_ACCT | XFS_ALL_QUOTA_ENFD ) ;
/*
* Switching on quota accounting must be done at mount time .
*/
accflags = flags & XFS_ALL_QUOTA_ACCT ;
flags & = ~ ( XFS_ALL_QUOTA_ACCT ) ;
sbflags = 0 ;
if ( flags = = 0 ) {
2011-03-07 02:07:35 +03:00
xfs_debug ( mp , " %s: zero flags, m_qflags=%x \n " ,
__func__ , mp - > m_qflags ) ;
2005-04-17 02:20:36 +04:00
return XFS_ERROR ( EINVAL ) ;
}
/* No fs can turn on quotas with a delayed effect */
ASSERT ( ( flags & XFS_ALL_QUOTA_ACCT ) = = 0 ) ;
/*
* Can ' t enforce without accounting . We check the superblock
* qflags here instead of m_qflags because rootfs can have
* quota acct on ondisk without m_qflags ' knowing .
*/
if ( ( ( flags & XFS_UQUOTA_ACCT ) = = 0 & &
( mp - > m_sb . sb_qflags & XFS_UQUOTA_ACCT ) = = 0 & &
( flags & XFS_UQUOTA_ENFD ) )
| |
2005-06-21 09:38:48 +04:00
( ( flags & XFS_PQUOTA_ACCT ) = = 0 & &
( mp - > m_sb . sb_qflags & XFS_PQUOTA_ACCT ) = = 0 & &
2007-05-08 07:49:15 +04:00
( flags & XFS_GQUOTA_ACCT ) = = 0 & &
2005-04-17 02:20:36 +04:00
( mp - > m_sb . sb_qflags & XFS_GQUOTA_ACCT ) = = 0 & &
2005-06-21 09:38:48 +04:00
( flags & XFS_OQUOTA_ENFD ) ) ) {
2011-03-07 02:07:35 +03:00
xfs_debug ( mp ,
" %s: Can't enforce without acct, flags=%x sbflags=%x \n " ,
__func__ , flags , mp - > m_sb . sb_qflags ) ;
2005-04-17 02:20:36 +04:00
return XFS_ERROR ( EINVAL ) ;
}
/*
* If everything ' s upto - date incore , then don ' t waste time .
*/
if ( ( mp - > m_qflags & flags ) = = flags )
return XFS_ERROR ( EEXIST ) ;
/*
* Change sb_qflags on disk but not incore mp - > qflags
* if this is the root filesystem .
*/
2007-10-11 11:42:32 +04:00
spin_lock ( & mp - > m_sb_lock ) ;
2005-04-17 02:20:36 +04:00
qf = mp - > m_sb . sb_qflags ;
mp - > m_sb . sb_qflags = qf | flags ;
2007-10-11 11:42:32 +04:00
spin_unlock ( & mp - > m_sb_lock ) ;
2005-04-17 02:20:36 +04:00
/*
* There ' s nothing to change if it ' s the same .
*/
if ( ( qf & flags ) = = flags & & sbflags = = 0 )
return XFS_ERROR ( EEXIST ) ;
sbflags | = XFS_SB_QFLAGS ;
if ( ( error = xfs_qm_write_sb_changes ( mp , sbflags ) ) )
return ( error ) ;
/*
* If we aren ' t trying to switch on quota enforcement , we are done .
*/
if ( ( ( mp - > m_sb . sb_qflags & XFS_UQUOTA_ACCT ) ! =
( mp - > m_qflags & XFS_UQUOTA_ACCT ) ) | |
2005-06-21 09:38:48 +04:00
( ( mp - > m_sb . sb_qflags & XFS_PQUOTA_ACCT ) ! =
( mp - > m_qflags & XFS_PQUOTA_ACCT ) ) | |
( ( mp - > m_sb . sb_qflags & XFS_GQUOTA_ACCT ) ! =
( mp - > m_qflags & XFS_GQUOTA_ACCT ) ) | |
2005-04-17 02:20:36 +04:00
( flags & XFS_ALL_QUOTA_ENFD ) = = 0 )
return ( 0 ) ;
if ( ! XFS_IS_QUOTA_RUNNING ( mp ) )
return XFS_ERROR ( ESRCH ) ;
/*
* Switch on quota enforcement in core .
*/
2010-04-20 11:01:30 +04:00
mutex_lock ( & mp - > m_quotainfo - > qi_quotaofflock ) ;
2005-04-17 02:20:36 +04:00
mp - > m_qflags | = ( flags & XFS_ALL_QUOTA_ENFD ) ;
2010-04-20 11:01:30 +04:00
mutex_unlock ( & mp - > m_quotainfo - > qi_quotaofflock ) ;
2005-04-17 02:20:36 +04:00
return ( 0 ) ;
}
/*
* Return quota status information , such as uquota - off , enforcements , etc .
*/
2009-02-09 10:47:34 +03:00
int
2005-04-17 02:20:36 +04:00
xfs_qm_scall_getqstat (
2010-04-20 11:01:30 +04:00
struct xfs_mount * mp ,
struct fs_quota_stat * out )
2005-04-17 02:20:36 +04:00
{
2010-04-20 11:01:30 +04:00
struct xfs_quotainfo * q = mp - > m_quotainfo ;
struct xfs_inode * uip , * gip ;
boolean_t tempuqip , tempgqip ;
2005-04-17 02:20:36 +04:00
uip = gip = NULL ;
tempuqip = tempgqip = B_FALSE ;
memset ( out , 0 , sizeof ( fs_quota_stat_t ) ) ;
out - > qs_version = FS_QSTAT_VERSION ;
2008-03-06 05:44:28 +03:00
if ( ! xfs_sb_version_hasquota ( & mp - > m_sb ) ) {
2005-04-17 02:20:36 +04:00
out - > qs_uquota . qfs_ino = NULLFSINO ;
out - > qs_gquota . qfs_ino = NULLFSINO ;
return ( 0 ) ;
}
out - > qs_flags = ( __uint16_t ) xfs_qm_export_flags ( mp - > m_qflags &
( XFS_ALL_QUOTA_ACCT |
XFS_ALL_QUOTA_ENFD ) ) ;
out - > qs_pad = 0 ;
out - > qs_uquota . qfs_ino = mp - > m_sb . sb_uquotino ;
out - > qs_gquota . qfs_ino = mp - > m_sb . sb_gquotino ;
2010-04-20 11:01:30 +04:00
if ( q ) {
uip = q - > qi_uquotaip ;
gip = q - > qi_gquotaip ;
2005-04-17 02:20:36 +04:00
}
if ( ! uip & & mp - > m_sb . sb_uquotino ! = NULLFSINO ) {
if ( xfs_iget ( mp , NULL , mp - > m_sb . sb_uquotino ,
2010-06-24 05:35:17 +04:00
0 , 0 , & uip ) = = 0 )
2005-04-17 02:20:36 +04:00
tempuqip = B_TRUE ;
}
if ( ! gip & & mp - > m_sb . sb_gquotino ! = NULLFSINO ) {
if ( xfs_iget ( mp , NULL , mp - > m_sb . sb_gquotino ,
2010-06-24 05:35:17 +04:00
0 , 0 , & gip ) = = 0 )
2005-04-17 02:20:36 +04:00
tempgqip = B_TRUE ;
}
if ( uip ) {
out - > qs_uquota . qfs_nblks = uip - > i_d . di_nblocks ;
out - > qs_uquota . qfs_nextents = uip - > i_d . di_nextents ;
if ( tempuqip )
2008-03-27 10:01:08 +03:00
IRELE ( uip ) ;
2005-04-17 02:20:36 +04:00
}
if ( gip ) {
out - > qs_gquota . qfs_nblks = gip - > i_d . di_nblocks ;
out - > qs_gquota . qfs_nextents = gip - > i_d . di_nextents ;
if ( tempgqip )
2008-03-27 10:01:08 +03:00
IRELE ( gip ) ;
2005-04-17 02:20:36 +04:00
}
2010-04-20 11:01:30 +04:00
if ( q ) {
out - > qs_incoredqs = q - > qi_dquots ;
out - > qs_btimelimit = q - > qi_btimelimit ;
out - > qs_itimelimit = q - > qi_itimelimit ;
out - > qs_rtbtimelimit = q - > qi_rtbtimelimit ;
out - > qs_bwarnlimit = q - > qi_bwarnlimit ;
out - > qs_iwarnlimit = q - > qi_iwarnlimit ;
2005-04-17 02:20:36 +04:00
}
2010-04-20 11:01:30 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2010-05-07 01:05:17 +04:00
# define XFS_DQ_MASK \
( FS_DQ_LIMIT_MASK | FS_DQ_TIMER_MASK | FS_DQ_WARNS_MASK )
2005-04-17 02:20:36 +04:00
/*
* Adjust quota limits , and start / stop timers accordingly .
*/
2009-02-09 10:47:34 +03:00
int
2005-04-17 02:20:36 +04:00
xfs_qm_scall_setqlim (
xfs_mount_t * mp ,
xfs_dqid_t id ,
uint type ,
fs_disk_quota_t * newlim )
{
2010-04-20 11:01:30 +04:00
struct xfs_quotainfo * q = mp - > m_quotainfo ;
2005-04-17 02:20:36 +04:00
xfs_disk_dquot_t * ddq ;
xfs_dquot_t * dqp ;
xfs_trans_t * tp ;
int error ;
xfs_qcnt_t hard , soft ;
2010-05-07 01:05:17 +04:00
if ( newlim - > d_fieldmask & ~ XFS_DQ_MASK )
return EINVAL ;
if ( ( newlim - > d_fieldmask & XFS_DQ_MASK ) = = 0 )
return 0 ;
2005-04-17 02:20:36 +04:00
tp = xfs_trans_alloc ( mp , XFS_TRANS_QM_SETQLIM ) ;
if ( ( error = xfs_trans_reserve ( tp , 0 , sizeof ( xfs_disk_dquot_t ) + 128 ,
0 , 0 , XFS_DEFAULT_LOG_COUNT ) ) ) {
xfs_trans_cancel ( tp , 0 ) ;
return ( error ) ;
}
/*
* We don ' t want to race with a quotaoff so take the quotaoff lock .
* ( We don ' t hold an inode lock , so there ' s nothing else to stop
* a quotaoff from happening ) . ( XXXThis doesn ' t currently happen
* because we take the vfslock before calling xfs_qm_sysent ) .
*/
2010-04-20 11:01:30 +04:00
mutex_lock ( & q - > qi_quotaofflock ) ;
2005-04-17 02:20:36 +04:00
/*
* Get the dquot ( locked ) , and join it to the transaction .
* Allocate the dquot if this doesn ' t exist .
*/
if ( ( error = xfs_qm_dqget ( mp , NULL , id , type , XFS_QMOPT_DQALLOC , & dqp ) ) ) {
xfs_trans_cancel ( tp , XFS_TRANS_ABORT ) ;
ASSERT ( error ! = ENOENT ) ;
2010-04-20 11:01:30 +04:00
goto out_unlock ;
2005-04-17 02:20:36 +04:00
}
xfs_trans_dqjoin ( tp , dqp ) ;
ddq = & dqp - > q_core ;
/*
* Make sure that hardlimits are > = soft limits before changing .
*/
hard = ( newlim - > d_fieldmask & FS_DQ_BHARD ) ?
( xfs_qcnt_t ) XFS_BB_TO_FSB ( mp , newlim - > d_blk_hardlimit ) :
2005-11-02 07:01:12 +03:00
be64_to_cpu ( ddq - > d_blk_hardlimit ) ;
2005-04-17 02:20:36 +04:00
soft = ( newlim - > d_fieldmask & FS_DQ_BSOFT ) ?
( xfs_qcnt_t ) XFS_BB_TO_FSB ( mp , newlim - > d_blk_softlimit ) :
2005-11-02 07:01:12 +03:00
be64_to_cpu ( ddq - > d_blk_softlimit ) ;
2005-04-17 02:20:36 +04:00
if ( hard = = 0 | | hard > = soft ) {
2005-11-02 07:01:12 +03:00
ddq - > d_blk_hardlimit = cpu_to_be64 ( hard ) ;
ddq - > d_blk_softlimit = cpu_to_be64 ( soft ) ;
2005-04-17 02:20:36 +04:00
if ( id = = 0 ) {
2010-04-20 11:01:30 +04:00
q - > qi_bhardlimit = hard ;
q - > qi_bsoftlimit = soft ;
2005-04-17 02:20:36 +04:00
}
} else {
2011-03-07 02:07:35 +03:00
xfs_debug ( mp , " blkhard %Ld < blksoft %Ld \n " , hard , soft ) ;
2005-04-17 02:20:36 +04:00
}
hard = ( newlim - > d_fieldmask & FS_DQ_RTBHARD ) ?
( xfs_qcnt_t ) XFS_BB_TO_FSB ( mp , newlim - > d_rtb_hardlimit ) :
2005-11-02 07:01:12 +03:00
be64_to_cpu ( ddq - > d_rtb_hardlimit ) ;
2005-04-17 02:20:36 +04:00
soft = ( newlim - > d_fieldmask & FS_DQ_RTBSOFT ) ?
( xfs_qcnt_t ) XFS_BB_TO_FSB ( mp , newlim - > d_rtb_softlimit ) :
2005-11-02 07:01:12 +03:00
be64_to_cpu ( ddq - > d_rtb_softlimit ) ;
2005-04-17 02:20:36 +04:00
if ( hard = = 0 | | hard > = soft ) {
2005-11-02 07:01:12 +03:00
ddq - > d_rtb_hardlimit = cpu_to_be64 ( hard ) ;
ddq - > d_rtb_softlimit = cpu_to_be64 ( soft ) ;
2005-04-17 02:20:36 +04:00
if ( id = = 0 ) {
2010-04-20 11:01:30 +04:00
q - > qi_rtbhardlimit = hard ;
q - > qi_rtbsoftlimit = soft ;
2005-04-17 02:20:36 +04:00
}
} else {
2011-03-07 02:07:35 +03:00
xfs_debug ( mp , " rtbhard %Ld < rtbsoft %Ld \n " , hard , soft ) ;
2005-04-17 02:20:36 +04:00
}
hard = ( newlim - > d_fieldmask & FS_DQ_IHARD ) ?
( xfs_qcnt_t ) newlim - > d_ino_hardlimit :
2005-11-02 07:01:12 +03:00
be64_to_cpu ( ddq - > d_ino_hardlimit ) ;
2005-04-17 02:20:36 +04:00
soft = ( newlim - > d_fieldmask & FS_DQ_ISOFT ) ?
( xfs_qcnt_t ) newlim - > d_ino_softlimit :
2005-11-02 07:01:12 +03:00
be64_to_cpu ( ddq - > d_ino_softlimit ) ;
2005-04-17 02:20:36 +04:00
if ( hard = = 0 | | hard > = soft ) {
2005-11-02 07:01:12 +03:00
ddq - > d_ino_hardlimit = cpu_to_be64 ( hard ) ;
ddq - > d_ino_softlimit = cpu_to_be64 ( soft ) ;
2005-04-17 02:20:36 +04:00
if ( id = = 0 ) {
2010-04-20 11:01:30 +04:00
q - > qi_ihardlimit = hard ;
q - > qi_isoftlimit = soft ;
2005-04-17 02:20:36 +04:00
}
} else {
2011-03-07 02:07:35 +03:00
xfs_debug ( mp , " ihard %Ld < isoft %Ld \n " , hard , soft ) ;
2005-04-17 02:20:36 +04:00
}
2005-06-21 09:49:06 +04:00
/*
* Update warnings counter ( s ) if requested
*/
if ( newlim - > d_fieldmask & FS_DQ_BWARNS )
2005-11-02 07:01:12 +03:00
ddq - > d_bwarns = cpu_to_be16 ( newlim - > d_bwarns ) ;
2005-06-21 09:49:06 +04:00
if ( newlim - > d_fieldmask & FS_DQ_IWARNS )
2005-11-02 07:01:12 +03:00
ddq - > d_iwarns = cpu_to_be16 ( newlim - > d_iwarns ) ;
2005-06-21 09:49:06 +04:00
if ( newlim - > d_fieldmask & FS_DQ_RTBWARNS )
2005-11-02 07:01:12 +03:00
ddq - > d_rtbwarns = cpu_to_be16 ( newlim - > d_rtbwarns ) ;
2005-06-21 09:49:06 +04:00
2005-04-17 02:20:36 +04:00
if ( id = = 0 ) {
/*
* Timelimits for the super user set the relative time
* the other users can be over quota for this file system .
* If it is zero a default is used . Ditto for the default
2005-06-21 09:49:06 +04:00
* soft and hard limit values ( already done , above ) , and
* for warnings .
2005-04-17 02:20:36 +04:00
*/
if ( newlim - > d_fieldmask & FS_DQ_BTIMER ) {
2010-04-20 11:01:30 +04:00
q - > qi_btimelimit = newlim - > d_btimer ;
2005-11-02 07:01:12 +03:00
ddq - > d_btimer = cpu_to_be32 ( newlim - > d_btimer ) ;
2005-04-17 02:20:36 +04:00
}
if ( newlim - > d_fieldmask & FS_DQ_ITIMER ) {
2010-04-20 11:01:30 +04:00
q - > qi_itimelimit = newlim - > d_itimer ;
2005-11-02 07:01:12 +03:00
ddq - > d_itimer = cpu_to_be32 ( newlim - > d_itimer ) ;
2005-04-17 02:20:36 +04:00
}
if ( newlim - > d_fieldmask & FS_DQ_RTBTIMER ) {
2010-04-20 11:01:30 +04:00
q - > qi_rtbtimelimit = newlim - > d_rtbtimer ;
2005-11-02 07:01:12 +03:00
ddq - > d_rtbtimer = cpu_to_be32 ( newlim - > d_rtbtimer ) ;
2005-04-17 02:20:36 +04:00
}
2005-06-21 09:49:06 +04:00
if ( newlim - > d_fieldmask & FS_DQ_BWARNS )
2010-04-20 11:01:30 +04:00
q - > qi_bwarnlimit = newlim - > d_bwarns ;
2005-06-21 09:49:06 +04:00
if ( newlim - > d_fieldmask & FS_DQ_IWARNS )
2010-04-20 11:01:30 +04:00
q - > qi_iwarnlimit = newlim - > d_iwarns ;
2005-06-21 09:49:06 +04:00
if ( newlim - > d_fieldmask & FS_DQ_RTBWARNS )
2010-04-20 11:01:30 +04:00
q - > qi_rtbwarnlimit = newlim - > d_rtbwarns ;
2005-06-21 09:49:06 +04:00
} else {
2005-04-17 02:20:36 +04:00
/*
* If the user is now over quota , start the timelimit .
* The user will not be ' warned ' .
* Note that we keep the timers ticking , whether enforcement
* is on or off . We don ' t really want to bother with iterating
* over all ondisk dquots and turning the timers on / off .
*/
xfs_qm_adjust_dqtimers ( mp , ddq ) ;
}
dqp - > dq_flags | = XFS_DQ_DIRTY ;
xfs_trans_log_dquot ( tp , dqp ) ;
2008-04-10 06:21:18 +04:00
error = xfs_trans_commit ( tp , 0 ) ;
2005-04-17 02:20:36 +04:00
xfs_qm_dqprint ( dqp ) ;
xfs_qm_dqrele ( dqp ) ;
2010-04-20 11:01:30 +04:00
out_unlock :
mutex_unlock ( & q - > qi_quotaofflock ) ;
2008-04-10 06:21:18 +04:00
return error ;
2005-04-17 02:20:36 +04:00
}
2009-02-09 10:47:34 +03:00
int
2005-04-17 02:20:36 +04:00
xfs_qm_scall_getquota (
xfs_mount_t * mp ,
xfs_dqid_t id ,
uint type ,
fs_disk_quota_t * out )
{
xfs_dquot_t * dqp ;
int error ;
/*
* Try to get the dquot . We don ' t want it allocated on disk , so
* we aren ' t passing the XFS_QMOPT_DOALLOC flag . If it doesn ' t
* exist , we ' ll get ENOENT back .
*/
if ( ( error = xfs_qm_dqget ( mp , NULL , id , type , 0 , & dqp ) ) ) {
return ( error ) ;
}
/*
* If everything ' s NULL , this dquot doesn ' t quite exist as far as
* our utility programs are concerned .
*/
if ( XFS_IS_DQUOT_UNINITIALIZED ( dqp ) ) {
xfs_qm_dqput ( dqp ) ;
return XFS_ERROR ( ENOENT ) ;
}
/* xfs_qm_dqprint(dqp); */
/*
* Convert the disk dquot to the exportable format
*/
xfs_qm_export_dquot ( mp , & dqp - > q_core , out ) ;
xfs_qm_dqput ( dqp ) ;
return ( error ? XFS_ERROR ( EFAULT ) : 0 ) ;
}
STATIC int
xfs_qm_log_quotaoff_end (
xfs_mount_t * mp ,
xfs_qoff_logitem_t * startqoff ,
uint flags )
{
2005-06-21 09:38:48 +04:00
xfs_trans_t * tp ;
2005-04-17 02:20:36 +04:00
int error ;
2005-06-21 09:38:48 +04:00
xfs_qoff_logitem_t * qoffi ;
2005-04-17 02:20:36 +04:00
tp = xfs_trans_alloc ( mp , XFS_TRANS_QM_QUOTAOFF_END ) ;
if ( ( error = xfs_trans_reserve ( tp , 0 , sizeof ( xfs_qoff_logitem_t ) * 2 ,
0 , 0 , XFS_DEFAULT_LOG_COUNT ) ) ) {
xfs_trans_cancel ( tp , 0 ) ;
return ( error ) ;
}
qoffi = xfs_trans_get_qoff_item ( tp , startqoff ,
flags & XFS_ALL_QUOTA_ACCT ) ;
xfs_trans_log_quotaoff_item ( tp , qoffi ) ;
/*
* We have to make sure that the transaction is secure on disk before we
* return and actually stop quota accounting . So , make it synchronous .
* We don ' t care about quotoff ' s performance .
*/
xfs_trans_set_sync ( tp ) ;
2007-05-08 07:48:42 +04:00
error = xfs_trans_commit ( tp , 0 ) ;
2005-04-17 02:20:36 +04:00
return ( error ) ;
}
STATIC int
xfs_qm_log_quotaoff (
xfs_mount_t * mp ,
xfs_qoff_logitem_t * * qoffstartp ,
uint flags )
{
xfs_trans_t * tp ;
int error ;
xfs_qoff_logitem_t * qoffi = NULL ;
uint oldsbqflag = 0 ;
tp = xfs_trans_alloc ( mp , XFS_TRANS_QM_QUOTAOFF ) ;
if ( ( error = xfs_trans_reserve ( tp , 0 ,
sizeof ( xfs_qoff_logitem_t ) * 2 +
mp - > m_sb . sb_sectsize + 128 ,
0 ,
0 ,
XFS_DEFAULT_LOG_COUNT ) ) ) {
goto error0 ;
}
qoffi = xfs_trans_get_qoff_item ( tp , NULL , flags & XFS_ALL_QUOTA_ACCT ) ;
xfs_trans_log_quotaoff_item ( tp , qoffi ) ;
2007-10-11 11:42:32 +04:00
spin_lock ( & mp - > m_sb_lock ) ;
2005-04-17 02:20:36 +04:00
oldsbqflag = mp - > m_sb . sb_qflags ;
mp - > m_sb . sb_qflags = ( mp - > m_qflags & ~ ( flags ) ) & XFS_MOUNT_QUOTA_ALL ;
2007-10-11 11:42:32 +04:00
spin_unlock ( & mp - > m_sb_lock ) ;
2005-04-17 02:20:36 +04:00
xfs_mod_sb ( tp , XFS_SB_QFLAGS ) ;
/*
* We have to make sure that the transaction is secure on disk before we
* return and actually stop quota accounting . So , make it synchronous .
* We don ' t care about quotoff ' s performance .
*/
xfs_trans_set_sync ( tp ) ;
2007-05-08 07:48:42 +04:00
error = xfs_trans_commit ( tp , 0 ) ;
2005-04-17 02:20:36 +04:00
error0 :
if ( error ) {
xfs_trans_cancel ( tp , 0 ) ;
/*
* No one else is modifying sb_qflags , so this is OK .
* We still hold the quotaofflock .
*/
2007-10-11 11:42:32 +04:00
spin_lock ( & mp - > m_sb_lock ) ;
2005-04-17 02:20:36 +04:00
mp - > m_sb . sb_qflags = oldsbqflag ;
2007-10-11 11:42:32 +04:00
spin_unlock ( & mp - > m_sb_lock ) ;
2005-04-17 02:20:36 +04:00
}
* qoffstartp = qoffi ;
return ( error ) ;
}
/*
* Translate an internal style on - disk - dquot to the exportable format .
* The main differences are that the counters / limits are all in Basic
* Blocks ( BBs ) instead of the internal FSBs , and all on - disk data has
* to be converted to the native endianness .
*/
STATIC void
xfs_qm_export_dquot (
xfs_mount_t * mp ,
xfs_disk_dquot_t * src ,
struct fs_disk_quota * dst )
{
memset ( dst , 0 , sizeof ( * dst ) ) ;
dst - > d_version = FS_DQUOT_VERSION ; /* different from src->d_version */
2005-11-02 07:01:12 +03:00
dst - > d_flags = xfs_qm_export_qtype_flags ( src - > d_flags ) ;
dst - > d_id = be32_to_cpu ( src - > d_id ) ;
dst - > d_blk_hardlimit =
XFS_FSB_TO_BB ( mp , be64_to_cpu ( src - > d_blk_hardlimit ) ) ;
dst - > d_blk_softlimit =
XFS_FSB_TO_BB ( mp , be64_to_cpu ( src - > d_blk_softlimit ) ) ;
dst - > d_ino_hardlimit = be64_to_cpu ( src - > d_ino_hardlimit ) ;
dst - > d_ino_softlimit = be64_to_cpu ( src - > d_ino_softlimit ) ;
dst - > d_bcount = XFS_FSB_TO_BB ( mp , be64_to_cpu ( src - > d_bcount ) ) ;
dst - > d_icount = be64_to_cpu ( src - > d_icount ) ;
dst - > d_btimer = be32_to_cpu ( src - > d_btimer ) ;
dst - > d_itimer = be32_to_cpu ( src - > d_itimer ) ;
dst - > d_iwarns = be16_to_cpu ( src - > d_iwarns ) ;
dst - > d_bwarns = be16_to_cpu ( src - > d_bwarns ) ;
dst - > d_rtb_hardlimit =
XFS_FSB_TO_BB ( mp , be64_to_cpu ( src - > d_rtb_hardlimit ) ) ;
dst - > d_rtb_softlimit =
XFS_FSB_TO_BB ( mp , be64_to_cpu ( src - > d_rtb_softlimit ) ) ;
dst - > d_rtbcount = XFS_FSB_TO_BB ( mp , be64_to_cpu ( src - > d_rtbcount ) ) ;
dst - > d_rtbtimer = be32_to_cpu ( src - > d_rtbtimer ) ;
dst - > d_rtbwarns = be16_to_cpu ( src - > d_rtbwarns ) ;
2005-04-17 02:20:36 +04:00
/*
* Internally , we don ' t reset all the timers when quota enforcement
2006-03-29 02:55:14 +04:00
* gets turned off . No need to confuse the user level code ,
2005-04-17 02:20:36 +04:00
* so return zeroes in that case .
*/
2007-05-08 07:49:33 +04:00
if ( ( ! XFS_IS_UQUOTA_ENFORCED ( mp ) & & src - > d_flags = = XFS_DQ_USER ) | |
( ! XFS_IS_OQUOTA_ENFORCED ( mp ) & &
( src - > d_flags & ( XFS_DQ_PROJ | XFS_DQ_GROUP ) ) ) ) {
2005-04-17 02:20:36 +04:00
dst - > d_btimer = 0 ;
dst - > d_itimer = 0 ;
dst - > d_rtbtimer = 0 ;
}
# ifdef DEBUG
2010-06-04 12:56:01 +04:00
if ( ( ( XFS_IS_UQUOTA_ENFORCED ( mp ) & & dst - > d_flags = = FS_USER_QUOTA ) | |
2007-05-08 07:49:33 +04:00
( XFS_IS_OQUOTA_ENFORCED ( mp ) & &
2010-06-04 12:56:01 +04:00
( dst - > d_flags & ( FS_PROJ_QUOTA | FS_GROUP_QUOTA ) ) ) ) & &
2007-05-08 07:49:33 +04:00
dst - > d_id ! = 0 ) {
2005-04-17 02:20:36 +04:00
if ( ( ( int ) dst - > d_bcount > = ( int ) dst - > d_blk_softlimit ) & &
( dst - > d_blk_softlimit > 0 ) ) {
ASSERT ( dst - > d_btimer ! = 0 ) ;
}
if ( ( ( int ) dst - > d_icount > = ( int ) dst - > d_ino_softlimit ) & &
( dst - > d_ino_softlimit > 0 ) ) {
ASSERT ( dst - > d_itimer ! = 0 ) ;
}
}
# endif
}
STATIC uint
xfs_qm_export_qtype_flags (
uint flags )
{
/*
2005-06-21 09:38:48 +04:00
* Can ' t be more than one , or none .
2005-04-17 02:20:36 +04:00
*/
2010-06-04 12:56:01 +04:00
ASSERT ( ( flags & ( FS_PROJ_QUOTA | FS_USER_QUOTA ) ) ! =
( FS_PROJ_QUOTA | FS_USER_QUOTA ) ) ;
ASSERT ( ( flags & ( FS_PROJ_QUOTA | FS_GROUP_QUOTA ) ) ! =
( FS_PROJ_QUOTA | FS_GROUP_QUOTA ) ) ;
ASSERT ( ( flags & ( FS_USER_QUOTA | FS_GROUP_QUOTA ) ) ! =
( FS_USER_QUOTA | FS_GROUP_QUOTA ) ) ;
ASSERT ( ( flags & ( FS_PROJ_QUOTA | FS_USER_QUOTA | FS_GROUP_QUOTA ) ) ! = 0 ) ;
2005-04-17 02:20:36 +04:00
return ( flags & XFS_DQ_USER ) ?
2010-06-04 12:56:01 +04:00
FS_USER_QUOTA : ( flags & XFS_DQ_PROJ ) ?
FS_PROJ_QUOTA : FS_GROUP_QUOTA ;
2005-04-17 02:20:36 +04:00
}
STATIC uint
xfs_qm_export_flags (
uint flags )
{
uint uflags ;
uflags = 0 ;
if ( flags & XFS_UQUOTA_ACCT )
2010-06-04 12:56:01 +04:00
uflags | = FS_QUOTA_UDQ_ACCT ;
2005-06-21 09:38:48 +04:00
if ( flags & XFS_PQUOTA_ACCT )
2010-06-04 12:56:01 +04:00
uflags | = FS_QUOTA_PDQ_ACCT ;
2005-04-17 02:20:36 +04:00
if ( flags & XFS_GQUOTA_ACCT )
2010-06-04 12:56:01 +04:00
uflags | = FS_QUOTA_GDQ_ACCT ;
2005-04-17 02:20:36 +04:00
if ( flags & XFS_UQUOTA_ENFD )
2010-06-04 12:56:01 +04:00
uflags | = FS_QUOTA_UDQ_ENFD ;
2005-06-21 09:38:48 +04:00
if ( flags & ( XFS_OQUOTA_ENFD ) ) {
uflags | = ( flags & XFS_GQUOTA_ACCT ) ?
2010-06-04 12:56:01 +04:00
FS_QUOTA_GDQ_ENFD : FS_QUOTA_PDQ_ENFD ;
2005-06-21 09:38:48 +04:00
}
2005-04-17 02:20:36 +04:00
return ( uflags ) ;
}
2009-06-08 17:35:27 +04:00
STATIC int
xfs_dqrele_inode (
struct xfs_inode * ip ,
struct xfs_perag * pag ,
int flags )
2005-04-17 02:20:36 +04:00
{
2009-06-08 17:35:27 +04:00
/* skip quota inodes */
2010-04-20 11:01:30 +04:00
if ( ip = = ip - > i_mount - > m_quotainfo - > qi_uquotaip | |
ip = = ip - > i_mount - > m_quotainfo - > qi_gquotaip ) {
2009-06-08 17:35:27 +04:00
ASSERT ( ip - > i_udquot = = NULL ) ;
ASSERT ( ip - > i_gdquot = = NULL ) ;
return 0 ;
}
2008-11-10 09:11:18 +03:00
2009-06-08 17:35:27 +04:00
xfs_ilock ( ip , XFS_ILOCK_EXCL ) ;
if ( ( flags & XFS_UQUOTA_ACCT ) & & ip - > i_udquot ) {
xfs_qm_dqrele ( ip - > i_udquot ) ;
ip - > i_udquot = NULL ;
}
if ( flags & ( XFS_PQUOTA_ACCT | XFS_GQUOTA_ACCT ) & & ip - > i_gdquot ) {
xfs_qm_dqrele ( ip - > i_gdquot ) ;
ip - > i_gdquot = NULL ;
}
2010-06-24 05:52:50 +04:00
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
2009-06-08 17:35:27 +04:00
return 0 ;
2008-10-30 09:08:03 +03:00
}
2009-06-08 17:35:27 +04:00
2008-10-30 09:08:03 +03:00
/*
* Go thru all the inodes in the file system , releasing their dquots .
2009-06-08 17:35:27 +04:00
*
2008-10-30 09:08:03 +03:00
* Note that the mount structure gets modified to indicate that quotas are off
2009-06-08 17:35:27 +04:00
* AFTER this , in the case of quotaoff .
2008-10-30 09:08:03 +03:00
*/
void
xfs_qm_dqrele_all_inodes (
struct xfs_mount * mp ,
uint flags )
{
ASSERT ( mp - > m_quotainfo ) ;
2010-09-24 12:40:15 +04:00
xfs_inode_ag_iterator ( mp , xfs_dqrele_inode , flags ) ;
2005-04-17 02:20:36 +04:00
}
/*------------------------------------------------------------------------*/
# ifdef DEBUG
/*
* This contains all the test functions for XFS disk quotas .
* Currently it does a quota accounting check . ie . it walks through
* all inodes in the file system , calculating the dquot accounting fields ,
* and prints out any inconsistencies .
*/
xfs_dqhash_t * qmtest_udqtab ;
xfs_dqhash_t * qmtest_gdqtab ;
int qmtest_hashmask ;
int qmtest_nfails ;
2009-03-29 11:51:00 +04:00
struct mutex qcheck_lock ;
2005-04-17 02:20:36 +04:00
# define DQTEST_HASHVAL(mp, id) (((__psunsigned_t)(mp) + \
( __psunsigned_t ) ( id ) ) & \
( qmtest_hashmask - 1 ) )
# define DQTEST_HASH(mp, id, type) ((type & XFS_DQ_USER) ? \
( qmtest_udqtab + \
DQTEST_HASHVAL ( mp , id ) ) : \
( qmtest_gdqtab + \
DQTEST_HASHVAL ( mp , id ) ) )
# define DQTEST_LIST_PRINT(l, NXT, title) \
{ \
xfs_dqtest_t * dqp ; int i = 0 ; \
2011-03-07 02:08:35 +03:00
xfs_debug ( NULL , " %s (#%d) " , title , ( int ) ( l ) - > qh_nelems ) ; \
2005-04-17 02:20:36 +04:00
for ( dqp = ( xfs_dqtest_t * ) ( l ) - > qh_next ; dqp ! = NULL ; \
dqp = ( xfs_dqtest_t * ) dqp - > NXT ) { \
2011-03-07 02:08:35 +03:00
xfs_debug ( dqp - > q_mount , \
" %d. \" %d (%s) \" bcnt = %d, icnt = %d " , \
2005-04-17 02:20:36 +04:00
+ + i , dqp - > d_id , DQFLAGTO_TYPESTR ( dqp ) , \
dqp - > d_bcount , dqp - > d_icount ) ; } \
}
typedef struct dqtest {
2010-04-13 09:06:53 +04:00
uint dq_flags ; /* various flags (XFS_DQ_*) */
2010-04-13 09:06:51 +04:00
struct list_head q_hashlist ;
2005-04-17 02:20:36 +04:00
xfs_dqhash_t * q_hash ; /* the hashchain header */
xfs_mount_t * q_mount ; /* filesystem this relates to */
xfs_dqid_t d_id ; /* user id or group id */
xfs_qcnt_t d_bcount ; /* # disk blocks owned by the user */
xfs_qcnt_t d_icount ; /* # inodes owned by the user */
} xfs_dqtest_t ;
STATIC void
xfs_qm_hashinsert ( xfs_dqhash_t * h , xfs_dqtest_t * dqp )
{
2010-04-13 09:06:51 +04:00
list_add ( & dqp - > q_hashlist , & h - > qh_list ) ;
h - > qh_version + + ;
h - > qh_nelems + + ;
2005-04-17 02:20:36 +04:00
}
STATIC void
xfs_qm_dqtest_print (
2011-03-07 02:08:35 +03:00
struct xfs_mount * mp ,
struct dqtest * d )
2005-04-17 02:20:36 +04:00
{
2011-03-07 02:08:35 +03:00
xfs_debug ( mp , " -----------DQTEST DQUOT---------------- " ) ;
xfs_debug ( mp , " ---- dquot ID = %d " , d - > d_id ) ;
xfs_debug ( mp , " ---- fs = 0x%p " , d - > q_mount ) ;
xfs_debug ( mp , " ---- bcount = %Lu (0x%x) " ,
2005-04-17 02:20:36 +04:00
d - > d_bcount , ( int ) d - > d_bcount ) ;
2011-03-07 02:08:35 +03:00
xfs_debug ( mp , " ---- icount = %Lu (0x%x) " ,
2005-04-17 02:20:36 +04:00
d - > d_icount , ( int ) d - > d_icount ) ;
2011-03-07 02:08:35 +03:00
xfs_debug ( mp , " --------------------------- " ) ;
2005-04-17 02:20:36 +04:00
}
STATIC void
xfs_qm_dqtest_failed (
xfs_dqtest_t * d ,
xfs_dquot_t * dqp ,
char * reason ,
xfs_qcnt_t a ,
xfs_qcnt_t b ,
int error )
{
qmtest_nfails + + ;
if ( error )
2011-03-07 02:08:35 +03:00
xfs_debug ( dqp - > q_mount ,
" quotacheck failed id=%d, err=%d \n reason: %s " ,
d - > d_id , error , reason ) ;
2005-04-17 02:20:36 +04:00
else
2011-03-07 02:08:35 +03:00
xfs_debug ( dqp - > q_mount ,
" quotacheck failed id=%d (%s) [%d != %d] " ,
d - > d_id , reason , ( int ) a , ( int ) b ) ;
xfs_qm_dqtest_print ( dqp - > q_mount , d ) ;
2005-04-17 02:20:36 +04:00
if ( dqp )
xfs_qm_dqprint ( dqp ) ;
}
STATIC int
xfs_dqtest_cmp2 (
xfs_dqtest_t * d ,
xfs_dquot_t * dqp )
{
int err = 0 ;
2005-11-02 07:01:12 +03:00
if ( be64_to_cpu ( dqp - > q_core . d_icount ) ! = d - > d_icount ) {
2005-04-17 02:20:36 +04:00
xfs_qm_dqtest_failed ( d , dqp , " icount mismatch " ,
2005-11-02 07:01:12 +03:00
be64_to_cpu ( dqp - > q_core . d_icount ) ,
2005-04-17 02:20:36 +04:00
d - > d_icount , 0 ) ;
err + + ;
}
2005-11-02 07:01:12 +03:00
if ( be64_to_cpu ( dqp - > q_core . d_bcount ) ! = d - > d_bcount ) {
2005-04-17 02:20:36 +04:00
xfs_qm_dqtest_failed ( d , dqp , " bcount mismatch " ,
2005-11-02 07:01:12 +03:00
be64_to_cpu ( dqp - > q_core . d_bcount ) ,
2005-04-17 02:20:36 +04:00
d - > d_bcount , 0 ) ;
err + + ;
}
2005-11-02 07:01:12 +03:00
if ( dqp - > q_core . d_blk_softlimit & &
be64_to_cpu ( dqp - > q_core . d_bcount ) > =
be64_to_cpu ( dqp - > q_core . d_blk_softlimit ) ) {
2005-04-17 02:20:36 +04:00
if ( ! dqp - > q_core . d_btimer & & dqp - > q_core . d_id ) {
2011-03-07 02:08:35 +03:00
xfs_debug ( dqp - > q_mount ,
" %d [%s] BLK TIMER NOT STARTED " ,
d - > d_id , DQFLAGTO_TYPESTR ( d ) ) ;
2005-04-17 02:20:36 +04:00
err + + ;
}
}
2005-11-02 07:01:12 +03:00
if ( dqp - > q_core . d_ino_softlimit & &
be64_to_cpu ( dqp - > q_core . d_icount ) > =
be64_to_cpu ( dqp - > q_core . d_ino_softlimit ) ) {
2005-04-17 02:20:36 +04:00
if ( ! dqp - > q_core . d_itimer & & dqp - > q_core . d_id ) {
2011-03-07 02:08:35 +03:00
xfs_debug ( dqp - > q_mount ,
" %d [%s] INO TIMER NOT STARTED " ,
d - > d_id , DQFLAGTO_TYPESTR ( d ) ) ;
2005-04-17 02:20:36 +04:00
err + + ;
}
}
# ifdef QUOTADEBUG
if ( ! err ) {
2011-03-07 02:08:35 +03:00
xfs_debug ( dqp - > q_mount , " %d [%s] qchecked " ,
d - > d_id , DQFLAGTO_TYPESTR ( d ) ) ;
2005-04-17 02:20:36 +04:00
}
# endif
return ( err ) ;
}
STATIC void
xfs_dqtest_cmp (
xfs_dqtest_t * d )
{
xfs_dquot_t * dqp ;
int error ;
/* xfs_qm_dqtest_print(d); */
if ( ( error = xfs_qm_dqget ( d - > q_mount , NULL , d - > d_id , d - > dq_flags , 0 ,
& dqp ) ) ) {
xfs_qm_dqtest_failed ( d , NULL , " dqget failed " , 0 , 0 , error ) ;
return ;
}
xfs_dqtest_cmp2 ( d , dqp ) ;
xfs_qm_dqput ( dqp ) ;
}
STATIC int
xfs_qm_internalqcheck_dqget (
xfs_mount_t * mp ,
xfs_dqid_t id ,
uint type ,
xfs_dqtest_t * * O_dq )
{
xfs_dqtest_t * d ;
xfs_dqhash_t * h ;
h = DQTEST_HASH ( mp , id , type ) ;
2010-04-13 09:06:51 +04:00
list_for_each_entry ( d , & h - > qh_list , q_hashlist ) {
2005-04-17 02:20:36 +04:00
if ( d - > d_id = = id & & mp = = d - > q_mount ) {
* O_dq = d ;
return ( 0 ) ;
}
}
d = kmem_zalloc ( sizeof ( xfs_dqtest_t ) , KM_SLEEP ) ;
d - > dq_flags = type ;
d - > d_id = id ;
d - > q_mount = mp ;
d - > q_hash = h ;
2010-04-13 09:06:51 +04:00
INIT_LIST_HEAD ( & d - > q_hashlist ) ;
2005-04-17 02:20:36 +04:00
xfs_qm_hashinsert ( h , d ) ;
* O_dq = d ;
return ( 0 ) ;
}
STATIC void
xfs_qm_internalqcheck_get_dquots (
xfs_mount_t * mp ,
xfs_dqid_t uid ,
2005-06-21 09:38:48 +04:00
xfs_dqid_t projid ,
2005-04-17 02:20:36 +04:00
xfs_dqid_t gid ,
xfs_dqtest_t * * ud ,
xfs_dqtest_t * * gd )
{
if ( XFS_IS_UQUOTA_ON ( mp ) )
xfs_qm_internalqcheck_dqget ( mp , uid , XFS_DQ_USER , ud ) ;
if ( XFS_IS_GQUOTA_ON ( mp ) )
xfs_qm_internalqcheck_dqget ( mp , gid , XFS_DQ_GROUP , gd ) ;
2005-06-21 09:38:48 +04:00
else if ( XFS_IS_PQUOTA_ON ( mp ) )
xfs_qm_internalqcheck_dqget ( mp , projid , XFS_DQ_PROJ , gd ) ;
2005-04-17 02:20:36 +04:00
}
STATIC void
xfs_qm_internalqcheck_dqadjust (
xfs_inode_t * ip ,
xfs_dqtest_t * d )
{
d - > d_icount + + ;
d - > d_bcount + = ( xfs_qcnt_t ) ip - > i_d . di_nblocks ;
}
STATIC int
xfs_qm_internalqcheck_adjust (
xfs_mount_t * mp , /* mount point for filesystem */
xfs_ino_t ino , /* inode number to get data for */
void __user * buffer , /* not used */
int ubsize , /* not used */
int * ubused , /* not used */
int * res ) /* bulkstat result code */
{
xfs_inode_t * ip ;
xfs_dqtest_t * ud , * gd ;
uint lock_flags ;
boolean_t ipreleased ;
int error ;
ASSERT ( XFS_IS_QUOTA_RUNNING ( mp ) ) ;
if ( ino = = mp - > m_sb . sb_uquotino | | ino = = mp - > m_sb . sb_gquotino ) {
* res = BULKSTAT_RV_NOTHING ;
2011-03-07 02:07:35 +03:00
xfs_debug ( mp , " %s: ino=%llu, uqino=%llu, gqino=%llu \n " ,
__func__ , ( unsigned long long ) ino ,
2005-04-17 02:20:36 +04:00
( unsigned long long ) mp - > m_sb . sb_uquotino ,
( unsigned long long ) mp - > m_sb . sb_gquotino ) ;
return XFS_ERROR ( EINVAL ) ;
}
ipreleased = B_FALSE ;
again :
lock_flags = XFS_ILOCK_SHARED ;
2010-06-24 05:35:17 +04:00
if ( ( error = xfs_iget ( mp , NULL , ino , 0 , lock_flags , & ip ) ) ) {
2005-04-17 02:20:36 +04:00
* res = BULKSTAT_RV_NOTHING ;
return ( error ) ;
}
/*
* This inode can have blocks after eof which can get released
* when we send it to inactive . Since we don ' t check the dquot
* until the after all our calculations are done , we must get rid
* of those now .
*/
if ( ! ipreleased ) {
2010-06-24 05:52:50 +04:00
xfs_iunlock ( ip , lock_flags ) ;
IRELE ( ip ) ;
2005-04-17 02:20:36 +04:00
ipreleased = B_TRUE ;
goto again ;
}
xfs_qm_internalqcheck_get_dquots ( mp ,
( xfs_dqid_t ) ip - > i_d . di_uid ,
2010-09-26 10:10:18 +04:00
( xfs_dqid_t ) xfs_get_projid ( ip ) ,
2005-04-17 02:20:36 +04:00
( xfs_dqid_t ) ip - > i_d . di_gid ,
& ud , & gd ) ;
if ( XFS_IS_UQUOTA_ON ( mp ) ) {
ASSERT ( ud ) ;
xfs_qm_internalqcheck_dqadjust ( ip , ud ) ;
}
2005-06-21 09:38:48 +04:00
if ( XFS_IS_OQUOTA_ON ( mp ) ) {
2005-04-17 02:20:36 +04:00
ASSERT ( gd ) ;
xfs_qm_internalqcheck_dqadjust ( ip , gd ) ;
}
2010-06-24 05:52:50 +04:00
xfs_iunlock ( ip , lock_flags ) ;
IRELE ( ip ) ;
2005-04-17 02:20:36 +04:00
* res = BULKSTAT_RV_DIDONE ;
return ( 0 ) ;
}
/* PRIVATE, debugging */
int
xfs_qm_internalqcheck (
xfs_mount_t * mp )
{
xfs_ino_t lastino ;
int done , count ;
int i ;
int error ;
lastino = 0 ;
qmtest_hashmask = 32 ;
count = 5 ;
done = 0 ;
qmtest_nfails = 0 ;
if ( ! XFS_IS_QUOTA_ON ( mp ) )
return XFS_ERROR ( ESRCH ) ;
2010-01-19 12:56:46 +03:00
xfs_log_force ( mp , XFS_LOG_SYNC ) ;
2005-04-17 02:20:36 +04:00
XFS_bflush ( mp - > m_ddev_targp ) ;
2010-01-19 12:56:46 +03:00
xfs_log_force ( mp , XFS_LOG_SYNC ) ;
2005-04-17 02:20:36 +04:00
XFS_bflush ( mp - > m_ddev_targp ) ;
2006-01-10 02:59:21 +03:00
mutex_lock ( & qcheck_lock ) ;
2005-04-17 02:20:36 +04:00
/* There should be absolutely no quota activity while this
is going on . */
qmtest_udqtab = kmem_zalloc ( qmtest_hashmask *
sizeof ( xfs_dqhash_t ) , KM_SLEEP ) ;
qmtest_gdqtab = kmem_zalloc ( qmtest_hashmask *
sizeof ( xfs_dqhash_t ) , KM_SLEEP ) ;
do {
/*
* Iterate thru all the inodes in the file system ,
* adjusting the corresponding dquot counters
*/
2010-06-23 12:11:11 +04:00
error = xfs_bulkstat ( mp , & lastino , & count ,
xfs_qm_internalqcheck_adjust ,
0 , NULL , & done ) ;
if ( error ) {
2011-03-07 02:08:35 +03:00
xfs_debug ( mp , " Bulkstat returned error 0x%x " , error ) ;
2005-04-17 02:20:36 +04:00
break ;
}
2010-06-23 12:11:11 +04:00
} while ( ! done ) ;
2011-03-07 02:08:35 +03:00
xfs_debug ( mp , " Checking results against system dquots " ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < qmtest_hashmask ; i + + ) {
2010-04-13 09:06:51 +04:00
xfs_dqtest_t * d , * n ;
xfs_dqhash_t * h ;
h = & qmtest_udqtab [ i ] ;
list_for_each_entry_safe ( d , n , & h - > qh_list , q_hashlist ) {
2005-04-17 02:20:36 +04:00
xfs_dqtest_cmp ( d ) ;
2008-05-19 10:31:57 +04:00
kmem_free ( d ) ;
2005-04-17 02:20:36 +04:00
}
2010-04-13 09:06:51 +04:00
h = & qmtest_gdqtab [ i ] ;
list_for_each_entry_safe ( d , n , & h - > qh_list , q_hashlist ) {
2005-04-17 02:20:36 +04:00
xfs_dqtest_cmp ( d ) ;
2008-05-19 10:31:57 +04:00
kmem_free ( d ) ;
2005-04-17 02:20:36 +04:00
}
}
if ( qmtest_nfails ) {
2011-03-07 02:08:35 +03:00
xfs_debug ( mp , " ******** quotacheck failed ******** " ) ;
xfs_debug ( mp , " failures = %d " , qmtest_nfails ) ;
2005-04-17 02:20:36 +04:00
} else {
2011-03-07 02:08:35 +03:00
xfs_debug ( mp , " ******** quotacheck successful! ******** " ) ;
2005-04-17 02:20:36 +04:00
}
2008-05-19 10:31:57 +04:00
kmem_free ( qmtest_udqtab ) ;
kmem_free ( qmtest_gdqtab ) ;
2005-04-17 02:20:36 +04:00
mutex_unlock ( & qcheck_lock ) ;
return ( qmtest_nfails ) ;
}
# endif /* DEBUG */