2005-04-17 02:20:36 +04:00
/*
2005-11-02 06:58:39 +03:00
* Copyright ( c ) 2000 - 2002 , 2005 Silicon Graphics , Inc .
* All Rights Reserved .
2005-04-17 02:20:36 +04:00
*
2005-11-02 06:58:39 +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:58:39 +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:58:39 +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"
2005-11-02 06:38:42 +03:00
# include "xfs_fs.h"
2005-04-17 02:20:36 +04:00
# include "xfs_types.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_dir2.h"
# include "xfs_dmapi.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"
# include "xfs_ialloc.h"
# include "xfs_itable.h"
# include "xfs_error.h"
2005-11-02 06:38:42 +03:00
# include "xfs_btree.h"
2005-04-17 02:20:36 +04:00
2006-09-28 05:06:15 +04:00
int
xfs_internal_inum (
xfs_mount_t * mp ,
xfs_ino_t ino )
{
return ( ino = = mp - > m_sb . sb_rbmino | | ino = = mp - > m_sb . sb_rsumino | |
( XFS_SB_VERSION_HASQUOTA ( & mp - > m_sb ) & &
( ino = = mp - > m_sb . sb_uquotino | | ino = = mp - > m_sb . sb_gquotino ) ) ) ;
}
2005-04-17 02:20:36 +04:00
STATIC int
xfs_bulkstat_one_iget (
xfs_mount_t * mp , /* mount point for filesystem */
xfs_ino_t ino , /* inode number to get data for */
xfs_daddr_t bno , /* starting bno of inode cluster */
xfs_bstat_t * buf , /* return buffer */
int * stat ) /* BULKSTAT_RV_... */
{
xfs_dinode_core_t * dic ; /* dinode core info pointer */
xfs_inode_t * ip ; /* incore inode pointer */
2006-06-09 11:00:52 +04:00
bhv_vnode_t * vp ;
2005-04-17 02:20:36 +04:00
int error ;
2006-09-28 05:02:23 +04:00
error = xfs_iget ( mp , NULL , ino ,
XFS_IGET_BULKSTAT , XFS_ILOCK_SHARED , & ip , bno ) ;
2005-04-17 02:20:36 +04:00
if ( error ) {
* stat = BULKSTAT_RV_NOTHING ;
return error ;
}
ASSERT ( ip ! = NULL ) ;
ASSERT ( ip - > i_blkno ! = ( xfs_daddr_t ) 0 ) ;
if ( ip - > i_d . di_mode = = 0 ) {
* stat = BULKSTAT_RV_NOTHING ;
error = XFS_ERROR ( ENOENT ) ;
goto out_iput ;
}
2006-01-11 07:35:17 +03:00
vp = XFS_ITOV ( ip ) ;
2005-04-17 02:20:36 +04:00
dic = & ip - > i_d ;
/* xfs_iget returns the following without needing
* further change .
*/
buf - > bs_nlink = dic - > di_nlink ;
buf - > bs_projid = dic - > di_projid ;
buf - > bs_ino = ino ;
buf - > bs_mode = dic - > di_mode ;
buf - > bs_uid = dic - > di_uid ;
buf - > bs_gid = dic - > di_gid ;
buf - > bs_size = dic - > di_size ;
2006-01-11 13:03:04 +03:00
vn_atime_to_bstime ( vp , & buf - > bs_atime ) ;
2005-04-17 02:20:36 +04:00
buf - > bs_mtime . tv_sec = dic - > di_mtime . t_sec ;
buf - > bs_mtime . tv_nsec = dic - > di_mtime . t_nsec ;
buf - > bs_ctime . tv_sec = dic - > di_ctime . t_sec ;
buf - > bs_ctime . tv_nsec = dic - > di_ctime . t_nsec ;
buf - > bs_xflags = xfs_ip2xflags ( ip ) ;
buf - > bs_extsize = dic - > di_extsize < < mp - > m_sb . sb_blocklog ;
buf - > bs_extents = dic - > di_nextents ;
buf - > bs_gen = dic - > di_gen ;
memset ( buf - > bs_pad , 0 , sizeof ( buf - > bs_pad ) ) ;
buf - > bs_dmevmask = dic - > di_dmevmask ;
buf - > bs_dmstate = dic - > di_dmstate ;
buf - > bs_aextents = dic - > di_anextents ;
switch ( dic - > di_format ) {
case XFS_DINODE_FMT_DEV :
buf - > bs_rdev = ip - > i_df . if_u2 . if_rdev ;
buf - > bs_blksize = BLKDEV_IOSIZE ;
buf - > bs_blocks = 0 ;
break ;
case XFS_DINODE_FMT_LOCAL :
case XFS_DINODE_FMT_UUID :
buf - > bs_rdev = 0 ;
buf - > bs_blksize = mp - > m_sb . sb_blocksize ;
buf - > bs_blocks = 0 ;
break ;
case XFS_DINODE_FMT_EXTENTS :
case XFS_DINODE_FMT_BTREE :
buf - > bs_rdev = 0 ;
buf - > bs_blksize = mp - > m_sb . sb_blocksize ;
buf - > bs_blocks = dic - > di_nblocks + ip - > i_delayed_blks ;
break ;
}
out_iput :
xfs_iput ( ip , XFS_ILOCK_SHARED ) ;
return error ;
}
STATIC int
xfs_bulkstat_one_dinode (
xfs_mount_t * mp , /* mount point for filesystem */
xfs_ino_t ino , /* inode number to get data for */
xfs_dinode_t * dip , /* dinode inode pointer */
xfs_bstat_t * buf ) /* return buffer */
{
xfs_dinode_core_t * dic ; /* dinode core info pointer */
dic = & dip - > di_core ;
/*
* The inode format changed when we moved the link count and
* made it 32 bits long . If this is an old format inode ,
* convert it in memory to look like a new one . If it gets
* flushed to disk we will convert back before flushing or
* logging it . We zero out the new projid field and the old link
* count field . We ' ll handle clearing the pad field ( the remains
* of the old uuid field ) when we actually convert the inode to
* the new format . We don ' t change the version number so that we
* can distinguish this from a real new format inode .
*/
if ( INT_GET ( dic - > di_version , ARCH_CONVERT ) = = XFS_DINODE_VERSION_1 ) {
buf - > bs_nlink = INT_GET ( dic - > di_onlink , ARCH_CONVERT ) ;
buf - > bs_projid = 0 ;
} else {
buf - > bs_nlink = INT_GET ( dic - > di_nlink , ARCH_CONVERT ) ;
buf - > bs_projid = INT_GET ( dic - > di_projid , ARCH_CONVERT ) ;
}
buf - > bs_ino = ino ;
buf - > bs_mode = INT_GET ( dic - > di_mode , ARCH_CONVERT ) ;
buf - > bs_uid = INT_GET ( dic - > di_uid , ARCH_CONVERT ) ;
buf - > bs_gid = INT_GET ( dic - > di_gid , ARCH_CONVERT ) ;
buf - > bs_size = INT_GET ( dic - > di_size , ARCH_CONVERT ) ;
buf - > bs_atime . tv_sec = INT_GET ( dic - > di_atime . t_sec , ARCH_CONVERT ) ;
buf - > bs_atime . tv_nsec = INT_GET ( dic - > di_atime . t_nsec , ARCH_CONVERT ) ;
buf - > bs_mtime . tv_sec = INT_GET ( dic - > di_mtime . t_sec , ARCH_CONVERT ) ;
buf - > bs_mtime . tv_nsec = INT_GET ( dic - > di_mtime . t_nsec , ARCH_CONVERT ) ;
buf - > bs_ctime . tv_sec = INT_GET ( dic - > di_ctime . t_sec , ARCH_CONVERT ) ;
buf - > bs_ctime . tv_nsec = INT_GET ( dic - > di_ctime . t_nsec , ARCH_CONVERT ) ;
buf - > bs_xflags = xfs_dic2xflags ( dic ) ;
buf - > bs_extsize = INT_GET ( dic - > di_extsize , ARCH_CONVERT ) < < mp - > m_sb . sb_blocklog ;
buf - > bs_extents = INT_GET ( dic - > di_nextents , ARCH_CONVERT ) ;
buf - > bs_gen = INT_GET ( dic - > di_gen , ARCH_CONVERT ) ;
memset ( buf - > bs_pad , 0 , sizeof ( buf - > bs_pad ) ) ;
buf - > bs_dmevmask = INT_GET ( dic - > di_dmevmask , ARCH_CONVERT ) ;
buf - > bs_dmstate = INT_GET ( dic - > di_dmstate , ARCH_CONVERT ) ;
buf - > bs_aextents = INT_GET ( dic - > di_anextents , ARCH_CONVERT ) ;
switch ( INT_GET ( dic - > di_format , ARCH_CONVERT ) ) {
case XFS_DINODE_FMT_DEV :
buf - > bs_rdev = INT_GET ( dip - > di_u . di_dev , ARCH_CONVERT ) ;
buf - > bs_blksize = BLKDEV_IOSIZE ;
buf - > bs_blocks = 0 ;
break ;
case XFS_DINODE_FMT_LOCAL :
case XFS_DINODE_FMT_UUID :
buf - > bs_rdev = 0 ;
buf - > bs_blksize = mp - > m_sb . sb_blocksize ;
buf - > bs_blocks = 0 ;
break ;
case XFS_DINODE_FMT_EXTENTS :
case XFS_DINODE_FMT_BTREE :
buf - > bs_rdev = 0 ;
buf - > bs_blksize = mp - > m_sb . sb_blocksize ;
buf - > bs_blocks = INT_GET ( dic - > di_nblocks , ARCH_CONVERT ) ;
break ;
}
return 0 ;
}
/*
* Return stat information for one inode .
* Return 0 if ok , else errno .
*/
int /* error status */
xfs_bulkstat_one (
xfs_mount_t * mp , /* mount point for filesystem */
xfs_ino_t ino , /* inode number to get data for */
void __user * buffer , /* buffer to place output in */
int ubsize , /* size of buffer */
void * private_data , /* my private data */
xfs_daddr_t bno , /* starting bno of inode cluster */
int * ubused , /* bytes used by me */
void * dibuff , /* on-disk inode buffer */
int * stat ) /* BULKSTAT_RV_... */
{
xfs_bstat_t * buf ; /* return buffer */
int error = 0 ; /* error value */
xfs_dinode_t * dip ; /* dinode inode pointer */
dip = ( xfs_dinode_t * ) dibuff ;
2006-09-28 05:06:15 +04:00
* stat = BULKSTAT_RV_NOTHING ;
2005-04-17 02:20:36 +04:00
2006-09-28 05:06:15 +04:00
if ( ! buffer | | xfs_internal_inum ( mp , ino ) )
2005-04-17 02:20:36 +04:00
return XFS_ERROR ( EINVAL ) ;
2006-09-28 05:06:15 +04:00
if ( ubsize < sizeof ( * buf ) )
2005-04-17 02:20:36 +04:00
return XFS_ERROR ( ENOMEM ) ;
buf = kmem_alloc ( sizeof ( * buf ) , KM_SLEEP ) ;
if ( dip = = NULL ) {
/* We're not being passed a pointer to a dinode. This happens
* if BULKSTAT_FG_IGET is selected . Do the iget .
*/
error = xfs_bulkstat_one_iget ( mp , ino , bno , buf , stat ) ;
if ( error )
goto out_free ;
} else {
xfs_bulkstat_one_dinode ( mp , ino , dip , buf ) ;
}
if ( copy_to_user ( buffer , buf , sizeof ( * buf ) ) ) {
2006-09-28 05:06:15 +04:00
error = EFAULT ;
2005-04-17 02:20:36 +04:00
goto out_free ;
}
* stat = BULKSTAT_RV_DIDONE ;
if ( ubused )
* ubused = sizeof ( * buf ) ;
out_free :
kmem_free ( buf , sizeof ( * buf ) ) ;
return error ;
}
2006-09-28 05:01:46 +04:00
/*
* Test to see whether we can use the ondisk inode directly , based
* on the given bulkstat flags , filling in dipp accordingly .
* Returns zero if the inode is dodgey .
*/
STATIC int
xfs_bulkstat_use_dinode (
xfs_mount_t * mp ,
int flags ,
xfs_buf_t * bp ,
int clustidx ,
xfs_dinode_t * * dipp )
{
xfs_dinode_t * dip ;
unsigned int aformat ;
* dipp = NULL ;
if ( ! bp | | ( flags & BULKSTAT_FG_IGET ) )
return 1 ;
dip = ( xfs_dinode_t * )
xfs_buf_offset ( bp , clustidx < < mp - > m_sb . sb_inodelog ) ;
if ( INT_GET ( dip - > di_core . di_magic , ARCH_CONVERT ) ! = XFS_DINODE_MAGIC | |
! XFS_DINODE_GOOD_VERSION (
INT_GET ( dip - > di_core . di_version , ARCH_CONVERT ) ) )
return 0 ;
if ( flags & BULKSTAT_FG_QUICK ) {
* dipp = dip ;
return 1 ;
}
/* BULKSTAT_FG_INLINE: if attr fork is local, or not there, use it */
aformat = INT_GET ( dip - > di_core . di_aformat , ARCH_CONVERT ) ;
if ( ( XFS_CFORK_Q ( & dip - > di_core ) = = 0 ) | |
( aformat = = XFS_DINODE_FMT_LOCAL ) | |
( aformat = = XFS_DINODE_FMT_EXTENTS & & ! dip - > di_core . di_anextents ) ) {
* dipp = dip ;
return 1 ;
}
return 1 ;
}
2005-04-17 02:20:36 +04:00
/*
* Return stat information in bulk ( by - inode ) for the filesystem .
*/
int /* error status */
xfs_bulkstat (
xfs_mount_t * mp , /* mount point for filesystem */
xfs_ino_t * lastinop , /* last inode returned */
int * ubcountp , /* size of buffer/count returned */
bulkstat_one_pf formatter , /* func that'd fill a single buf */
void * private_data , /* private data for formatter */
size_t statstruct_size , /* sizeof struct filling */
char __user * ubuffer , /* buffer with inode stats */
int flags , /* defined in xfs_itable.h */
2006-03-29 02:55:14 +04:00
int * done ) /* 1 if there are more stats to get */
2005-04-17 02:20:36 +04:00
{
xfs_agblock_t agbno = 0 ; /* allocation group block number */
xfs_buf_t * agbp ; /* agi header buffer */
xfs_agi_t * agi ; /* agi header data */
xfs_agino_t agino ; /* inode # in allocation group */
xfs_agnumber_t agno ; /* allocation group number */
xfs_daddr_t bno ; /* inode cluster start daddr */
int chunkidx ; /* current index into inode chunk */
int clustidx ; /* current index into inode cluster */
xfs_btree_cur_t * cur ; /* btree cursor for ialloc btree */
int end_of_ag ; /* set if we've seen the ag end */
int error ; /* error code */
int fmterror ; /* bulkstat formatter result */
__int32_t gcnt ; /* current btree rec's count */
xfs_inofree_t gfree ; /* current btree rec's free mask */
xfs_agino_t gino ; /* current btree rec's start inode */
int i ; /* loop index */
int icount ; /* count of inodes good in irbuf */
2006-09-28 05:04:43 +04:00
size_t irbsize ; /* size of irec buffer in bytes */
2005-04-17 02:20:36 +04:00
xfs_ino_t ino ; /* inode number (filesystem) */
2006-09-28 05:02:03 +04:00
xfs_inobt_rec_incore_t * irbp ; /* current irec buffer pointer */
xfs_inobt_rec_incore_t * irbuf ; /* start of irec buffer */
xfs_inobt_rec_incore_t * irbufend ; /* end of good irec buffer entries */
2005-04-17 02:20:36 +04:00
xfs_ino_t lastino = 0 ; /* last inode number returned */
int nbcluster ; /* # of blocks in a cluster */
int nicluster ; /* # of inodes in a cluster */
int nimask ; /* mask for inode clusters */
int nirbuf ; /* size of irbuf */
int rval ; /* return value error code */
int tmp ; /* result value from btree calls */
int ubcount ; /* size of user's buffer */
int ubleft ; /* bytes left in user's buffer */
char __user * ubufp ; /* pointer into user's buffer */
int ubelem ; /* spaces used in user's buffer */
int ubused ; /* bytes used by formatter */
xfs_buf_t * bp ; /* ptr to on-disk inode cluster buf */
xfs_dinode_t * dip ; /* ptr into bp for specific inode */
xfs_inode_t * ip ; /* ptr to in-core inode struct */
/*
* Get the last inode value , see if there ' s nothing to do .
*/
ino = ( xfs_ino_t ) * lastinop ;
dip = NULL ;
agno = XFS_INO_TO_AGNO ( mp , ino ) ;
agino = XFS_INO_TO_AGINO ( mp , ino ) ;
if ( agno > = mp - > m_sb . sb_agcount | |
ino ! = XFS_AGINO_TO_INO ( mp , agno , agino ) ) {
* done = 1 ;
* ubcountp = 0 ;
return 0 ;
}
ubcount = * ubcountp ; /* statstruct's */
ubleft = ubcount * statstruct_size ; /* bytes */
* ubcountp = ubelem = 0 ;
* done = 0 ;
fmterror = 0 ;
ubufp = ubuffer ;
nicluster = mp - > m_sb . sb_blocksize > = XFS_INODE_CLUSTER_SIZE ( mp ) ?
mp - > m_sb . sb_inopblock :
( XFS_INODE_CLUSTER_SIZE ( mp ) > > mp - > m_sb . sb_inodelog ) ;
nimask = ~ ( nicluster - 1 ) ;
nbcluster = nicluster > > mp - > m_sb . sb_inopblog ;
2006-09-28 05:03:27 +04:00
irbuf = kmem_zalloc_greedy ( & irbsize , NBPC , NBPC * 4 ,
KM_SLEEP | KM_MAYFAIL | KM_LARGE ) ;
2006-09-28 05:02:09 +04:00
nirbuf = irbsize / sizeof ( * irbuf ) ;
2005-04-17 02:20:36 +04:00
/*
* Loop over the allocation groups , starting from the last
* inode returned ; 0 means start of the allocation group .
*/
rval = 0 ;
while ( ubleft > = statstruct_size & & agno < mp - > m_sb . sb_agcount ) {
bp = NULL ;
down_read ( & mp - > m_peraglock ) ;
error = xfs_ialloc_read_agi ( mp , NULL , agno , & agbp ) ;
up_read ( & mp - > m_peraglock ) ;
if ( error ) {
/*
* Skip this allocation group and go to the next one .
*/
agno + + ;
agino = 0 ;
continue ;
}
agi = XFS_BUF_TO_AGI ( agbp ) ;
/*
* Allocate and initialize a btree cursor for ialloc btree .
*/
cur = xfs_btree_init_cursor ( mp , NULL , agbp , agno , XFS_BTNUM_INO ,
2006-09-28 05:02:03 +04:00
( xfs_inode_t * ) 0 , 0 ) ;
2005-04-17 02:20:36 +04:00
irbp = irbuf ;
irbufend = irbuf + nirbuf ;
end_of_ag = 0 ;
/*
* If we ' re returning in the middle of an allocation group ,
* we need to get the remainder of the chunk we ' re in .
*/
if ( agino > 0 ) {
/*
* Lookup the inode chunk that this inode lives in .
*/
error = xfs_inobt_lookup_le ( cur , agino , 0 , 0 , & tmp ) ;
if ( ! error & & /* no I/O error */
tmp & & /* lookup succeeded */
/* got the record, should always work */
! ( error = xfs_inobt_get_rec ( cur , & gino , & gcnt ,
& gfree , & i ) ) & &
i = = 1 & &
/* this is the right chunk */
agino < gino + XFS_INODES_PER_CHUNK & &
/* lastino was not last in chunk */
( chunkidx = agino - gino + 1 ) <
XFS_INODES_PER_CHUNK & &
/* there are some left allocated */
XFS_INOBT_MASKN ( chunkidx ,
XFS_INODES_PER_CHUNK - chunkidx ) & ~ gfree ) {
/*
* Grab the chunk record . Mark all the
* uninteresting inodes ( because they ' re
* before our start point ) free .
*/
for ( i = 0 ; i < chunkidx ; i + + ) {
if ( XFS_INOBT_MASK ( i ) & ~ gfree )
gcnt + + ;
}
gfree | = XFS_INOBT_MASKN ( 0 , chunkidx ) ;
2006-09-28 05:02:03 +04:00
irbp - > ir_startino = gino ;
irbp - > ir_freecount = gcnt ;
irbp - > ir_free = gfree ;
2005-04-17 02:20:36 +04:00
irbp + + ;
agino = gino + XFS_INODES_PER_CHUNK ;
icount = XFS_INODES_PER_CHUNK - gcnt ;
} else {
/*
* If any of those tests failed , bump the
* inode number ( just in case ) .
*/
agino + + ;
icount = 0 ;
}
/*
* In any case , increment to the next record .
*/
if ( ! error )
error = xfs_inobt_increment ( cur , 0 , & tmp ) ;
} else {
/*
* Start of ag . Lookup the first inode chunk .
*/
error = xfs_inobt_lookup_ge ( cur , 0 , 0 , 0 , & tmp ) ;
icount = 0 ;
}
/*
* Loop through inode btree records in this ag ,
* until we run out of inodes or space in the buffer .
*/
while ( irbp < irbufend & & icount < ubcount ) {
/*
* Loop as long as we ' re unable to read the
* inode btree .
*/
while ( error ) {
agino + = XFS_INODES_PER_CHUNK ;
if ( XFS_AGINO_TO_AGBNO ( mp , agino ) > =
2005-11-02 07:11:25 +03:00
be32_to_cpu ( agi - > agi_length ) )
2005-04-17 02:20:36 +04:00
break ;
error = xfs_inobt_lookup_ge ( cur , agino , 0 , 0 ,
& tmp ) ;
}
/*
* If ran off the end of the ag either with an error ,
* or the normal way , set end and stop collecting .
*/
if ( error | |
( error = xfs_inobt_get_rec ( cur , & gino , & gcnt ,
& gfree , & i ) ) | |
i = = 0 ) {
end_of_ag = 1 ;
break ;
}
/*
* If this chunk has any allocated inodes , save it .
2006-09-28 05:02:03 +04:00
* Also start read - ahead now for this chunk .
2005-04-17 02:20:36 +04:00
*/
if ( gcnt < XFS_INODES_PER_CHUNK ) {
2006-09-28 05:02:03 +04:00
/*
* Loop over all clusters in the next chunk .
* Do a readahead if there are any allocated
* inodes in that cluster .
*/
for ( agbno = XFS_AGINO_TO_AGBNO ( mp , gino ) ,
chunkidx = 0 ;
chunkidx < XFS_INODES_PER_CHUNK ;
chunkidx + = nicluster ,
agbno + = nbcluster ) {
if ( XFS_INOBT_MASKN ( chunkidx ,
nicluster ) & ~ gfree )
xfs_btree_reada_bufs ( mp , agno ,
agbno , nbcluster ) ;
}
irbp - > ir_startino = gino ;
irbp - > ir_freecount = gcnt ;
irbp - > ir_free = gfree ;
2005-04-17 02:20:36 +04:00
irbp + + ;
icount + = XFS_INODES_PER_CHUNK - gcnt ;
}
/*
* Set agino to after this chunk and bump the cursor .
*/
agino = gino + XFS_INODES_PER_CHUNK ;
error = xfs_inobt_increment ( cur , 0 , & tmp ) ;
}
/*
* Drop the btree buffers and the agi buffer .
* We can ' t hold any of the locks these represent
* when calling iget .
*/
xfs_btree_del_cursor ( cur , XFS_BTREE_NOERROR ) ;
xfs_buf_relse ( agbp ) ;
/*
* Now format all the good inodes into the user ' s buffer .
*/
irbufend = irbp ;
for ( irbp = irbuf ;
irbp < irbufend & & ubleft > = statstruct_size ; irbp + + ) {
/*
* Now process this chunk of inodes .
*/
2006-09-28 05:02:03 +04:00
for ( agino = irbp - > ir_startino , chunkidx = clustidx = 0 ;
2005-04-17 02:20:36 +04:00
ubleft > 0 & &
2006-09-28 05:02:03 +04:00
irbp - > ir_freecount < XFS_INODES_PER_CHUNK ;
2005-04-17 02:20:36 +04:00
chunkidx + + , clustidx + + , agino + + ) {
ASSERT ( chunkidx < XFS_INODES_PER_CHUNK ) ;
/*
* Recompute agbno if this is the
* first inode of the cluster .
*
* Careful with clustidx . There can be
* multple clusters per chunk , a single
* cluster per chunk or a cluster that has
* inodes represented from several different
* chunks ( if blocksize is large ) .
*
* Because of this , the starting clustidx is
* initialized to zero in this loop but must
* later be reset after reading in the cluster
* buffer .
*/
if ( ( chunkidx & ( nicluster - 1 ) ) = = 0 ) {
agbno = XFS_AGINO_TO_AGBNO ( mp ,
2006-09-28 05:02:03 +04:00
irbp - > ir_startino ) +
2005-04-17 02:20:36 +04:00
( ( chunkidx & nimask ) > >
mp - > m_sb . sb_inopblog ) ;
2006-09-28 05:01:46 +04:00
if ( flags & ( BULKSTAT_FG_QUICK |
BULKSTAT_FG_INLINE ) ) {
2005-04-17 02:20:36 +04:00
ino = XFS_AGINO_TO_INO ( mp , agno ,
agino ) ;
bno = XFS_AGB_TO_DADDR ( mp , agno ,
agbno ) ;
/*
* Get the inode cluster buffer
*/
ASSERT ( xfs_inode_zone ! = NULL ) ;
ip = kmem_zone_zalloc ( xfs_inode_zone ,
KM_SLEEP ) ;
ip - > i_ino = ino ;
ip - > i_mount = mp ;
2006-09-28 05:06:03 +04:00
spin_lock_init ( & ip - > i_flags_lock ) ;
2005-04-17 02:20:36 +04:00
if ( bp )
xfs_buf_relse ( bp ) ;
error = xfs_itobp ( mp , NULL , ip ,
2006-03-17 09:26:04 +03:00
& dip , & bp , bno ,
XFS_IMAP_BULKSTAT ) ;
2005-04-17 02:20:36 +04:00
if ( ! error )
clustidx = ip - > i_boffset / mp - > m_sb . sb_inodesize ;
kmem_zone_free ( xfs_inode_zone , ip ) ;
if ( XFS_TEST_ERROR ( error ! = 0 ,
mp , XFS_ERRTAG_BULKSTAT_READ_CHUNK ,
XFS_RANDOM_BULKSTAT_READ_CHUNK ) ) {
bp = NULL ;
2006-03-17 09:26:04 +03:00
ubleft = 0 ;
rval = error ;
2005-04-17 02:20:36 +04:00
break ;
}
}
}
/*
* Skip if this inode is free .
*/
2006-09-28 05:02:03 +04:00
if ( XFS_INOBT_MASK ( chunkidx ) & irbp - > ir_free )
2005-04-17 02:20:36 +04:00
continue ;
/*
* Count used inodes as free so we can tell
* when the chunk is used up .
*/
2006-09-28 05:02:03 +04:00
irbp - > ir_freecount + + ;
2005-04-17 02:20:36 +04:00
ino = XFS_AGINO_TO_INO ( mp , agno , agino ) ;
bno = XFS_AGB_TO_DADDR ( mp , agno , agbno ) ;
2006-09-28 05:01:46 +04:00
if ( ! xfs_bulkstat_use_dinode ( mp , flags , bp ,
clustidx , & dip ) )
continue ;
/*
* If we need to do an iget , cannot hold bp .
* Drop it , until starting the next cluster .
*/
if ( ( flags & BULKSTAT_FG_INLINE ) & & ! dip ) {
if ( bp )
xfs_buf_relse ( bp ) ;
bp = NULL ;
2005-04-17 02:20:36 +04:00
}
/*
* Get the inode and fill in a single buffer .
* BULKSTAT_FG_QUICK uses dip to fill it in .
* BULKSTAT_FG_IGET uses igets .
2006-09-28 05:01:46 +04:00
* BULKSTAT_FG_INLINE uses dip if we have an
* inline attr fork , else igets .
2005-04-17 02:20:36 +04:00
* See : xfs_bulkstat_one & xfs_dm_bulkstat_one .
* This is also used to count inodes / blks , etc
* in xfs_qm_quotacheck .
*/
ubused = statstruct_size ;
error = formatter ( mp , ino , ubufp ,
ubleft , private_data ,
bno , & ubused , dip , & fmterror ) ;
if ( fmterror = = BULKSTAT_RV_NOTHING ) {
2006-09-28 05:04:31 +04:00
if ( error = = EFAULT ) {
2006-09-28 05:04:24 +04:00
ubleft = 0 ;
rval = error ;
break ;
}
2006-09-28 05:04:31 +04:00
else if ( error = = ENOMEM )
ubleft = 0 ;
2006-09-28 05:06:21 +04:00
else
lastino = ino ;
2005-04-17 02:20:36 +04:00
continue ;
}
if ( fmterror = = BULKSTAT_RV_GIVEUP ) {
ubleft = 0 ;
ASSERT ( error ) ;
rval = error ;
break ;
}
if ( ubufp )
ubufp + = ubused ;
ubleft - = ubused ;
ubelem + + ;
lastino = ino ;
}
}
if ( bp )
xfs_buf_relse ( bp ) ;
/*
* Set up for the next loop iteration .
*/
if ( ubleft > 0 ) {
if ( end_of_ag ) {
agno + + ;
agino = 0 ;
} else
agino = XFS_INO_TO_AGINO ( mp , lastino ) ;
} else
break ;
}
/*
* Done , we ' re either out of filesystem or space to put the data .
*/
2006-09-28 05:02:09 +04:00
kmem_free ( irbuf , irbsize ) ;
2005-04-17 02:20:36 +04:00
* ubcountp = ubelem ;
if ( agno > = mp - > m_sb . sb_agcount ) {
/*
* If we ran out of filesystem , mark lastino as off
* the end of the filesystem , so the next call
* will return immediately .
*/
* lastinop = ( xfs_ino_t ) XFS_AGINO_TO_INO ( mp , agno , 0 ) ;
* done = 1 ;
} else
* lastinop = ( xfs_ino_t ) lastino ;
return rval ;
}
/*
* Return stat information in bulk ( by - inode ) for the filesystem .
* Special case for non - sequential one inode bulkstat .
*/
int /* error status */
xfs_bulkstat_single (
xfs_mount_t * mp , /* mount point for filesystem */
xfs_ino_t * lastinop , /* inode to return */
char __user * buffer , /* buffer with inode stats */
2006-03-29 02:55:14 +04:00
int * done ) /* 1 if there are more stats to get */
2005-04-17 02:20:36 +04:00
{
int count ; /* count value for bulkstat call */
int error ; /* return value */
xfs_ino_t ino ; /* filesystem inode number */
int res ; /* result from bs1 */
/*
* note that requesting valid inode numbers which are not allocated
* to inodes will most likely cause xfs_itobp to generate warning
* messages about bad magic numbers . This is ok . The fact that
* the inode isn ' t actually an inode is handled by the
* error check below . Done this way to make the usual case faster
* at the expense of the error case .
*/
ino = ( xfs_ino_t ) * lastinop ;
error = xfs_bulkstat_one ( mp , ino , buffer , sizeof ( xfs_bstat_t ) ,
NULL , 0 , NULL , NULL , & res ) ;
if ( error ) {
/*
* Special case way failed , do it the " long " way
* to see if that works .
*/
( * lastinop ) - - ;
count = 1 ;
if ( xfs_bulkstat ( mp , lastinop , & count , xfs_bulkstat_one ,
NULL , sizeof ( xfs_bstat_t ) , buffer ,
BULKSTAT_FG_IGET , done ) )
return error ;
if ( count = = 0 | | ( xfs_ino_t ) * lastinop ! = ino )
return error = = EFSCORRUPTED ?
XFS_ERROR ( EINVAL ) : error ;
else
return 0 ;
}
* done = 0 ;
return 0 ;
}
/*
* Return inode number table for the filesystem .
*/
int /* error status */
xfs_inumbers (
xfs_mount_t * mp , /* mount point for filesystem */
xfs_ino_t * lastino , /* last inode returned */
int * count , /* size of buffer/count returned */
xfs_inogrp_t __user * ubuffer ) /* buffer with inode descriptions */
{
xfs_buf_t * agbp ;
xfs_agino_t agino ;
xfs_agnumber_t agno ;
int bcount ;
xfs_inogrp_t * buffer ;
int bufidx ;
xfs_btree_cur_t * cur ;
int error ;
__int32_t gcnt ;
xfs_inofree_t gfree ;
xfs_agino_t gino ;
int i ;
xfs_ino_t ino ;
int left ;
int tmp ;
ino = ( xfs_ino_t ) * lastino ;
agno = XFS_INO_TO_AGNO ( mp , ino ) ;
agino = XFS_INO_TO_AGINO ( mp , ino ) ;
left = * count ;
* count = 0 ;
bcount = MIN ( left , ( int ) ( NBPP / sizeof ( * buffer ) ) ) ;
buffer = kmem_alloc ( bcount * sizeof ( * buffer ) , KM_SLEEP ) ;
error = bufidx = 0 ;
cur = NULL ;
agbp = NULL ;
while ( left > 0 & & agno < mp - > m_sb . sb_agcount ) {
if ( agbp = = NULL ) {
down_read ( & mp - > m_peraglock ) ;
error = xfs_ialloc_read_agi ( mp , NULL , agno , & agbp ) ;
up_read ( & mp - > m_peraglock ) ;
if ( error ) {
/*
* If we can ' t read the AGI of this ag ,
* then just skip to the next one .
*/
ASSERT ( cur = = NULL ) ;
agbp = NULL ;
agno + + ;
agino = 0 ;
continue ;
}
cur = xfs_btree_init_cursor ( mp , NULL , agbp , agno ,
XFS_BTNUM_INO , ( xfs_inode_t * ) 0 , 0 ) ;
error = xfs_inobt_lookup_ge ( cur , agino , 0 , 0 , & tmp ) ;
if ( error ) {
xfs_btree_del_cursor ( cur , XFS_BTREE_ERROR ) ;
cur = NULL ;
xfs_buf_relse ( agbp ) ;
agbp = NULL ;
/*
* Move up the the last inode in the current
* chunk . The lookup_ge will always get
* us the first inode in the next chunk .
*/
agino + = XFS_INODES_PER_CHUNK - 1 ;
continue ;
}
}
if ( ( error = xfs_inobt_get_rec ( cur , & gino , & gcnt , & gfree ,
& i ) ) | |
i = = 0 ) {
xfs_buf_relse ( agbp ) ;
agbp = NULL ;
xfs_btree_del_cursor ( cur , XFS_BTREE_NOERROR ) ;
cur = NULL ;
agno + + ;
agino = 0 ;
continue ;
}
agino = gino + XFS_INODES_PER_CHUNK - 1 ;
buffer [ bufidx ] . xi_startino = XFS_AGINO_TO_INO ( mp , agno , gino ) ;
buffer [ bufidx ] . xi_alloccount = XFS_INODES_PER_CHUNK - gcnt ;
buffer [ bufidx ] . xi_allocmask = ~ gfree ;
bufidx + + ;
left - - ;
if ( bufidx = = bcount ) {
if ( copy_to_user ( ubuffer , buffer ,
bufidx * sizeof ( * buffer ) ) ) {
error = XFS_ERROR ( EFAULT ) ;
break ;
}
ubuffer + = bufidx ;
* count + = bufidx ;
bufidx = 0 ;
}
if ( left ) {
error = xfs_inobt_increment ( cur , 0 , & tmp ) ;
if ( error ) {
xfs_btree_del_cursor ( cur , XFS_BTREE_ERROR ) ;
cur = NULL ;
xfs_buf_relse ( agbp ) ;
agbp = NULL ;
/*
* The agino value has already been bumped .
* Just try to skip up to it .
*/
agino + = XFS_INODES_PER_CHUNK ;
continue ;
}
}
}
if ( ! error ) {
if ( bufidx ) {
if ( copy_to_user ( ubuffer , buffer ,
bufidx * sizeof ( * buffer ) ) )
error = XFS_ERROR ( EFAULT ) ;
else
* count + = bufidx ;
}
* lastino = XFS_AGINO_TO_INO ( mp , agno , agino ) ;
}
kmem_free ( buffer , bcount * sizeof ( * buffer ) ) ;
if ( cur )
xfs_btree_del_cursor ( cur , ( error ? XFS_BTREE_ERROR :
XFS_BTREE_NOERROR ) ) ;
if ( agbp )
xfs_buf_relse ( agbp ) ;
return error ;
}