xfs_dir.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,224 行 · 第 1/3 页

C
1,224
字号
	xfs_da_args_t args;	int retval;	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);	XFS_STATS_INC(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);}/* * Implement readdir. */static int							/* error */xfs_dir_getdents(xfs_trans_t *trans, xfs_inode_t *dp, uio_t *uio, int *eofp){	xfs_dirent_t *dbp;	int  alignment, retval;	xfs_dir_put_t put;	XFS_STATS_INC(xs_dir_getdents);	ASSERT((dp->i_d.di_mode & S_IFMT) == S_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.	 */	alignment = sizeof(xfs_off_t) - 1;	if ((uio->uio_iovcnt == 1) &&	    (((__psint_t)uio->uio_iov[0].iov_base & alignment) == 0) &&	    ((uio->uio_iov[0].iov_len & alignment) == 0)) {		dbp = NULL;		put = xfs_dir_put_dirent64_direct;	} else {		dbp = kmem_alloc(sizeof(*dbp) + MAXNAMELEN, KM_SLEEP);		put = xfs_dir_put_dirent64_uio;	}	/*	 * 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);	}	if (dbp != NULL)		kmem_free(dbp, sizeof(*dbp) + MAXNAMELEN);	return(retval);}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 & S_IFMT) == S_IFDIR);	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);}static intxfs_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 ((INT_GET(dp->di_core.di_mode, ARCH_CONVERT) & S_IFMT) != S_IFDIR) {		return 0;	}	if (INT_GET(dp->di_core.di_format, ARCH_CONVERT) != XFS_DINODE_FMT_LOCAL) {		return 0;	}	if (INT_GET(dp->di_core.di_size, ARCH_CONVERT) < sizeof(sf->hdr)) {		xfs_fs_cmn_err(CE_WARN, mp, "Invalid shortform size: dp 0x%p",			dp);		return 1;	}	sf = (xfs_dir_shortform_t *)(&dp->di_u.di_dirsf);	ino = XFS_GET_DIR_INO_ARCH(mp, sf->hdr.parent, ARCH_CONVERT);	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", 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_ARCH(mp, sfe->inumber, ARCH_CONVERT);		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", 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", dp);		return 1;	}	return 0;}/*======================================================================== * External routines when dirsize == XFS_LBSIZE(dp->i_mount). *========================================================================*//* * Add a name to the leaf directory structure * This is the external routine. */intxfs_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);}/* * Remove a name from the leaf directory structure * This is the external routine. */STATIC intxfs_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(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == 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 = INT_GET(leaf->hdr.count, ARCH_CONVERT);		*totallen = INT_GET(leaf->hdr.namebytes, ARCH_CONVERT);		retval = 0;	}	xfs_da_buf_done(bp);	return(retval);}/* * Look up a name in a leaf directory structure. * This is the external routine. */STATIC intxfs_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);}/* * Copy out directory entries for getdents(), for leaf directories. */STATIC intxfs_dir_leaf_getdents(xfs_trans_t *trans, xfs_inode_t *dp, uio_t *uio,				  int *eofp, xfs_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);}/* * Look up a name in a leaf directory structure, replace the inode number. * This is the external routine. */STATIC intxfs_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, INT_GET(entry->nameidx, ARCH_CONVERT));		/* XXX - replace assert? */		XFS_DIR_SF_PUT_DIRINO_ARCH(&inum, &namest->inumber, ARCH_CONVERT);		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);}/*======================================================================== * 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 intxfs_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;	state->node_ents = state->mp->m_dir_node_ents;	/*	 * 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);}/* * 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 intxfs_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;	state->node_ents = state->mp->m_dir_node_ents;	/*	 * 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;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?