| // 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, |
| int *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, |
| int *nexp, |
| bmap_ext_t *bep) |
| { |
| struct xfs_btree_block *block; |
| xfs_fsblock_t bno; |
| xfs_fileoff_t curoffset; |
| xfs_dinode_t *dip; |
| xfs_fileoff_t eoffset; |
| xfs_bmbt_rec_t *ep; |
| enum xfs_dinode_fmt fmt; |
| int fsize; |
| xfs_bmbt_key_t *kp; |
| int n; |
| int nex; |
| xfs_fsblock_t nextbno; |
| int 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; |
| xfs_dinode_t *dip; |
| xfs_fileoff_t eo; |
| xfs_filblks_t len; |
| int 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 (be32_to_cpu(dip->di_nextents)) |
| dfork = 1; |
| if (be16_to_cpu(dip->di_anextents)) |
| 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, |
| int *idxp, |
| bmap_ext_t *bep) |
| { |
| xfs_filblks_t c; |
| xfs_fileoff_t curoffset; |
| int f; |
| int 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, |
| int nex, |
| bmap_ext_t *bmp) |
| { |
| int 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]); |
| } |