📄 xfs_dir2_node.c
字号:
/* * Copyright (c) 2000-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 "xfs.h"#include "xfs_fs.h"#include "xfs_types.h"#include "xfs_log.h"#include "xfs_inum.h"#include "xfs_trans.h"#include "xfs_sb.h"#include "xfs_ag.h"#include "xfs_dir2.h"#include "xfs_dmapi.h"#include "xfs_mount.h"#include "xfs_da_btree.h"#include "xfs_bmap_btree.h"#include "xfs_dir2_sf.h"#include "xfs_attr_sf.h"#include "xfs_dinode.h"#include "xfs_inode.h"#include "xfs_bmap.h"#include "xfs_dir2_data.h"#include "xfs_dir2_leaf.h"#include "xfs_dir2_block.h"#include "xfs_dir2_node.h"#include "xfs_dir2_trace.h"#include "xfs_error.h"/* * Function declarations. */static void xfs_dir2_free_log_header(xfs_trans_t *tp, xfs_dabuf_t *bp);static int xfs_dir2_leafn_add(xfs_dabuf_t *bp, xfs_da_args_t *args, int index);#ifdef DEBUGstatic void xfs_dir2_leafn_check(xfs_inode_t *dp, xfs_dabuf_t *bp);#else#define xfs_dir2_leafn_check(dp, bp)#endifstatic void xfs_dir2_leafn_moveents(xfs_da_args_t *args, xfs_dabuf_t *bp_s, int start_s, xfs_dabuf_t *bp_d, int start_d, int count);static void xfs_dir2_leafn_rebalance(xfs_da_state_t *state, xfs_da_state_blk_t *blk1, xfs_da_state_blk_t *blk2);static int xfs_dir2_leafn_remove(xfs_da_args_t *args, xfs_dabuf_t *bp, int index, xfs_da_state_blk_t *dblk, int *rval);static int xfs_dir2_node_addname_int(xfs_da_args_t *args, xfs_da_state_blk_t *fblk);/* * Log entries from a freespace block. */voidxfs_dir2_free_log_bests( xfs_trans_t *tp, /* transaction pointer */ xfs_dabuf_t *bp, /* freespace buffer */ int first, /* first entry to log */ int last) /* last entry to log */{ xfs_dir2_free_t *free; /* freespace structure */ free = bp->data; ASSERT(be32_to_cpu(free->hdr.magic) == XFS_DIR2_FREE_MAGIC); xfs_da_log_buf(tp, bp, (uint)((char *)&free->bests[first] - (char *)free), (uint)((char *)&free->bests[last] - (char *)free + sizeof(free->bests[0]) - 1));}/* * Log header from a freespace block. */static voidxfs_dir2_free_log_header( xfs_trans_t *tp, /* transaction pointer */ xfs_dabuf_t *bp) /* freespace buffer */{ xfs_dir2_free_t *free; /* freespace structure */ free = bp->data; ASSERT(be32_to_cpu(free->hdr.magic) == XFS_DIR2_FREE_MAGIC); xfs_da_log_buf(tp, bp, (uint)((char *)&free->hdr - (char *)free), (uint)(sizeof(xfs_dir2_free_hdr_t) - 1));}/* * Convert a leaf-format directory to a node-format directory. * We need to change the magic number of the leaf block, and copy * the freespace table out of the leaf block into its own block. */int /* error */xfs_dir2_leaf_to_node( xfs_da_args_t *args, /* operation arguments */ xfs_dabuf_t *lbp) /* leaf buffer */{ xfs_inode_t *dp; /* incore directory inode */ int error; /* error return value */ xfs_dabuf_t *fbp; /* freespace buffer */ xfs_dir2_db_t fdb; /* freespace block number */ xfs_dir2_free_t *free; /* freespace structure */ __be16 *from; /* pointer to freespace entry */ int i; /* leaf freespace index */ xfs_dir2_leaf_t *leaf; /* leaf structure */ xfs_dir2_leaf_tail_t *ltp; /* leaf tail structure */ xfs_mount_t *mp; /* filesystem mount point */ int n; /* count of live freespc ents */ xfs_dir2_data_off_t off; /* freespace entry value */ __be16 *to; /* pointer to freespace entry */ xfs_trans_t *tp; /* transaction pointer */ xfs_dir2_trace_args_b("leaf_to_node", args, lbp); dp = args->dp; mp = dp->i_mount; tp = args->trans; /* * Add a freespace block to the directory. */ if ((error = xfs_dir2_grow_inode(args, XFS_DIR2_FREE_SPACE, &fdb))) { return error; } ASSERT(fdb == XFS_DIR2_FREE_FIRSTDB(mp)); /* * Get the buffer for the new freespace block. */ if ((error = xfs_da_get_buf(tp, dp, xfs_dir2_db_to_da(mp, fdb), -1, &fbp, XFS_DATA_FORK))) { return error; } ASSERT(fbp != NULL); free = fbp->data; leaf = lbp->data; ltp = xfs_dir2_leaf_tail_p(mp, leaf); /* * Initialize the freespace block header. */ free->hdr.magic = cpu_to_be32(XFS_DIR2_FREE_MAGIC); free->hdr.firstdb = 0; ASSERT(be32_to_cpu(ltp->bestcount) <= (uint)dp->i_d.di_size / mp->m_dirblksize); free->hdr.nvalid = ltp->bestcount; /* * Copy freespace entries from the leaf block to the new block. * Count active entries. */ for (i = n = 0, from = xfs_dir2_leaf_bests_p(ltp), to = free->bests; i < be32_to_cpu(ltp->bestcount); i++, from++, to++) { if ((off = be16_to_cpu(*from)) != NULLDATAOFF) n++; *to = cpu_to_be16(off); } free->hdr.nused = cpu_to_be32(n); leaf->hdr.info.magic = cpu_to_be16(XFS_DIR2_LEAFN_MAGIC); /* * Log everything. */ xfs_dir2_leaf_log_header(tp, lbp); xfs_dir2_free_log_header(tp, fbp); xfs_dir2_free_log_bests(tp, fbp, 0, be32_to_cpu(free->hdr.nvalid) - 1); xfs_da_buf_done(fbp); xfs_dir2_leafn_check(dp, lbp); return 0;}/* * Add a leaf entry to a leaf block in a node-form directory. * The other work necessary is done from the caller. */static int /* error */xfs_dir2_leafn_add( xfs_dabuf_t *bp, /* leaf buffer */ xfs_da_args_t *args, /* operation arguments */ int index) /* insertion pt for new entry */{ int compact; /* compacting stale leaves */ xfs_inode_t *dp; /* incore directory inode */ int highstale; /* next stale entry */ xfs_dir2_leaf_t *leaf; /* leaf structure */ xfs_dir2_leaf_entry_t *lep; /* leaf entry */ int lfloghigh; /* high leaf entry logging */ int lfloglow; /* low leaf entry logging */ int lowstale; /* previous stale entry */ xfs_mount_t *mp; /* filesystem mount point */ xfs_trans_t *tp; /* transaction pointer */ xfs_dir2_trace_args_sb("leafn_add", args, index, bp); dp = args->dp; mp = dp->i_mount; tp = args->trans; leaf = bp->data; /* * Quick check just to make sure we are not going to index * into other peoples memory */ if (index < 0) return XFS_ERROR(EFSCORRUPTED); /* * If there are already the maximum number of leaf entries in * the block, if there are no stale entries it won't fit. * Caller will do a split. If there are stale entries we'll do * a compact. */ if (be16_to_cpu(leaf->hdr.count) == xfs_dir2_max_leaf_ents(mp)) { if (!leaf->hdr.stale) return XFS_ERROR(ENOSPC); compact = be16_to_cpu(leaf->hdr.stale) > 1; } else compact = 0; ASSERT(index == 0 || be32_to_cpu(leaf->ents[index - 1].hashval) <= args->hashval); ASSERT(index == be16_to_cpu(leaf->hdr.count) || be32_to_cpu(leaf->ents[index].hashval) >= args->hashval); if (args->justcheck) return 0; /* * Compact out all but one stale leaf entry. Leaves behind * the entry closest to index. */ if (compact) { xfs_dir2_leaf_compact_x1(bp, &index, &lowstale, &highstale, &lfloglow, &lfloghigh); } /* * Set impossible logging indices for this case. */ else if (leaf->hdr.stale) { lfloglow = be16_to_cpu(leaf->hdr.count); lfloghigh = -1; } /* * No stale entries, just insert a space for the new entry. */ if (!leaf->hdr.stale) { lep = &leaf->ents[index]; if (index < be16_to_cpu(leaf->hdr.count)) memmove(lep + 1, lep, (be16_to_cpu(leaf->hdr.count) - index) * sizeof(*lep)); lfloglow = index; lfloghigh = be16_to_cpu(leaf->hdr.count); be16_add(&leaf->hdr.count, 1); } /* * There are stale entries. We'll use one for the new entry. */ else { /* * If we didn't do a compact then we need to figure out * which stale entry will be used. */ if (compact == 0) { /* * Find first stale entry before our insertion point. */ for (lowstale = index - 1; lowstale >= 0 && be32_to_cpu(leaf->ents[lowstale].address) != XFS_DIR2_NULL_DATAPTR; lowstale--) continue; /* * Find next stale entry after insertion point. * Stop looking if the answer would be worse than * lowstale already found. */ for (highstale = index; highstale < be16_to_cpu(leaf->hdr.count) && be32_to_cpu(leaf->ents[highstale].address) != XFS_DIR2_NULL_DATAPTR && (lowstale < 0 || index - lowstale - 1 >= highstale - index); highstale++) continue; } /* * Using the low stale entry. * Shift entries up toward the stale slot. */ if (lowstale >= 0 && (highstale == be16_to_cpu(leaf->hdr.count) || index - lowstale - 1 < highstale - index)) { ASSERT(be32_to_cpu(leaf->ents[lowstale].address) == XFS_DIR2_NULL_DATAPTR); ASSERT(index - lowstale - 1 >= 0); if (index - lowstale - 1 > 0) memmove(&leaf->ents[lowstale], &leaf->ents[lowstale + 1], (index - lowstale - 1) * sizeof(*lep)); lep = &leaf->ents[index - 1]; lfloglow = MIN(lowstale, lfloglow); lfloghigh = MAX(index - 1, lfloghigh); } /* * Using the high stale entry. * Shift entries down toward the stale slot. */ else { ASSERT(be32_to_cpu(leaf->ents[highstale].address) == XFS_DIR2_NULL_DATAPTR); ASSERT(highstale - index >= 0); if (highstale - index > 0) memmove(&leaf->ents[index + 1], &leaf->ents[index], (highstale - index) * sizeof(*lep)); lep = &leaf->ents[index]; lfloglow = MIN(index, lfloglow); lfloghigh = MAX(highstale, lfloghigh); } be16_add(&leaf->hdr.stale, -1); } /* * Insert the new entry, log everything. */ lep->hashval = cpu_to_be32(args->hashval); lep->address = cpu_to_be32(xfs_dir2_db_off_to_dataptr(mp, args->blkno, args->index)); xfs_dir2_leaf_log_header(tp, bp); xfs_dir2_leaf_log_ents(tp, bp, lfloglow, lfloghigh); xfs_dir2_leafn_check(dp, bp); return 0;}#ifdef DEBUG/* * Check internal consistency of a leafn block. */voidxfs_dir2_leafn_check( xfs_inode_t *dp, /* incore directory inode */ xfs_dabuf_t *bp) /* leaf buffer */{ int i; /* leaf index */ xfs_dir2_leaf_t *leaf; /* leaf structure */ xfs_mount_t *mp; /* filesystem mount point */ int stale; /* count of stale leaves */ leaf = bp->data; mp = dp->i_mount; ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR2_LEAFN_MAGIC); ASSERT(be16_to_cpu(leaf->hdr.count) <= xfs_dir2_max_leaf_ents(mp)); for (i = stale = 0; i < be16_to_cpu(leaf->hdr.count); i++) { if (i + 1 < be16_to_cpu(leaf->hdr.count)) { ASSERT(be32_to_cpu(leaf->ents[i].hashval) <= be32_to_cpu(leaf->ents[i + 1].hashval)); } if (be32_to_cpu(leaf->ents[i].address) == XFS_DIR2_NULL_DATAPTR) stale++; } ASSERT(be16_to_cpu(leaf->hdr.stale) == stale);}#endif /* DEBUG *//* * Return the last hash value in the leaf. * Stale entries are ok. */xfs_dahash_t /* hash value */xfs_dir2_leafn_lasthash( xfs_dabuf_t *bp, /* leaf buffer */ int *count) /* count of entries in leaf */{ xfs_dir2_leaf_t *leaf; /* leaf structure */ leaf = bp->data; ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR2_LEAFN_MAGIC); if (count) *count = be16_to_cpu(leaf->hdr.count); if (!leaf->hdr.count) return 0; return be32_to_cpu(leaf->ents[be16_to_cpu(leaf->hdr.count) - 1].hashval);}/* * Look up a leaf entry in a node-format leaf block. * If this is an addname then the extrablk in state is a freespace block, * otherwise it's a data block. */intxfs_dir2_leafn_lookup_int( xfs_dabuf_t *bp, /* leaf buffer */ xfs_da_args_t *args, /* operation arguments */ int *indexp, /* out: leaf entry index */ xfs_da_state_t *state) /* state to fill in */{ xfs_dabuf_t *curbp; /* current data/free buffer */ xfs_dir2_db_t curdb; /* current data block number */ xfs_dir2_db_t curfdb; /* current free block number */ xfs_dir2_data_entry_t *dep; /* data block entry */ xfs_inode_t *dp; /* incore directory inode */ int error; /* error return value */ int fi; /* free entry index */ xfs_dir2_free_t *free=NULL; /* free block structure */ int index; /* leaf entry index */ xfs_dir2_leaf_t *leaf; /* leaf structure */ int length=0; /* length of new data entry */ xfs_dir2_leaf_entry_t *lep; /* leaf entry */ xfs_mount_t *mp; /* filesystem mount point */ xfs_dir2_db_t newdb; /* new data block number */ xfs_dir2_db_t newfdb; /* new free block number */ xfs_trans_t *tp; /* transaction pointer */ dp = args->dp; tp = args->trans; mp = dp->i_mount; leaf = bp->data; ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR2_LEAFN_MAGIC);#ifdef __KERNEL__ ASSERT(be16_to_cpu(leaf->hdr.count) > 0);#endif xfs_dir2_leafn_check(dp, bp); /* * Look up the hash value in the leaf entries. */ index = xfs_dir2_leaf_search_hash(args, bp); /* * Do we have a buffer coming in? */ if (state->extravalid) curbp = state->extrablk.bp; else curbp = NULL; /* * For addname, it's a free block buffer, get the block number. */ if (args->addname) { curfdb = curbp ? state->extrablk.blkno : -1; curdb = -1; length = xfs_dir2_data_entsize(args->namelen); if ((free = (curbp ? curbp->data : NULL))) ASSERT(be32_to_cpu(free->hdr.magic) == XFS_DIR2_FREE_MAGIC); } /* * For others, it's a data block buffer, get the block number. */ else { curfdb = -1; curdb = curbp ? state->extrablk.blkno : -1; } /* * Loop over leaf entries with the right hash value. */ for (lep = &leaf->ents[index]; index < be16_to_cpu(leaf->hdr.count) && be32_to_cpu(lep->hashval) == args->hashval; lep++, index++) { /* * Skip stale leaf entries. */ if (be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR) continue; /* * Pull the data block number from the entry. */ newdb = xfs_dir2_dataptr_to_db(mp, be32_to_cpu(lep->address)); /* * For addname, we're looking for a place to put the new entry. * We want to use a data block with an entry of equal * hash value to ours if there is one with room. */ if (args->addname) { /* * If this block isn't the data block we already have * in hand, take a look at it. */ if (newdb != curdb) { curdb = newdb; /* * Convert the data block to the free block * holding its freespace information. */ newfdb = xfs_dir2_db_to_fdb(mp, newdb); /* * If it's not the one we have in hand, * read it in. */ if (newfdb != curfdb) { /* * If we had one before, drop it. */ if (curbp) xfs_da_brelse(tp, curbp); /* * Read the free block. */ if ((error = xfs_da_read_buf(tp, dp, xfs_dir2_db_to_da(mp,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -