|  | // SPDX-License-Identifier: GPL-2.0+ | 
|  | /* | 
|  | * Copyright (C) 2016 Oracle.  All Rights Reserved. | 
|  | * Author: Darrick J. Wong <darrick.wong@oracle.com> | 
|  | */ | 
|  | #include "libxfs.h" | 
|  | #include "command.h" | 
|  | #include "fsmap.h" | 
|  | #include "output.h" | 
|  | #include "init.h" | 
|  |  | 
|  | struct fsmap_info { | 
|  | unsigned long long	nr; | 
|  | xfs_agnumber_t		agno; | 
|  | }; | 
|  |  | 
|  | static int | 
|  | fsmap_fn( | 
|  | struct xfs_btree_cur		*cur, | 
|  | const struct xfs_rmap_irec	*rec, | 
|  | void				*priv) | 
|  | { | 
|  | struct fsmap_info		*info = priv; | 
|  |  | 
|  | dbprintf(_("%llu: %u/%u len %u owner %lld offset %llu bmbt %d attrfork %d extflag %d\n"), | 
|  | info->nr, info->agno, rec->rm_startblock, | 
|  | rec->rm_blockcount, rec->rm_owner, rec->rm_offset, | 
|  | !!(rec->rm_flags & XFS_RMAP_BMBT_BLOCK), | 
|  | !!(rec->rm_flags & XFS_RMAP_ATTR_FORK), | 
|  | !!(rec->rm_flags & XFS_RMAP_UNWRITTEN)); | 
|  | info->nr++; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | fsmap( | 
|  | xfs_fsblock_t		start_fsb, | 
|  | xfs_fsblock_t		end_fsb) | 
|  | { | 
|  | struct fsmap_info	info; | 
|  | xfs_agnumber_t		start_ag; | 
|  | xfs_agnumber_t		end_ag; | 
|  | xfs_daddr_t		eofs; | 
|  | struct xfs_rmap_irec	low = {0}; | 
|  | struct xfs_rmap_irec	high = {0}; | 
|  | struct xfs_btree_cur	*bt_cur; | 
|  | struct xfs_buf		*agbp; | 
|  | struct xfs_perag	*pag; | 
|  | int			error; | 
|  |  | 
|  | eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks); | 
|  | if (XFS_FSB_TO_DADDR(mp, end_fsb) >= eofs) | 
|  | end_fsb = XFS_DADDR_TO_FSB(mp, eofs - 1); | 
|  |  | 
|  | low.rm_startblock = XFS_FSB_TO_AGBNO(mp, start_fsb); | 
|  | high.rm_startblock = -1U; | 
|  | high.rm_owner = ULLONG_MAX; | 
|  | high.rm_offset = ULLONG_MAX; | 
|  | high.rm_flags = XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK | XFS_RMAP_UNWRITTEN; | 
|  |  | 
|  | start_ag = XFS_FSB_TO_AGNO(mp, start_fsb); | 
|  | end_ag = XFS_FSB_TO_AGNO(mp, end_fsb); | 
|  |  | 
|  | info.nr = 0; | 
|  | for_each_perag_range(mp, start_ag, end_ag, pag) { | 
|  | if (pag->pag_agno == end_ag) | 
|  | high.rm_startblock = XFS_FSB_TO_AGBNO(mp, end_fsb); | 
|  |  | 
|  | error = -libxfs_alloc_read_agf(pag, NULL, 0, &agbp); | 
|  | if (error) { | 
|  | libxfs_perag_put(pag); | 
|  | dbprintf(_("Error %d while reading AGF.\n"), error); | 
|  | return; | 
|  | } | 
|  |  | 
|  | bt_cur = libxfs_rmapbt_init_cursor(mp, NULL, agbp, pag); | 
|  | if (!bt_cur) { | 
|  | libxfs_buf_relse(agbp); | 
|  | libxfs_perag_put(pag); | 
|  | dbprintf(_("Not enough memory.\n")); | 
|  | return; | 
|  | } | 
|  |  | 
|  | info.agno = pag->pag_agno; | 
|  | error = -libxfs_rmap_query_range(bt_cur, &low, &high, | 
|  | fsmap_fn, &info); | 
|  | if (error) { | 
|  | libxfs_btree_del_cursor(bt_cur, XFS_BTREE_ERROR); | 
|  | libxfs_buf_relse(agbp); | 
|  | libxfs_perag_put(pag); | 
|  | dbprintf(_("Error %d while querying fsmap btree.\n"), | 
|  | error); | 
|  | return; | 
|  | } | 
|  |  | 
|  | libxfs_btree_del_cursor(bt_cur, XFS_BTREE_NOERROR); | 
|  | libxfs_buf_relse(agbp); | 
|  |  | 
|  | if (pag->pag_agno == start_ag) | 
|  | low.rm_startblock = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int | 
|  | fsmap_f( | 
|  | int			argc, | 
|  | char			**argv) | 
|  | { | 
|  | char			*p; | 
|  | int			c; | 
|  | xfs_fsblock_t		start_fsb = 0; | 
|  | xfs_fsblock_t		end_fsb = NULLFSBLOCK; | 
|  |  | 
|  | if (!xfs_has_rmapbt(mp)) { | 
|  | dbprintf(_("Filesystem does not support reverse mapping btree.\n")); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | while ((c = getopt(argc, argv, "")) != EOF) { | 
|  | switch (c) { | 
|  | default: | 
|  | dbprintf(_("Bad option for fsmap command.\n")); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (argc > optind) { | 
|  | start_fsb = strtoull(argv[optind], &p, 0); | 
|  | if (*p != '\0' || start_fsb >= mp->m_sb.sb_dblocks) { | 
|  | dbprintf(_("Bad fsmap start_fsb %s.\n"), argv[optind]); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (argc > optind + 1) { | 
|  | end_fsb = strtoull(argv[optind + 1], &p, 0); | 
|  | if (*p != '\0') { | 
|  | dbprintf(_("Bad fsmap end_fsb %s.\n"), argv[optind + 1]); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | fsmap(start_fsb, end_fsb); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const cmdinfo_t	fsmap_cmd = | 
|  | { "fsmap", NULL, fsmap_f, 0, 2, 0, | 
|  | N_("[start_fsb] [end_fsb]"), | 
|  | N_("display reverse mapping(s)"), NULL }; | 
|  |  | 
|  | void | 
|  | fsmap_init(void) | 
|  | { | 
|  | add_command(&fsmap_cmd); | 
|  | } |