📄 xfs_dir2_sf.c
字号:
/* * Copyright (c) 2000-2003,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_inode_item.h"#include "xfs_error.h"#include "xfs_dir2_data.h"#include "xfs_dir2_leaf.h"#include "xfs_dir2_block.h"#include "xfs_dir2_trace.h"/* * Prototypes for internal functions. */static void xfs_dir2_sf_addname_easy(xfs_da_args_t *args, xfs_dir2_sf_entry_t *sfep, xfs_dir2_data_aoff_t offset, int new_isize);static void xfs_dir2_sf_addname_hard(xfs_da_args_t *args, int objchange, int new_isize);static int xfs_dir2_sf_addname_pick(xfs_da_args_t *args, int objchange, xfs_dir2_sf_entry_t **sfepp, xfs_dir2_data_aoff_t *offsetp);#ifdef DEBUGstatic void xfs_dir2_sf_check(xfs_da_args_t *args);#else#define xfs_dir2_sf_check(args)#endif /* DEBUG */#if XFS_BIG_INUMSstatic void xfs_dir2_sf_toino4(xfs_da_args_t *args);static void xfs_dir2_sf_toino8(xfs_da_args_t *args);#endif /* XFS_BIG_INUMS *//* * Given a block directory (dp/block), calculate its size as a shortform (sf) * directory and a header for the sf directory, if it will fit it the * space currently present in the inode. If it won't fit, the output * size is too big (but not accurate). */int /* size for sf form */xfs_dir2_block_sfsize( xfs_inode_t *dp, /* incore inode pointer */ xfs_dir2_block_t *block, /* block directory data */ xfs_dir2_sf_hdr_t *sfhp) /* output: header for sf form */{ xfs_dir2_dataptr_t addr; /* data entry address */ xfs_dir2_leaf_entry_t *blp; /* leaf area of the block */ xfs_dir2_block_tail_t *btp; /* tail area of the block */ int count; /* shortform entry count */ xfs_dir2_data_entry_t *dep; /* data entry in the block */ int i; /* block entry index */ int i8count; /* count of big-inode entries */ int isdot; /* entry is "." */ int isdotdot; /* entry is ".." */ xfs_mount_t *mp; /* mount structure pointer */ int namelen; /* total name bytes */ xfs_ino_t parent = 0; /* parent inode number */ int size=0; /* total computed size */ mp = dp->i_mount; count = i8count = namelen = 0; btp = xfs_dir2_block_tail_p(mp, block); blp = xfs_dir2_block_leaf_p(btp); /* * Iterate over the block's data entries by using the leaf pointers. */ for (i = 0; i < be32_to_cpu(btp->count); i++) { if ((addr = be32_to_cpu(blp[i].address)) == XFS_DIR2_NULL_DATAPTR) continue; /* * Calculate the pointer to the entry at hand. */ dep = (xfs_dir2_data_entry_t *) ((char *)block + xfs_dir2_dataptr_to_off(mp, addr)); /* * Detect . and .., so we can special-case them. * . is not included in sf directories. * .. is included by just the parent inode number. */ isdot = dep->namelen == 1 && dep->name[0] == '.'; isdotdot = dep->namelen == 2 && dep->name[0] == '.' && dep->name[1] == '.';#if XFS_BIG_INUMS if (!isdot) i8count += be64_to_cpu(dep->inumber) > XFS_DIR2_MAX_SHORT_INUM;#endif if (!isdot && !isdotdot) { count++; namelen += dep->namelen; } else if (isdotdot) parent = be64_to_cpu(dep->inumber); /* * Calculate the new size, see if we should give up yet. */ size = xfs_dir2_sf_hdr_size(i8count) + /* header */ count + /* namelen */ count * (uint)sizeof(xfs_dir2_sf_off_t) + /* offset */ namelen + /* name */ (i8count ? /* inumber */ (uint)sizeof(xfs_dir2_ino8_t) * count : (uint)sizeof(xfs_dir2_ino4_t) * count); if (size > XFS_IFORK_DSIZE(dp)) return size; /* size value is a failure */ } /* * Create the output header, if it worked. */ sfhp->count = count; sfhp->i8count = i8count; xfs_dir2_sf_put_inumber((xfs_dir2_sf_t *)sfhp, &parent, &sfhp->parent); return size;}/* * Convert a block format directory to shortform. * Caller has already checked that it will fit, and built us a header. */int /* error */xfs_dir2_block_to_sf( xfs_da_args_t *args, /* operation arguments */ xfs_dabuf_t *bp, /* block buffer */ int size, /* shortform directory size */ xfs_dir2_sf_hdr_t *sfhp) /* shortform directory hdr */{ xfs_dir2_block_t *block; /* block structure */ xfs_dir2_block_tail_t *btp; /* block tail pointer */ xfs_dir2_data_entry_t *dep; /* data entry pointer */ xfs_inode_t *dp; /* incore directory inode */ xfs_dir2_data_unused_t *dup; /* unused data pointer */ char *endptr; /* end of data entries */ int error; /* error return value */ int logflags; /* inode logging flags */ xfs_mount_t *mp; /* filesystem mount point */ char *ptr; /* current data pointer */ xfs_dir2_sf_entry_t *sfep; /* shortform entry */ xfs_dir2_sf_t *sfp; /* shortform structure */ xfs_ino_t temp; xfs_dir2_trace_args_sb("block_to_sf", args, size, bp); dp = args->dp; mp = dp->i_mount; /* * Make a copy of the block data, so we can shrink the inode * and add local data. */ block = kmem_alloc(mp->m_dirblksize, KM_SLEEP); memcpy(block, bp->data, mp->m_dirblksize); logflags = XFS_ILOG_CORE; if ((error = xfs_dir2_shrink_inode(args, mp->m_dirdatablk, bp))) { ASSERT(error != ENOSPC); goto out; } /* * The buffer is now unconditionally gone, whether * xfs_dir2_shrink_inode worked or not. * * Convert the inode to local format. */ dp->i_df.if_flags &= ~XFS_IFEXTENTS; dp->i_df.if_flags |= XFS_IFINLINE; dp->i_d.di_format = XFS_DINODE_FMT_LOCAL; ASSERT(dp->i_df.if_bytes == 0); xfs_idata_realloc(dp, size, XFS_DATA_FORK); logflags |= XFS_ILOG_DDATA; /* * Copy the header into the newly allocate local space. */ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; memcpy(sfp, sfhp, xfs_dir2_sf_hdr_size(sfhp->i8count)); dp->i_d.di_size = size; /* * Set up to loop over the block's entries. */ btp = xfs_dir2_block_tail_p(mp, block); ptr = (char *)block->u; endptr = (char *)xfs_dir2_block_leaf_p(btp); sfep = xfs_dir2_sf_firstentry(sfp); /* * Loop over the active and unused entries. * Stop when we reach the leaf/tail portion of the block. */ while (ptr < endptr) { /* * If it's unused, just skip over it. */ dup = (xfs_dir2_data_unused_t *)ptr; if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { ptr += be16_to_cpu(dup->length); continue; } dep = (xfs_dir2_data_entry_t *)ptr; /* * Skip . */ if (dep->namelen == 1 && dep->name[0] == '.') ASSERT(be64_to_cpu(dep->inumber) == dp->i_ino); /* * Skip .., but make sure the inode number is right. */ else if (dep->namelen == 2 && dep->name[0] == '.' && dep->name[1] == '.') ASSERT(be64_to_cpu(dep->inumber) == xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent)); /* * Normal entry, copy it into shortform. */ else { sfep->namelen = dep->namelen; xfs_dir2_sf_put_offset(sfep, (xfs_dir2_data_aoff_t) ((char *)dep - (char *)block)); memcpy(sfep->name, dep->name, dep->namelen); temp = be64_to_cpu(dep->inumber); xfs_dir2_sf_put_inumber(sfp, &temp, xfs_dir2_sf_inumberp(sfep)); sfep = xfs_dir2_sf_nextentry(sfp, sfep); } ptr += xfs_dir2_data_entsize(dep->namelen); } ASSERT((char *)sfep - (char *)sfp == size); xfs_dir2_sf_check(args);out: xfs_trans_log_inode(args->trans, dp, logflags); kmem_free(block, mp->m_dirblksize); return error;}/* * Add a name to a shortform directory. * There are two algorithms, "easy" and "hard" which we decide on * before changing anything. * Convert to block form if necessary, if the new entry won't fit. */int /* error */xfs_dir2_sf_addname( xfs_da_args_t *args) /* operation arguments */{ int add_entsize; /* size of the new entry */ xfs_inode_t *dp; /* incore directory inode */ int error; /* error return value */ int incr_isize; /* total change in size */ int new_isize; /* di_size after adding name */ int objchange; /* changing to 8-byte inodes */ xfs_dir2_data_aoff_t offset = 0; /* offset for new entry */ int old_isize; /* di_size before adding name */ int pick; /* which algorithm to use */ xfs_dir2_sf_t *sfp; /* shortform structure */ xfs_dir2_sf_entry_t *sfep = NULL; /* shortform entry */ xfs_dir2_trace_args("sf_addname", args); ASSERT(xfs_dir2_sf_lookup(args) == ENOENT); dp = args->dp; ASSERT(dp->i_df.if_flags & XFS_IFINLINE); /* * Make sure the shortform value has some of its header. */ if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) { ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount)); return XFS_ERROR(EIO); } ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); ASSERT(dp->i_df.if_u1.if_data != NULL); sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->hdr.i8count)); /* * Compute entry (and change in) size. */ add_entsize = xfs_dir2_sf_entsize_byname(sfp, args->namelen); incr_isize = add_entsize; objchange = 0;#if XFS_BIG_INUMS /* * Do we have to change to 8 byte inodes? */ if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->hdr.i8count == 0) { /* * Yes, adjust the entry size and the total size. */ add_entsize += (uint)sizeof(xfs_dir2_ino8_t) - (uint)sizeof(xfs_dir2_ino4_t); incr_isize += (sfp->hdr.count + 2) * ((uint)sizeof(xfs_dir2_ino8_t) - (uint)sizeof(xfs_dir2_ino4_t)); objchange = 1; }#endif old_isize = (int)dp->i_d.di_size; new_isize = old_isize + incr_isize; /* * Won't fit as shortform any more (due to size), * or the pick routine says it won't (due to offset values). */ if (new_isize > XFS_IFORK_DSIZE(dp) || (pick = xfs_dir2_sf_addname_pick(args, objchange, &sfep, &offset)) == 0) { /* * Just checking or no space reservation, it doesn't fit. */ if (args->justcheck || args->total == 0) return XFS_ERROR(ENOSPC); /* * Convert to block form then add the name. */ error = xfs_dir2_sf_to_block(args); if (error) return error; return xfs_dir2_block_addname(args); } /* * Just checking, it fits. */ if (args->justcheck) return 0; /* * Do it the easy way - just add it at the end. */ if (pick == 1) xfs_dir2_sf_addname_easy(args, sfep, offset, new_isize); /* * Do it the hard way - look for a place to insert the new entry. * Convert to 8 byte inode numbers first if necessary. */ else { ASSERT(pick == 2);#if XFS_BIG_INUMS if (objchange) xfs_dir2_sf_toino8(args);#endif xfs_dir2_sf_addname_hard(args, objchange, new_isize); } xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); return 0;}/* * Add the new entry the "easy" way. * This is copying the old directory and adding the new entry at the end. * Since it's sorted by "offset" we need room after the last offset * that's already there, and then room to convert to a block directory. * This is already checked by the pick routine. */static voidxfs_dir2_sf_addname_easy( xfs_da_args_t *args, /* operation arguments */ xfs_dir2_sf_entry_t *sfep, /* pointer to new entry */ xfs_dir2_data_aoff_t offset, /* offset to use for new ent */ int new_isize) /* new directory size */{ int byteoff; /* byte offset in sf dir */ xfs_inode_t *dp; /* incore directory inode */ xfs_dir2_sf_t *sfp; /* shortform structure */ dp = args->dp; sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; byteoff = (int)((char *)sfep - (char *)sfp); /* * Grow the in-inode space. */ xfs_idata_realloc(dp, xfs_dir2_sf_entsize_byname(sfp, args->namelen), XFS_DATA_FORK); /* * Need to set up again due to realloc of the inode data. */ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; sfep = (xfs_dir2_sf_entry_t *)((char *)sfp + byteoff); /* * Fill in the new entry. */ sfep->namelen = args->namelen; xfs_dir2_sf_put_offset(sfep, offset); memcpy(sfep->name, args->name, sfep->namelen); xfs_dir2_sf_put_inumber(sfp, &args->inumber, xfs_dir2_sf_inumberp(sfep)); /* * Update the header and inode. */ sfp->hdr.count++;#if XFS_BIG_INUMS if (args->inumber > XFS_DIR2_MAX_SHORT_INUM) sfp->hdr.i8count++;#endif dp->i_d.di_size = new_isize; xfs_dir2_sf_check(args);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -