📄 xfs_vnodeops.c
字号:
/* * Copyright (c) 2000-2006 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_da_btree.h"#include "xfs_bmap_btree.h"#include "xfs_alloc_btree.h"#include "xfs_ialloc_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_itable.h"#include "xfs_btree.h"#include "xfs_ialloc.h"#include "xfs_alloc.h"#include "xfs_bmap.h"#include "xfs_attr.h"#include "xfs_rw.h"#include "xfs_error.h"#include "xfs_quota.h"#include "xfs_utils.h"#include "xfs_rtalloc.h"#include "xfs_refcache.h"#include "xfs_trans_space.h"#include "xfs_log_priv.h"#include "xfs_filestream.h"#include "xfs_vnodeops.h"intxfs_open( xfs_inode_t *ip){ int mode; 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 (S_ISDIR(ip->i_d.di_mode) && 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 */intxfs_getattr( xfs_inode_t *ip, bhv_vattr_t *vap, int flags){ bhv_vnode_t *vp = XFS_ITOV(ip); xfs_mount_t *mp = ip->i_mount; vn_trace_entry(ip, __FUNCTION__, (inst_t *)__return_address); if (XFS_FORCED_SHUTDOWN(mp)) return XFS_ERROR(EIO); if (!(flags & ATTR_LAZY)) xfs_ilock(ip, XFS_ILOCK_SHARED); vap->va_size = XFS_ISIZE(ip); 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_mode = ip->i_d.di_mode; 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. */ switch (ip->i_d.di_mode & S_IFMT) { case S_IFBLK: case S_IFCHR: vap->va_rdev = ip->i_df.if_u2.if_rdev; vap->va_blocksize = BLKDEV_IOSIZE; break; default: vap->va_rdev = 0; if (!(ip->i_d.di_flags & XFS_DIFLAG_REALTIME)) { vap->va_blocksize = xfs_preferred_iosize(mp); } 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 = xfs_get_extsz_hint(ip) << mp->m_sb.sb_blocklog; } break; } vn_atime_to_timespec(vp, &vap->va_atime); 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_ip2xflags(ip); /* * 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( xfs_inode_t *ip, bhv_vattr_t *vap, int flags, cred_t *credp){ bhv_vnode_t *vp = XFS_ITOV(ip); xfs_mount_t *mp = ip->i_mount; xfs_trans_t *tp; 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; 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 = 1; vn_trace_entry(ip, __FUNCTION__, (inst_t *)__return_address); if (mp->m_flags & XFS_MOUNT_RDONLY) return XFS_ERROR(EROFS); /* * Cannot set certain attributes. */ mask = vap->va_mask; if (mask & XFS_AT_NOSET) { return XFS_ERROR(EINVAL); } 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|XFS_AT_PROJID))) { uint qflags = 0; if ((mask & XFS_AT_UID) && XFS_IS_UQUOTA_ON(mp)) { uid = vap->va_uid; qflags |= XFS_QMOPT_UQUOTA; } else { uid = ip->i_d.di_uid; } if ((mask & XFS_AT_GID) && XFS_IS_GQUOTA_ON(mp)) { gid = vap->va_gid; qflags |= XFS_QMOPT_GQUOTA; } else { gid = ip->i_d.di_gid; } if ((mask & XFS_AT_PROJID) && XFS_IS_PQUOTA_ON(mp)) { projid = vap->va_projid; qflags |= XFS_QMOPT_PQUOTA; } else { projid = ip->i_d.di_projid; } /* * 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, projid, 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 (flags & ATTR_NOLOCK) need_iolock = 0; 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(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) && !VN_ISDIR(vp)) 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. */ 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/projid/gid is actually * going to change. */ if ((XFS_IS_UQUOTA_ON(mp) && iuid != uid) || (XFS_IS_PQUOTA_ON(mp) && iprojid != projid) || (XFS_IS_GQUOTA_ON(mp) && igid != gid)) { ASSERT(tp); code = XFS_QM_DQVOPCHOWNRESV(mp, tp, ip, udqp, gdqp, capable(CAP_FOWNER) ? XFS_QMOPT_FORCE_RES : 0); if (code) /* out of quota */ goto error_return; } } /* * Truncate file. Must have write permission and not be a directory. */ if (mask & XFS_AT_SIZE) { /* Short circuit the truncate case for zero length files */ if ((vap->va_size == 0) && (ip->i_size == 0) && (ip->i_d.di_nextents == 0)) { xfs_iunlock(ip, XFS_ILOCK_EXCL); lock_flags &= ~XFS_ILOCK_EXCL; if (mask & XFS_AT_CTIME) xfs_ichgtime(ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); code = 0; goto error_return; } if (VN_ISDIR(vp)) { code = XFS_ERROR(EISDIR); goto error_return; } else if (!VN_ISREG(vp)) { code = XFS_ERROR(EINVAL); goto error_return; } /* * Make sure that the dquots are attached to the inode. */ if ((code = XFS_QM_DQATTACH(mp, ip, XFS_QMOPT_ILOCKED))) goto error_return; } /* * Change file access or modified times. */ if (mask & (XFS_AT_ATIME|XFS_AT_MTIME)) { if (!file_owner) { if ((flags & ATTR_UTIME) && !capable(CAP_FOWNER)) { code = XFS_ERROR(EPERM); goto error_return; } } } /* * Change extent size or realtime flag. */ if (mask & (XFS_AT_EXTSIZE|XFS_AT_XFLAGS)) { /* * Can't change extent size if any extents are allocated. */ if (ip->i_d.di_nextents && (mask & XFS_AT_EXTSIZE) && ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) !=
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -