|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. | 
|  | * All Rights Reserved. | 
|  | */ | 
|  |  | 
|  | #include "libxfs.h" | 
|  | #include "command.h" | 
|  | #include "type.h" | 
|  | #include "fprint.h" | 
|  | #include "faddr.h" | 
|  | #include "field.h" | 
|  | #include "bmap.h" | 
|  | #include "io.h" | 
|  | #include "inode.h" | 
|  | #include "output.h" | 
|  | #include "init.h" | 
|  |  | 
|  | static int		bmap_f(int argc, char **argv); | 
|  | static int		bmap_one_extent(xfs_bmbt_rec_t *ep, | 
|  | xfs_fileoff_t *offp, xfs_fileoff_t eoff, | 
|  | xfs_extnum_t *idxp, bmap_ext_t *bep); | 
|  | static xfs_fsblock_t	select_child(xfs_fileoff_t off, xfs_bmbt_key_t *kp, | 
|  | xfs_bmbt_ptr_t *pp, int nrecs); | 
|  |  | 
|  | static const cmdinfo_t	bmap_cmd = | 
|  | { "bmap", NULL, bmap_f, 0, 3, 0, N_("[-ad] [block [len]]"), | 
|  | N_("show block map for current file"), NULL }; | 
|  |  | 
|  | void | 
|  | bmap( | 
|  | xfs_fileoff_t		offset, | 
|  | xfs_filblks_t		len, | 
|  | int			whichfork, | 
|  | xfs_extnum_t		*nexp, | 
|  | bmap_ext_t		*bep) | 
|  | { | 
|  | struct xfs_btree_block	*block; | 
|  | xfs_fsblock_t		bno; | 
|  | xfs_fileoff_t		curoffset; | 
|  | struct xfs_dinode	*dip; | 
|  | xfs_fileoff_t		eoffset; | 
|  | xfs_bmbt_rec_t		*ep; | 
|  | enum xfs_dinode_fmt	fmt; | 
|  | int			fsize; | 
|  | xfs_bmbt_key_t		*kp; | 
|  | xfs_extnum_t		n; | 
|  | int			nex; | 
|  | xfs_fsblock_t		nextbno; | 
|  | xfs_extnum_t		nextents; | 
|  | xfs_bmbt_ptr_t		*pp; | 
|  | xfs_bmdr_block_t	*rblock; | 
|  | typnm_t			typ; | 
|  | xfs_bmbt_rec_t		*xp; | 
|  |  | 
|  | push_cur(); | 
|  | set_cur_inode(iocur_top->ino); | 
|  | nex = *nexp; | 
|  | *nexp = 0; | 
|  | ASSERT(nex > 0); | 
|  | dip = iocur_top->data; | 
|  | n = 0; | 
|  | eoffset = offset + len - 1; | 
|  | curoffset = offset; | 
|  | fmt = (enum xfs_dinode_fmt)XFS_DFORK_FORMAT(dip, whichfork); | 
|  | typ = whichfork == XFS_DATA_FORK ? TYP_BMAPBTD : TYP_BMAPBTA; | 
|  | ASSERT(typtab[typ].typnm == typ); | 
|  | ASSERT(fmt == XFS_DINODE_FMT_LOCAL || fmt == XFS_DINODE_FMT_EXTENTS || | 
|  | fmt == XFS_DINODE_FMT_BTREE); | 
|  | if (fmt == XFS_DINODE_FMT_EXTENTS) { | 
|  | nextents = xfs_dfork_nextents(dip, whichfork); | 
|  | xp = (xfs_bmbt_rec_t *)XFS_DFORK_PTR(dip, whichfork); | 
|  | for (ep = xp; ep < &xp[nextents] && n < nex; ep++) { | 
|  | if (!bmap_one_extent(ep, &curoffset, eoffset, &n, bep)) | 
|  | break; | 
|  | } | 
|  | } else if (fmt == XFS_DINODE_FMT_BTREE) { | 
|  | push_cur(); | 
|  | rblock = (xfs_bmdr_block_t *)XFS_DFORK_PTR(dip, whichfork); | 
|  | fsize = XFS_DFORK_SIZE(dip, mp, whichfork); | 
|  | pp = XFS_BMDR_PTR_ADDR(rblock, 1, libxfs_bmdr_maxrecs(fsize, 0)); | 
|  | kp = XFS_BMDR_KEY_ADDR(rblock, 1); | 
|  | bno = select_child(curoffset, kp, pp, | 
|  | be16_to_cpu(rblock->bb_numrecs)); | 
|  | for (;;) { | 
|  | set_cur(&typtab[typ], XFS_FSB_TO_DADDR(mp, bno), | 
|  | blkbb, DB_RING_IGN, NULL); | 
|  | block = (struct xfs_btree_block *)iocur_top->data; | 
|  | if (be16_to_cpu(block->bb_level) == 0) | 
|  | break; | 
|  | pp = XFS_BMBT_PTR_ADDR(mp, block, 1, | 
|  | libxfs_bmbt_maxrecs(mp, mp->m_sb.sb_blocksize, 0)); | 
|  | kp = XFS_BMBT_KEY_ADDR(mp, block, 1); | 
|  | bno = select_child(curoffset, kp, pp, | 
|  | be16_to_cpu(block->bb_numrecs)); | 
|  | } | 
|  | for (;;) { | 
|  | nextbno = be64_to_cpu(block->bb_u.l.bb_rightsib); | 
|  | nextents = be16_to_cpu(block->bb_numrecs); | 
|  | xp = (xfs_bmbt_rec_t *) | 
|  | XFS_BMBT_REC_ADDR(mp, block, 1); | 
|  | for (ep = xp; ep < &xp[nextents] && n < nex; ep++) { | 
|  | if (!bmap_one_extent(ep, &curoffset, eoffset, | 
|  | &n, bep)) { | 
|  | nextbno = NULLFSBLOCK; | 
|  | break; | 
|  | } | 
|  | } | 
|  | bno = nextbno; | 
|  | if (bno == NULLFSBLOCK) | 
|  | break; | 
|  | set_cur(&typtab[typ], XFS_FSB_TO_DADDR(mp, bno), | 
|  | blkbb, DB_RING_IGN, NULL); | 
|  | block = (struct xfs_btree_block *)iocur_top->data; | 
|  | } | 
|  | pop_cur(); | 
|  | } | 
|  | pop_cur(); | 
|  | *nexp = n; | 
|  | } | 
|  |  | 
|  | static int | 
|  | bmap_f( | 
|  | int			argc, | 
|  | char			**argv) | 
|  | { | 
|  | int			afork = 0; | 
|  | bmap_ext_t		be; | 
|  | int			c; | 
|  | xfs_fileoff_t		co, cosave; | 
|  | int			dfork = 0; | 
|  | struct xfs_dinode	*dip; | 
|  | xfs_fileoff_t		eo; | 
|  | xfs_filblks_t		len; | 
|  | xfs_extnum_t		nex; | 
|  | char			*p; | 
|  | int			whichfork; | 
|  |  | 
|  | if (iocur_top->ino == NULLFSINO) { | 
|  | dbprintf(_("no current inode\n")); | 
|  | return 0; | 
|  | } | 
|  | optind = 0; | 
|  | if (argc) while ((c = getopt(argc, argv, "ad")) != EOF) { | 
|  | switch (c) { | 
|  | case 'a': | 
|  | afork = 1; | 
|  | break; | 
|  | case 'd': | 
|  | dfork = 1; | 
|  | break; | 
|  | default: | 
|  | dbprintf(_("bad option for bmap command\n")); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | if (afork + dfork == 0) { | 
|  | push_cur(); | 
|  | set_cur_inode(iocur_top->ino); | 
|  | dip = iocur_top->data; | 
|  | if (xfs_dfork_data_extents(dip)) | 
|  | dfork = 1; | 
|  | if (xfs_dfork_attr_extents(dip)) | 
|  | afork = 1; | 
|  | pop_cur(); | 
|  | } | 
|  | if (optind < argc) { | 
|  | co = (xfs_fileoff_t)strtoull(argv[optind], &p, 0); | 
|  | if (*p != '\0') { | 
|  | dbprintf(_("bad block number for bmap %s\n"), | 
|  | argv[optind]); | 
|  | return 0; | 
|  | } | 
|  | optind++; | 
|  | if (optind < argc) { | 
|  | len = (xfs_filblks_t)strtoull(argv[optind], &p, 0); | 
|  | if (*p != '\0') { | 
|  | dbprintf(_("bad len for bmap %s\n"), argv[optind]); | 
|  | return 0; | 
|  | } | 
|  | eo = co + len - 1; | 
|  | } else | 
|  | eo = co; | 
|  | } else { | 
|  | co = 0; | 
|  | eo = -1; | 
|  | } | 
|  | cosave = co; | 
|  | for (whichfork = XFS_DATA_FORK; | 
|  | whichfork <= XFS_ATTR_FORK; | 
|  | whichfork++) { | 
|  | if (whichfork == XFS_DATA_FORK && !dfork) | 
|  | continue; | 
|  | if (whichfork == XFS_ATTR_FORK && !afork) | 
|  | continue; | 
|  | for (;;) { | 
|  | nex = 1; | 
|  | bmap(co, eo - co + 1, whichfork, &nex, &be); | 
|  | if (nex == 0) | 
|  | break; | 
|  | dbprintf(_("%s offset %lld startblock %llu (%u/%u) count " | 
|  | "%llu flag %u\n"), | 
|  | whichfork == XFS_DATA_FORK ? _("data") : _("attr"), | 
|  | be.startoff, be.startblock, | 
|  | XFS_FSB_TO_AGNO(mp, be.startblock), | 
|  | XFS_FSB_TO_AGBNO(mp, be.startblock), | 
|  | be.blockcount, be.flag); | 
|  | co = be.startoff + be.blockcount; | 
|  | } | 
|  | co = cosave; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | bmap_init(void) | 
|  | { | 
|  | add_command(&bmap_cmd); | 
|  | } | 
|  |  | 
|  | static int | 
|  | bmap_one_extent( | 
|  | xfs_bmbt_rec_t		*ep, | 
|  | xfs_fileoff_t		*offp, | 
|  | xfs_fileoff_t		eoff, | 
|  | xfs_extnum_t		*idxp, | 
|  | bmap_ext_t		*bep) | 
|  | { | 
|  | xfs_filblks_t		c; | 
|  | xfs_fileoff_t		curoffset; | 
|  | int			f; | 
|  | xfs_extnum_t		idx; | 
|  | xfs_fileoff_t		o; | 
|  | xfs_fsblock_t		s; | 
|  |  | 
|  | convert_extent(ep, &o, &s, &c, &f); | 
|  | curoffset = *offp; | 
|  | idx = *idxp; | 
|  | if (o + c <= curoffset) | 
|  | return 1; | 
|  | if (o > eoff) | 
|  | return 0; | 
|  | if (o < curoffset) { | 
|  | c -= curoffset - o; | 
|  | s += curoffset - o; | 
|  | o = curoffset; | 
|  | } | 
|  | if (o + c - 1 > eoff) | 
|  | c -= (o + c - 1) - eoff; | 
|  | bep[idx].startoff = o; | 
|  | bep[idx].startblock = s; | 
|  | bep[idx].blockcount = c; | 
|  | bep[idx].flag = f; | 
|  | *idxp = idx + 1; | 
|  | *offp = o + c; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | void | 
|  | convert_extent( | 
|  | xfs_bmbt_rec_t		*rp, | 
|  | xfs_fileoff_t		*op, | 
|  | xfs_fsblock_t		*sp, | 
|  | xfs_filblks_t		*cp, | 
|  | int			*fp) | 
|  | { | 
|  | struct xfs_bmbt_irec	irec; | 
|  |  | 
|  | libxfs_bmbt_disk_get_all(rp, &irec); | 
|  | *fp = irec.br_state == XFS_EXT_UNWRITTEN; | 
|  | *op = irec.br_startoff; | 
|  | *sp = irec.br_startblock; | 
|  | *cp = irec.br_blockcount; | 
|  | } | 
|  |  | 
|  | void | 
|  | make_bbmap( | 
|  | bbmap_t		*bbmap, | 
|  | xfs_extnum_t	nex, | 
|  | bmap_ext_t	*bmp) | 
|  | { | 
|  | xfs_extnum_t	i; | 
|  |  | 
|  | for (i = 0; i < nex; i++) { | 
|  | bbmap->b[i].bm_bn = XFS_FSB_TO_DADDR(mp, bmp[i].startblock); | 
|  | bbmap->b[i].bm_len = XFS_FSB_TO_BB(mp, bmp[i].blockcount); | 
|  | } | 
|  | bbmap->nmaps = nex; | 
|  | } | 
|  |  | 
|  | static xfs_fsblock_t | 
|  | select_child( | 
|  | xfs_fileoff_t	off, | 
|  | xfs_bmbt_key_t	*kp, | 
|  | xfs_bmbt_ptr_t	*pp, | 
|  | int		nrecs) | 
|  | { | 
|  | int		i; | 
|  |  | 
|  | for (i = 0; i < nrecs; i++) { | 
|  | if (be64_to_cpu(kp[i].br_startoff) == off) | 
|  | return be64_to_cpu(pp[i]); | 
|  | if (be64_to_cpu(kp[i].br_startoff) > off) { | 
|  | if (i == 0) | 
|  | return be64_to_cpu(pp[i]); | 
|  | else | 
|  | return be64_to_cpu(pp[i - 1]); | 
|  | } | 
|  | } | 
|  | return be64_to_cpu(pp[nrecs - 1]); | 
|  | } |