/*
 * Copyright (c) 2000-2001,2004-2005 Silicon Graphics, Inc.
 * All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * 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.
 *
 * 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
 */

#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;
	xfs_dir2_ino4_t	ino4;

	ASSERT(next == TYP_INODE);
	ino = (xfs_ino_t)getbitval(obj, bit, bitsz(ino4), 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;
	xfs_dir2_ino8_t	ino8;

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