xfs_dir2_node.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,021 行 · 第 1/4 页
C
2,021 行
/* * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 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. * * Further, this software is distributed without any warranty that it is * free of the rightful claim of any third person regarding infringement * or the like. Any license provided herein, whether implied or * otherwise, applies only to this software file. Patent licenses, if * any, provided herein do not apply to combinations of this program with * other software, or any other product whatsoever. * * You should have received a copy of the GNU General Public License along * with this program; if not, write the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston MA 02111-1307, USA. * * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, * Mountain View, CA 94043, or: * * http://www.sgi.com * * For further information regarding this notice, see: * * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ *//* * xfs_dir2_node.c * XFS directory implementation, version 2, node form files * See data structures in xfs_dir2_node.h and xfs_da_btree.h. */#include "xfs.h"#include "xfs_macros.h"#include "xfs_types.h"#include "xfs_inum.h"#include "xfs_log.h"#include "xfs_trans.h"#include "xfs_sb.h"#include "xfs_dir.h"#include "xfs_dir2.h"#include "xfs_dmapi.h"#include "xfs_mount.h"#include "xfs_bmap_btree.h"#include "xfs_attr_sf.h"#include "xfs_dir_sf.h"#include "xfs_dir2_sf.h"#include "xfs_dinode.h"#include "xfs_inode.h"#include "xfs_bmap.h"#include "xfs_da_btree.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(INT_GET(free->hdr.magic, ARCH_CONVERT) == 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(INT_GET(free->hdr.magic, ARCH_CONVERT) == 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 */ xfs_dir2_data_off_t *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 */ xfs_dir2_data_off_t *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. */ INT_SET(free->hdr.magic, ARCH_CONVERT, XFS_DIR2_FREE_MAGIC); INT_ZERO(free->hdr.firstdb, ARCH_CONVERT); ASSERT(INT_GET(ltp->bestcount, ARCH_CONVERT) <= (uint)dp->i_d.di_size / mp->m_dirblksize); INT_COPY(free->hdr.nvalid, ltp->bestcount, ARCH_CONVERT); /* * Copy freespace entries from the leaf block to the new block. * Count active entries. */ for (i = n = 0, from = XFS_DIR2_LEAF_BESTS_P_ARCH(ltp, ARCH_CONVERT), to = free->bests; i < INT_GET(ltp->bestcount, ARCH_CONVERT); i++, from++, to++) { if ((off = INT_GET(*from, ARCH_CONVERT)) != NULLDATAOFF) n++; INT_SET(*to, ARCH_CONVERT, off); } INT_SET(free->hdr.nused, ARCH_CONVERT, n); INT_SET(leaf->hdr.info.magic, ARCH_CONVERT, 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, INT_GET(free->hdr.nvalid, ARCH_CONVERT) - 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 (INT_GET(leaf->hdr.count, ARCH_CONVERT) == XFS_DIR2_MAX_LEAF_ENTS(mp)) { if (INT_ISZERO(leaf->hdr.stale, ARCH_CONVERT)) return XFS_ERROR(ENOSPC); compact = INT_GET(leaf->hdr.stale, ARCH_CONVERT) > 1; } else compact = 0; ASSERT(index == 0 || INT_GET(leaf->ents[index - 1].hashval, ARCH_CONVERT) <= args->hashval); ASSERT(index == INT_GET(leaf->hdr.count, ARCH_CONVERT) || INT_GET(leaf->ents[index].hashval, ARCH_CONVERT) >= 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 (!INT_ISZERO(leaf->hdr.stale, ARCH_CONVERT)) { lfloglow = INT_GET(leaf->hdr.count, ARCH_CONVERT); lfloghigh = -1; } /* * No stale entries, just insert a space for the new entry. */ if (INT_ISZERO(leaf->hdr.stale, ARCH_CONVERT)) { lep = &leaf->ents[index]; if (index < INT_GET(leaf->hdr.count, ARCH_CONVERT)) memmove(lep + 1, lep, (INT_GET(leaf->hdr.count, ARCH_CONVERT) - index) * sizeof(*lep)); lfloglow = index; lfloghigh = INT_GET(leaf->hdr.count, ARCH_CONVERT); INT_MOD(leaf->hdr.count, ARCH_CONVERT, +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 && INT_GET(leaf->ents[lowstale].address, ARCH_CONVERT) != 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 < INT_GET(leaf->hdr.count, ARCH_CONVERT) && INT_GET(leaf->ents[highstale].address, ARCH_CONVERT) != 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 == INT_GET(leaf->hdr.count, ARCH_CONVERT) || index - lowstale - 1 < highstale - index)) { ASSERT(INT_GET(leaf->ents[lowstale].address, ARCH_CONVERT) == 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(INT_GET(leaf->ents[highstale].address, ARCH_CONVERT) == 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); } INT_MOD(leaf->hdr.stale, ARCH_CONVERT, -1); } /* * Insert the new entry, log everything. */ INT_SET(lep->hashval, ARCH_CONVERT, args->hashval); INT_SET(lep->address, ARCH_CONVERT, 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(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC); ASSERT(INT_GET(leaf->hdr.count, ARCH_CONVERT) <= XFS_DIR2_MAX_LEAF_ENTS(mp)); for (i = stale = 0; i < INT_GET(leaf->hdr.count, ARCH_CONVERT); i++) { if (i + 1 < INT_GET(leaf->hdr.count, ARCH_CONVERT)) { ASSERT(INT_GET(leaf->ents[i].hashval, ARCH_CONVERT) <= INT_GET(leaf->ents[i + 1].hashval, ARCH_CONVERT)); } if (INT_GET(leaf->ents[i].address, ARCH_CONVERT) == XFS_DIR2_NULL_DATAPTR) stale++; } ASSERT(INT_GET(leaf->hdr.stale, ARCH_CONVERT) == 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(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC); if (count) *count = INT_GET(leaf->hdr.count, ARCH_CONVERT); if (INT_ISZERO(leaf->hdr.count, ARCH_CONVERT)) return 0; return INT_GET(leaf->ents[INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT);}/* * 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(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC);#ifdef __KERNEL__ ASSERT(INT_GET(leaf->hdr.count, ARCH_CONVERT) > 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(INT_GET(free->hdr.magic, ARCH_CONVERT) == 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 < INT_GET(leaf->hdr.count, ARCH_CONVERT) && INT_GET(lep->hashval, ARCH_CONVERT) == args->hashval; lep++, index++) { /* * Skip stale leaf entries. */ if (INT_GET(lep->address, ARCH_CONVERT) == XFS_DIR2_NULL_DATAPTR) continue; /* * Pull the data block number from the entry. */ newdb = XFS_DIR2_DATAPTR_TO_DB(mp, INT_GET(lep->address, ARCH_CONVERT)); /* * 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.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?