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"
# 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_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_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"
2010-06-24 05:52:50 +04:00
# include "xfs_trace.h"
2005-04-17 02:20:36 +04:00
2009-07-02 09:09:33 +04:00
STATIC int
2006-09-28 05:06:15 +04:00
xfs_internal_inum (
xfs_mount_t * mp ,
xfs_ino_t ino )
{
return ( ino = = mp - > m_sb . sb_rbmino | | ino = = mp - > m_sb . sb_rsumino | |
2008-03-06 05:44:28 +03:00
( xfs_sb_version_hasquota ( & mp - > m_sb ) & &
2006-09-28 05:06:15 +04:00
( ino = = mp - > m_sb . sb_uquotino | | ino = = mp - > m_sb . sb_gquotino ) ) ) ;
}
2010-06-23 12:11:11 +04:00
/*
* Return stat information for one inode .
* Return 0 if ok , else errno .
*/
int
xfs_bulkstat_one_int (
struct xfs_mount * mp , /* mount point for filesystem */
xfs_ino_t ino , /* inode to get data for */
void __user * buffer , /* buffer to place output in */
int ubsize , /* size of buffer */
bulkstat_one_fmt_pf formatter , /* formatter, copy to user */
int * ubused , /* bytes used by me */
int * stat ) /* BULKSTAT_RV_... */
2005-04-17 02:20:36 +04:00
{
2010-06-23 12:11:11 +04:00
struct xfs_icdinode * dic ; /* dinode core info pointer */
struct xfs_inode * ip ; /* incore inode pointer */
struct xfs_bstat * buf ; /* return buffer */
int error = 0 ; /* error value */
* stat = BULKSTAT_RV_NOTHING ;
if ( ! buffer | | xfs_internal_inum ( mp , ino ) )
return XFS_ERROR ( EINVAL ) ;
buf = kmem_alloc ( sizeof ( * buf ) , KM_SLEEP | KM_MAYFAIL ) ;
if ( ! buf )
return XFS_ERROR ( ENOMEM ) ;
2005-04-17 02:20:36 +04:00
2006-09-28 05:02:23 +04:00
error = xfs_iget ( mp , NULL , ino ,
2012-03-22 09:15:10 +04:00
( XFS_IGET_DONTCACHE | XFS_IGET_UNTRUSTED ) ,
XFS_ILOCK_SHARED , & ip ) ;
2005-04-17 02:20:36 +04:00
if ( error ) {
* stat = BULKSTAT_RV_NOTHING ;
2010-06-23 12:11:11 +04:00
goto out_free ;
2005-04-17 02:20:36 +04:00
}
ASSERT ( ip ! = NULL ) ;
2008-11-28 06:23:41 +03:00
ASSERT ( ip - > i_imap . im_blkno ! = 0 ) ;
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 ;
2010-09-26 10:10:18 +04:00
buf - > bs_projid_lo = dic - > di_projid_lo ;
buf - > bs_projid_hi = dic - > di_projid_hi ;
2005-04-17 02:20:36 +04:00
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 ;
2012-02-29 13:53:52 +04:00
buf - > bs_atime . tv_sec = dic - > di_atime . t_sec ;
buf - > bs_atime . tv_nsec = dic - > di_atime . t_nsec ;
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 ;
2005-04-17 02:20:36 +04:00
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 ;
2010-03-05 07:41:14 +03:00
buf - > bs_forkoff = XFS_IFORK_BOFF ( ip ) ;
2005-04-17 02:20:36 +04:00
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 ;
}
2010-06-24 05:52:50 +04:00
xfs_iunlock ( ip , XFS_ILOCK_SHARED ) ;
IRELE ( ip ) ;
2005-04-17 02:20:36 +04:00
2010-06-23 12:11:11 +04:00
error = formatter ( buffer , ubsize , ubused , buf ) ;
2005-04-17 02:20:36 +04:00
2010-06-23 12:11:11 +04:00
if ( ! error )
* stat = BULKSTAT_RV_DIDONE ;
2005-04-17 02:20:36 +04:00
2010-06-23 12:11:11 +04:00
out_free :
kmem_free ( buf ) ;
return error ;
2005-04-17 02:20:36 +04:00
}
2008-11-26 06:20:12 +03:00
/* Return 0 on success or positive error */
2007-07-11 05:10:19 +04:00
STATIC int
xfs_bulkstat_one_fmt (
void __user * ubuffer ,
2008-11-26 06:20:12 +03:00
int ubsize ,
int * ubused ,
2007-07-11 05:10:19 +04:00
const xfs_bstat_t * buffer )
{
2008-11-26 06:20:12 +03:00
if ( ubsize < sizeof ( * buffer ) )
return XFS_ERROR ( ENOMEM ) ;
2007-07-11 05:10:19 +04:00
if ( copy_to_user ( ubuffer , buffer , sizeof ( * buffer ) ) )
2008-11-26 06:20:12 +03:00
return XFS_ERROR ( EFAULT ) ;
if ( ubused )
* ubused = sizeof ( * buffer ) ;
return 0 ;
2007-07-11 05:10:19 +04:00
}
2008-11-26 06:20:11 +03:00
int
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 */
int * ubused , /* bytes used by me */
int * stat ) /* BULKSTAT_RV_... */
{
return xfs_bulkstat_one_int ( mp , ino , buffer , ubsize ,
2010-06-24 05:35:17 +04:00
xfs_bulkstat_one_fmt , ubused , stat ) ;
2006-09-28 05:01:46 +04:00
}
2007-11-23 08:30:32 +03:00
# define XFS_BULKSTAT_UBLEFT(ubleft) ((ubleft) >= statstruct_size)
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 */
size_t statstruct_size , /* sizeof struct filling */
char __user * ubuffer , /* 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
{
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 */
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 */
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 */
2007-11-23 08:30:32 +03:00
xfs_ino_t lastino ; /* last inode number returned */
2005-04-17 02:20:36 +04:00
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 */
/*
* Get the last inode value , see if there ' s nothing to do .
*/
ino = ( xfs_ino_t ) * lastinop ;
2007-11-23 08:30:32 +03:00
lastino = ino ;
2005-04-17 02:20:36 +04:00
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 ;
}
2007-11-23 08:30:32 +03:00
if ( ! ubcountp | | * ubcountp < = 0 ) {
return EINVAL ;
}
2005-04-17 02:20:36 +04:00
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 ;
2010-01-21 00:55:30 +03:00
irbuf = kmem_zalloc_greedy ( & irbsize , PAGE_SIZE , PAGE_SIZE * 4 ) ;
if ( ! irbuf )
return ENOMEM ;
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 ;
2007-11-23 08:30:32 +03:00
while ( XFS_BULKSTAT_UBLEFT ( ubleft ) & & agno < mp - > m_sb . sb_agcount ) {
cond_resched ( ) ;
2005-04-17 02:20:36 +04:00
bp = NULL ;
error = xfs_ialloc_read_agi ( mp , NULL , agno , & agbp ) ;
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 .
*/
2008-10-30 08:53:59 +03:00
cur = xfs_inobt_init_cursor ( mp , NULL , agbp , agno ) ;
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 ) {
2009-09-01 03:56:58 +04:00
xfs_inobt_rec_incore_t r ;
2005-04-17 02:20:36 +04:00
/*
* Lookup the inode chunk that this inode lives in .
*/
2009-09-01 03:58:21 +04:00
error = xfs_inobt_lookup ( cur , agino , XFS_LOOKUP_LE ,
& tmp ) ;
2005-04-17 02:20:36 +04:00
if ( ! error & & /* no I/O error */
tmp & & /* lookup succeeded */
/* got the record, should always work */
2009-09-01 03:56:58 +04:00
! ( error = xfs_inobt_get_rec ( cur , & r , & i ) ) & &
2005-04-17 02:20:36 +04:00
i = = 1 & &
/* this is the right chunk */
2009-09-01 03:56:58 +04:00
agino < r . ir_startino + XFS_INODES_PER_CHUNK & &
2005-04-17 02:20:36 +04:00
/* lastino was not last in chunk */
2009-09-01 03:56:58 +04:00
( chunkidx = agino - r . ir_startino + 1 ) <
2005-04-17 02:20:36 +04:00
XFS_INODES_PER_CHUNK & &
/* there are some left allocated */
2009-01-15 08:22:07 +03:00
xfs_inobt_maskn ( chunkidx ,
2009-09-01 03:56:58 +04:00
XFS_INODES_PER_CHUNK - chunkidx ) &
~ r . ir_free ) {
2005-04-17 02:20:36 +04:00
/*
* Grab the chunk record . Mark all the
* uninteresting inodes ( because they ' re
* before our start point ) free .
*/
for ( i = 0 ; i < chunkidx ; i + + ) {
2009-09-01 03:56:58 +04:00
if ( XFS_INOBT_MASK ( i ) & ~ r . ir_free )
r . ir_freecount + + ;
2005-04-17 02:20:36 +04:00
}
2009-09-01 03:56:58 +04:00
r . ir_free | = xfs_inobt_maskn ( 0 , chunkidx ) ;
irbp - > ir_startino = r . ir_startino ;
irbp - > ir_freecount = r . ir_freecount ;
irbp - > ir_free = r . ir_free ;
2005-04-17 02:20:36 +04:00
irbp + + ;
2009-09-01 03:56:58 +04:00
agino = r . ir_startino + XFS_INODES_PER_CHUNK ;
icount = XFS_INODES_PER_CHUNK - r . ir_freecount ;
2005-04-17 02:20:36 +04:00
} 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 )
2008-10-30 08:55:45 +03:00
error = xfs_btree_increment ( cur , 0 , & tmp ) ;
2005-04-17 02:20:36 +04:00
} else {
/*
* Start of ag . Lookup the first inode chunk .
*/
2009-09-01 03:58:21 +04:00
error = xfs_inobt_lookup ( cur , 0 , XFS_LOOKUP_GE , & tmp ) ;
2005-04-17 02:20:36 +04:00
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 ) {
2009-09-01 03:56:58 +04:00
xfs_inobt_rec_incore_t r ;
2005-04-17 02:20:36 +04:00
/*
* 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 ;
2009-09-01 03:58:21 +04:00
error = xfs_inobt_lookup ( cur , agino ,
XFS_LOOKUP_GE , & tmp ) ;
2007-11-23 08:30:32 +03:00
cond_resched ( ) ;
2005-04-17 02:20:36 +04:00
}
/*
* If ran off the end of the ag either with an error ,
* or the normal way , set end and stop collecting .
*/
2009-09-01 03:56:58 +04:00
if ( error ) {
end_of_ag = 1 ;
break ;
}
error = xfs_inobt_get_rec ( cur , & r , & i ) ;
if ( error | | i = = 0 ) {
2005-04-17 02:20:36 +04:00
end_of_ag = 1 ;
break ;
}
2009-09-01 03:56:58 +04:00
2005-04-17 02:20:36 +04:00
/*
* 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
*/
2009-09-01 03:56:58 +04:00
if ( r . ir_freecount < 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 .
*/
2009-09-01 03:56:58 +04:00
agbno = XFS_AGINO_TO_AGBNO ( mp , r . ir_startino ) ;
for ( chunkidx = 0 ;
2006-09-28 05:02:03 +04:00
chunkidx < XFS_INODES_PER_CHUNK ;
chunkidx + = nicluster ,
agbno + = nbcluster ) {
2009-09-01 03:56:58 +04:00
if ( xfs_inobt_maskn ( chunkidx , nicluster )
& ~ r . ir_free )
2006-09-28 05:02:03 +04:00
xfs_btree_reada_bufs ( mp , agno ,
agbno , nbcluster ) ;
}
2009-09-01 03:56:58 +04:00
irbp - > ir_startino = r . ir_startino ;
irbp - > ir_freecount = r . ir_freecount ;
irbp - > ir_free = r . ir_free ;
2005-04-17 02:20:36 +04:00
irbp + + ;
2009-09-01 03:56:58 +04:00
icount + = XFS_INODES_PER_CHUNK - r . ir_freecount ;
2005-04-17 02:20:36 +04:00
}
/*
* Set agino to after this chunk and bump the cursor .
*/
2009-09-01 03:56:58 +04:00
agino = r . ir_startino + XFS_INODES_PER_CHUNK ;
2008-10-30 08:55:45 +03:00
error = xfs_btree_increment ( cur , 0 , & tmp ) ;
2007-11-23 08:30:32 +03:00
cond_resched ( ) ;
2005-04-17 02:20:36 +04:00
}
/*
* 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 ;
2007-11-23 08:30:32 +03:00
irbp < irbufend & & XFS_BULKSTAT_UBLEFT ( ubleft ) ; irbp + + ) {
2005-04-17 02:20:36 +04:00
/*
* Now process this chunk of inodes .
*/
2006-09-28 05:02:03 +04:00
for ( agino = irbp - > ir_startino , chunkidx = clustidx = 0 ;
2007-11-23 08:30:32 +03:00
XFS_BULKSTAT_UBLEFT ( ubleft ) & &
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
2009-03-29 11:55:42 +04:00
* multiple clusters per chunk , a single
2005-04-17 02:20:36 +04:00
* 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 ) ;
}
2007-10-12 05:12:20 +04:00
ino = XFS_AGINO_TO_INO ( mp , agno , agino ) ;
2005-04-17 02:20:36 +04:00
/*
* Skip if this inode is free .
*/
2007-10-12 05:12:20 +04:00
if ( XFS_INOBT_MASK ( chunkidx ) & irbp - > ir_free ) {
lastino = ino ;
2005-04-17 02:20:36 +04:00
continue ;
2007-10-12 05:12:20 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* 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
/*
* Get the inode and fill in a single buffer .
*/
ubused = statstruct_size ;
2010-06-24 05:35:17 +04:00
error = formatter ( mp , ino , ubufp , ubleft ,
2010-06-23 12:11:11 +04:00
& ubused , & fmterror ) ;
2005-04-17 02:20:36 +04:00
if ( fmterror = = BULKSTAT_RV_NOTHING ) {
2007-11-23 08:30:32 +03:00
if ( error & & error ! = ENOENT & &
error ! = EINVAL ) {
2006-09-28 05:04:31 +04:00
ubleft = 0 ;
2007-11-23 08:30:32 +03:00
rval = error ;
break ;
}
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 ;
}
2007-11-23 08:30:32 +03:00
cond_resched ( ) ;
2005-04-17 02:20:36 +04:00
}
if ( bp )
xfs_buf_relse ( bp ) ;
/*
* Set up for the next loop iteration .
*/
2007-11-23 08:30:32 +03:00
if ( XFS_BULKSTAT_UBLEFT ( ubleft ) ) {
2005-04-17 02:20:36 +04:00
if ( end_of_ag ) {
agno + + ;
agino = 0 ;
2007-11-23 08:30:32 +03:00
} else
agino = XFS_INO_TO_AGINO ( mp , lastino ) ;
2005-04-17 02:20:36 +04:00
} else
break ;
}
/*
* Done , we ' re either out of filesystem or space to put the data .
*/
2010-01-21 00:55:30 +03:00
kmem_free_large ( irbuf ) ;
2005-04-17 02:20:36 +04:00
* ubcountp = ubelem ;
2007-11-23 08:30:32 +03:00
/*
* Found some inodes , return them now and return the error next time .
*/
if ( ubelem )
rval = 0 ;
2005-04-17 02:20:36 +04:00
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 ;
2010-06-24 05:35:17 +04:00
error = xfs_bulkstat_one ( mp , ino , buffer , sizeof ( xfs_bstat_t ) , 0 , & res ) ;
2005-04-17 02:20:36 +04:00
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 ,
2010-06-23 12:11:11 +04:00
sizeof ( xfs_bstat_t ) , buffer , done ) )
2005-04-17 02:20:36 +04:00
return error ;
if ( count = = 0 | | ( xfs_ino_t ) * lastinop ! = ino )
return error = = EFSCORRUPTED ?
XFS_ERROR ( EINVAL ) : error ;
else
return 0 ;
}
* done = 0 ;
return 0 ;
}
2007-07-11 05:10:19 +04:00
int
xfs_inumbers_fmt (
void __user * ubuffer , /* buffer to write to */
const xfs_inogrp_t * buffer , /* buffer to read from */
long count , /* # of elements to read */
long * written ) /* # of bytes written */
{
if ( copy_to_user ( ubuffer , buffer , count * sizeof ( * buffer ) ) )
return - EFAULT ;
* written = count * sizeof ( * buffer ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
/*
* 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 */
2007-07-11 05:10:19 +04:00
void __user * ubuffer , /* buffer with inode descriptions */
inumbers_fmt_pf formatter )
2005-04-17 02:20:36 +04:00
{
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 ;
2009-09-01 03:56:58 +04:00
xfs_inobt_rec_incore_t r ;
2005-04-17 02:20:36 +04:00
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 ;
2007-11-23 08:30:42 +03:00
bcount = MIN ( left , ( int ) ( PAGE_SIZE / sizeof ( * buffer ) ) ) ;
2005-04-17 02:20:36 +04:00
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 ) {
error = xfs_ialloc_read_agi ( mp , NULL , agno , & agbp ) ;
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 ;
}
2008-10-30 08:53:59 +03:00
cur = xfs_inobt_init_cursor ( mp , NULL , agbp , agno ) ;
2009-09-01 03:58:21 +04:00
error = xfs_inobt_lookup ( cur , agino , XFS_LOOKUP_GE ,
& tmp ) ;
2005-04-17 02:20:36 +04:00
if ( error ) {
xfs_btree_del_cursor ( cur , XFS_BTREE_ERROR ) ;
cur = NULL ;
xfs_buf_relse ( agbp ) ;
agbp = NULL ;
/*
2007-05-09 10:57:56 +04:00
* Move up the last inode in the current
2005-04-17 02:20:36 +04:00
* chunk . The lookup_ge will always get
* us the first inode in the next chunk .
*/
agino + = XFS_INODES_PER_CHUNK - 1 ;
continue ;
}
}
2009-09-01 03:56:58 +04:00
error = xfs_inobt_get_rec ( cur , & r , & i ) ;
if ( error | | i = = 0 ) {
2005-04-17 02:20:36 +04:00
xfs_buf_relse ( agbp ) ;
agbp = NULL ;
xfs_btree_del_cursor ( cur , XFS_BTREE_NOERROR ) ;
cur = NULL ;
agno + + ;
agino = 0 ;
continue ;
}
2009-09-01 03:56:58 +04:00
agino = r . ir_startino + XFS_INODES_PER_CHUNK - 1 ;
buffer [ bufidx ] . xi_startino =
XFS_AGINO_TO_INO ( mp , agno , r . ir_startino ) ;
buffer [ bufidx ] . xi_alloccount =
XFS_INODES_PER_CHUNK - r . ir_freecount ;
buffer [ bufidx ] . xi_allocmask = ~ r . ir_free ;
2005-04-17 02:20:36 +04:00
bufidx + + ;
left - - ;
if ( bufidx = = bcount ) {
2007-07-11 05:10:19 +04:00
long written ;
if ( formatter ( ubuffer , buffer , bufidx , & written ) ) {
2005-04-17 02:20:36 +04:00
error = XFS_ERROR ( EFAULT ) ;
break ;
}
2007-07-11 05:10:19 +04:00
ubuffer + = written ;
2005-04-17 02:20:36 +04:00
* count + = bufidx ;
bufidx = 0 ;
}
if ( left ) {
2008-10-30 08:55:45 +03:00
error = xfs_btree_increment ( cur , 0 , & tmp ) ;
2005-04-17 02:20:36 +04:00
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 ) {
2007-07-11 05:10:19 +04:00
long written ;
if ( formatter ( ubuffer , buffer , bufidx , & written ) )
2005-04-17 02:20:36 +04:00
error = XFS_ERROR ( EFAULT ) ;
else
* count + = bufidx ;
}
* lastino = XFS_AGINO_TO_INO ( mp , agno , agino ) ;
}
2008-05-19 10:31:57 +04:00
kmem_free ( buffer ) ;
2005-04-17 02:20:36 +04:00
if ( cur )
xfs_btree_del_cursor ( cur , ( error ? XFS_BTREE_ERROR :
XFS_BTREE_NOERROR ) ) ;
if ( agbp )
xfs_buf_relse ( agbp ) ;
return error ;
}