xfs_da_btree.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,203 行 · 第 1/5 页
C
2,203 行
* For version 1 directories, adjust the file size if it changed. */ if (w == XFS_DATA_FORK && XFS_DIR_IS_V1(mp)) { ASSERT(mapi == 1); if ((error = xfs_bmap_last_offset(tp, dp, &bno, w))) return error; size = XFS_FSB_TO_B(mp, bno); if (size != dp->i_d.di_size) { dp->i_d.di_size = size; xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); } } return 0;}/* * Ick. We need to always be able to remove a btree block, even * if there's no space reservation because the filesystem is full. * This is called if xfs_bunmapi on a btree block fails due to ENOSPC. * It swaps the target block with the last block in the file. The * last block in the file can always be removed since it can't cause * a bmap btree split to do that. */STATIC intxfs_da_swap_lastblock(xfs_da_args_t *args, xfs_dablk_t *dead_blknop, xfs_dabuf_t **dead_bufp){ xfs_dablk_t dead_blkno, last_blkno, sib_blkno, par_blkno; xfs_dabuf_t *dead_buf, *last_buf, *sib_buf, *par_buf; xfs_fileoff_t lastoff; xfs_inode_t *ip; xfs_trans_t *tp; xfs_mount_t *mp; int error, w, entno, level, dead_level; xfs_da_blkinfo_t *dead_info, *sib_info; xfs_da_intnode_t *par_node, *dead_node; xfs_dir_leafblock_t *dead_leaf; xfs_dir2_leaf_t *dead_leaf2; xfs_dahash_t dead_hash; dead_buf = *dead_bufp; dead_blkno = *dead_blknop; tp = args->trans; ip = args->dp; w = args->whichfork; ASSERT(w == XFS_DATA_FORK); mp = ip->i_mount; if (XFS_DIR_IS_V2(mp)) { lastoff = mp->m_dirfreeblk; error = xfs_bmap_last_before(tp, ip, &lastoff, w); } else error = xfs_bmap_last_offset(tp, ip, &lastoff, w); if (error) return error; if (unlikely(lastoff == 0)) { XFS_ERROR_REPORT("xfs_da_swap_lastblock(1)", XFS_ERRLEVEL_LOW, mp); return XFS_ERROR(EFSCORRUPTED); } /* * Read the last block in the btree space. */ last_blkno = (xfs_dablk_t)lastoff - mp->m_dirblkfsbs; if ((error = xfs_da_read_buf(tp, ip, last_blkno, -1, &last_buf, w))) return error; /* * Copy the last block into the dead buffer and log it. */ memcpy(dead_buf->data, last_buf->data, mp->m_dirblksize); xfs_da_log_buf(tp, dead_buf, 0, mp->m_dirblksize - 1); dead_info = dead_buf->data; /* * Get values from the moved block. */ if (INT_GET(dead_info->magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC) { ASSERT(XFS_DIR_IS_V1(mp)); dead_leaf = (xfs_dir_leafblock_t *)dead_info; dead_level = 0; dead_hash = INT_GET(dead_leaf->entries[INT_GET(dead_leaf->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT); } else if (INT_GET(dead_info->magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC) { ASSERT(XFS_DIR_IS_V2(mp)); dead_leaf2 = (xfs_dir2_leaf_t *)dead_info; dead_level = 0; dead_hash = INT_GET(dead_leaf2->ents[INT_GET(dead_leaf2->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT); } else { ASSERT(INT_GET(dead_info->magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC); dead_node = (xfs_da_intnode_t *)dead_info; dead_level = INT_GET(dead_node->hdr.level, ARCH_CONVERT); dead_hash = INT_GET(dead_node->btree[INT_GET(dead_node->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT); } sib_buf = par_buf = NULL; /* * If the moved block has a left sibling, fix up the pointers. */ if ((sib_blkno = INT_GET(dead_info->back, ARCH_CONVERT))) { if ((error = xfs_da_read_buf(tp, ip, sib_blkno, -1, &sib_buf, w))) goto done; sib_info = sib_buf->data; if (unlikely( INT_GET(sib_info->forw, ARCH_CONVERT) != last_blkno || INT_GET(sib_info->magic, ARCH_CONVERT) != INT_GET(dead_info->magic, ARCH_CONVERT))) { XFS_ERROR_REPORT("xfs_da_swap_lastblock(2)", XFS_ERRLEVEL_LOW, mp); error = XFS_ERROR(EFSCORRUPTED); goto done; } INT_SET(sib_info->forw, ARCH_CONVERT, dead_blkno); xfs_da_log_buf(tp, sib_buf, XFS_DA_LOGRANGE(sib_info, &sib_info->forw, sizeof(sib_info->forw))); xfs_da_buf_done(sib_buf); sib_buf = NULL; } /* * If the moved block has a right sibling, fix up the pointers. */ if ((sib_blkno = INT_GET(dead_info->forw, ARCH_CONVERT))) { if ((error = xfs_da_read_buf(tp, ip, sib_blkno, -1, &sib_buf, w))) goto done; sib_info = sib_buf->data; if (unlikely( INT_GET(sib_info->back, ARCH_CONVERT) != last_blkno || INT_GET(sib_info->magic, ARCH_CONVERT) != INT_GET(dead_info->magic, ARCH_CONVERT))) { XFS_ERROR_REPORT("xfs_da_swap_lastblock(3)", XFS_ERRLEVEL_LOW, mp); error = XFS_ERROR(EFSCORRUPTED); goto done; } INT_SET(sib_info->back, ARCH_CONVERT, dead_blkno); xfs_da_log_buf(tp, sib_buf, XFS_DA_LOGRANGE(sib_info, &sib_info->back, sizeof(sib_info->back))); xfs_da_buf_done(sib_buf); sib_buf = NULL; } par_blkno = XFS_DIR_IS_V1(mp) ? 0 : mp->m_dirleafblk; level = -1; /* * Walk down the tree looking for the parent of the moved block. */ for (;;) { if ((error = xfs_da_read_buf(tp, ip, par_blkno, -1, &par_buf, w))) goto done; par_node = par_buf->data; if (unlikely( INT_GET(par_node->hdr.info.magic, ARCH_CONVERT) != XFS_DA_NODE_MAGIC || (level >= 0 && level != INT_GET(par_node->hdr.level, ARCH_CONVERT) + 1))) { XFS_ERROR_REPORT("xfs_da_swap_lastblock(4)", XFS_ERRLEVEL_LOW, mp); error = XFS_ERROR(EFSCORRUPTED); goto done; } level = INT_GET(par_node->hdr.level, ARCH_CONVERT); for (entno = 0; entno < INT_GET(par_node->hdr.count, ARCH_CONVERT) && INT_GET(par_node->btree[entno].hashval, ARCH_CONVERT) < dead_hash; entno++) continue; if (unlikely(entno == INT_GET(par_node->hdr.count, ARCH_CONVERT))) { XFS_ERROR_REPORT("xfs_da_swap_lastblock(5)", XFS_ERRLEVEL_LOW, mp); error = XFS_ERROR(EFSCORRUPTED); goto done; } par_blkno = INT_GET(par_node->btree[entno].before, ARCH_CONVERT); if (level == dead_level + 1) break; xfs_da_brelse(tp, par_buf); par_buf = NULL; } /* * We're in the right parent block. * Look for the right entry. */ for (;;) { for (; entno < INT_GET(par_node->hdr.count, ARCH_CONVERT) && INT_GET(par_node->btree[entno].before, ARCH_CONVERT) != last_blkno; entno++) continue; if (entno < INT_GET(par_node->hdr.count, ARCH_CONVERT)) break; par_blkno = INT_GET(par_node->hdr.info.forw, ARCH_CONVERT); xfs_da_brelse(tp, par_buf); par_buf = NULL; if (unlikely(par_blkno == 0)) { XFS_ERROR_REPORT("xfs_da_swap_lastblock(6)", XFS_ERRLEVEL_LOW, mp); error = XFS_ERROR(EFSCORRUPTED); goto done; } if ((error = xfs_da_read_buf(tp, ip, par_blkno, -1, &par_buf, w))) goto done; par_node = par_buf->data; if (unlikely( INT_GET(par_node->hdr.level, ARCH_CONVERT) != level || INT_GET(par_node->hdr.info.magic, ARCH_CONVERT) != XFS_DA_NODE_MAGIC)) { XFS_ERROR_REPORT("xfs_da_swap_lastblock(7)", XFS_ERRLEVEL_LOW, mp); error = XFS_ERROR(EFSCORRUPTED); goto done; } entno = 0; } /* * Update the parent entry pointing to the moved block. */ INT_SET(par_node->btree[entno].before, ARCH_CONVERT, dead_blkno); xfs_da_log_buf(tp, par_buf, XFS_DA_LOGRANGE(par_node, &par_node->btree[entno].before, sizeof(par_node->btree[entno].before))); xfs_da_buf_done(par_buf); xfs_da_buf_done(dead_buf); *dead_blknop = last_blkno; *dead_bufp = last_buf; return 0;done: if (par_buf) xfs_da_brelse(tp, par_buf); if (sib_buf) xfs_da_brelse(tp, sib_buf); xfs_da_brelse(tp, last_buf); return error;}/* * Remove a btree block from a directory or attribute. */intxfs_da_shrink_inode(xfs_da_args_t *args, xfs_dablk_t dead_blkno, xfs_dabuf_t *dead_buf){ xfs_inode_t *dp; int done, error, w, count; xfs_fileoff_t bno; xfs_fsize_t size; xfs_trans_t *tp; xfs_mount_t *mp; dp = args->dp; w = args->whichfork; tp = args->trans; mp = dp->i_mount; if (w == XFS_DATA_FORK && XFS_DIR_IS_V2(mp)) count = mp->m_dirblkfsbs; else count = 1; for (;;) { /* * Remove extents. If we get ENOSPC for a dir we have to move * the last block to the place we want to kill. */ if ((error = xfs_bunmapi(tp, dp, dead_blkno, count, XFS_BMAPI_AFLAG(w)|XFS_BMAPI_METADATA, 0, args->firstblock, args->flist, &done)) == ENOSPC) { if (w != XFS_DATA_FORK) goto done; if ((error = xfs_da_swap_lastblock(args, &dead_blkno, &dead_buf))) goto done; } else if (error) goto done; else break; } ASSERT(done); xfs_da_binval(tp, dead_buf); /* * Adjust the directory size for version 1. */ if (w == XFS_DATA_FORK && XFS_DIR_IS_V1(mp)) { if ((error = xfs_bmap_last_offset(tp, dp, &bno, w))) return error; size = XFS_FSB_TO_B(dp->i_mount, bno); if (size != dp->i_d.di_size) { dp->i_d.di_size = size; xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); } } return 0;done: xfs_da_binval(tp, dead_buf); return error;}/* * See if the mapping(s) for this btree block are valid, i.e. * don't contain holes, are logically contiguous, and cover the whole range. */STATIC intxfs_da_map_covers_blocks( int nmap, xfs_bmbt_irec_t *mapp, xfs_dablk_t bno, int count){ int i; xfs_fileoff_t off; for (i = 0, off = bno; i < nmap; i++) { if (mapp[i].br_startblock == HOLESTARTBLOCK || mapp[i].br_startblock == DELAYSTARTBLOCK) { return 0; } if (off != mapp[i].br_startoff) { return 0; } off += mapp[i].br_blockcount; } return off == bno + count;}/* * Make a dabuf. * Used for get_buf, read_buf, read_bufr, and reada_buf. */STATIC intxfs_da_do_buf( xfs_trans_t *trans, xfs_inode_t *dp, xfs_dablk_t bno, xfs_daddr_t *mappedbnop, xfs_dabuf_t **bpp, int whichfork, int caller, inst_t *ra){ xfs_buf_t *bp = NULL; xfs_buf_t **bplist; int error=0; int i; xfs_bmbt_irec_t map; xfs_bmbt_irec_t *mapp; xfs_daddr_t mappedbno; xfs_mount_t *mp; int nbplist=0; int nfsb; int nmap; xfs_dabuf_t *rbp; mp = dp->i_mount; if (whichfork == XFS_DATA_FORK && XFS_DIR_IS_V2(mp)) nfsb = mp->m_dirblkfsbs; else nfsb = 1; mappedbno = *mappedbnop; /* * Caller doesn't have a mapping. -2 means don't complain * if we land in a hole. */ if (mappedbno == -1 || mappedbno == -2) { /* * Optimize the one-block case. */ if (nfsb == 1) { xfs_fsblock_t fsb; if ((error = xfs_bmapi_single(trans, dp, whichfork, &fsb, (xfs_fileoff_t)bno))) { return error; } mapp = ↦ if (fsb == NULLFSBLOCK) { nmap = 0; } else { map.br_startblock = fsb; map.br_startoff = (xfs_fileoff_t)bno; map.br_blockcount = 1; nmap = 1; } } else { mapp = kmem_alloc(sizeof(*mapp) * nfsb, KM_SLEEP); nmap = nfsb; if ((error = xfs_bmapi(trans, dp, (xfs_fileoff_t)bno, nfsb, XFS_BMAPI_METADATA | XFS_BMAPI_AFLAG(whichfork), NULL, 0, mapp, &nmap, NULL))) goto exit0; } } else { map.br_startblock = XFS_DADDR_TO_FSB(mp, mappedbno); map.br_startoff = (xfs_fileoff_t)bno; map.br_blockcount = nfsb; mapp = ↦ nmap = 1; } if (!xfs_da_map_covers_blocks(nmap, mapp, bno, nfsb)) { error = mappedbno == -2 ? 0 : XFS_ERROR(EFSCORRUPTED); if (unlikely(error == EFSCORRUPTED)) { if (xfs_error_level >= XFS_ERRLEVEL_LOW) { int i; cmn_err(CE_ALERT, "xfs_da_do_buf: bno %lld\n", (long long)bno); cmn_err(CE_ALERT, "dir: inode %lld\n", (long long)dp->i_ino); for (i = 0; i < nmap; i++) { cmn_err(CE_ALERT, "[%02d] br_startoff %lld br_startblock %lld br_blockcount %lld br_state %d\n", i, (long long)mapp[i].br_startoff, (long long)mapp[i].br_startblock, (long long)mapp[i].br_blockcount, mapp[i].br_state); } } XFS_ERROR_REPORT("xfs_da_do_buf(1)", XFS_ERRLEVEL_LOW, mp); } goto exit0; } if (caller != 3 && nmap > 1) { bplist = kmem_alloc(sizeof(*bplist) * nmap, KM_SLEEP); nbplist = 0; } else bplist = NULL; /* * Turn the mapping(s) into buffer(s). */ for (i = 0; i < nmap; i++) { int nmapped; mappedbno = XFS_FSB_TO_DADDR(mp, mapp[i].br_startblock); if (i == 0) *mappedbnop = mappedbno; nmapped = (int)XFS_FSB_TO_BB(mp, mapp[i].br_blockcount); switch (caller) { case 0: bp = xfs_trans_get_buf(trans, mp->m_ddev_targp, mappedbno, nmapped, 0); error = bp ? XFS_BUF_GETERROR(bp) : XFS_ERROR(EIO); break; case 1:#ifndef __KERNEL__ cas
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?