vserver 1.9.3
[linux-2.6.git] / fs / xfs / xfs_vnodeops.c
index 05ab682..95fea44 100644 (file)
@@ -283,7 +283,7 @@ xfs_getattr(
 /*
  * xfs_setattr
  */
-STATIC int
+int
 xfs_setattr(
        bhv_desc_t              *bdp,
        vattr_t                 *vap,
@@ -305,6 +305,7 @@ xfs_setattr(
        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);
@@ -406,7 +407,8 @@ xfs_setattr(
                                goto error_return;
                        }
                }
-               lock_flags |= XFS_IOLOCK_EXCL;
+               if (need_iolock)
+                       lock_flags |= XFS_IOLOCK_EXCL;
        }
 
        xfs_ilock(ip, lock_flags);
@@ -678,7 +680,8 @@ xfs_setattr(
                                             XFS_TRANS_PERM_LOG_RES,
                                             XFS_ITRUNCATE_LOG_COUNT))) {
                        xfs_trans_cancel(tp, 0);
-                       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+                       if (need_iolock)
+                               xfs_iunlock(ip, XFS_IOLOCK_EXCL);
                        return code;
                }
                commit_flags = XFS_TRANS_RELEASE_LOG_RES;
@@ -823,26 +826,38 @@ xfs_setattr(
                                mp->m_sb.sb_blocklog;
                }
                if (mask & XFS_AT_XFLAGS) {
-                       ip->i_d.di_flags = 0;
-                       if (vap->va_xflags & XFS_XFLAG_REALTIME) {
-                               ip->i_d.di_flags |= XFS_DIFLAG_REALTIME;
-                               ip->i_iocore.io_flags |= XFS_IOCORE_RT;
-                       }
+                       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)
-                               ip->i_d.di_flags |= XFS_DIFLAG_IMMUTABLE;
+                               di_flags |= XFS_DIFLAG_IMMUTABLE;
                        if (vap->va_xflags & XFS_XFLAG_IUNLINK)
-                               ip->i_d.di_flags |= XFS_DIFLAG_IUNLINK;
+                               di_flags |= XFS_DIFLAG_IUNLINK;
                        if (vap->va_xflags & XFS_XFLAG_BARRIER)
-                               ip->i_d.di_flags |= XFS_DIFLAG_BARRIER;
+                               di_flags |= XFS_DIFLAG_BARRIER;
                        if (vap->va_xflags & XFS_XFLAG_APPEND)
-                               ip->i_d.di_flags |= XFS_DIFLAG_APPEND;
+                               di_flags |= XFS_DIFLAG_APPEND;
                        if (vap->va_xflags & XFS_XFLAG_SYNC)
-                               ip->i_d.di_flags |= XFS_DIFLAG_SYNC;
+                               di_flags |= XFS_DIFLAG_SYNC;
                        if (vap->va_xflags & XFS_XFLAG_NOATIME)
-                               ip->i_d.di_flags |= XFS_DIFLAG_NOATIME;
+                               di_flags |= XFS_DIFLAG_NOATIME;
                        if (vap->va_xflags & XFS_XFLAG_NODUMP)
-                               ip->i_d.di_flags |= XFS_DIFLAG_NODUMP;
-                       /* can't set PREALLOC this way, just ignore it */
+                               di_flags |= XFS_DIFLAG_NODUMP;
+                       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;
+                       } else {
+                               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;
+                               }
+                       }
+                       ip->i_d.di_flags = di_flags;
                }
                xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
                timeflags |= XFS_ICHGTIME_CHG;
@@ -1600,7 +1615,7 @@ xfs_inactive(
         * If the inode is already free, then there can be nothing
         * to clean up here.
         */
-       if (ip->i_d.di_mode == 0) {
+       if (ip->i_d.di_mode == 0 || VN_BAD(vp)) {
                ASSERT(ip->i_df.if_real_bytes == 0);
                ASSERT(ip->i_df.if_broot_bytes == 0);
                return VN_INACTIVE_CACHE;
@@ -1826,8 +1841,6 @@ xfs_lookup(
 }
 
 
-#define        XFS_CREATE_NEW_MAXTRIES 10000
-
 /*
  * xfs_create (create a new file).
  */
@@ -3385,6 +3398,14 @@ xfs_symlink(
 
        xfs_ilock(dp, XFS_ILOCK_EXCL);
 
+       /*
+        * Check whether the directory allows new symlinks or not.
+        */
+       if (dp->i_d.di_flags & XFS_DIFLAG_NOSYMLINKS) {
+               error = XFS_ERROR(EPERM);
+               goto error_return;
+       }
+
        /*
         * Reserve disk quota : blocks and inode.
         */
@@ -3791,11 +3812,17 @@ xfs_reclaim(
        vnode_t         *vp;
 
        vp = BHV_TO_VNODE(bdp);
+       ip = XFS_BHVTOI(bdp);
 
        vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address);
 
        ASSERT(!VN_MAPPED(vp));
-       ip = XFS_BHVTOI(bdp);
+
+       /* bad inode, get out here ASAP */
+       if (VN_BAD(vp)) {
+               xfs_ireclaim(ip);
+               return 0;
+       }
 
        if ((ip->i_d.di_mode & S_IFMT) == S_IFREG) {
                if (ip->i_d.di_size > 0) {
@@ -3873,8 +3900,12 @@ xfs_finish_reclaim(
        int             sync_mode)
 {
        xfs_ihash_t     *ih = ip->i_hash;
+       vnode_t         *vp = XFS_ITOV_NULL(ip);
        int             error;
 
+       if (vp && VN_BAD(vp))
+               return 0;
+
        /* The hash lock here protects a thread in xfs_iget_core from
         * racing with us on linking the inode back with a vnode.
         * Once we have the XFS_IRECLAIM flag set it will not touch
@@ -3882,8 +3913,7 @@ xfs_finish_reclaim(
         */
        write_lock(&ih->ih_lock);
        if ((ip->i_flags & XFS_IRECLAIM) ||
-           (!(ip->i_flags & XFS_IRECLAIMABLE) &&
-             (XFS_ITOV_NULL(ip) == NULL))) {
+           (!(ip->i_flags & XFS_IRECLAIMABLE) && vp == NULL)) {
                write_unlock(&ih->ih_lock);
                if (locked) {
                        xfs_ifunlock(ip);
@@ -3950,15 +3980,13 @@ int
 xfs_finish_reclaim_all(xfs_mount_t *mp, int noblock)
 {
        int             purged;
-       struct list_head        *curr, *next;
-       xfs_inode_t     *ip;
+       xfs_inode_t     *ip, *n;
        int             done = 0;
 
        while (!done) {
                purged = 0;
                XFS_MOUNT_ILOCK(mp);
-               list_for_each_safe(curr, next, &mp->m_del_inodes) {
-                       ip = list_entry(curr, xfs_inode_t, i_reclaim);
+               list_for_each_entry_safe(ip, n, &mp->m_del_inodes, i_reclaim) {
                        if (noblock) {
                                if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL) == 0)
                                        continue;
@@ -4296,6 +4324,7 @@ xfs_free_file_space(
        int                     rt;
        xfs_fileoff_t           startoffset_fsb;
        xfs_trans_t             *tp;
+       int                     need_iolock = (attr_flags & ATTR_DMI) == 0;
 
        vn_trace_entry(XFS_ITOV(ip), __FUNCTION__, (inst_t *)__return_address);
        mp = ip->i_mount;
@@ -4323,7 +4352,8 @@ xfs_free_file_space(
                        return(error);
        }
 
-       xfs_ilock(ip, XFS_IOLOCK_EXCL);
+       if (need_iolock)
+               xfs_ilock(ip, XFS_IOLOCK_EXCL);
        rounding = MAX((__uint8_t)(1 << mp->m_sb.sb_blocklog),
                        (__uint8_t)NBPP);
        ilen = len + (offset & (rounding - 1));
@@ -4342,7 +4372,7 @@ xfs_free_file_space(
                error = xfs_bmapi(NULL, ip, startoffset_fsb, 1, 0, NULL, 0,
                        &imap, &nimap, NULL);
                if (error)
-                       return error;
+                       goto out_unlock_iolock;
                ASSERT(nimap == 0 || nimap == 1);
                if (nimap && imap.br_startblock != HOLESTARTBLOCK) {
                        xfs_daddr_t     block;
@@ -4357,7 +4387,7 @@ xfs_free_file_space(
                error = xfs_bmapi(NULL, ip, endoffset_fsb - 1, 1, 0, NULL, 0,
                        &imap, &nimap, NULL);
                if (error)
-                       return error;
+                       goto out_unlock_iolock;
                ASSERT(nimap == 0 || nimap == 1);
                if (nimap && imap.br_startblock != HOLESTARTBLOCK) {
                        ASSERT(imap.br_startblock != DELAYSTARTBLOCK);
@@ -4446,14 +4476,17 @@ xfs_free_file_space(
                xfs_iunlock(ip, XFS_ILOCK_EXCL);
        }
 
-       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+ out_unlock_iolock:
+       if (need_iolock)
+               xfs_iunlock(ip, XFS_IOLOCK_EXCL);
        return error;
 
  error0:
        xfs_bmap_cancel(&free_list);
  error1:
        xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
-       xfs_iunlock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+       xfs_iunlock(ip, need_iolock ? (XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL) :
+                   XFS_ILOCK_EXCL);
        return error;
 }
 
@@ -4610,20 +4643,21 @@ xfs_change_file_space(
        xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
        xfs_trans_ihold(tp, ip);
 
-       ip->i_d.di_mode &= ~S_ISUID;
-
-       /*
-        * Note that we don't have to worry about mandatory
-        * file locking being disabled here because we only
-        * clear the S_ISGID bit if the Group execute bit is
-        * on, but if it was on then mandatory locking wouldn't
-        * have been enabled.
-        */
-       if (ip->i_d.di_mode & S_IXGRP)
-               ip->i_d.di_mode &= ~S_ISGID;
+       if ((attr_flags & ATTR_DMI) == 0) {
+               ip->i_d.di_mode &= ~S_ISUID;
 
-       xfs_ichgtime(ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+               /*
+                * Note that we don't have to worry about mandatory
+                * file locking being disabled here because we only
+                * clear the S_ISGID bit if the Group execute bit is
+                * on, but if it was on then mandatory locking wouldn't
+                * have been enabled.
+                */
+               if (ip->i_d.di_mode & S_IXGRP)
+                       ip->i_d.di_mode &= ~S_ISGID;
 
+               xfs_ichgtime(ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+       }
        if (setprealloc)
                ip->i_d.di_flags |= XFS_DIFLAG_PREALLOC;
        else if (clrprealloc)