⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 xfs_utils.c

📁 linux 内核源代码
💻 C
字号:
/* * Copyright (c) 2000-2002,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_bit.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_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_bmap.h"#include "xfs_error.h"#include "xfs_quota.h"#include "xfs_rw.h"#include "xfs_itable.h"#include "xfs_utils.h"/* * xfs_get_dir_entry is used to get a reference to an inode given * its parent directory inode and the name of the file.	 It does * not lock the child inode, and it unlocks the directory before * returning.  The directory's generation number is returned for * use by a later call to xfs_lock_dir_and_entry. */intxfs_get_dir_entry(	bhv_vname_t	*dentry,	xfs_inode_t	**ipp){	bhv_vnode_t	*vp;	vp = VNAME_TO_VNODE(dentry);	*ipp = xfs_vtoi(vp);	if (!*ipp)		return XFS_ERROR(ENOENT);	VN_HOLD(vp);	return 0;}intxfs_dir_lookup_int(	xfs_inode_t	*dp,	uint		lock_mode,	bhv_vname_t	*dentry,	xfs_ino_t	*inum,	xfs_inode_t	**ipp){	int		error;	vn_trace_entry(dp, __FUNCTION__, (inst_t *)__return_address);	error = xfs_dir_lookup(NULL, dp, VNAME(dentry), VNAMELEN(dentry), inum);	if (!error) {		/*		 * Unlock the directory. We do this because we can't		 * hold the directory lock while doing the vn_get()		 * in xfs_iget().  Doing so could cause us to hold		 * a lock while waiting for the inode to finish		 * being inactive while it's waiting for a log		 * reservation in the inactive routine.		 */		xfs_iunlock(dp, lock_mode);		error = xfs_iget(dp->i_mount, NULL, *inum, 0, 0, ipp, 0);		xfs_ilock(dp, lock_mode);		if (error) {			*ipp = NULL;		} else if ((*ipp)->i_d.di_mode == 0) {			/*			 * The inode has been freed.  Something is			 * wrong so just get out of here.			 */			xfs_iunlock(dp, lock_mode);			xfs_iput_new(*ipp, 0);			*ipp = NULL;			xfs_ilock(dp, lock_mode);			error = XFS_ERROR(ENOENT);		}	}	return error;}/* * Allocates a new inode from disk and return a pointer to the * incore copy. This routine will internally commit the current * transaction and allocate a new one if the Space Manager needed * to do an allocation to replenish the inode free-list. * * This routine is designed to be called from xfs_create and * xfs_create_dir. * */intxfs_dir_ialloc(	xfs_trans_t	**tpp,		/* input: current transaction;					   output: may be a new transaction. */	xfs_inode_t	*dp,		/* directory within whose allocate					   the inode. */	mode_t		mode,	xfs_nlink_t	nlink,	xfs_dev_t	rdev,	cred_t		*credp,	prid_t		prid,		/* project id */	int		okalloc,	/* ok to allocate new space */	xfs_inode_t	**ipp,		/* pointer to inode; it will be					   locked. */	int		*committed){	xfs_trans_t	*tp;	xfs_trans_t	*ntp;	xfs_inode_t	*ip;	xfs_buf_t	*ialloc_context = NULL;	boolean_t	call_again = B_FALSE;	int		code;	uint		log_res;	uint		log_count;	void		*dqinfo;	uint		tflags;	tp = *tpp;	ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);	/*	 * xfs_ialloc will return a pointer to an incore inode if	 * the Space Manager has an available inode on the free	 * list. Otherwise, it will do an allocation and replenish	 * the freelist.  Since we can only do one allocation per	 * transaction without deadlocks, we will need to commit the	 * current transaction and start a new one.  We will then	 * need to call xfs_ialloc again to get the inode.	 *	 * If xfs_ialloc did an allocation to replenish the freelist,	 * it returns the bp containing the head of the freelist as	 * ialloc_context. We will hold a lock on it across the	 * transaction commit so that no other process can steal	 * the inode(s) that we've just allocated.	 */	code = xfs_ialloc(tp, dp, mode, nlink, rdev, credp, prid, okalloc,			  &ialloc_context, &call_again, &ip);	/*	 * Return an error if we were unable to allocate a new inode.	 * This should only happen if we run out of space on disk or	 * encounter a disk error.	 */	if (code) {		*ipp = NULL;		return code;	}	if (!call_again && (ip == NULL)) {		*ipp = NULL;		return XFS_ERROR(ENOSPC);	}	/*	 * If call_again is set, then we were unable to get an	 * inode in one operation.  We need to commit the current	 * transaction and call xfs_ialloc() again.  It is guaranteed	 * to succeed the second time.	 */	if (call_again) {		/*		 * Normally, xfs_trans_commit releases all the locks.		 * We call bhold to hang on to the ialloc_context across		 * the commit.  Holding this buffer prevents any other		 * processes from doing any allocations in this		 * allocation group.		 */		xfs_trans_bhold(tp, ialloc_context);		/*		 * Save the log reservation so we can use		 * them in the next transaction.		 */		log_res = xfs_trans_get_log_res(tp);		log_count = xfs_trans_get_log_count(tp);		/*		 * We want the quota changes to be associated with the next		 * transaction, NOT this one. So, detach the dqinfo from this		 * and attach it to the next transaction.		 */		dqinfo = NULL;		tflags = 0;		if (tp->t_dqinfo) {			dqinfo = (void *)tp->t_dqinfo;			tp->t_dqinfo = NULL;			tflags = tp->t_flags & XFS_TRANS_DQ_DIRTY;			tp->t_flags &= ~(XFS_TRANS_DQ_DIRTY);		}		ntp = xfs_trans_dup(tp);		code = xfs_trans_commit(tp, 0);		tp = ntp;		if (committed != NULL) {			*committed = 1;		}		/*		 * If we get an error during the commit processing,		 * release the buffer that is still held and return		 * to the caller.		 */		if (code) {			xfs_buf_relse(ialloc_context);			if (dqinfo) {				tp->t_dqinfo = dqinfo;				XFS_TRANS_FREE_DQINFO(tp->t_mountp, tp);			}			*tpp = ntp;			*ipp = NULL;			return code;		}		code = xfs_trans_reserve(tp, 0, log_res, 0,					 XFS_TRANS_PERM_LOG_RES, log_count);		/*		 * Re-attach the quota info that we detached from prev trx.		 */		if (dqinfo) {			tp->t_dqinfo = dqinfo;			tp->t_flags |= tflags;		}		if (code) {			xfs_buf_relse(ialloc_context);			*tpp = ntp;			*ipp = NULL;			return code;		}		xfs_trans_bjoin(tp, ialloc_context);		/*		 * Call ialloc again. Since we've locked out all		 * other allocations in this allocation group,		 * this call should always succeed.		 */		code = xfs_ialloc(tp, dp, mode, nlink, rdev, credp, prid,				  okalloc, &ialloc_context, &call_again, &ip);		/*		 * If we get an error at this point, return to the caller		 * so that the current transaction can be aborted.		 */		if (code) {			*tpp = tp;			*ipp = NULL;			return code;		}		ASSERT ((!call_again) && (ip != NULL));	} else {		if (committed != NULL) {			*committed = 0;		}	}	*ipp = ip;	*tpp = tp;	return 0;}/* * Decrement the link count on an inode & log the change. * If this causes the link count to go to zero, initiate the * logging activity required to truncate a file. */int				/* error */xfs_droplink(	xfs_trans_t *tp,	xfs_inode_t *ip){	int	error;	xfs_ichgtime(ip, XFS_ICHGTIME_CHG);	ASSERT (ip->i_d.di_nlink > 0);	ip->i_d.di_nlink--;	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);	error = 0;	if (ip->i_d.di_nlink == 0) {		/*		 * We're dropping the last link to this file.		 * Move the on-disk inode to the AGI unlinked list.		 * From xfs_inactive() we will pull the inode from		 * the list and free it.		 */		error = xfs_iunlink(tp, ip);	}	return error;}/* * This gets called when the inode's version needs to be changed from 1 to 2. * Currently this happens when the nlink field overflows the old 16-bit value * or when chproj is called to change the project for the first time. * As a side effect the superblock version will also get rev'd * to contain the NLINK bit. */voidxfs_bump_ino_vers2(	xfs_trans_t	*tp,	xfs_inode_t	*ip){	xfs_mount_t	*mp;	unsigned long		s;	ASSERT(ismrlocked (&ip->i_lock, MR_UPDATE));	ASSERT(ip->i_d.di_version == XFS_DINODE_VERSION_1);	ip->i_d.di_version = XFS_DINODE_VERSION_2;	ip->i_d.di_onlink = 0;	memset(&(ip->i_d.di_pad[0]), 0, sizeof(ip->i_d.di_pad));	mp = tp->t_mountp;	if (!XFS_SB_VERSION_HASNLINK(&mp->m_sb)) {		s = XFS_SB_LOCK(mp);		if (!XFS_SB_VERSION_HASNLINK(&mp->m_sb)) {			XFS_SB_VERSION_ADDNLINK(&mp->m_sb);			XFS_SB_UNLOCK(mp, s);			xfs_mod_sb(tp, XFS_SB_VERSIONNUM);		} else {			XFS_SB_UNLOCK(mp, s);		}	}	/* Caller must log the inode */}/* * Increment the link count on an inode & log the change. */intxfs_bumplink(	xfs_trans_t *tp,	xfs_inode_t *ip){	if (ip->i_d.di_nlink >= XFS_MAXLINK)		return XFS_ERROR(EMLINK);	xfs_ichgtime(ip, XFS_ICHGTIME_CHG);	ASSERT(ip->i_d.di_nlink > 0);	ip->i_d.di_nlink++;	if ((ip->i_d.di_version == XFS_DINODE_VERSION_1) &&	    (ip->i_d.di_nlink > XFS_MAXLINK_1)) {		/*		 * The inode has increased its number of links beyond		 * what can fit in an old format inode.  It now needs		 * to be converted to a version 2 inode with a 32 bit		 * link count.  If this is the first inode in the file		 * system to do this, then we need to bump the superblock		 * version number as well.		 */		xfs_bump_ino_vers2(tp, ip);	}	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);	return 0;}/* * Try to truncate the given file to 0 length.  Currently called * only out of xfs_remove when it has to truncate a file to free * up space for the remove to proceed. */intxfs_truncate_file(	xfs_mount_t	*mp,	xfs_inode_t	*ip){	xfs_trans_t	*tp;	int		error;#ifdef QUOTADEBUG	/*	 * This is called to truncate the quotainodes too.	 */	if (XFS_IS_UQUOTA_ON(mp)) {		if (ip->i_ino != mp->m_sb.sb_uquotino)			ASSERT(ip->i_udquot);	}	if (XFS_IS_OQUOTA_ON(mp)) {		if (ip->i_ino != mp->m_sb.sb_gquotino)			ASSERT(ip->i_gdquot);	}#endif	/*	 * Make the call to xfs_itruncate_start before starting the	 * transaction, because we cannot make the call while we're	 * in a transaction.	 */	xfs_ilock(ip, XFS_IOLOCK_EXCL);	error = xfs_itruncate_start(ip, XFS_ITRUNC_DEFINITE, (xfs_fsize_t)0);	if (error) {		xfs_iunlock(ip, XFS_IOLOCK_EXCL);		return error;	}	tp = xfs_trans_alloc(mp, XFS_TRANS_TRUNCATE_FILE);	if ((error = xfs_trans_reserve(tp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0,				      XFS_TRANS_PERM_LOG_RES,				      XFS_ITRUNCATE_LOG_COUNT))) {		xfs_trans_cancel(tp, 0);		xfs_iunlock(ip, XFS_IOLOCK_EXCL);		return error;	}	/*	 * Follow the normal truncate locking protocol.  Since we	 * hold the inode in the transaction, we know that it's number	 * of references will stay constant.	 */	xfs_ilock(ip, XFS_ILOCK_EXCL);	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);	xfs_trans_ihold(tp, ip);	/*	 * Signal a sync xaction.  The only case where that isn't	 * the case is if we're truncating an already unlinked file	 * on a wsync fs.  In that case, we know the blocks can't	 * reappear in the file because the links to file are	 * permanently toast.  Currently, we're always going to	 * want a sync transaction because this code is being	 * called from places where nlink is guaranteed to be 1	 * but I'm leaving the tests in to protect against future	 * changes -- rcc.	 */	error = xfs_itruncate_finish(&tp, ip, (xfs_fsize_t)0,				     XFS_DATA_FORK,				     ((ip->i_d.di_nlink != 0 ||				       !(mp->m_flags & XFS_MOUNT_WSYNC))				      ? 1 : 0));	if (error) {		xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES |				 XFS_TRANS_ABORT);	} else {		xfs_ichgtime(ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);		error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);	}	xfs_iunlock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);	return error;}

⌨️ 快捷键说明

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