162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. 462306a36Sopenharmony_ci * All Rights Reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "xfs.h" 762306a36Sopenharmony_ci#include "xfs_fs.h" 862306a36Sopenharmony_ci#include "xfs_shared.h" 962306a36Sopenharmony_ci#include "xfs_format.h" 1062306a36Sopenharmony_ci#include "xfs_log_format.h" 1162306a36Sopenharmony_ci#include "xfs_trans_resv.h" 1262306a36Sopenharmony_ci#include "xfs_mount.h" 1362306a36Sopenharmony_ci#include "xfs_inode.h" 1462306a36Sopenharmony_ci#include "xfs_btree.h" 1562306a36Sopenharmony_ci#include "xfs_ialloc.h" 1662306a36Sopenharmony_ci#include "xfs_ialloc_btree.h" 1762306a36Sopenharmony_ci#include "xfs_iwalk.h" 1862306a36Sopenharmony_ci#include "xfs_itable.h" 1962306a36Sopenharmony_ci#include "xfs_error.h" 2062306a36Sopenharmony_ci#include "xfs_icache.h" 2162306a36Sopenharmony_ci#include "xfs_health.h" 2262306a36Sopenharmony_ci#include "xfs_trans.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * Bulk Stat 2662306a36Sopenharmony_ci * ========= 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * Use the inode walking functions to fill out struct xfs_bulkstat for every 2962306a36Sopenharmony_ci * allocated inode, then pass the stat information to some externally provided 3062306a36Sopenharmony_ci * iteration function. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct xfs_bstat_chunk { 3462306a36Sopenharmony_ci bulkstat_one_fmt_pf formatter; 3562306a36Sopenharmony_ci struct xfs_ibulk *breq; 3662306a36Sopenharmony_ci struct xfs_bulkstat *buf; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * Fill out the bulkstat info for a single inode and report it somewhere. 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * bc->breq->lastino is effectively the inode cursor as we walk through the 4362306a36Sopenharmony_ci * filesystem. Therefore, we update it any time we need to move the cursor 4462306a36Sopenharmony_ci * forward, regardless of whether or not we're sending any bstat information 4562306a36Sopenharmony_ci * back to userspace. If the inode is internal metadata or, has been freed 4662306a36Sopenharmony_ci * out from under us, we just simply keep going. 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * However, if any other type of error happens we want to stop right where we 4962306a36Sopenharmony_ci * are so that userspace will call back with exact number of the bad inode and 5062306a36Sopenharmony_ci * we can send back an error code. 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * Note that if the formatter tells us there's no space left in the buffer we 5362306a36Sopenharmony_ci * move the cursor forward and abort the walk. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ciSTATIC int 5662306a36Sopenharmony_cixfs_bulkstat_one_int( 5762306a36Sopenharmony_ci struct xfs_mount *mp, 5862306a36Sopenharmony_ci struct mnt_idmap *idmap, 5962306a36Sopenharmony_ci struct xfs_trans *tp, 6062306a36Sopenharmony_ci xfs_ino_t ino, 6162306a36Sopenharmony_ci struct xfs_bstat_chunk *bc) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct user_namespace *sb_userns = mp->m_super->s_user_ns; 6462306a36Sopenharmony_ci struct xfs_inode *ip; /* incore inode pointer */ 6562306a36Sopenharmony_ci struct inode *inode; 6662306a36Sopenharmony_ci struct xfs_bulkstat *buf = bc->buf; 6762306a36Sopenharmony_ci xfs_extnum_t nextents; 6862306a36Sopenharmony_ci int error = -EINVAL; 6962306a36Sopenharmony_ci vfsuid_t vfsuid; 7062306a36Sopenharmony_ci vfsgid_t vfsgid; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (xfs_internal_inum(mp, ino)) 7362306a36Sopenharmony_ci goto out_advance; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci error = xfs_iget(mp, tp, ino, 7662306a36Sopenharmony_ci (XFS_IGET_DONTCACHE | XFS_IGET_UNTRUSTED), 7762306a36Sopenharmony_ci XFS_ILOCK_SHARED, &ip); 7862306a36Sopenharmony_ci if (error == -ENOENT || error == -EINVAL) 7962306a36Sopenharmony_ci goto out_advance; 8062306a36Sopenharmony_ci if (error) 8162306a36Sopenharmony_ci goto out; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* Reload the incore unlinked list to avoid failure in inodegc. */ 8462306a36Sopenharmony_ci if (xfs_inode_unlinked_incomplete(ip)) { 8562306a36Sopenharmony_ci error = xfs_inode_reload_unlinked_bucket(tp, ip); 8662306a36Sopenharmony_ci if (error) { 8762306a36Sopenharmony_ci xfs_iunlock(ip, XFS_ILOCK_SHARED); 8862306a36Sopenharmony_ci xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); 8962306a36Sopenharmony_ci xfs_irele(ip); 9062306a36Sopenharmony_ci return error; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci ASSERT(ip != NULL); 9562306a36Sopenharmony_ci ASSERT(ip->i_imap.im_blkno != 0); 9662306a36Sopenharmony_ci inode = VFS_I(ip); 9762306a36Sopenharmony_ci vfsuid = i_uid_into_vfsuid(idmap, inode); 9862306a36Sopenharmony_ci vfsgid = i_gid_into_vfsgid(idmap, inode); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* xfs_iget returns the following without needing 10162306a36Sopenharmony_ci * further change. 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ci buf->bs_projectid = ip->i_projid; 10462306a36Sopenharmony_ci buf->bs_ino = ino; 10562306a36Sopenharmony_ci buf->bs_uid = from_kuid(sb_userns, vfsuid_into_kuid(vfsuid)); 10662306a36Sopenharmony_ci buf->bs_gid = from_kgid(sb_userns, vfsgid_into_kgid(vfsgid)); 10762306a36Sopenharmony_ci buf->bs_size = ip->i_disk_size; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci buf->bs_nlink = inode->i_nlink; 11062306a36Sopenharmony_ci buf->bs_atime = inode->i_atime.tv_sec; 11162306a36Sopenharmony_ci buf->bs_atime_nsec = inode->i_atime.tv_nsec; 11262306a36Sopenharmony_ci buf->bs_mtime = inode->i_mtime.tv_sec; 11362306a36Sopenharmony_ci buf->bs_mtime_nsec = inode->i_mtime.tv_nsec; 11462306a36Sopenharmony_ci buf->bs_ctime = inode_get_ctime(inode).tv_sec; 11562306a36Sopenharmony_ci buf->bs_ctime_nsec = inode_get_ctime(inode).tv_nsec; 11662306a36Sopenharmony_ci buf->bs_gen = inode->i_generation; 11762306a36Sopenharmony_ci buf->bs_mode = inode->i_mode; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci buf->bs_xflags = xfs_ip2xflags(ip); 12062306a36Sopenharmony_ci buf->bs_extsize_blks = ip->i_extsize; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci nextents = xfs_ifork_nextents(&ip->i_df); 12362306a36Sopenharmony_ci if (!(bc->breq->flags & XFS_IBULK_NREXT64)) 12462306a36Sopenharmony_ci buf->bs_extents = min(nextents, XFS_MAX_EXTCNT_DATA_FORK_SMALL); 12562306a36Sopenharmony_ci else 12662306a36Sopenharmony_ci buf->bs_extents64 = nextents; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci xfs_bulkstat_health(ip, buf); 12962306a36Sopenharmony_ci buf->bs_aextents = xfs_ifork_nextents(&ip->i_af); 13062306a36Sopenharmony_ci buf->bs_forkoff = xfs_inode_fork_boff(ip); 13162306a36Sopenharmony_ci buf->bs_version = XFS_BULKSTAT_VERSION_V5; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (xfs_has_v3inodes(mp)) { 13462306a36Sopenharmony_ci buf->bs_btime = ip->i_crtime.tv_sec; 13562306a36Sopenharmony_ci buf->bs_btime_nsec = ip->i_crtime.tv_nsec; 13662306a36Sopenharmony_ci if (ip->i_diflags2 & XFS_DIFLAG2_COWEXTSIZE) 13762306a36Sopenharmony_ci buf->bs_cowextsize_blks = ip->i_cowextsize; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci switch (ip->i_df.if_format) { 14162306a36Sopenharmony_ci case XFS_DINODE_FMT_DEV: 14262306a36Sopenharmony_ci buf->bs_rdev = sysv_encode_dev(inode->i_rdev); 14362306a36Sopenharmony_ci buf->bs_blksize = BLKDEV_IOSIZE; 14462306a36Sopenharmony_ci buf->bs_blocks = 0; 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci case XFS_DINODE_FMT_LOCAL: 14762306a36Sopenharmony_ci buf->bs_rdev = 0; 14862306a36Sopenharmony_ci buf->bs_blksize = mp->m_sb.sb_blocksize; 14962306a36Sopenharmony_ci buf->bs_blocks = 0; 15062306a36Sopenharmony_ci break; 15162306a36Sopenharmony_ci case XFS_DINODE_FMT_EXTENTS: 15262306a36Sopenharmony_ci case XFS_DINODE_FMT_BTREE: 15362306a36Sopenharmony_ci buf->bs_rdev = 0; 15462306a36Sopenharmony_ci buf->bs_blksize = mp->m_sb.sb_blocksize; 15562306a36Sopenharmony_ci buf->bs_blocks = ip->i_nblocks + ip->i_delayed_blks; 15662306a36Sopenharmony_ci break; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci xfs_iunlock(ip, XFS_ILOCK_SHARED); 15962306a36Sopenharmony_ci xfs_irele(ip); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci error = bc->formatter(bc->breq, buf); 16262306a36Sopenharmony_ci if (error == -ECANCELED) 16362306a36Sopenharmony_ci goto out_advance; 16462306a36Sopenharmony_ci if (error) 16562306a36Sopenharmony_ci goto out; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ciout_advance: 16862306a36Sopenharmony_ci /* 16962306a36Sopenharmony_ci * Advance the cursor to the inode that comes after the one we just 17062306a36Sopenharmony_ci * looked at. We want the caller to move along if the bulkstat 17162306a36Sopenharmony_ci * information was copied successfully; if we tried to grab the inode 17262306a36Sopenharmony_ci * but it's no longer allocated; or if it's internal metadata. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci bc->breq->startino = ino + 1; 17562306a36Sopenharmony_ciout: 17662306a36Sopenharmony_ci return error; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* Bulkstat a single inode. */ 18062306a36Sopenharmony_ciint 18162306a36Sopenharmony_cixfs_bulkstat_one( 18262306a36Sopenharmony_ci struct xfs_ibulk *breq, 18362306a36Sopenharmony_ci bulkstat_one_fmt_pf formatter) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct xfs_bstat_chunk bc = { 18662306a36Sopenharmony_ci .formatter = formatter, 18762306a36Sopenharmony_ci .breq = breq, 18862306a36Sopenharmony_ci }; 18962306a36Sopenharmony_ci struct xfs_trans *tp; 19062306a36Sopenharmony_ci int error; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (breq->idmap != &nop_mnt_idmap) { 19362306a36Sopenharmony_ci xfs_warn_ratelimited(breq->mp, 19462306a36Sopenharmony_ci "bulkstat not supported inside of idmapped mounts."); 19562306a36Sopenharmony_ci return -EINVAL; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci ASSERT(breq->icount == 1); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci bc.buf = kmem_zalloc(sizeof(struct xfs_bulkstat), 20162306a36Sopenharmony_ci KM_MAYFAIL); 20262306a36Sopenharmony_ci if (!bc.buf) 20362306a36Sopenharmony_ci return -ENOMEM; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* 20662306a36Sopenharmony_ci * Grab an empty transaction so that we can use its recursive buffer 20762306a36Sopenharmony_ci * locking abilities to detect cycles in the inobt without deadlocking. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci error = xfs_trans_alloc_empty(breq->mp, &tp); 21062306a36Sopenharmony_ci if (error) 21162306a36Sopenharmony_ci goto out; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci error = xfs_bulkstat_one_int(breq->mp, breq->idmap, tp, 21462306a36Sopenharmony_ci breq->startino, &bc); 21562306a36Sopenharmony_ci xfs_trans_cancel(tp); 21662306a36Sopenharmony_ciout: 21762306a36Sopenharmony_ci kmem_free(bc.buf); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* 22062306a36Sopenharmony_ci * If we reported one inode to userspace then we abort because we hit 22162306a36Sopenharmony_ci * the end of the buffer. Don't leak that back to userspace. 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_ci if (error == -ECANCELED) 22462306a36Sopenharmony_ci error = 0; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return error; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int 23062306a36Sopenharmony_cixfs_bulkstat_iwalk( 23162306a36Sopenharmony_ci struct xfs_mount *mp, 23262306a36Sopenharmony_ci struct xfs_trans *tp, 23362306a36Sopenharmony_ci xfs_ino_t ino, 23462306a36Sopenharmony_ci void *data) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct xfs_bstat_chunk *bc = data; 23762306a36Sopenharmony_ci int error; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci error = xfs_bulkstat_one_int(mp, bc->breq->idmap, tp, ino, data); 24062306a36Sopenharmony_ci /* bulkstat just skips over missing inodes */ 24162306a36Sopenharmony_ci if (error == -ENOENT || error == -EINVAL) 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci return error; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci/* 24762306a36Sopenharmony_ci * Check the incoming lastino parameter. 24862306a36Sopenharmony_ci * 24962306a36Sopenharmony_ci * We allow any inode value that could map to physical space inside the 25062306a36Sopenharmony_ci * filesystem because if there are no inodes there, bulkstat moves on to the 25162306a36Sopenharmony_ci * next chunk. In other words, the magic agino value of zero takes us to the 25262306a36Sopenharmony_ci * first chunk in the AG, and an agino value past the end of the AG takes us to 25362306a36Sopenharmony_ci * the first chunk in the next AG. 25462306a36Sopenharmony_ci * 25562306a36Sopenharmony_ci * Therefore we can end early if the requested inode is beyond the end of the 25662306a36Sopenharmony_ci * filesystem or doesn't map properly. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_cistatic inline bool 25962306a36Sopenharmony_cixfs_bulkstat_already_done( 26062306a36Sopenharmony_ci struct xfs_mount *mp, 26162306a36Sopenharmony_ci xfs_ino_t startino) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, startino); 26462306a36Sopenharmony_ci xfs_agino_t agino = XFS_INO_TO_AGINO(mp, startino); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return agno >= mp->m_sb.sb_agcount || 26762306a36Sopenharmony_ci startino != XFS_AGINO_TO_INO(mp, agno, agino); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* Return stat information in bulk (by-inode) for the filesystem. */ 27162306a36Sopenharmony_ciint 27262306a36Sopenharmony_cixfs_bulkstat( 27362306a36Sopenharmony_ci struct xfs_ibulk *breq, 27462306a36Sopenharmony_ci bulkstat_one_fmt_pf formatter) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct xfs_bstat_chunk bc = { 27762306a36Sopenharmony_ci .formatter = formatter, 27862306a36Sopenharmony_ci .breq = breq, 27962306a36Sopenharmony_ci }; 28062306a36Sopenharmony_ci struct xfs_trans *tp; 28162306a36Sopenharmony_ci unsigned int iwalk_flags = 0; 28262306a36Sopenharmony_ci int error; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (breq->idmap != &nop_mnt_idmap) { 28562306a36Sopenharmony_ci xfs_warn_ratelimited(breq->mp, 28662306a36Sopenharmony_ci "bulkstat not supported inside of idmapped mounts."); 28762306a36Sopenharmony_ci return -EINVAL; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci if (xfs_bulkstat_already_done(breq->mp, breq->startino)) 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci bc.buf = kmem_zalloc(sizeof(struct xfs_bulkstat), 29362306a36Sopenharmony_ci KM_MAYFAIL); 29462306a36Sopenharmony_ci if (!bc.buf) 29562306a36Sopenharmony_ci return -ENOMEM; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* 29862306a36Sopenharmony_ci * Grab an empty transaction so that we can use its recursive buffer 29962306a36Sopenharmony_ci * locking abilities to detect cycles in the inobt without deadlocking. 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_ci error = xfs_trans_alloc_empty(breq->mp, &tp); 30262306a36Sopenharmony_ci if (error) 30362306a36Sopenharmony_ci goto out; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (breq->flags & XFS_IBULK_SAME_AG) 30662306a36Sopenharmony_ci iwalk_flags |= XFS_IWALK_SAME_AG; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci error = xfs_iwalk(breq->mp, tp, breq->startino, iwalk_flags, 30962306a36Sopenharmony_ci xfs_bulkstat_iwalk, breq->icount, &bc); 31062306a36Sopenharmony_ci xfs_trans_cancel(tp); 31162306a36Sopenharmony_ciout: 31262306a36Sopenharmony_ci kmem_free(bc.buf); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* 31562306a36Sopenharmony_ci * We found some inodes, so clear the error status and return them. 31662306a36Sopenharmony_ci * The lastino pointer will point directly at the inode that triggered 31762306a36Sopenharmony_ci * any error that occurred, so on the next call the error will be 31862306a36Sopenharmony_ci * triggered again and propagated to userspace as there will be no 31962306a36Sopenharmony_ci * formatted inodes in the buffer. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ci if (breq->ocount > 0) 32262306a36Sopenharmony_ci error = 0; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci return error; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci/* Convert bulkstat (v5) to bstat (v1). */ 32862306a36Sopenharmony_civoid 32962306a36Sopenharmony_cixfs_bulkstat_to_bstat( 33062306a36Sopenharmony_ci struct xfs_mount *mp, 33162306a36Sopenharmony_ci struct xfs_bstat *bs1, 33262306a36Sopenharmony_ci const struct xfs_bulkstat *bstat) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci /* memset is needed here because of padding holes in the structure. */ 33562306a36Sopenharmony_ci memset(bs1, 0, sizeof(struct xfs_bstat)); 33662306a36Sopenharmony_ci bs1->bs_ino = bstat->bs_ino; 33762306a36Sopenharmony_ci bs1->bs_mode = bstat->bs_mode; 33862306a36Sopenharmony_ci bs1->bs_nlink = bstat->bs_nlink; 33962306a36Sopenharmony_ci bs1->bs_uid = bstat->bs_uid; 34062306a36Sopenharmony_ci bs1->bs_gid = bstat->bs_gid; 34162306a36Sopenharmony_ci bs1->bs_rdev = bstat->bs_rdev; 34262306a36Sopenharmony_ci bs1->bs_blksize = bstat->bs_blksize; 34362306a36Sopenharmony_ci bs1->bs_size = bstat->bs_size; 34462306a36Sopenharmony_ci bs1->bs_atime.tv_sec = bstat->bs_atime; 34562306a36Sopenharmony_ci bs1->bs_mtime.tv_sec = bstat->bs_mtime; 34662306a36Sopenharmony_ci bs1->bs_ctime.tv_sec = bstat->bs_ctime; 34762306a36Sopenharmony_ci bs1->bs_atime.tv_nsec = bstat->bs_atime_nsec; 34862306a36Sopenharmony_ci bs1->bs_mtime.tv_nsec = bstat->bs_mtime_nsec; 34962306a36Sopenharmony_ci bs1->bs_ctime.tv_nsec = bstat->bs_ctime_nsec; 35062306a36Sopenharmony_ci bs1->bs_blocks = bstat->bs_blocks; 35162306a36Sopenharmony_ci bs1->bs_xflags = bstat->bs_xflags; 35262306a36Sopenharmony_ci bs1->bs_extsize = XFS_FSB_TO_B(mp, bstat->bs_extsize_blks); 35362306a36Sopenharmony_ci bs1->bs_extents = bstat->bs_extents; 35462306a36Sopenharmony_ci bs1->bs_gen = bstat->bs_gen; 35562306a36Sopenharmony_ci bs1->bs_projid_lo = bstat->bs_projectid & 0xFFFF; 35662306a36Sopenharmony_ci bs1->bs_forkoff = bstat->bs_forkoff; 35762306a36Sopenharmony_ci bs1->bs_projid_hi = bstat->bs_projectid >> 16; 35862306a36Sopenharmony_ci bs1->bs_sick = bstat->bs_sick; 35962306a36Sopenharmony_ci bs1->bs_checked = bstat->bs_checked; 36062306a36Sopenharmony_ci bs1->bs_cowextsize = XFS_FSB_TO_B(mp, bstat->bs_cowextsize_blks); 36162306a36Sopenharmony_ci bs1->bs_dmevmask = 0; 36262306a36Sopenharmony_ci bs1->bs_dmstate = 0; 36362306a36Sopenharmony_ci bs1->bs_aextents = bstat->bs_aextents; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistruct xfs_inumbers_chunk { 36762306a36Sopenharmony_ci inumbers_fmt_pf formatter; 36862306a36Sopenharmony_ci struct xfs_ibulk *breq; 36962306a36Sopenharmony_ci}; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci/* 37262306a36Sopenharmony_ci * INUMBERS 37362306a36Sopenharmony_ci * ======== 37462306a36Sopenharmony_ci * This is how we export inode btree records to userspace, so that XFS tools 37562306a36Sopenharmony_ci * can figure out where inodes are allocated. 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci/* 37962306a36Sopenharmony_ci * Format the inode group structure and report it somewhere. 38062306a36Sopenharmony_ci * 38162306a36Sopenharmony_ci * Similar to xfs_bulkstat_one_int, lastino is the inode cursor as we walk 38262306a36Sopenharmony_ci * through the filesystem so we move it forward unless there was a runtime 38362306a36Sopenharmony_ci * error. If the formatter tells us the buffer is now full we also move the 38462306a36Sopenharmony_ci * cursor forward and abort the walk. 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_ciSTATIC int 38762306a36Sopenharmony_cixfs_inumbers_walk( 38862306a36Sopenharmony_ci struct xfs_mount *mp, 38962306a36Sopenharmony_ci struct xfs_trans *tp, 39062306a36Sopenharmony_ci xfs_agnumber_t agno, 39162306a36Sopenharmony_ci const struct xfs_inobt_rec_incore *irec, 39262306a36Sopenharmony_ci void *data) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct xfs_inumbers inogrp = { 39562306a36Sopenharmony_ci .xi_startino = XFS_AGINO_TO_INO(mp, agno, irec->ir_startino), 39662306a36Sopenharmony_ci .xi_alloccount = irec->ir_count - irec->ir_freecount, 39762306a36Sopenharmony_ci .xi_allocmask = ~irec->ir_free, 39862306a36Sopenharmony_ci .xi_version = XFS_INUMBERS_VERSION_V5, 39962306a36Sopenharmony_ci }; 40062306a36Sopenharmony_ci struct xfs_inumbers_chunk *ic = data; 40162306a36Sopenharmony_ci int error; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci error = ic->formatter(ic->breq, &inogrp); 40462306a36Sopenharmony_ci if (error && error != -ECANCELED) 40562306a36Sopenharmony_ci return error; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci ic->breq->startino = XFS_AGINO_TO_INO(mp, agno, irec->ir_startino) + 40862306a36Sopenharmony_ci XFS_INODES_PER_CHUNK; 40962306a36Sopenharmony_ci return error; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci/* 41362306a36Sopenharmony_ci * Return inode number table for the filesystem. 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_ciint 41662306a36Sopenharmony_cixfs_inumbers( 41762306a36Sopenharmony_ci struct xfs_ibulk *breq, 41862306a36Sopenharmony_ci inumbers_fmt_pf formatter) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct xfs_inumbers_chunk ic = { 42162306a36Sopenharmony_ci .formatter = formatter, 42262306a36Sopenharmony_ci .breq = breq, 42362306a36Sopenharmony_ci }; 42462306a36Sopenharmony_ci struct xfs_trans *tp; 42562306a36Sopenharmony_ci int error = 0; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (xfs_bulkstat_already_done(breq->mp, breq->startino)) 42862306a36Sopenharmony_ci return 0; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* 43162306a36Sopenharmony_ci * Grab an empty transaction so that we can use its recursive buffer 43262306a36Sopenharmony_ci * locking abilities to detect cycles in the inobt without deadlocking. 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_ci error = xfs_trans_alloc_empty(breq->mp, &tp); 43562306a36Sopenharmony_ci if (error) 43662306a36Sopenharmony_ci goto out; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci error = xfs_inobt_walk(breq->mp, tp, breq->startino, breq->flags, 43962306a36Sopenharmony_ci xfs_inumbers_walk, breq->icount, &ic); 44062306a36Sopenharmony_ci xfs_trans_cancel(tp); 44162306a36Sopenharmony_ciout: 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* 44462306a36Sopenharmony_ci * We found some inode groups, so clear the error status and return 44562306a36Sopenharmony_ci * them. The lastino pointer will point directly at the inode that 44662306a36Sopenharmony_ci * triggered any error that occurred, so on the next call the error 44762306a36Sopenharmony_ci * will be triggered again and propagated to userspace as there will be 44862306a36Sopenharmony_ci * no formatted inode groups in the buffer. 44962306a36Sopenharmony_ci */ 45062306a36Sopenharmony_ci if (breq->ocount > 0) 45162306a36Sopenharmony_ci error = 0; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci return error; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci/* Convert an inumbers (v5) struct to a inogrp (v1) struct. */ 45762306a36Sopenharmony_civoid 45862306a36Sopenharmony_cixfs_inumbers_to_inogrp( 45962306a36Sopenharmony_ci struct xfs_inogrp *ig1, 46062306a36Sopenharmony_ci const struct xfs_inumbers *ig) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci /* memset is needed here because of padding holes in the structure. */ 46362306a36Sopenharmony_ci memset(ig1, 0, sizeof(struct xfs_inogrp)); 46462306a36Sopenharmony_ci ig1->xi_startino = ig->xi_startino; 46562306a36Sopenharmony_ci ig1->xi_alloccount = ig->xi_alloccount; 46662306a36Sopenharmony_ci ig1->xi_allocmask = ig->xi_allocmask; 46762306a36Sopenharmony_ci} 468