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