xfs_vnodeops.c

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

C
2,550
字号
/* * Copyright (c) 2000-2003 Silicon Graphics, Inc.  All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 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. * * Further, this software is distributed without any warranty that it is * free of the rightful claim of any third person regarding infringement * or the like.  Any license provided herein, whether implied or * otherwise, applies only to this software file.  Patent licenses, if * any, provided herein do not apply to combinations of this program with * other software, or any other product whatsoever. * * 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. * * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, * Mountain View, CA  94043, or: * * http://www.sgi.com * * For further information regarding this notice, see: * * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ */#include "xfs.h"#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_ag.h"#include "xfs_dir.h"#include "xfs_dir2.h"#include "xfs_dmapi.h"#include "xfs_mount.h"#include "xfs_alloc_btree.h"#include "xfs_bmap_btree.h"#include "xfs_ialloc_btree.h"#include "xfs_itable.h"#include "xfs_btree.h"#include "xfs_ialloc.h"#include "xfs_alloc.h"#include "xfs_attr_sf.h"#include "xfs_dir_sf.h"#include "xfs_dir2_sf.h"#include "xfs_dinode.h"#include "xfs_inode_item.h"#include "xfs_inode.h"#include "xfs_bmap.h"#include "xfs_da_btree.h"#include "xfs_attr.h"#include "xfs_rw.h"#include "xfs_refcache.h"#include "xfs_error.h"#include "xfs_bit.h"#include "xfs_rtalloc.h"#include "xfs_quota.h"#include "xfs_utils.h"#include "xfs_trans_space.h"#include "xfs_dir_leaf.h"#include "xfs_mac.h"#include "xfs_log_priv.h"/* * The maximum pathlen is 1024 bytes. Since the minimum file system * blocksize is 512 bytes, we can get a max of 2 extents back from * bmapi. */#define SYMLINK_MAPS 2/* * For xfs, we check that the file isn't too big to be opened by this kernel. * No other open action is required for regular files.  Devices are handled * through the specfs file system, pipes through fifofs.  Device and * fifo vnodes are "wrapped" by specfs and fifofs vnodes, respectively, * when a new vnode is first looked up or created. */STATIC intxfs_open(	bhv_desc_t	*bdp,	cred_t		*credp){	int		mode;	vnode_t		*vp;	xfs_inode_t	*ip;	vp = BHV_TO_VNODE(bdp);	ip = XFS_BHVTOI(bdp);	if (XFS_FORCED_SHUTDOWN(ip->i_mount))		return XFS_ERROR(EIO);	/*	 * If it's a directory with any blocks, read-ahead block 0	 * as we're almost certain to have the next operation be a read there.	 */	if (vp->v_type == VDIR && ip->i_d.di_nextents > 0) {		mode = xfs_ilock_map_shared(ip);		if (ip->i_d.di_nextents > 0)			(void)xfs_da_reada_buf(NULL, ip, 0, XFS_DATA_FORK);		xfs_iunlock(ip, mode);	}	return 0;}/* * xfs_getattr */STATIC intxfs_getattr(	bhv_desc_t	*bdp,	vattr_t		*vap,	int		flags,	cred_t		*credp){	xfs_inode_t	*ip;	xfs_mount_t	*mp;	vnode_t		*vp;	vp  = BHV_TO_VNODE(bdp);	vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address);	ip = XFS_BHVTOI(bdp);	mp = ip->i_mount;	if (XFS_FORCED_SHUTDOWN(mp))		return XFS_ERROR(EIO);	if (!(flags & ATTR_LAZY))		xfs_ilock(ip, XFS_ILOCK_SHARED);	vap->va_size = ip->i_d.di_size;	if (vap->va_mask == XFS_AT_SIZE)		goto all_done;	vap->va_nblocks =		XFS_FSB_TO_BB(mp, ip->i_d.di_nblocks + ip->i_delayed_blks);	vap->va_nodeid = ip->i_ino;#if XFS_BIG_INUMS	vap->va_nodeid += mp->m_inoadd;#endif	vap->va_nlink = ip->i_d.di_nlink;	/*	 * Quick exit for non-stat callers	 */	if ((vap->va_mask &	    ~(XFS_AT_SIZE|XFS_AT_FSID|XFS_AT_NODEID|	      XFS_AT_NLINK|XFS_AT_BLKSIZE)) == 0)		goto all_done;	/*	 * Copy from in-core inode.	 */	vap->va_type = vp->v_type;	vap->va_mode = ip->i_d.di_mode & MODEMASK;	vap->va_uid = ip->i_d.di_uid;	vap->va_gid = ip->i_d.di_gid;	vap->va_projid = ip->i_d.di_projid;	/*	 * Check vnode type block/char vs. everything else.	 * Do it with bitmask because that's faster than looking	 * for multiple values individually.	 */	if (((1 << vp->v_type) & ((1<<VBLK) | (1<<VCHR))) == 0) {		vap->va_rdev = 0;		if (!(ip->i_d.di_flags & XFS_DIFLAG_REALTIME)) {#if 0			/* Large block sizes confuse various			 * user space programs, so letting the			 * stripe size through is not a good			 * idea for now.			 */			vap->va_blocksize = mp->m_swidth ?				/*				 * If the underlying volume is a stripe, then				 * return the stripe width in bytes as the				 * recommended I/O size.				 */				(mp->m_swidth << mp->m_sb.sb_blocklog) :				/*				 * Return the largest of the preferred buffer				 * sizes since doing small I/Os into larger				 * buffers causes buffers to be decommissioned.				 * The value returned is in bytes.				 */				(1 << (int)MAX(mp->m_readio_log,					       mp->m_writeio_log));#else			vap->va_blocksize =				/*				 * Return the largest of the preferred buffer				 * sizes since doing small I/Os into larger				 * buffers causes buffers to be decommissioned.				 * The value returned is in bytes.				 */				1 << (int)MAX(mp->m_readio_log,					       mp->m_writeio_log);#endif		} else {			/*			 * If the file blocks are being allocated from a			 * realtime partition, then return the inode's			 * realtime extent size or the realtime volume's			 * extent size.			 */			vap->va_blocksize = ip->i_d.di_extsize ?				(ip->i_d.di_extsize << mp->m_sb.sb_blocklog) :				(mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog);		}	} else {		vap->va_rdev = ip->i_df.if_u2.if_rdev;		vap->va_blocksize = BLKDEV_IOSIZE;	}	vap->va_atime.tv_sec = ip->i_d.di_atime.t_sec;	vap->va_atime.tv_nsec = ip->i_d.di_atime.t_nsec;	vap->va_mtime.tv_sec = ip->i_d.di_mtime.t_sec;	vap->va_mtime.tv_nsec = ip->i_d.di_mtime.t_nsec;	vap->va_ctime.tv_sec = ip->i_d.di_ctime.t_sec;	vap->va_ctime.tv_nsec = ip->i_d.di_ctime.t_nsec;	/*	 * Exit for stat callers.  See if any of the rest of the fields	 * to be filled in are needed.	 */	if ((vap->va_mask &	     (XFS_AT_XFLAGS|XFS_AT_EXTSIZE|XFS_AT_NEXTENTS|XFS_AT_ANEXTENTS|	      XFS_AT_GENCOUNT|XFS_AT_VCODE)) == 0)		goto all_done;	/*	 * Convert di_flags to xflags.	 */	vap->va_xflags = xfs_dic2xflags(&ip->i_d, ARCH_NOCONVERT);	/*	 * Exit for inode revalidate.  See if any of the rest of	 * the fields to be filled in are needed.	 */	if ((vap->va_mask &	     (XFS_AT_EXTSIZE|XFS_AT_NEXTENTS|XFS_AT_ANEXTENTS|	      XFS_AT_GENCOUNT|XFS_AT_VCODE)) == 0)		goto all_done;	vap->va_extsize = ip->i_d.di_extsize << mp->m_sb.sb_blocklog;	vap->va_nextents =		(ip->i_df.if_flags & XFS_IFEXTENTS) ?			ip->i_df.if_bytes / sizeof(xfs_bmbt_rec_t) :			ip->i_d.di_nextents;	if (ip->i_afp)		vap->va_anextents =			(ip->i_afp->if_flags & XFS_IFEXTENTS) ?				ip->i_afp->if_bytes / sizeof(xfs_bmbt_rec_t) :				 ip->i_d.di_anextents;	else		vap->va_anextents = 0;	vap->va_gen = ip->i_d.di_gen; all_done:	if (!(flags & ATTR_LAZY))		xfs_iunlock(ip, XFS_ILOCK_SHARED);	return 0;}/* * xfs_setattr */intxfs_setattr(	bhv_desc_t		*bdp,	vattr_t			*vap,	int			flags,	cred_t			*credp){	xfs_inode_t		*ip;	xfs_trans_t		*tp;	xfs_mount_t		*mp;	int			mask;	int			code;	uint			lock_flags;	uint			commit_flags=0;	uid_t			uid=0, iuid=0;	gid_t			gid=0, igid=0;	int			timeflags = 0;	vnode_t			*vp;	xfs_prid_t		projid=0, iprojid=0;	int			mandlock_before, mandlock_after;	struct xfs_dquot	*udqp, *gdqp, *olddquot1, *olddquot2;	int			file_owner;	int			need_iolock = (flags & ATTR_DMI) == 0;	vp = BHV_TO_VNODE(bdp);	vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address);	if (vp->v_vfsp->vfs_flag & VFS_RDONLY)		return XFS_ERROR(EROFS);	/*	 * Cannot set certain attributes.	 */	mask = vap->va_mask;	if (mask & XFS_AT_NOSET) {		return XFS_ERROR(EINVAL);	}	ip = XFS_BHVTOI(bdp);	mp = ip->i_mount;	if (XFS_FORCED_SHUTDOWN(mp))		return XFS_ERROR(EIO);	/*	 * Timestamps do not need to be logged and hence do not	 * need to be done within a transaction.	 */	if (mask & XFS_AT_UPDTIMES) {		ASSERT((mask & ~XFS_AT_UPDTIMES) == 0);		timeflags = ((mask & XFS_AT_UPDATIME) ? XFS_ICHGTIME_ACC : 0) |			    ((mask & XFS_AT_UPDCTIME) ? XFS_ICHGTIME_CHG : 0) |			    ((mask & XFS_AT_UPDMTIME) ? XFS_ICHGTIME_MOD : 0);		xfs_ichgtime(ip, timeflags);		return 0;	}	olddquot1 = olddquot2 = NULL;	udqp = gdqp = NULL;	/*	 * If disk quotas is on, we make sure that the dquots do exist on disk,	 * before we start any other transactions. Trying to do this later	 * is messy. We don't care to take a readlock to look at the ids	 * in inode here, because we can't hold it across the trans_reserve.	 * If the IDs do change before we take the ilock, we're covered	 * because the i_*dquot fields will get updated anyway.	 */	if (XFS_IS_QUOTA_ON(mp) && (mask & (XFS_AT_UID|XFS_AT_GID))) {		uint	qflags = 0;		if (mask & XFS_AT_UID) {			uid = vap->va_uid;			qflags |= XFS_QMOPT_UQUOTA;		} else {			uid = ip->i_d.di_uid;		}		if (mask & XFS_AT_GID) {			gid = vap->va_gid;			qflags |= XFS_QMOPT_GQUOTA;		}  else {			gid = ip->i_d.di_gid;		}		/*		 * We take a reference when we initialize udqp and gdqp,		 * so it is important that we never blindly double trip on		 * the same variable. See xfs_create() for an example.		 */		ASSERT(udqp == NULL);		ASSERT(gdqp == NULL);		code = XFS_QM_DQVOPALLOC(mp, ip, uid,gid, qflags, &udqp, &gdqp);		if (code)			return (code);	}	/*	 * For the other attributes, we acquire the inode lock and	 * first do an error checking pass.	 */	tp = NULL;	lock_flags = XFS_ILOCK_EXCL;	if (!(mask & XFS_AT_SIZE)) {		if ((mask != (XFS_AT_CTIME|XFS_AT_ATIME|XFS_AT_MTIME)) ||		    (mp->m_flags & XFS_MOUNT_WSYNC)) {			tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE);			commit_flags = 0;			if ((code = xfs_trans_reserve(tp, 0,						     XFS_ICHANGE_LOG_RES(mp), 0,						     0, 0))) {				lock_flags = 0;				goto error_return;			}		}	} else {		if (DM_EVENT_ENABLED (vp->v_vfsp, ip, DM_EVENT_TRUNCATE) &&		    !(flags & ATTR_DMI)) {			int dmflags = AT_DELAY_FLAG(flags) | DM_SEM_FLAG_WR;			code = XFS_SEND_DATA(mp, DM_EVENT_TRUNCATE, vp,				vap->va_size, 0, dmflags, NULL);			if (code) {				lock_flags = 0;				goto error_return;			}		}		if (need_iolock)			lock_flags |= XFS_IOLOCK_EXCL;	}	xfs_ilock(ip, lock_flags);	/* boolean: are we the file owner? */	file_owner = (current_fsuid(credp) == ip->i_d.di_uid);	/*	 * Change various properties of a file.	 * Only the owner or users with CAP_FOWNER	 * capability may do these things.	 */	if (mask &	    (XFS_AT_MODE|XFS_AT_XFLAGS|XFS_AT_EXTSIZE|XFS_AT_UID|	     XFS_AT_GID|XFS_AT_PROJID)) {		/*		 * CAP_FOWNER overrides the following restrictions:		 *		 * The user ID of the calling process must be equal		 * to the file owner ID, except in cases where the		 * CAP_FSETID capability is applicable.		 */		if (!file_owner && !capable(CAP_FOWNER)) {			code = XFS_ERROR(EPERM);			goto error_return;		}		/*		 * CAP_FSETID overrides the following restrictions:		 *		 * The effective user ID of the calling process shall match		 * the file owner when setting the set-user-ID and		 * set-group-ID bits on that file.		 *		 * The effective group ID or one of the supplementary group		 * IDs of the calling process shall match the group owner of		 * the file when setting the set-group-ID bit on that file		 */		if (mask & XFS_AT_MODE) {			mode_t m = 0;			if ((vap->va_mode & S_ISUID) && !file_owner)				m |= S_ISUID;			if ((vap->va_mode & S_ISGID) &&			    !in_group_p((gid_t)ip->i_d.di_gid))				m |= S_ISGID;#if 0			/* Linux allows this, Irix doesn't. */			if ((vap->va_mode & S_ISVTX) && vp->v_type != VDIR)				m |= S_ISVTX;#endif			if (m && !capable(CAP_FSETID))				vap->va_mode &= ~m;		}	}	/*	 * Change file ownership.  Must be the owner or privileged.	 * If the system was configured with the "restricted_chown"	 * option, the owner is not permitted to give away the file,	 * and can change the group id only to a group of which he	 * or she is a member.	 */	if (mask & (XFS_AT_UID|XFS_AT_GID|XFS_AT_PROJID)) {		/*		 * These IDs could have changed since we last looked at them.		 * But, we're assured that if the ownership did change		 * while we didn't have the inode locked, inode's dquot(s)		 * would have changed also.		 */		iuid = ip->i_d.di_uid;		iprojid = ip->i_d.di_projid;		igid = ip->i_d.di_gid;		gid = (mask & XFS_AT_GID) ? vap->va_gid : igid;		uid = (mask & XFS_AT_UID) ? vap->va_uid : iuid;		projid = (mask & XFS_AT_PROJID) ? (xfs_prid_t)vap->va_projid :			 iprojid;		/*		 * CAP_CHOWN overrides the following restrictions:		 *		 * If _POSIX_CHOWN_RESTRICTED is defined, this capability		 * shall override the restriction that a process cannot		 * change the user ID of a file it owns and the restriction		 * that the group ID supplied to the chown() function		 * shall be equal to either the group ID or one of the		 * supplementary group IDs of the calling process.		 *		 * XXX: How does restricted_chown affect projid?		 */		if (restricted_chown &&		    (iuid != uid || (igid != gid &&				     !in_group_p((gid_t)gid))) &&		    !capable(CAP_CHOWN)) {			code = XFS_ERROR(EPERM);			goto error_return;		}		/*		 * Do a quota reservation only if uid or gid is actually

⌨️ 快捷键说明

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