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"
2013-10-23 03:36:05 +04:00
# include "xfs_shared.h"
2013-10-23 03:51:50 +04:00
# include "xfs_format.h"
2013-10-23 03:50:10 +04:00
# include "xfs_log_format.h"
# include "xfs_trans_resv.h"
2005-04-17 02:20:36 +04:00
# include "xfs_mount.h"
# include "xfs_inode.h"
2013-10-23 03:51:50 +04:00
# include "xfs_btree.h"
2005-04-17 02:20:36 +04:00
# include "xfs_ialloc.h"
2013-10-23 03:51:50 +04:00
# include "xfs_ialloc_btree.h"
2005-04-17 02:20:36 +04:00
# include "xfs_itable.h"
# include "xfs_error.h"
2010-06-24 05:52:50 +04:00
# include "xfs_trace.h"
2012-10-08 14:56:11 +04:00
# include "xfs_icache.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 ) & &
2013-06-28 02:25:04 +04:00
xfs_is_quota_inode ( & mp - > m_sb , ino ) ) ) ;
2006-09-28 05:06:15 +04:00
}
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 ) )
2014-06-25 08:58:08 +04:00
return - EINVAL ;
2010-06-23 12:11:11 +04:00
buf = kmem_alloc ( sizeof ( * buf ) , KM_SLEEP | KM_MAYFAIL ) ;
if ( ! buf )
2014-06-25 08:58:08 +04:00
return - 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 ) ;
2014-07-24 05:33:28 +04:00
if ( error )
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 ) ;
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 ) )
2014-06-25 08:58:08 +04:00
return - ENOMEM ;
2007-07-11 05:10:19 +04:00
if ( copy_to_user ( ubuffer , buffer , sizeof ( * buffer ) ) )
2014-06-25 08:58:08 +04:00
return - EFAULT ;
2008-11-26 06:20:12 +03:00
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
}
2014-07-24 12:41:18 +04:00
/*
* Loop over all clusters in a chunk for a given incore inode allocation btree
* record . Do a readahead if there are any allocated inodes in that cluster .
*/
STATIC void
xfs_bulkstat_ichunk_ra (
struct xfs_mount * mp ,
xfs_agnumber_t agno ,
struct xfs_inobt_rec_incore * irec )
{
xfs_agblock_t agbno ;
struct blk_plug plug ;
int blks_per_cluster ;
int inodes_per_cluster ;
int i ; /* inode chunk index */
agbno = XFS_AGINO_TO_AGBNO ( mp , irec - > ir_startino ) ;
blks_per_cluster = xfs_icluster_size_fsb ( mp ) ;
inodes_per_cluster = blks_per_cluster < < mp - > m_sb . sb_inopblog ;
blk_start_plug ( & plug ) ;
for ( i = 0 ; i < XFS_INODES_PER_CHUNK ;
i + = inodes_per_cluster , agbno + = blks_per_cluster ) {
if ( xfs_inobt_maskn ( i , inodes_per_cluster ) & ~ irec - > ir_free ) {
xfs_btree_reada_bufs ( mp , agno , agbno , blks_per_cluster ,
& xfs_inode_buf_ops ) ;
}
}
blk_finish_plug ( & plug ) ;
}
2014-07-24 12:42:21 +04:00
/*
* Lookup the inode chunk that the given inode lives in and then get the record
* if we found the chunk . If the inode was not the last in the chunk and there
* are some left allocated , update the data for the pointed - to record as well as
* return the count of grabbed inodes .
*/
STATIC int
xfs_bulkstat_grab_ichunk (
struct xfs_btree_cur * cur , /* btree cursor */
xfs_agino_t agino , /* starting inode of chunk */
int * icount , /* return # of inodes grabbed */
struct xfs_inobt_rec_incore * irec ) /* btree record */
{
int idx ; /* index into inode chunk */
int stat ;
int error = 0 ;
/* Lookup the inode chunk that this inode lives in */
error = xfs_inobt_lookup ( cur , agino , XFS_LOOKUP_LE , & stat ) ;
if ( error )
return error ;
if ( ! stat ) {
* icount = 0 ;
return error ;
}
/* Get the record, should always work */
error = xfs_inobt_get_rec ( cur , irec , & stat ) ;
if ( error )
return error ;
2015-02-23 14:39:13 +03:00
XFS_WANT_CORRUPTED_RETURN ( cur - > bc_mp , stat = = 1 ) ;
2014-07-24 12:42:21 +04:00
/* Check if the record contains the inode in request */
2014-11-07 00:31:15 +03:00
if ( irec - > ir_startino + XFS_INODES_PER_CHUNK < = agino ) {
* icount = 0 ;
return 0 ;
}
2014-07-24 12:42:21 +04:00
idx = agino - irec - > ir_startino + 1 ;
if ( idx < XFS_INODES_PER_CHUNK & &
( xfs_inobt_maskn ( idx , XFS_INODES_PER_CHUNK - idx ) & ~ irec - > ir_free ) ) {
int i ;
/* We got a right chunk with some left inodes allocated at it.
* Grab the chunk record . Mark all the uninteresting inodes
* free - - because they ' re before our start point .
*/
for ( i = 0 ; i < idx ; i + + ) {
if ( XFS_INOBT_MASK ( i ) & ~ irec - > ir_free )
irec - > ir_freecount + + ;
}
irec - > ir_free | = xfs_inobt_maskn ( 0 , idx ) ;
* icount = XFS_INODES_PER_CHUNK - irec - > ir_freecount ;
}
return 0 ;
}
2007-11-23 08:30:32 +03:00
# define XFS_BULKSTAT_UBLEFT(ubleft) ((ubleft) >= statstruct_size)
2014-11-07 00:30:30 +03:00
struct xfs_bulkstat_agichunk {
char __user * * ac_ubuffer ; /* pointer into user's buffer */
int ac_ubleft ; /* bytes left in user's buffer */
int ac_ubelem ; /* spaces used in user's buffer */
} ;
2014-08-04 05:22:31 +04:00
/*
* Process inodes in chunk with a pointer to a formatter function
* that will iget the inode and fill in the appropriate structure .
*/
2014-11-07 00:30:30 +03:00
static int
2014-08-04 05:22:31 +04:00
xfs_bulkstat_ag_ichunk (
struct xfs_mount * mp ,
xfs_agnumber_t agno ,
struct xfs_inobt_rec_incore * irbp ,
bulkstat_one_pf formatter ,
size_t statstruct_size ,
2014-11-07 00:30:30 +03:00
struct xfs_bulkstat_agichunk * acp ,
2014-11-07 00:33:52 +03:00
xfs_agino_t * last_agino )
2014-08-04 05:22:31 +04:00
{
char __user * * ubufp = acp - > ac_ubuffer ;
2014-11-07 00:30:58 +03:00
int chunkidx ;
2014-08-04 05:22:31 +04:00
int error = 0 ;
2014-11-07 00:33:52 +03:00
xfs_agino_t agino = irbp - > ir_startino ;
2014-08-04 05:22:31 +04:00
2014-11-07 00:30:58 +03:00
for ( chunkidx = 0 ; chunkidx < XFS_INODES_PER_CHUNK ;
chunkidx + + , agino + + ) {
int fmterror ;
2014-08-04 05:22:31 +04:00
int ubused ;
2014-11-07 00:33:52 +03:00
/* inode won't fit in buffer, we are done */
if ( acp - > ac_ubleft < statstruct_size )
break ;
2014-08-04 05:22:31 +04:00
/* Skip if this inode is free */
2014-11-07 00:33:52 +03:00
if ( XFS_INOBT_MASK ( chunkidx ) & irbp - > ir_free )
2014-08-04 05:22:31 +04:00
continue ;
/* Get the inode and fill in a single buffer */
ubused = statstruct_size ;
2014-11-07 00:33:52 +03:00
error = formatter ( mp , XFS_AGINO_TO_INO ( mp , agno , agino ) ,
* ubufp , acp - > ac_ubleft , & ubused , & fmterror ) ;
2014-11-07 00:30:58 +03:00
if ( fmterror = = BULKSTAT_RV_GIVEUP | |
( error & & error ! = - ENOENT & & error ! = - EINVAL ) ) {
acp - > ac_ubleft = 0 ;
2014-08-04 05:22:31 +04:00
ASSERT ( error ) ;
break ;
}
2014-11-07 00:30:58 +03:00
/* be careful not to leak error if at end of chunk */
if ( fmterror = = BULKSTAT_RV_NOTHING | | error ) {
error = 0 ;
continue ;
}
* ubufp + = ubused ;
acp - > ac_ubleft - = ubused ;
acp - > ac_ubelem + + ;
}
2014-08-04 05:22:31 +04:00
2014-11-07 00:33:52 +03:00
/*
* Post - update * last_agino . At this point , agino will always point one
* inode past the last inode we processed successfully . Hence we
* substract that inode when setting the * last_agino cursor so that we
* return the correct cookie to userspace . On the next bulkstat call ,
* the inode under the lastino cookie will be skipped as we have already
* processed it here .
*/
* last_agino = agino - 1 ;
2014-08-04 05:22:31 +04:00
return error ;
}
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_buf_t * agbp ; /* agi header buffer */
xfs_agino_t agino ; /* inode # in allocation group */
xfs_agnumber_t agno ; /* allocation group number */
xfs_btree_cur_t * cur ; /* btree cursor for ialloc btree */
2006-09-28 05:04:43 +04:00
size_t irbsize ; /* size of irec buffer in bytes */
2006-09-28 05:02:03 +04:00
xfs_inobt_rec_incore_t * irbuf ; /* start of irec buffer */
2005-04-17 02:20:36 +04:00
int nirbuf ; /* size of irbuf */
int ubcount ; /* size of user's buffer */
2014-11-07 00:30:30 +03:00
struct xfs_bulkstat_agichunk ac ;
2014-11-07 00:31:13 +03:00
int error = 0 ;
2005-04-17 02:20:36 +04:00
/*
* Get the last inode value , see if there ' s nothing to do .
*/
2014-11-07 00:33:52 +03:00
agno = XFS_INO_TO_AGNO ( mp , * lastinop ) ;
agino = XFS_INO_TO_AGINO ( mp , * lastinop ) ;
2005-04-17 02:20:36 +04:00
if ( agno > = mp - > m_sb . sb_agcount | |
2014-11-07 00:33:52 +03:00
* lastinop ! = XFS_AGINO_TO_INO ( mp , agno , agino ) ) {
2005-04-17 02:20:36 +04:00
* done = 1 ;
* ubcountp = 0 ;
return 0 ;
}
2014-07-24 12:40:26 +04:00
2005-04-17 02:20:36 +04:00
ubcount = * ubcountp ; /* statstruct's */
2014-11-07 00:30:30 +03:00
ac . ac_ubuffer = & ubuffer ;
ac . ac_ubleft = ubcount * statstruct_size ; /* bytes */ ;
ac . ac_ubelem = 0 ;
* ubcountp = 0 ;
2005-04-17 02:20:36 +04:00
* done = 0 ;
2014-11-07 00:30:30 +03:00
2010-01-21 00:55:30 +03:00
irbuf = kmem_zalloc_greedy ( & irbsize , PAGE_SIZE , PAGE_SIZE * 4 ) ;
if ( ! irbuf )
2014-06-25 08:58:08 +04:00
return - ENOMEM ;
2010-01-21 00:55:30 +03:00
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 .
*/
2014-11-07 00:31:13 +03:00
while ( agno < mp - > m_sb . sb_agcount ) {
struct xfs_inobt_rec_incore * irbp = irbuf ;
struct xfs_inobt_rec_incore * irbufend = irbuf + nirbuf ;
bool end_of_ag = false ;
int icount = 0 ;
int stat ;
2005-04-17 02:20:36 +04:00
error = xfs_ialloc_read_agi ( mp , NULL , agno , & agbp ) ;
2014-07-24 12:40:43 +04:00
if ( error )
break ;
2005-04-17 02:20:36 +04:00
/*
* Allocate and initialize a btree cursor for ialloc btree .
*/
2014-04-24 10:00:50 +04:00
cur = xfs_inobt_init_cursor ( mp , NULL , agbp , agno ,
XFS_BTNUM_INO ) ;
2005-04-17 02:20:36 +04:00
if ( agino > 0 ) {
/*
2014-07-24 12:42:21 +04:00
* In the middle of an allocation group , we need to get
* the remainder of the chunk we ' re in .
2005-04-17 02:20:36 +04:00
*/
2014-07-24 12:42:21 +04:00
struct xfs_inobt_rec_incore r ;
error = xfs_bulkstat_grab_ichunk ( cur , agino , & icount , & r ) ;
if ( error )
2014-10-29 00:22:18 +03:00
goto del_cursor ;
2014-07-24 12:42:21 +04:00
if ( icount ) {
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 + + ;
}
2014-07-24 12:42:21 +04:00
/* Increment to the next record */
2014-11-07 00:29:57 +03:00
error = xfs_btree_increment ( cur , 0 , & stat ) ;
2005-04-17 02:20:36 +04:00
} else {
2014-07-24 12:42:21 +04:00
/* Start of ag. Lookup the first inode chunk */
2014-11-07 00:29:57 +03:00
error = xfs_inobt_lookup ( cur , 0 , XFS_LOOKUP_GE , & stat ) ;
2005-04-17 02:20:36 +04:00
}
2014-11-07 00:29:57 +03:00
if ( error | | stat = = 0 ) {
2014-11-07 00:31:13 +03:00
end_of_ag = true ;
2014-10-29 00:22:18 +03:00
goto del_cursor ;
2014-11-07 00:29:57 +03:00
}
2014-07-24 12:40:43 +04:00
2005-04-17 02:20:36 +04:00
/*
* Loop through inode btree records in this ag ,
* until we run out of inodes or space in the buffer .
*/
while ( irbp < irbufend & & icount < ubcount ) {
2014-07-24 12:40:43 +04:00
struct xfs_inobt_rec_incore r ;
2009-09-01 03:56:58 +04:00
2014-11-07 00:29:57 +03:00
error = xfs_inobt_get_rec ( cur , & r , & stat ) ;
if ( error | | stat = = 0 ) {
2014-11-07 00:31:13 +03:00
end_of_ag = true ;
2014-10-29 00:22:18 +03:00
goto del_cursor ;
2005-04-17 02:20:36 +04:00
}
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 ) {
2014-07-24 12:41:18 +04:00
xfs_bulkstat_ichunk_ra ( mp , agno , & r ) ;
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
}
2014-11-07 00:29:57 +03:00
error = xfs_btree_increment ( cur , 0 , & stat ) ;
if ( error | | stat = = 0 ) {
2014-11-07 00:31:13 +03:00
end_of_ag = true ;
2014-10-30 02:34:52 +03:00
goto del_cursor ;
}
2007-11-23 08:30:32 +03:00
cond_resched ( ) ;
2005-04-17 02:20:36 +04:00
}
2014-10-29 00:22:18 +03:00
2005-04-17 02:20:36 +04:00
/*
2014-10-29 00:22:18 +03:00
* Drop the btree buffers and the agi buffer as we can ' t hold any
* of the locks these represent when calling iget . If there is a
* pending error , then we are done .
2005-04-17 02:20:36 +04:00
*/
2014-10-29 00:22:18 +03:00
del_cursor :
2005-04-17 02:20:36 +04:00
xfs_btree_del_cursor ( cur , XFS_BTREE_NOERROR ) ;
xfs_buf_relse ( agbp ) ;
2014-10-29 00:22:18 +03:00
if ( error )
break ;
2005-04-17 02:20:36 +04:00
/*
2014-11-07 00:33:52 +03:00
* Now format all the good inodes into the user ' s buffer . The
* call to xfs_bulkstat_ag_ichunk ( ) sets up the agino pointer
* for the next loop iteration .
2005-04-17 02:20:36 +04:00
*/
irbufend = irbp ;
for ( irbp = irbuf ;
2014-11-07 00:31:13 +03:00
irbp < irbufend & & ac . ac_ubleft > = statstruct_size ;
2014-11-07 00:30:30 +03:00
irbp + + ) {
2014-08-04 05:22:31 +04:00
error = xfs_bulkstat_ag_ichunk ( mp , agno , irbp ,
2014-11-07 00:30:30 +03:00
formatter , statstruct_size , & ac ,
2014-11-07 00:33:52 +03:00
& agino ) ;
2014-08-04 05:22:31 +04:00
if ( error )
2014-11-07 00:31:15 +03:00
break ;
2014-08-04 05:22:31 +04:00
2007-11-23 08:30:32 +03:00
cond_resched ( ) ;
2005-04-17 02:20:36 +04:00
}
2014-11-07 00:30:30 +03:00
2014-11-07 00:31:15 +03:00
/*
* If we ' ve run out of space or had a formatting error , we
* are now done
*/
if ( ac . ac_ubleft < statstruct_size | | error )
2005-04-17 02:20:36 +04:00
break ;
2014-11-07 00:31:13 +03:00
if ( end_of_ag ) {
agno + + ;
agino = 0 ;
2014-11-07 00:33:52 +03:00
}
2005-04-17 02:20:36 +04:00
}
/*
* Done , we ' re either out of filesystem or space to put the data .
*/
2013-09-02 14:53:00 +04:00
kmem_free ( irbuf ) ;
2014-11-07 00:30:30 +03:00
* ubcountp = ac . ac_ubelem ;
2014-11-07 00:31:15 +03:00
2007-11-23 08:30:32 +03:00
/*
2014-11-07 00:31:15 +03:00
* We found some inodes , so clear the error status and return them .
* The lastino pointer will point directly at the inode that triggered
* any error that occurred , so on the next call the error will be
* triggered again and propagated to userspace as there will be no
* formatted inodes in the buffer .
2007-11-23 08:30:32 +03:00
*/
2014-11-07 00:30:30 +03:00
if ( ac . ac_ubelem )
2014-11-07 00:31:15 +03:00
error = 0 ;
2014-11-07 00:33:52 +03:00
/*
* If we ran out of filesystem , lastino will point off the end of
* the filesystem so the next call will return immediately .
*/
* lastinop = XFS_AGINO_TO_INO ( mp , agno , agino ) ;
if ( agno > = mp - > m_sb . sb_agcount )
2005-04-17 02:20:36 +04:00
* done = 1 ;
2014-11-07 00:31:15 +03:00
return error ;
2005-04-17 02:20:36 +04:00
}
2007-07-11 05:10:19 +04:00
int
xfs_inumbers_fmt (
void __user * ubuffer , /* buffer to write to */
2014-07-24 06:11:47 +04:00
const struct xfs_inogrp * buffer , /* buffer to read from */
2007-07-11 05:10:19 +04:00
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 (
2014-07-24 06:11:47 +04:00
struct xfs_mount * mp , /* mount point for filesystem */
xfs_ino_t * lastino , /* last inode returned */
int * count , /* size of buffer/count returned */
void __user * ubuffer , /* buffer with inode descriptions */
inumbers_fmt_pf formatter )
2005-04-17 02:20:36 +04:00
{
2014-07-24 06:11:47 +04:00
xfs_agnumber_t agno = XFS_INO_TO_AGNO ( mp , * lastino ) ;
xfs_agino_t agino = XFS_INO_TO_AGINO ( mp , * lastino ) ;
struct xfs_btree_cur * cur = NULL ;
2014-07-24 06:18:47 +04:00
struct xfs_buf * agbp = NULL ;
2014-07-24 06:11:47 +04:00
struct xfs_inogrp * buffer ;
int bcount ;
int left = * count ;
int bufidx = 0 ;
int error = 0 ;
2005-04-17 02:20:36 +04:00
* count = 0 ;
2014-07-24 06:11:47 +04:00
if ( agno > = mp - > m_sb . sb_agcount | |
* lastino ! = XFS_AGINO_TO_INO ( mp , agno , agino ) )
return error ;
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 ) ;
2014-07-24 06:18:47 +04:00
do {
2014-07-24 06:11:47 +04:00
struct xfs_inobt_rec_incore r ;
int stat ;
2014-07-24 06:18:47 +04:00
if ( ! agbp ) {
2005-04-17 02:20:36 +04:00
error = xfs_ialloc_read_agi ( mp , NULL , agno , & agbp ) ;
2014-07-24 06:18:47 +04:00
if ( error )
break ;
2014-04-24 10:00:50 +04:00
cur = xfs_inobt_init_cursor ( mp , NULL , agbp , agno ,
XFS_BTNUM_INO ) ;
2009-09-01 03:58:21 +04:00
error = xfs_inobt_lookup ( cur , agino , XFS_LOOKUP_GE ,
2014-07-24 06:11:47 +04:00
& stat ) ;
2014-07-24 06:18:47 +04:00
if ( error )
break ;
if ( ! stat )
goto next_ag ;
2005-04-17 02:20:36 +04:00
}
2014-07-24 06:18:47 +04:00
2014-07-24 06:11:47 +04:00
error = xfs_inobt_get_rec ( cur , & r , & stat ) ;
2014-07-24 06:18:47 +04:00
if ( error )
break ;
if ( ! stat )
goto next_ag ;
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 ;
2014-07-24 06:18:47 +04:00
if ( + + bufidx = = bcount ) {
long written ;
2014-07-24 06:11:47 +04:00
error = formatter ( ubuffer , buffer , bufidx , & written ) ;
if ( error )
2005-04-17 02:20:36 +04:00
break ;
2007-07-11 05:10:19 +04:00
ubuffer + = written ;
2005-04-17 02:20:36 +04:00
* count + = bufidx ;
bufidx = 0 ;
}
2014-07-24 06:18:47 +04:00
if ( ! - - left )
break ;
error = xfs_btree_increment ( cur , 0 , & stat ) ;
if ( error )
break ;
if ( stat )
continue ;
next_ag :
xfs_btree_del_cursor ( cur , XFS_BTREE_ERROR ) ;
cur = NULL ;
xfs_buf_relse ( agbp ) ;
agbp = NULL ;
agino = 0 ;
2014-10-13 03:21:53 +04:00
agno + + ;
} while ( agno < mp - > m_sb . sb_agcount ) ;
2014-07-24 06:18:47 +04:00
2005-04-17 02:20:36 +04:00
if ( ! error ) {
if ( bufidx ) {
2014-07-24 06:18:47 +04:00
long written ;
2014-07-24 06:11:47 +04:00
error = formatter ( ubuffer , buffer , bufidx , & written ) ;
if ( ! error )
2005-04-17 02:20:36 +04:00
* count + = bufidx ;
}
* lastino = XFS_AGINO_TO_INO ( mp , agno , agino ) ;
}
2014-07-24 06:18:47 +04:00
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 ) ;
2014-07-24 06:18:47 +04:00
2005-04-17 02:20:36 +04:00
return error ;
}