File: [Development] / xfs-linux / Attic / xfs_dir.c (download)
Revision 1.115, Sun Jan 30 09:59:06 2000 UTC (17 years, 8 months ago) by kenmcd
Branch: MAIN
Changes since 1.114: +12 -17
lines
Encumbrance review done.
Add copyright and license words consistent with GPL.
Refer to http://fsg.melbourne.sgi.com/reviews/ for details.
There is a slight change in the license terms and conditions words
to go with the copyrights, so most of the files are not getting
new GPL's, just updated versions ... but there are 20-30 more files
here as well.
|
/*
* Copyright (C) 1999 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; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
#ident "$Revision: 1.114 $"
#if defined(__linux__)
#include <xfs_linux.h>
#endif
#ifdef SIM
#define _KERNEL 1
#endif /* SIM */
#include <sys/param.h>
#include "xfs_buf.h"
#include <sys/debug.h>
#include <sys/cmn_err.h>
#ifdef SIM
#undef _KERNEL
#endif /* SIM */
#include <sys/errno.h>
#include <sys/vnode.h>
#include <sys/kabi.h>
#include <sys/kmem.h>
#include <sys/uio.h>
#ifdef SIM
#define _KERNEL 1
#endif /* SIM */
#include <sys/dirent.h>
#include <sys/uuid.h>
#include <sys/grio.h>
#include <sys/ktrace.h>
#include <sys/sysinfo.h>
#include <sys/ksa.h>
#include <sys/fcntl.h>
#ifdef SIM
#undef _KERNEL
#include <bstring.h>
#include <stdio.h>
#else
#include <sys/systm.h>
#endif
#ifdef CELL_CAPABLE
#include <sys/kthread.h>
#endif
#ifndef __linux__
#include <string.h>
#endif
#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_mount.h"
#include "xfs_alloc_btree.h"
#include "xfs_bmap_btree.h"
#include "xfs_ialloc_btree.h"
#include "xfs_alloc.h"
#include "xfs_bmap.h"
#include "xfs_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_dir_leaf.h"
#include "xfs_error.h"
#ifdef SIM
#include "sim.h"
#endif
/*
* xfs_dir.c
*
* Provide the external interfaces to manage directories.
*/
/*========================================================================
* Function prototypes for the kernel.
*========================================================================*/
/*
* Functions for the dirops interfaces.
*/
static void xfs_dir_mount(struct xfs_mount *mp);
static int xfs_dir_isempty(struct xfs_inode *dp);
static int xfs_dir_init(struct xfs_trans *trans,
struct xfs_inode *dir,
struct xfs_inode *parent_dir);
static int xfs_dir_createname(struct xfs_trans *trans,
struct xfs_inode *dp,
char *name_string,
int name_len,
xfs_ino_t inode_number,
xfs_fsblock_t *firstblock,
xfs_bmap_free_t *flist,
xfs_extlen_t total);
static int xfs_dir_lookup(struct xfs_trans *tp,
struct xfs_inode *dp,
char *name_string,
int name_length,
xfs_ino_t *inode_number);
#if defined(XFS_REPAIR_SIM) || !defined(SIM)
static int xfs_dir_removename(struct xfs_trans *trans,
struct xfs_inode *dp,
char *name_string,
int name_length,
xfs_ino_t ino,
xfs_fsblock_t *firstblock,
xfs_bmap_free_t *flist,
xfs_extlen_t total);
#endif /* XFS_REPAIR_SIM || !SIM */
#ifdef XFS_REPAIR_SIM
static int xfs_dir_bogus_removename(xfs_trans_t *trans,
xfs_inode_t *dp,
char *name,
xfs_fsblock_t *firstblock,
xfs_bmap_free_t *flist,
xfs_extlen_t total,
xfs_dahash_t hashval,
int namelen);
#endif /* XFS_REPAIR_SIM */
#ifndef SIM
static int xfs_dir_getdents(struct xfs_trans *tp,
struct xfs_inode *dp,
struct uio *uiop,
int *eofp);
#endif /* !SIM */
#if defined(XFS_REPAIR_SIM) || !defined(SIM)
static int xfs_dir_replace(struct xfs_trans *tp,
struct xfs_inode *dp,
char *name_string,
int name_length,
xfs_ino_t inode_number,
xfs_fsblock_t *firstblock,
xfs_bmap_free_t *flist,
xfs_extlen_t total);
#endif /* XFS_REPAIR_SIM || !SIM */
static int xfs_dir_canenter(struct xfs_trans *tp,
struct xfs_inode *dp,
char *name_string,
int name_length);
static int xfs_dir_shortform_validate_ondisk(xfs_mount_t *mp,
xfs_dinode_t *dip);
xfs_dirops_t xfsv1_dirops = {
xfs_dir_mount,
xfs_dir_isempty,
xfs_dir_init,
xfs_dir_createname,
xfs_dir_lookup,
#if defined(XFS_REPAIR_SIM) || !defined(SIM)
xfs_dir_removename,
#endif /* XFS_REPAIR_SIM || !SIM */
#ifdef XFS_REPAIR_SIM
xfs_dir_bogus_removename,
#endif /* XFS_REPAIR_SIM */
#ifndef SIM
xfs_dir_getdents,
#endif /* !SIM */
#if defined(XFS_REPAIR_SIM) || !defined(SIM)
xfs_dir_replace,
#endif /* XFS_REPAIR_SIM || !SIM */
xfs_dir_canenter,
xfs_dir_shortform_validate_ondisk,
xfs_dir_shortform_to_leaf,
};
/*
* Internal routines when dirsize == XFS_LBSIZE(mp).
*/
STATIC int xfs_dir_leaf_lookup(xfs_da_args_t *args);
#if defined(XFS_REPAIR_SIM) || !defined(SIM)
STATIC int xfs_dir_leaf_removename(xfs_da_args_t *args, int *number_entries,
int *total_namebytes);
#endif /* XFS_REPAIR_SIM || !SIM */
#ifndef SIM
STATIC int xfs_dir_leaf_getdents(xfs_trans_t *trans, xfs_inode_t *dp,
uio_t *uio, int *eofp,
dirent_t *dbp,
xfs_dir_put_t put);
#endif /* !SIM */
#if defined(XFS_REPAIR_SIM) || !defined(SIM)
STATIC int xfs_dir_leaf_replace(xfs_da_args_t *args);
#endif /* XFS_REPAIR_SIM || !SIM */
/*
* Internal routines when dirsize > XFS_LBSIZE(mp).
*/
STATIC int xfs_dir_node_addname(xfs_da_args_t *args);
STATIC int xfs_dir_node_lookup(xfs_da_args_t *args);
#if defined(XFS_REPAIR_SIM) || !defined(SIM)
STATIC int xfs_dir_node_removename(xfs_da_args_t *args);
#endif /* XFS_REPAIR_SIM || !SIM */
#ifndef SIM
STATIC int xfs_dir_node_getdents(xfs_trans_t *trans, xfs_inode_t *dp,
uio_t *uio, int *eofp,
dirent_t *dbp,
xfs_dir_put_t put);
#endif /* !SIM */
#if defined(XFS_REPAIR_SIM) || !defined(SIM)
STATIC int xfs_dir_node_replace(xfs_da_args_t *args);
#endif /* XFS_REPAIR_SIM || !SIM */
#if defined(DEBUG) && !defined(SIM)
ktrace_t *xfs_dir_trace_buf;
#endif
/*========================================================================
* Overall external interface routines.
*========================================================================*/
xfs_dahash_t xfs_dir_hash_dot, xfs_dir_hash_dotdot;
/*
* One-time startup routine called from xfs_init().
*/
void
xfs_dir_startup(void)
{
xfs_dir_hash_dot = xfs_da_hashname(".", 1);
xfs_dir_hash_dotdot = xfs_da_hashname("..", 2);
}
/*
* Initialize directory-related fields in the mount structure.
*/
static void
xfs_dir_mount(xfs_mount_t *mp)
{
uint shortcount, leafcount, count;
mp->m_dirversion = 1;
shortcount = (mp->m_attroffset - (uint)sizeof(xfs_dir_sf_hdr_t)) /
(uint)sizeof(xfs_dir_sf_entry_t);
leafcount = (XFS_LBSIZE(mp) - (uint)sizeof(xfs_dir_leaf_hdr_t)) /
((uint)sizeof(xfs_dir_leaf_entry_t) +
(uint)sizeof(xfs_dir_leaf_name_t));
count = shortcount > leafcount ? shortcount : leafcount;
mp->m_dircook_elog = xfs_da_log2_roundup(count + 1);
ASSERT(mp->m_dircook_elog <= mp->m_sb.sb_blocklog);
mp->m_da_node_ents =
(XFS_LBSIZE(mp) - (uint)sizeof(xfs_da_node_hdr_t)) /
(uint)sizeof(xfs_da_node_entry_t);
mp->m_dir_magicpct = (XFS_LBSIZE(mp) * 37) / 100;
mp->m_dirblksize = mp->m_sb.sb_blocksize;
mp->m_dirblkfsbs = 1;
}
/*
* Return 1 if directory contains only "." and "..".
*/
static int
xfs_dir_isempty(xfs_inode_t *dp)
{
xfs_dir_sf_hdr_t *hdr;
ASSERT((dp->i_d.di_mode & IFMT) == IFDIR);
if (dp->i_d.di_size == 0)
return(1);
if (dp->i_d.di_size > XFS_IFORK_DSIZE(dp))
return(0);
hdr = (xfs_dir_sf_hdr_t *)dp->i_df.if_u1.if_data;
return(hdr->count == 0);
}
/*
* Initialize a directory with its "." and ".." entries.
*/
static int
xfs_dir_init(xfs_trans_t *trans, xfs_inode_t *dir, xfs_inode_t *parent_dir)
{
xfs_da_args_t args;
int error;
bzero((char *)&args, sizeof(args));
args.dp = dir;
args.trans = trans;
ASSERT((dir->i_d.di_mode & IFMT) == IFDIR);
if (error = xfs_dir_ino_validate(trans->t_mountp, parent_dir->i_ino))
return error;
return(xfs_dir_shortform_create(&args, parent_dir->i_ino));
}
/*
* Generic handler routine to add a name to a directory.
* Transitions directory from shortform to Btree as necessary.
*/
static int /* error */
xfs_dir_createname(xfs_trans_t *trans, xfs_inode_t *dp, char *name,
int namelen, xfs_ino_t inum, xfs_fsblock_t *firstblock,
xfs_bmap_free_t *flist, xfs_extlen_t total)
{
xfs_da_args_t args;
int retval, newsize, done;
ASSERT((dp->i_d.di_mode & IFMT) == IFDIR);
if (retval = xfs_dir_ino_validate(trans->t_mountp, inum))
return (retval);
XFSSTATS.xs_dir_create++;
/*
* Fill in the arg structure for this request.
*/
args.name = name;
args.namelen = namelen;
args.hashval = xfs_da_hashname(name, namelen);
args.inumber = inum;
args.dp = dp;
args.firstblock = firstblock;
args.flist = flist;
args.total = total;
args.whichfork = XFS_DATA_FORK;
args.trans = trans;
args.justcheck = 0;
args.addname = args.oknoent = 1;
/*
* Decide on what work routines to call based on the inode size.
*/
done = 0;
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
newsize = XFS_DIR_SF_ENTSIZE_BYNAME(args.namelen);
if ((dp->i_d.di_size + newsize) <= XFS_IFORK_DSIZE(dp)) {
retval = xfs_dir_shortform_addname(&args);
done = 1;
} else {
if (total == 0)
return XFS_ERROR(ENOSPC);
retval = xfs_dir_shortform_to_leaf(&args);
done = retval != 0;
}
}
if (!done && xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
retval = xfs_dir_leaf_addname(&args);
done = retval != ENOSPC;
if (!done) {
if (total == 0)
return XFS_ERROR(ENOSPC);
retval = xfs_dir_leaf_to_node(&args);
done = retval != 0;
}
}
if (!done) {
retval = xfs_dir_node_addname(&args);
}
return(retval);
}
/*
* Generic handler routine to check if a name can be added to a directory,
* without adding any blocks to the directory.
*/
static int /* error */
xfs_dir_canenter(xfs_trans_t *trans, xfs_inode_t *dp, char *name, int namelen)
{
xfs_da_args_t args;
int retval, newsize;
ASSERT((dp->i_d.di_mode & IFMT) == IFDIR);
/*
* Fill in the arg structure for this request.
*/
args.name = name;
args.namelen = namelen;
args.hashval = xfs_da_hashname(name, namelen);
args.inumber = 0;
args.dp = dp;
args.firstblock = NULL;
args.flist = NULL;
args.total = 0;
args.whichfork = XFS_DATA_FORK;
args.trans = trans;
args.justcheck = args.addname = args.oknoent = 1;
/*
* Decide on what work routines to call based on the inode size.
*/
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
newsize = XFS_DIR_SF_ENTSIZE_BYNAME(args.namelen);
if ((dp->i_d.di_size + newsize) <= XFS_IFORK_DSIZE(dp))
retval = 0;
else
retval = XFS_ERROR(ENOSPC);
} else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
retval = xfs_dir_leaf_addname(&args);
} else {
retval = xfs_dir_node_addname(&args);
}
return(retval);
}
#if defined(XFS_REPAIR_SIM) || !defined(SIM)
/*
* Generic handler routine to remove a name from a directory.
* Transitions directory from Btree to shortform as necessary.
*/
static int /* error */
xfs_dir_removename(xfs_trans_t *trans, xfs_inode_t *dp, char *name,
int namelen, xfs_ino_t ino, xfs_fsblock_t *firstblock,
xfs_bmap_free_t *flist, xfs_extlen_t total)
{
xfs_da_args_t args;
int count, totallen, newsize, retval;
ASSERT((dp->i_d.di_mode & IFMT) == IFDIR);
XFSSTATS.xs_dir_remove++;
/*
* Fill in the arg structure for this request.
*/
args.name = name;
args.namelen = namelen;
args.hashval = xfs_da_hashname(name, namelen);
args.inumber = ino;
args.dp = dp;
args.firstblock = firstblock;
args.flist = flist;
args.total = total;
args.whichfork = XFS_DATA_FORK;
args.trans = trans;
args.justcheck = args.addname = args.oknoent = 0;
/*
* Decide on what work routines to call based on the inode size.
*/
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
retval = xfs_dir_shortform_removename(&args);
} else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
retval = xfs_dir_leaf_removename(&args, &count, &totallen);
if (retval == 0) {
newsize = XFS_DIR_SF_ALLFIT(count, totallen);
if (newsize <= XFS_IFORK_DSIZE(dp)) {
retval = xfs_dir_leaf_to_shortform(&args);
}
}
} else {
retval = xfs_dir_node_removename(&args);
}
return(retval);
}
#endif /* XFS_REPAIR_SIM || !SIM */
#ifdef XFS_REPAIR_SIM
/*
* Like above only for removing entries with (name, hashvalue)
* pairs that may not be consistent (hashvalue may not be correctly
* set for the name)
*/
static int /* error */
xfs_dir_bogus_removename(xfs_trans_t *trans, xfs_inode_t *dp, char *name,
xfs_fsblock_t *firstblock, xfs_bmap_free_t *flist,
xfs_extlen_t total, xfs_dahash_t hashval, int namelen)
{
xfs_da_args_t args;
int count, totallen, newsize, retval;
ASSERT((dp->i_d.di_mode & IFMT) == IFDIR);
if (namelen >= MAXNAMELEN) {
return(XFS_ERROR(EINVAL));
}
XFSSTATS.xs_dir_remove++;
/*
* Fill in the arg structure for this request.
*/
args.name = name;
args.namelen = namelen;
args.hashval = hashval;
args.inumber = 0;
args.dp = dp;
args.firstblock = firstblock;
args.flist = flist;
args.total = total;
args.whichfork = XFS_DATA_FORK;
args.trans = trans;
args.justcheck = args.addname = 0;
args.oknoent = 1;
/*
* Decide on what work routines to call based on the inode size.
*/
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
retval = xfs_dir_shortform_removename(&args);
} else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
retval = xfs_dir_leaf_removename(&args, &count, &totallen);
if (retval == 0) {
newsize = XFS_DIR_SF_ALLFIT(count, totallen);
if (newsize <= XFS_IFORK_DSIZE(dp)) {
retval = xfs_dir_leaf_to_shortform(&args);
}
}
} else {
retval = xfs_dir_node_removename(&args);
}
return(retval);
}
#endif /* XFS_REPAIR_SIM */
static int /* error */
xfs_dir_lookup(xfs_trans_t *trans, xfs_inode_t *dp, char *name, int namelen,
xfs_ino_t *inum)
{
xfs_da_args_t args;
int retval;
ASSERT((dp->i_d.di_mode & IFMT) == IFDIR);
if (namelen >= MAXNAMELEN) {
return(XFS_ERROR(EINVAL));
}
XFSSTATS.xs_dir_lookup++;
/*
* Fill in the arg structure for this request.
*/
args.name = name;
args.namelen = namelen;
args.hashval = xfs_da_hashname(name, namelen);
args.inumber = 0;
args.dp = dp;
args.firstblock = NULL;
args.flist = NULL;
args.total = 0;
args.whichfork = XFS_DATA_FORK;
args.trans = trans;
args.justcheck = args.addname = 0;
args.oknoent = 1;
/*
* Decide on what work routines to call based on the inode size.
*/
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
retval = xfs_dir_shortform_lookup(&args);
} else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
retval = xfs_dir_leaf_lookup(&args);
} else {
retval = xfs_dir_node_lookup(&args);
}
if (retval == EEXIST)
retval = 0;
*inum = args.inumber;
return(retval);
}
#ifndef SIM
/*
* Implement readdir.
*/
static int /* error */
xfs_dir_getdents(xfs_trans_t *trans, xfs_inode_t *dp, uio_t *uio, int *eofp)
{
dirent_t *dbp;
caddr_t lockaddr;
int locklen = 0, alignment, retval, is32;
xfs_dir_put_t put;
int error;
XFSSTATS.xs_dir_getdents++;
ASSERT((dp->i_d.di_mode & IFMT) == IFDIR);
/*
* If our caller has given us a single contiguous memory buffer,
* just work directly within that buffer. If it's in user memory,
* lock it down first.
*/
is32 = ABI_IS_IRIX5(GETDENTS_ABI(get_current_abi(), uio));
alignment = (is32 ? sizeof(irix5_off_t) : sizeof(off_t)) - 1;
#ifndef __linux__
if ((uio->uio_iovcnt == 1) &&
#if CELL_CAPABLE
!KT_CUR_ISXTHREAD() &&
#endif
(((__psint_t)uio->uio_iov[0].iov_base & alignment) == 0) &&
((uio->uio_iov[0].iov_len & alignment) == 0)) {
dbp = NULL;
if (uio->uio_segflg == UIO_SYSSPACE) {
ASSERT(!is32);
lockaddr = NULL;
locklen = 0;
put = xfs_dir_put_dirent64_direct;
} else {
if (error = useracc(uio->uio_iov[0].iov_base,
uio->uio_iov[0].iov_len,
(B_READ|B_PHYS), NULL)) {
*eofp = 0;
return (XFS_ERROR(error));
}
lockaddr = uio->uio_iov[0].iov_base;
locklen = uio->uio_iov[0].iov_len;
put = is32 ?
xfs_dir_put_dirent32_direct :
xfs_dir_put_dirent64_direct;
}
} else {
dbp = kmem_alloc(sizeof(*dbp) + MAXNAMELEN, KM_SLEEP);
put = is32 ?
xfs_dir_put_dirent32_uio :
xfs_dir_put_dirent64_uio;
}
#else
dbp = NULL;
put = is32 ?
xfs_dir_put_dirent32_uio :
xfs_dir_put_dirent64_uio;
#endif /* __linux__ */
/*
* Decide on what work routines to call based on the inode size.
*/
*eofp = 0;
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
retval = xfs_dir_shortform_getdents(dp, uio, eofp, dbp, put);
} else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
retval = xfs_dir_leaf_getdents(trans, dp, uio, eofp, dbp, put);
} else {
retval = xfs_dir_node_getdents(trans, dp, uio, eofp, dbp, put);
}
#ifndef __linux__
if (dbp != NULL)
kmem_free(dbp, sizeof(*dbp) + MAXNAMELEN);
else if (locklen)
unuseracc(lockaddr, locklen, (B_READ|B_PHYS));
#endif
return(retval);
}
#endif /* !SIM */
#if defined(XFS_REPAIR_SIM) || !defined(SIM)
static int /* error */
xfs_dir_replace(xfs_trans_t *trans, xfs_inode_t *dp, char *name, int namelen,
xfs_ino_t inum, xfs_fsblock_t *firstblock,
xfs_bmap_free_t *flist, xfs_extlen_t total)
{
xfs_da_args_t args;
int retval;
ASSERT((dp->i_d.di_mode & IFMT) == IFDIR);
if (namelen >= MAXNAMELEN) {
return(XFS_ERROR(EINVAL));
}
if (retval = xfs_dir_ino_validate(trans->t_mountp, inum))
return retval;
/*
* Fill in the arg structure for this request.
*/
args.name = name;
args.namelen = namelen;
args.hashval = xfs_da_hashname(name, namelen);
args.inumber = inum;
args.dp = dp;
args.firstblock = firstblock;
args.flist = flist;
args.total = total;
args.whichfork = XFS_DATA_FORK;
args.trans = trans;
args.justcheck = args.addname = args.oknoent = 0;
/*
* Decide on what work routines to call based on the inode size.
*/
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
retval = xfs_dir_shortform_replace(&args);
} else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
retval = xfs_dir_leaf_replace(&args);
} else {
retval = xfs_dir_node_replace(&args);
}
return(retval);
}
#endif /* XFS_REPAIR_SIM || !SIM */
#ifndef XFS_REPAIR_SIM
static int
xfs_dir_shortform_validate_ondisk(xfs_mount_t *mp, xfs_dinode_t *dp)
{
xfs_ino_t ino;
int namelen_sum;
int count;
xfs_dir_shortform_t *sf;
xfs_dir_sf_entry_t *sfe;
int i;
if ((dp->di_core.di_mode & IFMT) != IFDIR) {
return 0;
}
if (dp->di_core.di_format != XFS_DINODE_FMT_LOCAL) {
return 0;
}
if (dp->di_core.di_size < sizeof(sf->hdr)) {
xfs_fs_cmn_err(CE_WARN, mp, "Invalid shortform size: dp 0x%p\n",
dp);
return 1;
}
sf = (xfs_dir_shortform_t *)(&dp->di_u.di_dirsf);
ino = XFS_GET_DIR_INO(mp, sf->hdr.parent);
if (xfs_dir_ino_validate(mp, ino))
return 1;
count = sf->hdr.count;
if ((count < 0) || ((count * 10) > XFS_LITINO(mp))) {
xfs_fs_cmn_err(CE_WARN, mp,
"Invalid shortform count: dp 0x%p\n", dp);
return(1);
}
if (count == 0) {
return 0;
}
namelen_sum = 0;
sfe = &sf->list[0];
for (i = sf->hdr.count - 1; i >= 0; i--) {
ino = XFS_GET_DIR_INO(mp, sfe->inumber);
xfs_dir_ino_validate(mp, ino);
if (sfe->namelen >= XFS_LITINO(mp)) {
xfs_fs_cmn_err(CE_WARN, mp,
"Invalid shortform namelen: dp 0x%p\n", dp);
return 1;
}
namelen_sum += sfe->namelen;
sfe = XFS_DIR_SF_NEXTENTRY(sfe);
}
if (namelen_sum >= XFS_LITINO(mp)) {
xfs_fs_cmn_err(CE_WARN, mp,
"Invalid shortform namelen: dp 0x%p\n", dp);
return 1;
}
return 0;
}
#else
/* ARGSUSED */
static int
xfs_dir_shortform_validate_ondisk(xfs_mount_t *mp, xfs_dinode_t *dp)
{
return 0;
}
#endif /* !XFS_REPAIR_SIM */
/*========================================================================
* External routines when dirsize == XFS_LBSIZE(dp->i_mount).
*========================================================================*/
/*
* Add a name to the leaf directory structure
* This is the external routine.
*/
int
xfs_dir_leaf_addname(xfs_da_args_t *args)
{
int index, retval;
xfs_dabuf_t *bp;
retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp,
XFS_DATA_FORK);
if (retval)
return(retval);
ASSERT(bp != NULL);
retval = xfs_dir_leaf_lookup_int(bp, args, &index);
if (retval == ENOENT)
retval = xfs_dir_leaf_add(bp, args, index);
xfs_da_buf_done(bp);
return(retval);
}
#if defined(XFS_REPAIR_SIM) || !defined(SIM)
/*
* Remove a name from the leaf directory structure
* This is the external routine.
*/
STATIC int
xfs_dir_leaf_removename(xfs_da_args_t *args, int *count, int *totallen)
{
xfs_dir_leafblock_t *leaf;
int index, retval;
xfs_dabuf_t *bp;
retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp,
XFS_DATA_FORK);
if (retval)
return(retval);
ASSERT(bp != NULL);
leaf = bp->data;
ASSERT(leaf->hdr.info.magic == XFS_DIR_LEAF_MAGIC);
retval = xfs_dir_leaf_lookup_int(bp, args, &index);
if (retval == EEXIST) {
(void)xfs_dir_leaf_remove(args->trans, bp, index);
*count = leaf->hdr.count;
*totallen = leaf->hdr.namebytes;
retval = 0;
}
xfs_da_buf_done(bp);
return(retval);
}
#endif /* XFS_REPAIR_SIM || !SIM */
/*
* Look up a name in a leaf directory structure.
* This is the external routine.
*/
STATIC int
xfs_dir_leaf_lookup(xfs_da_args_t *args)
{
int index, retval;
xfs_dabuf_t *bp;
retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp,
XFS_DATA_FORK);
if (retval)
return(retval);
ASSERT(bp != NULL);
retval = xfs_dir_leaf_lookup_int(bp, args, &index);
xfs_da_brelse(args->trans, bp);
return(retval);
}
#ifndef SIM
/*
* Copy out directory entries for getdents(), for leaf directories.
*/
STATIC int
xfs_dir_leaf_getdents(xfs_trans_t *trans, xfs_inode_t *dp, uio_t *uio,
int *eofp, dirent_t *dbp, xfs_dir_put_t put)
{
xfs_dabuf_t *bp;
int retval, eob;
retval = xfs_da_read_buf(dp->i_transp, dp, 0, -1, &bp, XFS_DATA_FORK);
if (retval)
return(retval);
ASSERT(bp != NULL);
retval = xfs_dir_leaf_getdents_int(bp, dp, 0, uio, &eob, dbp, put, -1);
xfs_da_brelse(trans, bp);
*eofp = (eob == 0);
return(retval);
}
#endif /* !SIM */
#if defined(XFS_REPAIR_SIM) || !defined(SIM)
/*
* Look up a name in a leaf directory structure, replace the inode number.
* This is the external routine.
*/
STATIC int
xfs_dir_leaf_replace(xfs_da_args_t *args)
{
int index, retval;
xfs_dabuf_t *bp;
xfs_ino_t inum;
xfs_dir_leafblock_t *leaf;
xfs_dir_leaf_entry_t *entry;
xfs_dir_leaf_name_t *namest;
inum = args->inumber;
retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp,
XFS_DATA_FORK);
if (retval)
return(retval);
ASSERT(bp != NULL);
retval = xfs_dir_leaf_lookup_int(bp, args, &index);
if (retval == EEXIST) {
leaf = bp->data;
entry = &leaf->entries[index];
namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, entry->nameidx);
ASSERT(bcmp((char *)&inum, (char *)&namest->inumber,
sizeof(inum)));
XFS_DIR_SF_PUT_DIRINO(&inum, &namest->inumber);
xfs_da_log_buf(args->trans, bp,
XFS_DA_LOGRANGE(leaf, namest, sizeof(namest->inumber)));
xfs_da_buf_done(bp);
retval = 0;
} else
xfs_da_brelse(args->trans, bp);
return(retval);
}
#endif /* XFS_REPAIR_SIM || !SIM */
/*========================================================================
* External routines when dirsize > XFS_LBSIZE(mp).
*========================================================================*/
/*
* Add a name to a Btree-format directory.
*
* This will involve walking down the Btree, and may involve splitting
* leaf nodes and even splitting intermediate nodes up to and including
* the root node (a special case of an intermediate node).
*/
STATIC int
xfs_dir_node_addname(xfs_da_args_t *args)
{
xfs_da_state_t *state;
xfs_da_state_blk_t *blk;
int retval, error;
/*
* Fill in bucket of arguments/results/context to carry around.
*/
state = xfs_da_state_alloc();
state->args = args;
state->mp = args->dp->i_mount;
state->blocksize = state->mp->m_sb.sb_blocksize;
/*
* Search to see if name already exists, and get back a pointer
* to where it should go.
*/
error = xfs_da_node_lookup_int(state, &retval);
if (error)
retval = error;
if (retval != ENOENT)
goto error;
blk = &state->path.blk[ state->path.active-1 ];
ASSERT(blk->magic == XFS_DIR_LEAF_MAGIC);
retval = xfs_dir_leaf_add(blk->bp, args, blk->index);
if (retval == 0) {
/*
* Addition succeeded, update Btree hashvals.
*/
if (!args->justcheck)
xfs_da_fixhashpath(state, &state->path);
} else {
/*
* Addition failed, split as many Btree elements as required.
*/
if (args->total == 0) {
ASSERT(retval == ENOSPC);
goto error;
}
retval = xfs_da_split(state);
}
error:
xfs_da_state_free(state);
return(retval);
}
#if defined(XFS_REPAIR_SIM) || !defined(SIM)
/*
* Remove a name from a B-tree directory.
*
* This will involve walking down the Btree, and may involve joining
* leaf nodes and even joining intermediate nodes up to and including
* the root node (a special case of an intermediate node).
*/
STATIC int
xfs_dir_node_removename(xfs_da_args_t *args)
{
xfs_da_state_t *state;
xfs_da_state_blk_t *blk;
int retval, error;
state = xfs_da_state_alloc();
state->args = args;
state->mp = args->dp->i_mount;
state->blocksize = state->mp->m_sb.sb_blocksize;
/*
* Search to see if name exists, and get back a pointer to it.
*/
error = xfs_da_node_lookup_int(state, &retval);
if (error)
retval = error;
if (retval != EEXIST) {
xfs_da_state_free(state);
return(retval);
}
/*
* Remove the name and update the hashvals in the tree.
*/
blk = &state->path.blk[ state->path.active-1 ];
ASSERT(blk->magic == XFS_DIR_LEAF_MAGIC);
retval = xfs_dir_leaf_remove(args->trans, blk->bp, blk->index);
xfs_da_fixhashpath(state, &state->path);
/*
* Check to see if the tree needs to be collapsed.
*/
error = 0;
if (retval) {
error = xfs_da_join(state);
}
xfs_da_state_free(state);
if (error)
return(error);
return(0);
}
#endif /* XFS_REPAIR_SIM || !SIM */
/*
* Look up a filename in a int directory.
* Use an internal routine to actually do all the work.
*/
STATIC int
xfs_dir_node_lookup(xfs_da_args_t *args)
{
xfs_da_state_t *state;
int retval, error, i;
state = xfs_da_state_alloc();
state->args = args;
state->mp = args->dp->i_mount;
state->blocksize = state->mp->m_sb.sb_blocksize;
/*
* Search to see if name exists,
* and get back a pointer to it.
*/
error = xfs_da_node_lookup_int(state, &retval);
if (error) {
retval = error;
}
/*
* If not in a transaction, we have to release all the buffers.
*/
for (i = 0; i < state->path.active; i++) {
xfs_da_brelse(args->trans, state->path.blk[i].bp);
state->path.blk[i].bp = NULL;
}
xfs_da_state_free(state);
return(retval);
}
#ifndef SIM
STATIC int
xfs_dir_node_getdents(xfs_trans_t *trans, xfs_inode_t *dp, uio_t *uio,
int *eofp, dirent_t *dbp, xfs_dir_put_t put)
{
xfs_da_intnode_t *node;
xfs_da_node_entry_t *btree;
xfs_dir_leafblock_t *leaf;
xfs_dablk_t bno, nextbno;
xfs_dahash_t cookhash;
xfs_mount_t *mp;
int error, eob, i;
xfs_dabuf_t *bp;
daddr_t nextda;
/*
* Pick up our context.
*/
mp = dp->i_mount;
bp = NULL;
bno = XFS_DA_COOKIE_BNO(mp, uio->uio_offset);
cookhash = XFS_DA_COOKIE_HASH(mp, uio->uio_offset);
xfs_dir_trace_g_du("node: start", dp, uio);
/*
* Re-find our place, even if we're confused about what our place is.
*
* First we check the block number from the magic cookie, it is a
* cache of where we ended last time. If we find a leaf block, and
* the starting hashval in that block is less than our desired
* hashval, then we run with it.
*/
if (bno > 0) {
error = xfs_da_read_buf(trans, dp, bno, -1, &bp, XFS_DATA_FORK);
if ((error != 0) && (error != EFSCORRUPTED))
return(error);
if (bp)
leaf = bp->data;
if (bp && leaf->hdr.info.magic != XFS_DIR_LEAF_MAGIC) {
xfs_dir_trace_g_dub("node: block not a leaf",
dp, uio, bno);
xfs_da_brelse(trans, bp);
bp = NULL;
}
if (bp && leaf->entries[0].hashval > cookhash) {
xfs_dir_trace_g_dub("node: leaf hash too large",
dp, uio, bno);
xfs_da_brelse(trans, bp);
bp = NULL;
}
if (bp &&
cookhash > leaf->entries[leaf->hdr.count - 1].hashval) {
xfs_dir_trace_g_dub("node: leaf hash too small",
dp, uio, bno);
xfs_da_brelse(trans, bp);
bp = NULL;
}
}
/*
* If we did not find a leaf block from the blockno in the cookie,
* or we there was no blockno in the cookie (eg: first time thru),
* the we start at the top of the Btree and re-find our hashval.
*/
if (bp == NULL) {
xfs_dir_trace_g_du("node: start at root" , dp, uio);
bno = 0;
for (;;) {
error = xfs_da_read_buf(trans, dp, bno, -1, &bp,
XFS_DATA_FORK);
if (error)
return(error);
if (bp == NULL)
return(XFS_ERROR(EFSCORRUPTED));
node = bp->data;
if (node->hdr.info.magic != XFS_DA_NODE_MAGIC)
break;
btree = &node->btree[0];
xfs_dir_trace_g_dun("node: node detail", dp, uio, node);
for (i = 0; i < node->hdr.count; btree++, i++) {
if (btree->hashval >= cookhash) {
bno = btree->before;
break;
}
}
if (i == node->hdr.count) {
xfs_da_brelse(trans, bp);
xfs_dir_trace_g_du("node: hash beyond EOF",
dp, uio);
uio->uio_offset = XFS_DA_MAKE_COOKIE(mp, 0, 0,
XFS_DA_MAXHASH);
*eofp = 1;
return(0);
}
xfs_dir_trace_g_dub("node: going to block",
dp, uio, bno);
xfs_da_brelse(trans, bp);
}
}
ASSERT(cookhash != XFS_DA_MAXHASH);
/*
* We've dropped down to the (first) leaf block that contains the
* hashval we are interested in. Continue rolling upward thru the
* leaf blocks until we fill up our buffer.
*/
for (;;) {
leaf = bp->data;
if (leaf->hdr.info.magic != XFS_DIR_LEAF_MAGIC) {
xfs_dir_trace_g_dul("node: not a leaf", dp, uio, leaf);
xfs_da_brelse(trans, bp);
return XFS_ERROR(EFSCORRUPTED);
}
xfs_dir_trace_g_dul("node: leaf detail", dp, uio, leaf);
if (nextbno = leaf->hdr.info.forw) {
nextda = xfs_da_reada_buf(trans, dp, nextbno,
XFS_DATA_FORK);
} else
nextda = -1;
error = xfs_dir_leaf_getdents_int(bp, dp, bno, uio, &eob, dbp,
put, nextda);
xfs_da_brelse(trans, bp);
bno = nextbno;
if (eob) {
xfs_dir_trace_g_dub("node: E-O-B", dp, uio, bno);
*eofp = 0;
return(error);
}
if (bno == 0)
break;
error = xfs_da_read_buf(trans, dp, bno, nextda, &bp,
XFS_DATA_FORK);
if (error)
return(error);
if (bp == NULL)
return(XFS_ERROR(EFSCORRUPTED));
}
*eofp = 1;
xfs_dir_trace_g_du("node: E-O-F", dp, uio);
return(0);
}
#endif /* !SIM */
#if defined(XFS_REPAIR_SIM) || !defined(SIM)
/*
* Look up a filename in an int directory, replace the inode number.
* Use an internal routine to actually do the lookup.
*/
STATIC int
xfs_dir_node_replace(xfs_da_args_t *args)
{
xfs_da_state_t *state;
xfs_da_state_blk_t *blk;
xfs_dir_leafblock_t *leaf;
xfs_dir_leaf_entry_t *entry;
xfs_dir_leaf_name_t *namest;
xfs_ino_t inum;
int retval, error, i;
xfs_dabuf_t *bp;
state = xfs_da_state_alloc();
state->args = args;
state->mp = args->dp->i_mount;
state->blocksize = state->mp->m_sb.sb_blocksize;
inum = args->inumber;
/*
* Search to see if name exists,
* and get back a pointer to it.
*/
error = xfs_da_node_lookup_int(state, &retval);
if (error) {
retval = error;
}
if (retval == EEXIST) {
blk = &state->path.blk[state->path.active - 1];
ASSERT(blk->magic == XFS_DIR_LEAF_MAGIC);
bp = blk->bp;
leaf = bp->data;
entry = &leaf->entries[blk->index];
namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, entry->nameidx);
ASSERT(bcmp((char *)&inum, (char *)&namest->inumber,
sizeof(inum)));
XFS_DIR_SF_PUT_DIRINO(&inum, &namest->inumber);
xfs_da_log_buf(args->trans, bp,
XFS_DA_LOGRANGE(leaf, namest, sizeof(namest->inumber)));
xfs_da_buf_done(bp);
blk->bp = NULL;
retval = 0;
} else {
i = state->path.active - 1;
xfs_da_brelse(args->trans, state->path.blk[i].bp);
state->path.blk[i].bp = NULL;
}
for (i = 0; i < state->path.active - 1; i++) {
xfs_da_brelse(args->trans, state->path.blk[i].bp);
state->path.blk[i].bp = NULL;
}
xfs_da_state_free(state);
return(retval);
}
#endif /* XFS_REPAIR_SIM || !SIM */
#if defined(XFS_DIR_TRACE)
/*
* Add a trace buffer entry for an inode and a uio.
*/
void
xfs_dir_trace_g_du(char *where, xfs_inode_t *dp, uio_t *uio)
{
xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DU, where,
(__psunsigned_t)dp, (__psunsigned_t)dp->i_mount,
(__psunsigned_t)(uio->uio_offset >> 32),
(__psunsigned_t)(uio->uio_offset & 0xFFFFFFFF),
(__psunsigned_t)uio->uio_resid,
NULL, NULL, NULL, NULL, NULL, NULL, NULL);
}
/*
* Add a trace buffer entry for an inode and a uio.
*/
void
xfs_dir_trace_g_dub(char *where, xfs_inode_t *dp, uio_t *uio, xfs_dablk_t bno)
{
xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUB, where,
(__psunsigned_t)dp, (__psunsigned_t)dp->i_mount,
(__psunsigned_t)(uio->uio_offset >> 32),
(__psunsigned_t)(uio->uio_offset & 0xFFFFFFFF),
(__psunsigned_t)uio->uio_resid,
(__psunsigned_t)bno,
NULL, NULL, NULL, NULL, NULL, NULL);
}
/*
* Add a trace buffer entry for an inode and a uio.
*/
void
xfs_dir_trace_g_dun(char *where, xfs_inode_t *dp, uio_t *uio,
xfs_da_intnode_t *node)
{
xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUN, where,
(__psunsigned_t)dp, (__psunsigned_t)dp->i_mount,
(__psunsigned_t)(uio->uio_offset >> 32),
(__psunsigned_t)(uio->uio_offset & 0xFFFFFFFF),
(__psunsigned_t)uio->uio_resid,
(__psunsigned_t)node->hdr.info.forw,
(__psunsigned_t)node->hdr.count,
(__psunsigned_t)node->btree[0].hashval,
(__psunsigned_t)node->btree[node->hdr.count-1].hashval,
NULL, NULL, NULL);
}
/*
* Add a trace buffer entry for an inode and a uio.
*/
void
xfs_dir_trace_g_dul(char *where, xfs_inode_t *dp, uio_t *uio,
xfs_dir_leafblock_t *leaf)
{
xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUL, where,
(__psunsigned_t)dp, (__psunsigned_t)dp->i_mount,
(__psunsigned_t)(uio->uio_offset >> 32),
(__psunsigned_t)(uio->uio_offset & 0xFFFFFFFF),
(__psunsigned_t)uio->uio_resid,
(__psunsigned_t)leaf->hdr.info.forw,
(__psunsigned_t)leaf->hdr.count,
(__psunsigned_t)leaf->entries[0].hashval,
(__psunsigned_t)leaf->entries[ leaf->hdr.count-1 ].hashval,
NULL, NULL, NULL);
}
/*
* Add a trace buffer entry for an inode and a uio.
*/
void
xfs_dir_trace_g_due(char *where, xfs_inode_t *dp, uio_t *uio,
xfs_dir_leaf_entry_t *entry)
{
xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUE, where,
(__psunsigned_t)dp, (__psunsigned_t)dp->i_mount,
(__psunsigned_t)(uio->uio_offset >> 32),
(__psunsigned_t)(uio->uio_offset & 0xFFFFFFFF),
(__psunsigned_t)uio->uio_resid,
(__psunsigned_t)entry->hashval,
NULL, NULL, NULL, NULL, NULL, NULL);
}
/*
* Add a trace buffer entry for an inode and a uio.
*/
void
xfs_dir_trace_g_duc(char *where, xfs_inode_t *dp, uio_t *uio, off_t cookie)
{
xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUC, where,
(__psunsigned_t)dp, (__psunsigned_t)dp->i_mount,
(__psunsigned_t)(uio->uio_offset >> 32),
(__psunsigned_t)(uio->uio_offset & 0xFFFFFFFF),
(__psunsigned_t)uio->uio_resid,
(__psunsigned_t)(cookie >> 32),
(__psunsigned_t)(cookie & 0xFFFFFFFF),
NULL, NULL, NULL, NULL, NULL);
}
/*
* Add a trace buffer entry for the arguments given to the routine,
* generic form.
*/
void
xfs_dir_trace_enter(int type, char *where,
__psunsigned_t a0, __psunsigned_t a1,
__psunsigned_t a2, __psunsigned_t a3,
__psunsigned_t a4, __psunsigned_t a5,
__psunsigned_t a6, __psunsigned_t a7,
__psunsigned_t a8, __psunsigned_t a9,
__psunsigned_t a10, __psunsigned_t a11)
{
ASSERT(xfs_dir_trace_buf);
ktrace_enter(xfs_dir_trace_buf, (void *)((__psunsigned_t)type),
(void *)where,
(void *)a0, (void *)a1, (void *)a2,
(void *)a3, (void *)a4, (void *)a5,
(void *)a6, (void *)a7, (void *)a8,
(void *)a9, (void *)a10, (void *)a11,
NULL, NULL);
}
#endif /* XFS_DIR_TRACE */