VFS quota: start enforcing limits
authorFrancois Tigeot <ftigeot@wolfpond.org>
Thu, 22 Mar 2012 13:33:43 +0000 (14:33 +0100)
committerFran├žois Tigeot <ftigeot@wolfpond.org>
Mon, 16 Apr 2012 19:18:51 +0000 (21:18 +0200)
* Add a function vq_write_ok() to check if writing a specified amount
  of data to a mounted filesystem is allowed.

* It only checks a global per mount-point limit for now.

* Enforce this limit check in vop_write().

sys/kern/vfs_quota.c
sys/kern/vfs_vopops.c
sys/sys/vfs_quota.h

index 1fdd488..a7723ef 100644 (file)
@@ -357,7 +357,7 @@ done:
        return error;
 }
 
-/* 
+/*
  * Returns a valid mount point for accounting purposes
  * We cannot simply use vp->v_mount if the vnode belongs
  * to a PFS mount point
@@ -376,3 +376,19 @@ vq_vptomp(struct vnode *vp)
                return vp->v_mount;
        }
 }
+
+int
+vq_write_ok(struct mount *mp, uid_t uid, gid_t gid, uint64_t delta)
+{
+       int rv = 1;
+
+       spin_lock(&mp->mnt_acct.ac_spin);
+
+       if (mp->mnt_acct.ac_limit == 0)
+               goto done;
+       if ((mp->mnt_acct.ac_bytes + delta) > mp->mnt_acct.ac_limit)
+               rv = 0;
+done:
+       spin_unlock(&mp->mnt_acct.ac_spin);
+       return rv;
+}
index 8343d9a..c503d35 100644 (file)
@@ -419,6 +419,7 @@ vop_write(struct vop_ops *ops, struct vnode *vp, struct uio *uio, int ioflag,
        struct vattr va;
        uint64_t size_before=0, size_after=0;
        struct mount *mp;
+       uint64_t offset, delta;
 
        ap.a_head.a_desc = &vop_write_desc;
        ap.a_head.a_ops = ops;
@@ -428,22 +429,34 @@ vop_write(struct vop_ops *ops, struct vnode *vp, struct uio *uio, int ioflag,
        ap.a_cred = cred;
 
        /* is this a regular vnode ? */
-       if ((vp->v_type == VREG) && vfs_accounting_enabled) {
+       VFS_MPLOCK_FLAG(vp->v_mount, MNTK_WR_MPSAFE);
+       if (vfs_accounting_enabled && (vp->v_type == VREG)) {
                if ((error = VOP_GETATTR(vp, &va)) != 0)
-                       return (error);
+                       goto done;
                size_before = va.va_size;
                /* this file may already have been removed */
                if (va.va_nlink > 0)
                        do_accounting = 1;
-       }
 
-       VFS_MPLOCK_FLAG(vp->v_mount, MNTK_WR_MPSAFE);
+               offset = uio->uio_offset;
+               if (ioflag & IO_APPEND)
+                       offset = size_before;
+               size_after = offset + uio->uio_resid;
+               if (size_after < size_before)
+                       size_after = size_before;
+               delta = size_after - size_before;
+               mp = vq_vptomp(vp);
+               /* QUOTA CHECK */
+               if (!vq_write_ok(mp, va.va_uid, va.va_gid, delta)) {
+                       error = EDQUOT;
+                       goto done;
+               }
+       }
        DO_OPS(ops, error, &ap, vop_write);
        if ((error == 0) && do_accounting) {
-               size_after = vp->v_filesize;
-               mp = vq_vptomp(vp);
                VFS_ACCOUNT(mp, va.va_uid, va.va_gid, size_after - size_before);
        }
+done:
        VFS_MPUNLOCK(vp->v_mount);
        return(error);
 }
index 9329109..8748aba 100644 (file)
@@ -45,8 +45,10 @@ int vquotactl(const char *path, struct plistref *pref);
 extern int vfs_accounting_enabled;
 
 #if defined(_KERNEL) || defined(_KERNEL_STRUCTURES)
-struct mount* vq_vptomp(struct vnode*);
+struct mount* vq_vptomp(struct vnode *vp);
 #endif
 
+int vq_write_ok(struct mount *mp, uid_t uid, gid_t gid, uint64_t delta);
+
 #endif