📄 xfs_vnodeops.c
字号:
vap->va_extsize) ) { code = XFS_ERROR(EINVAL); /* EFBIG? */ goto error_return; } /* * Can't change realtime flag if any extents are allocated. */ if ((ip->i_d.di_nextents || ip->i_delayed_blks) && (mask & XFS_AT_XFLAGS) && (ip->i_d.di_flags & XFS_DIFLAG_REALTIME) != (vap->va_xflags & XFS_XFLAG_REALTIME)) { code = XFS_ERROR(EINVAL); /* EFBIG? */ goto error_return; } /* * Extent size must be a multiple of the appropriate block * size, if set at all. */ if ((mask & XFS_AT_EXTSIZE) && vap->va_extsize != 0) { xfs_extlen_t size; if ((ip->i_d.di_flags & XFS_DIFLAG_REALTIME) || ((mask & XFS_AT_XFLAGS) && (vap->va_xflags & XFS_XFLAG_REALTIME))) { size = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog; } else { size = mp->m_sb.sb_blocksize; } if (vap->va_extsize % size) { code = XFS_ERROR(EINVAL); goto error_return; } } /* * If realtime flag is set then must have realtime data. */ if ((mask & XFS_AT_XFLAGS) && (vap->va_xflags & XFS_XFLAG_REALTIME)) { if ((mp->m_sb.sb_rblocks == 0) || (mp->m_sb.sb_rextsize == 0) || (ip->i_d.di_extsize % mp->m_sb.sb_rextsize)) { code = XFS_ERROR(EINVAL); goto error_return; } } /* * Can't modify an immutable/append-only file unless * we have appropriate permission. */ if ((mask & XFS_AT_XFLAGS) && (ip->i_d.di_flags & (XFS_DIFLAG_IMMUTABLE|XFS_DIFLAG_APPEND) || (vap->va_xflags & (XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND))) && !capable(CAP_LINUX_IMMUTABLE)) { code = XFS_ERROR(EPERM); goto error_return; } } /* * Now we can make the changes. Before we join the inode * to the transaction, if XFS_AT_SIZE is set then take care of * the part of the truncation that must be done without the * inode lock. This needs to be done before joining the inode * to the transaction, because the inode cannot be unlocked * once it is a part of the transaction. */ if (mask & XFS_AT_SIZE) { code = 0; if ((vap->va_size > ip->i_size) && (flags & ATTR_NOSIZETOK) == 0) { code = xfs_igrow_start(ip, vap->va_size, credp); } xfs_iunlock(ip, XFS_ILOCK_EXCL); /* * We are going to log the inode size change in this * transaction so any previous writes that are beyond the on * disk EOF and the new EOF that have not been written out need * to be written here. If we do not write the data out, we * expose ourselves to the null files problem. * * Only flush from the on disk size to the smaller of the in * memory file size or the new size as that's the range we * really care about here and prevents waiting for other data * not within the range we care about here. */ if (!code && (ip->i_size != ip->i_d.di_size) && (vap->va_size > ip->i_d.di_size)) { code = xfs_flush_pages(ip, ip->i_d.di_size, vap->va_size, XFS_B_ASYNC, FI_NONE); } /* wait for all I/O to complete */ vn_iowait(ip); if (!code) code = xfs_itruncate_data(ip, vap->va_size); if (code) { ASSERT(tp == NULL); lock_flags &= ~XFS_ILOCK_EXCL; ASSERT(lock_flags == XFS_IOLOCK_EXCL); goto error_return; } tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_SIZE); if ((code = 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); if (need_iolock) xfs_iunlock(ip, XFS_IOLOCK_EXCL); return code; } commit_flags = XFS_TRANS_RELEASE_LOG_RES; xfs_ilock(ip, XFS_ILOCK_EXCL); } if (tp) { xfs_trans_ijoin(tp, ip, lock_flags); xfs_trans_ihold(tp, ip); } /* determine whether mandatory locking mode changes */ mandlock_before = MANDLOCK(vp, ip->i_d.di_mode); /* * Truncate file. Must have write permission and not be a directory. */ if (mask & XFS_AT_SIZE) { if (vap->va_size > ip->i_size) { xfs_igrow_finish(tp, ip, vap->va_size, !(flags & ATTR_DMI)); } else if ((vap->va_size <= ip->i_size) || ((vap->va_size == 0) && ip->i_d.di_nextents)) { /* * signal a sync transaction unless * we're truncating an already unlinked * file on a wsync filesystem */ code = xfs_itruncate_finish(&tp, ip, (xfs_fsize_t)vap->va_size, XFS_DATA_FORK, ((ip->i_d.di_nlink != 0 || !(mp->m_flags & XFS_MOUNT_WSYNC)) ? 1 : 0)); if (code) goto abort_return; /* * Truncated "down", so we're removing references * to old data here - if we now delay flushing for * a long time, we expose ourselves unduly to the * notorious NULL files problem. So, we mark this * vnode and flush it when the file is closed, and * do not wait the usual (long) time for writeout. */ xfs_iflags_set(ip, XFS_ITRUNCATED); } /* * Have to do this even if the file's size doesn't change. */ timeflags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG; } /* * Change file access modes. */ if (mask & XFS_AT_MODE) { ip->i_d.di_mode &= S_IFMT; ip->i_d.di_mode |= vap->va_mode & ~S_IFMT; xfs_trans_log_inode (tp, ip, XFS_ILOG_CORE); timeflags |= XFS_ICHGTIME_CHG; } /* * 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)) { /* * CAP_FSETID overrides the following restrictions: * * The set-user-ID and set-group-ID bits of a file will be * cleared upon successful return from chown() */ if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) && !capable(CAP_FSETID)) { ip->i_d.di_mode &= ~(S_ISUID|S_ISGID); } /* * Change the ownerships and register quota modifications * in the transaction. */ if (iuid != uid) { if (XFS_IS_UQUOTA_ON(mp)) { ASSERT(mask & XFS_AT_UID); ASSERT(udqp); olddquot1 = XFS_QM_DQVOPCHOWN(mp, tp, ip, &ip->i_udquot, udqp); } ip->i_d.di_uid = uid; } if (igid != gid) { if (XFS_IS_GQUOTA_ON(mp)) { ASSERT(!XFS_IS_PQUOTA_ON(mp)); ASSERT(mask & XFS_AT_GID); ASSERT(gdqp); olddquot2 = XFS_QM_DQVOPCHOWN(mp, tp, ip, &ip->i_gdquot, gdqp); } ip->i_d.di_gid = gid; } if (iprojid != projid) { if (XFS_IS_PQUOTA_ON(mp)) { ASSERT(!XFS_IS_GQUOTA_ON(mp)); ASSERT(mask & XFS_AT_PROJID); ASSERT(gdqp); olddquot2 = XFS_QM_DQVOPCHOWN(mp, tp, ip, &ip->i_gdquot, gdqp); } ip->i_d.di_projid = projid; /* * We may have to rev the inode as well as * the superblock version number since projids didn't * exist before DINODE_VERSION_2 and SB_VERSION_NLINK. */ if (ip->i_d.di_version == XFS_DINODE_VERSION_1) xfs_bump_ino_vers2(tp, ip); } xfs_trans_log_inode (tp, ip, XFS_ILOG_CORE); timeflags |= XFS_ICHGTIME_CHG; } /* * Change file access or modified times. */ if (mask & (XFS_AT_ATIME|XFS_AT_MTIME)) { if (mask & XFS_AT_ATIME) { ip->i_d.di_atime.t_sec = vap->va_atime.tv_sec; ip->i_d.di_atime.t_nsec = vap->va_atime.tv_nsec; ip->i_update_core = 1; timeflags &= ~XFS_ICHGTIME_ACC; } if (mask & XFS_AT_MTIME) { ip->i_d.di_mtime.t_sec = vap->va_mtime.tv_sec; ip->i_d.di_mtime.t_nsec = vap->va_mtime.tv_nsec; timeflags &= ~XFS_ICHGTIME_MOD; timeflags |= XFS_ICHGTIME_CHG; } if (tp && (flags & ATTR_UTIME)) xfs_trans_log_inode (tp, ip, XFS_ILOG_CORE); } /* * Change XFS-added attributes. */ if (mask & (XFS_AT_EXTSIZE|XFS_AT_XFLAGS)) { if (mask & XFS_AT_EXTSIZE) { /* * Converting bytes to fs blocks. */ ip->i_d.di_extsize = vap->va_extsize >> mp->m_sb.sb_blocklog; } if (mask & XFS_AT_XFLAGS) { uint di_flags; /* can't set PREALLOC this way, just preserve it */ di_flags = (ip->i_d.di_flags & XFS_DIFLAG_PREALLOC); if (vap->va_xflags & XFS_XFLAG_IMMUTABLE) di_flags |= XFS_DIFLAG_IMMUTABLE; if (vap->va_xflags & XFS_XFLAG_APPEND) di_flags |= XFS_DIFLAG_APPEND; if (vap->va_xflags & XFS_XFLAG_SYNC) di_flags |= XFS_DIFLAG_SYNC; if (vap->va_xflags & XFS_XFLAG_NOATIME) di_flags |= XFS_DIFLAG_NOATIME; if (vap->va_xflags & XFS_XFLAG_NODUMP) di_flags |= XFS_DIFLAG_NODUMP; if (vap->va_xflags & XFS_XFLAG_PROJINHERIT) di_flags |= XFS_DIFLAG_PROJINHERIT; if (vap->va_xflags & XFS_XFLAG_NODEFRAG) di_flags |= XFS_DIFLAG_NODEFRAG; if (vap->va_xflags & XFS_XFLAG_FILESTREAM) di_flags |= XFS_DIFLAG_FILESTREAM; if ((ip->i_d.di_mode & S_IFMT) == S_IFDIR) { if (vap->va_xflags & XFS_XFLAG_RTINHERIT) di_flags |= XFS_DIFLAG_RTINHERIT; if (vap->va_xflags & XFS_XFLAG_NOSYMLINKS) di_flags |= XFS_DIFLAG_NOSYMLINKS; if (vap->va_xflags & XFS_XFLAG_EXTSZINHERIT) di_flags |= XFS_DIFLAG_EXTSZINHERIT; } else if ((ip->i_d.di_mode & S_IFMT) == S_IFREG) { if (vap->va_xflags & XFS_XFLAG_REALTIME) { di_flags |= XFS_DIFLAG_REALTIME; ip->i_iocore.io_flags |= XFS_IOCORE_RT; } else { ip->i_iocore.io_flags &= ~XFS_IOCORE_RT; } if (vap->va_xflags & XFS_XFLAG_EXTSIZE) di_flags |= XFS_DIFLAG_EXTSIZE; } ip->i_d.di_flags = di_flags; } xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); timeflags |= XFS_ICHGTIME_CHG; } /* * Change file inode change time only if XFS_AT_CTIME set * AND we have been called by a DMI function. */ if ( (flags & ATTR_DMI) && (mask & XFS_AT_CTIME) ) { ip->i_d.di_ctime.t_sec = vap->va_ctime.tv_sec; ip->i_d.di_ctime.t_nsec = vap->va_ctime.tv_nsec; ip->i_update_core = 1; timeflags &= ~XFS_ICHGTIME_CHG; } /* * Send out timestamp changes that need to be set to the * current time. Not done when called by a DMI function. */ if (timeflags && !(flags & ATTR_DMI)) xfs_ichgtime(ip, timeflags); XFS_STATS_INC(xs_ig_attrchg); /* * If this is a synchronous mount, make sure that the * transaction goes to disk before returning to the user. * This is slightly sub-optimal in that truncates require * two sync transactions instead of one for wsync filesystems. * One for the truncate and one for the timestamps since we * don't want to change the timestamps unless we're sure the * truncate worked. Truncates are less than 1% of the laddis * mix so this probably isn't worth the trouble to optimize. */ code = 0; if (tp) { if (mp->m_flags & XFS_MOUNT_WSYNC) xfs_trans_set_sync(tp); code = xfs_trans_commit(tp, commit_flags); } /* * If the (regular) file's mandatory locking mode changed, then * notify the vnode. We do this under the inode lock to prevent * racing calls to vop_vnode_change. */ mandlock_after = MANDLOCK(vp, ip->i_d.di_mode); xfs_iunlock(ip, lock_flags); /* * Release any dquot(s) the inode had kept before chown. */ XFS_QM_DQRELE(mp, olddquot1); XFS_QM_DQRELE(mp, olddquot2); XFS_QM_DQRELE(mp, udqp); XFS_QM_DQRELE(mp, gdqp); if (code) { return code; } if (DM_EVENT_ENABLED(ip, DM_EVENT_ATTRIBUTE) && !(flags & ATTR_DMI)) { (void) XFS_SEND_NAMESP(mp, DM_EVENT_ATTRIBUTE, vp, DM_RIGHT_NULL, NULL, DM_RIGHT_NULL, NULL, NULL, 0, 0, AT_DELAY_FLAG(flags)); } return 0; abort_return: commit_flags |= XFS_TRANS_ABORT; /* FALLTHROUGH */ error_return: XFS_QM_DQRELE(mp, udqp); XFS_QM_DQRELE(mp, gdqp); if (tp) { xfs_trans_cancel(tp, commit_flags); } if (lock_flags != 0) { xfs_iunlock(ip, lock_flags); } return code;}/* * xfs_access * Null conversion from vnode mode bits to inode mode bits, as in efs. */intxfs_access( xfs_inode_t *ip, int mode, cred_t *credp){ int error; vn_trace_entry(ip, __FUNCTION__, (inst_t *)__return_address); xfs_ilock(ip, XFS_ILOCK_SHARED); error = xfs_iaccess(ip, mode, credp); xfs_iunlock(ip, XFS_ILOCK_SHARED); return error;}/* * 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 2STATIC intxfs_readlink_bmap( xfs_inode_t *ip, char *link){ xfs_mount_t *mp = ip->i_mount; int pathlen = ip->i_d.di_size; int nmaps = SYMLINK_MAPS; xfs_bmbt_irec_t mval[SYMLINK_MAPS]; xfs_daddr_t d; int byte_cnt; int n; xfs_buf_t *bp; int error = 0; error = xfs_bmapi(NULL, ip, 0, XFS_B_TO_FSB(mp, pathlen), 0, NULL, 0, mval, &nmaps, NULL, NULL); if (error) goto out; for (n = 0; n < nmaps; n++) { d = XFS_FSB_TO_DADDR(mp, mval[n].br_startblock); byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount); bp = xfs_buf_read(mp->m_ddev_targp, d, BTOBB(byte_cnt), 0); error = XFS_BUF_GETERROR(bp); if (error) { xfs_ioerror_alert("xfs_readlink", ip->i_mount, bp, XFS_BUF_ADDR(bp)); xfs_buf_relse(bp); goto out; } if (pathlen < byte_cnt) byte_cnt = pathlen; pathlen -= byte_cnt; memcpy(link, XFS_BUF_PTR(bp), byte_cnt); xfs_buf_relse(bp); } link[ip->i_d.di_size] = '\0'; error = 0; out: return error;}intxfs_readlink( xfs_inode_t *ip, char *link){ xfs_mount_t *mp = ip->i_mount; int pathlen; int error = 0; vn_trace_entry(ip, __FUNCTION__, (inst_t *)__return_address); if (XFS_FORCED_SHUTDOWN(mp)) return XFS_ERROR(EIO); xfs_ilock(ip, XFS_ILOCK_SHARED); ASSERT((ip->i_d.di_mode & S_IFMT) == S_IFLNK); ASSERT(ip->i_d.di_size <= MAXPATHLEN); pathlen = ip->i_d.di_size;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -