// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2000-2001,2004-2005 Silicon Graphics, Inc.
 * All Rights Reserved.
 */

#include "libxfs.h"
#include "type.h"
#include "fprint.h"
#include "faddr.h"
#include "field.h"
#include "inode.h"
#include "io.h"
#include "bit.h"
#include "bmap.h"
#include "output.h"
#include "init.h"

void
fa_agblock(
	void		*obj,
	int		bit,
	typnm_t		next)
{
	xfs_agblock_t	bno;

	if (cur_agno == NULLAGNUMBER) {
		dbprintf(_("no current allocation group, cannot set new addr\n"));
		return;
	}
	bno = (xfs_agblock_t)getbitval(obj, bit, bitsz(bno), BVUNSIGNED);
	if (bno == NULLAGBLOCK) {
		dbprintf(_("null block number, cannot set new addr\n"));
		return;
	}
	ASSERT(typtab[next].typnm == next);
	set_cur(&typtab[next], XFS_AGB_TO_DADDR(mp, cur_agno, bno), blkbb,
		DB_RING_ADD, NULL);
}

/*ARGSUSED*/
void
fa_agino(
	void		*obj,
	int		bit,
	typnm_t		next)
{
	xfs_agino_t	agino;

	if (cur_agno == NULLAGNUMBER) {
		dbprintf(_("no current allocation group, cannot set new addr\n"));
		return;
	}
	agino = (xfs_agino_t)getbitval(obj, bit, bitsz(agino), BVUNSIGNED);
	if (agino == NULLAGINO) {
		dbprintf(_("null inode number, cannot set new addr\n"));
		return;
	}
	set_cur_inode(XFS_AGINO_TO_INO(mp, cur_agno, agino));
}

/*ARGSUSED*/
void
fa_attrblock(
	void		*obj,
	int		bit,
	typnm_t		next)
{
	bmap_ext_t	bm;
	uint32_t	bno;
	xfs_fsblock_t	dfsbno;
	int		nex;

	bno = (uint32_t)getbitval(obj, bit, bitsz(bno), BVUNSIGNED);
	if (bno == 0) {
		dbprintf(_("null attribute block number, cannot set new addr\n"));
		return;
	}
	nex = 1;
	bmap(bno, 1, XFS_ATTR_FORK, &nex, &bm);
	if (nex == 0) {
		dbprintf(_("attribute block is unmapped\n"));
		return;
	}
	dfsbno = bm.startblock + (bno - bm.startoff);
	ASSERT(typtab[next].typnm == next);
	set_cur(&typtab[next], (int64_t)XFS_FSB_TO_DADDR(mp, dfsbno), blkbb,
		DB_RING_ADD, NULL);
}

void
fa_cfileoffa(
	void		*obj,
	int		bit,
	typnm_t		next)
{
	bmap_ext_t	bm;
	xfs_fileoff_t	bno;
	xfs_fsblock_t	dfsbno;
	int		nex;

	bno = (xfs_fileoff_t)getbitval(obj, bit, BMBT_STARTOFF_BITLEN,
		BVUNSIGNED);
	if (bno == NULLFILEOFF) {
		dbprintf(_("null block number, cannot set new addr\n"));
		return;
	}
	nex = 1;
	bmap(bno, 1, XFS_ATTR_FORK, &nex, &bm);
	if (nex == 0) {
		dbprintf(_("file block is unmapped\n"));
		return;
	}
	dfsbno = bm.startblock + (bno - bm.startoff);
	ASSERT(typtab[next].typnm == next);
	set_cur(&typtab[next], XFS_FSB_TO_DADDR(mp, dfsbno), blkbb, DB_RING_ADD,
		NULL);
}

void
fa_cfileoffd(
	void		*obj,
	int		bit,
	typnm_t		next)
{
	bbmap_t		bbmap;
	bmap_ext_t	*bmp;
	xfs_fileoff_t	bno;
	xfs_fsblock_t	dfsbno;
	int		nb;
	int		nex;

	bno = (xfs_fileoff_t)getbitval(obj, bit, BMBT_STARTOFF_BITLEN,
		BVUNSIGNED);
	if (bno == NULLFILEOFF) {
		dbprintf(_("null block number, cannot set new addr\n"));
		return;
	}
	nex = nb = next == TYP_DIR2 ? mp->m_dir_geo->fsbcount : 1;
	bmp = malloc(nb * sizeof(*bmp));
	bmap(bno, nb, XFS_DATA_FORK, &nex, bmp);
	if (nex == 0) {
		dbprintf(_("file block is unmapped\n"));
		free(bmp);
		return;
	}
	dfsbno = bmp->startblock + (bno - bmp->startoff);
	ASSERT(typtab[next].typnm == next);
	if (nex > 1)
		make_bbmap(&bbmap, nex, bmp);
	set_cur(&typtab[next], XFS_FSB_TO_DADDR(mp, dfsbno), nb * blkbb,
		DB_RING_ADD, nex > 1 ? &bbmap: NULL);
	free(bmp);
}

void
fa_cfsblock(
	void		*obj,
	int		bit,
	typnm_t		next)
{
	xfs_fsblock_t	bno;
	int		nb;

	bno = (xfs_fsblock_t)getbitval(obj, bit, BMBT_STARTBLOCK_BITLEN,
		BVUNSIGNED);
	if (bno == NULLFSBLOCK) {
		dbprintf(_("null block number, cannot set new addr\n"));
		return;
	}
	nb = next == TYP_DIR2 ? mp->m_dir_geo->fsbcount : 1;
	ASSERT(typtab[next].typnm == next);
	set_cur(&typtab[next], XFS_FSB_TO_DADDR(mp, bno), nb * blkbb,
		DB_RING_ADD, NULL);
}

void
fa_dfiloffa(
	void		*obj,
	int		bit,
	typnm_t		next)
{
	bmap_ext_t	bm;
	xfs_fileoff_t	bno;
	xfs_fsblock_t	dfsbno;
	int		nex;

	bno = (xfs_fileoff_t)getbitval(obj, bit, bitsz(bno), BVUNSIGNED);
	if (bno == NULLFILEOFF) {
		dbprintf(_("null block number, cannot set new addr\n"));
		return;
	}
	nex = 1;
	bmap(bno, 1, XFS_ATTR_FORK, &nex, &bm);
	if (nex == 0) {
		dbprintf(_("file block is unmapped\n"));
		return;
	}
	dfsbno = bm.startblock + (bno - bm.startoff);
	ASSERT(typtab[next].typnm == next);
	set_cur(&typtab[next], XFS_FSB_TO_DADDR(mp, dfsbno), blkbb, DB_RING_ADD,
		NULL);
}

void
fa_dfiloffd(
	void		*obj,
	int		bit,
	typnm_t		next)
{
	bbmap_t		bbmap;
	bmap_ext_t	*bmp;
	xfs_fileoff_t	bno;
	xfs_fsblock_t	dfsbno;
	int		nb;
	int		nex;

	bno = (xfs_fileoff_t)getbitval(obj, bit, bitsz(bno), BVUNSIGNED);
	if (bno == NULLFILEOFF) {
		dbprintf(_("null block number, cannot set new addr\n"));
		return;
	}
	nex = nb = next == TYP_DIR2 ? mp->m_dir_geo->fsbcount : 1;
	bmp = malloc(nb * sizeof(*bmp));
	bmap(bno, nb, XFS_DATA_FORK, &nex, bmp);
	if (nex == 0) {
		dbprintf(_("file block is unmapped\n"));
		free(bmp);
		return;
	}
	dfsbno = bmp->startblock + (bno - bmp->startoff);
	ASSERT(typtab[next].typnm == next);
	if (nex > 1)
		make_bbmap(&bbmap, nex, bmp);
	set_cur(&typtab[next], XFS_FSB_TO_DADDR(mp, dfsbno), nb * blkbb,
		DB_RING_ADD, nex > 1 ? &bbmap : NULL);
	free(bmp);
}

void
fa_dfsbno(
	void		*obj,
	int		bit,
	typnm_t		next)
{
	xfs_fsblock_t	bno;

	bno = (xfs_fsblock_t)getbitval(obj, bit, bitsz(bno), BVUNSIGNED);
	if (bno == NULLFSBLOCK) {
		dbprintf(_("null block number, cannot set new addr\n"));
		return;
	}
	ASSERT(typtab[next].typnm == next);
	set_cur(&typtab[next], XFS_FSB_TO_DADDR(mp, bno), blkbb, DB_RING_ADD,
		NULL);
}

/*ARGSUSED*/
void
fa_dirblock(
	void		*obj,
	int		bit,
	typnm_t		next)
{
	bbmap_t		bbmap;
	bmap_ext_t	*bmp;
	uint32_t	bno;
	xfs_fsblock_t	dfsbno;
	int		nex;

	bno = (uint32_t)getbitval(obj, bit, bitsz(bno), BVUNSIGNED);
	if (bno == 0) {
		dbprintf(_("null directory block number, cannot set new addr\n"));
		return;
	}
	nex = mp->m_dir_geo->fsbcount;
	bmp = malloc(nex * sizeof(*bmp));
	bmap(bno, mp->m_dir_geo->fsbcount, XFS_DATA_FORK, &nex, bmp);
	if (nex == 0) {
		dbprintf(_("directory block is unmapped\n"));
		free(bmp);
		return;
	}
	dfsbno = bmp->startblock + (bno - bmp->startoff);
	ASSERT(typtab[next].typnm == next);
	if (nex > 1)
		make_bbmap(&bbmap, nex, bmp);
	set_cur(&typtab[next], (int64_t)XFS_FSB_TO_DADDR(mp, dfsbno),
		XFS_FSB_TO_BB(mp, mp->m_dir_geo->fsbcount), DB_RING_ADD,
		nex > 1 ? &bbmap : NULL);
	free(bmp);
}

void
fa_drfsbno(
	void		*obj,
	int		bit,
	typnm_t		next)
{
	xfs_rfsblock_t	bno;

	bno = (xfs_rfsblock_t)getbitval(obj, bit, bitsz(bno), BVUNSIGNED);
	if (bno == NULLRFSBLOCK) {
		dbprintf(_("null block number, cannot set new addr\n"));
		return;
	}
	ASSERT(typtab[next].typnm == next);
	set_cur(&typtab[next], (int64_t)XFS_FSB_TO_BB(mp, bno), blkbb,
		DB_RING_ADD, NULL);
}

/*ARGSUSED*/
void
fa_drtbno(
	void	*obj,
	int	bit,
	typnm_t	next)
{
	xfs_rtblock_t	bno;

	bno = (xfs_rtblock_t)getbitval(obj, bit, bitsz(bno), BVUNSIGNED);
	if (bno == NULLRTBLOCK) {
		dbprintf(_("null block number, cannot set new addr\n"));
		return;
	}
	/* need set_cur to understand rt subvolume */
}

/*ARGSUSED*/
void
fa_ino(
	void		*obj,
	int		bit,
	typnm_t		next)
{
	xfs_ino_t	ino;

	ASSERT(next == TYP_INODE);
	ino = (xfs_ino_t)getbitval(obj, bit, bitsz(ino), BVUNSIGNED);
	if (ino == NULLFSINO) {
		dbprintf(_("null inode number, cannot set new addr\n"));
		return;
	}
	set_cur_inode(ino);
}

void
fa_ino4(
	void		*obj,
	int		bit,
	typnm_t		next)
{
	xfs_ino_t	ino;

	ASSERT(next == TYP_INODE);
	ino = (xfs_ino_t)getbitval(obj, bit, bitsz(XFS_INO32_SIZE), BVUNSIGNED);
	if (ino == NULLFSINO) {
		dbprintf(_("null inode number, cannot set new addr\n"));
		return;
	}
	set_cur_inode(ino);
}

void
fa_ino8(
	void		*obj,
	int		bit,
	typnm_t		next)
{
	xfs_ino_t	ino;

	ASSERT(next == TYP_INODE);
	ino = (xfs_ino_t)getbitval(obj, bit, bitsz(XFS_INO64_SIZE), BVUNSIGNED);
	if (ino == NULLFSINO) {
		dbprintf(_("null inode number, cannot set new addr\n"));
		return;
	}
	set_cur_inode(ino);
}
