xfs_dir2_block_tail_t * xfs_dir2_block_tail_p(xfs_mount_t *mp, xfs_dir2_block_t *block) { return XFS_DIR2_BLOCK_TAIL_P(mp, block); }
/* * 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 (INT_GET(dup->freetag, ARCH_CONVERT) == XFS_DIR2_DATA_FREE_TAG) { ptr += INT_GET(dup->length, ARCH_CONVERT); continue; } dep = (xfs_dir2_data_entry_t *)ptr; /* * Skip . */ if (dep->namelen == 1 && dep->name[0] == '.') ASSERT(INT_GET(dep->inumber, ARCH_CONVERT) == dp->i_ino); /* * Skip .., but make sure the inode number is right. */ else if (dep->namelen == 2 && dep->name[0] == '.' && dep->name[1] == '.') ASSERT(INT_GET(dep->inumber, ARCH_CONVERT) == 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=INT_GET(dep->inumber, ARCH_CONVERT); 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; }
/* * 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 < INT_GET(btp->count, ARCH_CONVERT); i++) { if ((addr = INT_GET(blp[i].address, ARCH_CONVERT)) == 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 += INT_GET(dep->inumber, ARCH_CONVERT) > XFS_DIR2_MAX_SHORT_INUM; #endif if (!isdot && !isdotdot) { count++; namelen += dep->namelen; } else if (isdotdot) parent = INT_GET(dep->inumber, ARCH_CONVERT); /* * Calculate the new size, see if we should give up yet. */ /* Add by Jerry 2007/10/25 Fixed bug that XFS file system corrupted under Winthrax test. This is the "special" structure alignment on the ARM. Port a workaround solution from a patch on http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2004-March/020287.html and http://www.nas-central.org/index.php/Buffalo_ARM9_Kernel_Port#XFS_Arm_Issues This patch was never accepted by XFS because the code is incorrect for all other platforms. */ #if 0 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); #else size = XFS_DIR2_SF_HDR_SIZE(i8count) /* header */ + namelen + (count * (sizeof(xfs_dir2_sf_entry_t) - 1)) - (count * (((i8count == 0) ? 1 : 0) * (sizeof(xfs_dir2_ino8_t) - sizeof(xfs_dir2_ino4_t)))); #endif 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; }
/* * 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; /* 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 < INT_GET(btp->count, ARCH_CONVERT); i++) { if ((addr = INT_GET(blp[i].address, ARCH_CONVERT)) == 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 += INT_GET(dep->inumber, ARCH_CONVERT) > XFS_DIR2_MAX_SHORT_INUM; #endif if (!isdot && !isdotdot) { count++; namelen += dep->namelen; } else if (isdotdot) parent = INT_GET(dep->inumber, ARCH_CONVERT); /* * 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; }