xfs_da_btree.c

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

C
2,203
字号
	 * For version 1 directories, adjust the file size if it changed.	 */	if (w == XFS_DATA_FORK && XFS_DIR_IS_V1(mp)) {		ASSERT(mapi == 1);		if ((error = xfs_bmap_last_offset(tp, dp, &bno, w)))			return error;		size = XFS_FSB_TO_B(mp, bno);		if (size != dp->i_d.di_size) {			dp->i_d.di_size = size;			xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);		}	}	return 0;}/* * Ick.  We need to always be able to remove a btree block, even * if there's no space reservation because the filesystem is full. * This is called if xfs_bunmapi on a btree block fails due to ENOSPC. * It swaps the target block with the last block in the file.  The * last block in the file can always be removed since it can't cause * a bmap btree split to do that. */STATIC intxfs_da_swap_lastblock(xfs_da_args_t *args, xfs_dablk_t *dead_blknop,		      xfs_dabuf_t **dead_bufp){	xfs_dablk_t dead_blkno, last_blkno, sib_blkno, par_blkno;	xfs_dabuf_t *dead_buf, *last_buf, *sib_buf, *par_buf;	xfs_fileoff_t lastoff;	xfs_inode_t *ip;	xfs_trans_t *tp;	xfs_mount_t *mp;	int error, w, entno, level, dead_level;	xfs_da_blkinfo_t *dead_info, *sib_info;	xfs_da_intnode_t *par_node, *dead_node;	xfs_dir_leafblock_t *dead_leaf;	xfs_dir2_leaf_t *dead_leaf2;	xfs_dahash_t dead_hash;	dead_buf = *dead_bufp;	dead_blkno = *dead_blknop;	tp = args->trans;	ip = args->dp;	w = args->whichfork;	ASSERT(w == XFS_DATA_FORK);	mp = ip->i_mount;	if (XFS_DIR_IS_V2(mp)) {		lastoff = mp->m_dirfreeblk;		error = xfs_bmap_last_before(tp, ip, &lastoff, w);	} else		error = xfs_bmap_last_offset(tp, ip, &lastoff, w);	if (error)		return error;	if (unlikely(lastoff == 0)) {		XFS_ERROR_REPORT("xfs_da_swap_lastblock(1)", XFS_ERRLEVEL_LOW,				 mp);		return XFS_ERROR(EFSCORRUPTED);	}	/*	 * Read the last block in the btree space.	 */	last_blkno = (xfs_dablk_t)lastoff - mp->m_dirblkfsbs;	if ((error = xfs_da_read_buf(tp, ip, last_blkno, -1, &last_buf, w)))		return error;	/*	 * Copy the last block into the dead buffer and log it.	 */	memcpy(dead_buf->data, last_buf->data, mp->m_dirblksize);	xfs_da_log_buf(tp, dead_buf, 0, mp->m_dirblksize - 1);	dead_info = dead_buf->data;	/*	 * Get values from the moved block.	 */	if (INT_GET(dead_info->magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC) {		ASSERT(XFS_DIR_IS_V1(mp));		dead_leaf = (xfs_dir_leafblock_t *)dead_info;		dead_level = 0;		dead_hash =			INT_GET(dead_leaf->entries[INT_GET(dead_leaf->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT);	} else if (INT_GET(dead_info->magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC) {		ASSERT(XFS_DIR_IS_V2(mp));		dead_leaf2 = (xfs_dir2_leaf_t *)dead_info;		dead_level = 0;		dead_hash = INT_GET(dead_leaf2->ents[INT_GET(dead_leaf2->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT);	} else {		ASSERT(INT_GET(dead_info->magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC);		dead_node = (xfs_da_intnode_t *)dead_info;		dead_level = INT_GET(dead_node->hdr.level, ARCH_CONVERT);		dead_hash = INT_GET(dead_node->btree[INT_GET(dead_node->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT);	}	sib_buf = par_buf = NULL;	/*	 * If the moved block has a left sibling, fix up the pointers.	 */	if ((sib_blkno = INT_GET(dead_info->back, ARCH_CONVERT))) {		if ((error = xfs_da_read_buf(tp, ip, sib_blkno, -1, &sib_buf, w)))			goto done;		sib_info = sib_buf->data;		if (unlikely(		    INT_GET(sib_info->forw, ARCH_CONVERT) != last_blkno ||		    INT_GET(sib_info->magic, ARCH_CONVERT) != INT_GET(dead_info->magic, ARCH_CONVERT))) {			XFS_ERROR_REPORT("xfs_da_swap_lastblock(2)",					 XFS_ERRLEVEL_LOW, mp);			error = XFS_ERROR(EFSCORRUPTED);			goto done;		}		INT_SET(sib_info->forw, ARCH_CONVERT, dead_blkno);		xfs_da_log_buf(tp, sib_buf,			XFS_DA_LOGRANGE(sib_info, &sib_info->forw,					sizeof(sib_info->forw)));		xfs_da_buf_done(sib_buf);		sib_buf = NULL;	}	/*	 * If the moved block has a right sibling, fix up the pointers.	 */	if ((sib_blkno = INT_GET(dead_info->forw, ARCH_CONVERT))) {		if ((error = xfs_da_read_buf(tp, ip, sib_blkno, -1, &sib_buf, w)))			goto done;		sib_info = sib_buf->data;		if (unlikely(		       INT_GET(sib_info->back, ARCH_CONVERT) != last_blkno		    || INT_GET(sib_info->magic, ARCH_CONVERT)				!= INT_GET(dead_info->magic, ARCH_CONVERT))) {			XFS_ERROR_REPORT("xfs_da_swap_lastblock(3)",					 XFS_ERRLEVEL_LOW, mp);			error = XFS_ERROR(EFSCORRUPTED);			goto done;		}		INT_SET(sib_info->back, ARCH_CONVERT, dead_blkno);		xfs_da_log_buf(tp, sib_buf,			XFS_DA_LOGRANGE(sib_info, &sib_info->back,					sizeof(sib_info->back)));		xfs_da_buf_done(sib_buf);		sib_buf = NULL;	}	par_blkno = XFS_DIR_IS_V1(mp) ? 0 : mp->m_dirleafblk;	level = -1;	/*	 * Walk down the tree looking for the parent of the moved block.	 */	for (;;) {		if ((error = xfs_da_read_buf(tp, ip, par_blkno, -1, &par_buf, w)))			goto done;		par_node = par_buf->data;		if (unlikely(		    INT_GET(par_node->hdr.info.magic, ARCH_CONVERT) != XFS_DA_NODE_MAGIC ||		    (level >= 0 && level != INT_GET(par_node->hdr.level, ARCH_CONVERT) + 1))) {			XFS_ERROR_REPORT("xfs_da_swap_lastblock(4)",					 XFS_ERRLEVEL_LOW, mp);			error = XFS_ERROR(EFSCORRUPTED);			goto done;		}		level = INT_GET(par_node->hdr.level, ARCH_CONVERT);		for (entno = 0;		     entno < INT_GET(par_node->hdr.count, ARCH_CONVERT) &&		     INT_GET(par_node->btree[entno].hashval, ARCH_CONVERT) < dead_hash;		     entno++)			continue;		if (unlikely(entno == INT_GET(par_node->hdr.count, ARCH_CONVERT))) {			XFS_ERROR_REPORT("xfs_da_swap_lastblock(5)",					 XFS_ERRLEVEL_LOW, mp);			error = XFS_ERROR(EFSCORRUPTED);			goto done;		}		par_blkno = INT_GET(par_node->btree[entno].before, ARCH_CONVERT);		if (level == dead_level + 1)			break;		xfs_da_brelse(tp, par_buf);		par_buf = NULL;	}	/*	 * We're in the right parent block.	 * Look for the right entry.	 */	for (;;) {		for (;		     entno < INT_GET(par_node->hdr.count, ARCH_CONVERT) &&		     INT_GET(par_node->btree[entno].before, ARCH_CONVERT) != last_blkno;		     entno++)			continue;		if (entno < INT_GET(par_node->hdr.count, ARCH_CONVERT))			break;		par_blkno = INT_GET(par_node->hdr.info.forw, ARCH_CONVERT);		xfs_da_brelse(tp, par_buf);		par_buf = NULL;		if (unlikely(par_blkno == 0)) {			XFS_ERROR_REPORT("xfs_da_swap_lastblock(6)",					 XFS_ERRLEVEL_LOW, mp);			error = XFS_ERROR(EFSCORRUPTED);			goto done;		}		if ((error = xfs_da_read_buf(tp, ip, par_blkno, -1, &par_buf, w)))			goto done;		par_node = par_buf->data;		if (unlikely(		    INT_GET(par_node->hdr.level, ARCH_CONVERT) != level ||		    INT_GET(par_node->hdr.info.magic, ARCH_CONVERT) != XFS_DA_NODE_MAGIC)) {			XFS_ERROR_REPORT("xfs_da_swap_lastblock(7)",					 XFS_ERRLEVEL_LOW, mp);			error = XFS_ERROR(EFSCORRUPTED);			goto done;		}		entno = 0;	}	/*	 * Update the parent entry pointing to the moved block.	 */	INT_SET(par_node->btree[entno].before, ARCH_CONVERT, dead_blkno);	xfs_da_log_buf(tp, par_buf,		XFS_DA_LOGRANGE(par_node, &par_node->btree[entno].before,				sizeof(par_node->btree[entno].before)));	xfs_da_buf_done(par_buf);	xfs_da_buf_done(dead_buf);	*dead_blknop = last_blkno;	*dead_bufp = last_buf;	return 0;done:	if (par_buf)		xfs_da_brelse(tp, par_buf);	if (sib_buf)		xfs_da_brelse(tp, sib_buf);	xfs_da_brelse(tp, last_buf);	return error;}/* * Remove a btree block from a directory or attribute. */intxfs_da_shrink_inode(xfs_da_args_t *args, xfs_dablk_t dead_blkno,		    xfs_dabuf_t *dead_buf){	xfs_inode_t *dp;	int done, error, w, count;	xfs_fileoff_t bno;	xfs_fsize_t size;	xfs_trans_t *tp;	xfs_mount_t *mp;	dp = args->dp;	w = args->whichfork;	tp = args->trans;	mp = dp->i_mount;	if (w == XFS_DATA_FORK && XFS_DIR_IS_V2(mp))		count = mp->m_dirblkfsbs;	else		count = 1;	for (;;) {		/*		 * Remove extents.  If we get ENOSPC for a dir we have to move		 * the last block to the place we want to kill.		 */		if ((error = xfs_bunmapi(tp, dp, dead_blkno, count,				XFS_BMAPI_AFLAG(w)|XFS_BMAPI_METADATA,				0, args->firstblock, args->flist,				&done)) == ENOSPC) {			if (w != XFS_DATA_FORK)				goto done;			if ((error = xfs_da_swap_lastblock(args, &dead_blkno,					&dead_buf)))				goto done;		} else if (error)			goto done;		else			break;	}	ASSERT(done);	xfs_da_binval(tp, dead_buf);	/*	 * Adjust the directory size for version 1.	 */	if (w == XFS_DATA_FORK && XFS_DIR_IS_V1(mp)) {		if ((error = xfs_bmap_last_offset(tp, dp, &bno, w)))			return error;		size = XFS_FSB_TO_B(dp->i_mount, bno);		if (size != dp->i_d.di_size) {			dp->i_d.di_size = size;			xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);		}	}	return 0;done:	xfs_da_binval(tp, dead_buf);	return error;}/* * See if the mapping(s) for this btree block are valid, i.e. * don't contain holes, are logically contiguous, and cover the whole range. */STATIC intxfs_da_map_covers_blocks(	int		nmap,	xfs_bmbt_irec_t	*mapp,	xfs_dablk_t	bno,	int		count){	int		i;	xfs_fileoff_t	off;	for (i = 0, off = bno; i < nmap; i++) {		if (mapp[i].br_startblock == HOLESTARTBLOCK ||		    mapp[i].br_startblock == DELAYSTARTBLOCK) {			return 0;		}		if (off != mapp[i].br_startoff) {			return 0;		}		off += mapp[i].br_blockcount;	}	return off == bno + count;}/* * Make a dabuf. * Used for get_buf, read_buf, read_bufr, and reada_buf. */STATIC intxfs_da_do_buf(	xfs_trans_t	*trans,	xfs_inode_t	*dp,	xfs_dablk_t	bno,	xfs_daddr_t	*mappedbnop,	xfs_dabuf_t	**bpp,	int		whichfork,	int		caller,	inst_t		*ra){	xfs_buf_t	*bp = NULL;	xfs_buf_t	**bplist;	int		error=0;	int		i;	xfs_bmbt_irec_t	map;	xfs_bmbt_irec_t	*mapp;	xfs_daddr_t	mappedbno;	xfs_mount_t	*mp;	int		nbplist=0;	int		nfsb;	int		nmap;	xfs_dabuf_t	*rbp;	mp = dp->i_mount;	if (whichfork == XFS_DATA_FORK && XFS_DIR_IS_V2(mp))		nfsb = mp->m_dirblkfsbs;	else		nfsb = 1;	mappedbno = *mappedbnop;	/*	 * Caller doesn't have a mapping.  -2 means don't complain	 * if we land in a hole.	 */	if (mappedbno == -1 || mappedbno == -2) {		/*		 * Optimize the one-block case.		 */		if (nfsb == 1) {			xfs_fsblock_t	fsb;			if ((error =			    xfs_bmapi_single(trans, dp, whichfork, &fsb,				    (xfs_fileoff_t)bno))) {				return error;			}			mapp = &map;			if (fsb == NULLFSBLOCK) {				nmap = 0;			} else {				map.br_startblock = fsb;				map.br_startoff = (xfs_fileoff_t)bno;				map.br_blockcount = 1;				nmap = 1;			}		} else {			mapp = kmem_alloc(sizeof(*mapp) * nfsb, KM_SLEEP);			nmap = nfsb;			if ((error = xfs_bmapi(trans, dp, (xfs_fileoff_t)bno,					nfsb,					XFS_BMAPI_METADATA |						XFS_BMAPI_AFLAG(whichfork),					NULL, 0, mapp, &nmap, NULL)))				goto exit0;		}	} else {		map.br_startblock = XFS_DADDR_TO_FSB(mp, mappedbno);		map.br_startoff = (xfs_fileoff_t)bno;		map.br_blockcount = nfsb;		mapp = &map;		nmap = 1;	}	if (!xfs_da_map_covers_blocks(nmap, mapp, bno, nfsb)) {		error = mappedbno == -2 ? 0 : XFS_ERROR(EFSCORRUPTED);		if (unlikely(error == EFSCORRUPTED)) {			if (xfs_error_level >= XFS_ERRLEVEL_LOW) {				int	i;				cmn_err(CE_ALERT, "xfs_da_do_buf: bno %lld\n",					(long long)bno);				cmn_err(CE_ALERT, "dir: inode %lld\n",					(long long)dp->i_ino);				for (i = 0; i < nmap; i++) {					cmn_err(CE_ALERT,						"[%02d] br_startoff %lld br_startblock %lld br_blockcount %lld br_state %d\n",						i,						(long long)mapp[i].br_startoff,						(long long)mapp[i].br_startblock,						(long long)mapp[i].br_blockcount,						mapp[i].br_state);				}			}			XFS_ERROR_REPORT("xfs_da_do_buf(1)",					 XFS_ERRLEVEL_LOW, mp);		}		goto exit0;	}	if (caller != 3 && nmap > 1) {		bplist = kmem_alloc(sizeof(*bplist) * nmap, KM_SLEEP);		nbplist = 0;	} else		bplist = NULL;	/*	 * Turn the mapping(s) into buffer(s).	 */	for (i = 0; i < nmap; i++) {		int	nmapped;		mappedbno = XFS_FSB_TO_DADDR(mp, mapp[i].br_startblock);		if (i == 0)			*mappedbnop = mappedbno;		nmapped = (int)XFS_FSB_TO_BB(mp, mapp[i].br_blockcount);		switch (caller) {		case 0:			bp = xfs_trans_get_buf(trans, mp->m_ddev_targp,				mappedbno, nmapped, 0);			error = bp ? XFS_BUF_GETERROR(bp) : XFS_ERROR(EIO);			break;		case 1:#ifndef __KERNEL__		cas

⌨️ 快捷键说明

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