fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / quota.c
index 3dd9be6..879e255 100644 (file)
 #include <linux/kernel.h>
 #include <linux/smp_lock.h>
 #include <linux/security.h>
+#include <linux/syscalls.h>
+#include <linux/buffer_head.h>
+#include <linux/capability.h>
+#include <linux/quotaops.h>
+#include <linux/vs_context.h>
 
-/* Check validity of quotactl */
-static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
+/* Check validity of generic quotactl commands */
+static int generic_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
 {
        if (type >= MAXQUOTAS)
                return -EINVAL;
@@ -56,6 +61,48 @@ static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t
                        if (sb && !sb->s_qcop->quota_sync)
                                return -ENOSYS;
                        break;
+               default:
+                       return -EINVAL;
+       }
+
+       /* Is quota turned on for commands which need it? */
+       switch (cmd) {
+               case Q_GETFMT:
+               case Q_GETINFO:
+               case Q_QUOTAOFF:
+               case Q_SETINFO:
+               case Q_SETQUOTA:
+               case Q_GETQUOTA:
+                       /* This is just informative test so we are satisfied without a lock */
+                       if (!sb_has_quota_enabled(sb, type))
+                               return -ESRCH;
+       }
+
+       /* Check privileges */
+       if (cmd == Q_GETQUOTA) {
+               if (((type == USRQUOTA && current->euid != id) ||
+                    (type == GRPQUOTA && !in_egroup_p(id))) &&
+                   !vx_capable(CAP_SYS_ADMIN, VXC_QUOTA_CTL))
+                       return -EPERM;
+       }
+       else if (cmd != Q_GETFMT && cmd != Q_SYNC && cmd != Q_GETINFO)
+               if (!vx_capable(CAP_SYS_ADMIN, VXC_QUOTA_CTL))
+                       return -EPERM;
+
+       return 0;
+}
+
+/* Check validity of XFS Quota Manager commands */
+static int xqm_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
+{
+       if (type >= XQM_MAXQUOTAS)
+               return -EINVAL;
+       if (!sb)
+               return -ENODEV;
+       if (!sb->s_qcop)
+               return -ENOSYS;
+
+       switch (cmd) {
                case Q_XQUOTAON:
                case Q_XQUOTAOFF:
                case Q_XQUOTARM:
@@ -74,46 +121,92 @@ static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t
                        if (!sb->s_qcop->get_xquota)
                                return -ENOSYS;
                        break;
+               case Q_XQUOTASYNC:
+                       if (!sb->s_qcop->quota_sync)
+                               return -ENOSYS;
+                       break;
                default:
                        return -EINVAL;
        }
 
-       /* Is quota turned on for commands which need it? */
-       switch (cmd) {
-               case Q_GETFMT:
-               case Q_GETINFO:
-               case Q_QUOTAOFF:
-               case Q_SETINFO:
-               case Q_SETQUOTA:
-               case Q_GETQUOTA:
-                       /* This is just informative test so we are satisfied without a lock */
-                       if (!sb_has_quota_enabled(sb, type))
-                               return -ESRCH;
-       }
        /* Check privileges */
-       if (cmd == Q_GETQUOTA || cmd == Q_XGETQUOTA) {
-               if (((type == USRQUOTA && current->euid != id) ||
-                    (type == GRPQUOTA && !in_egroup_p(id))) &&
-                   !capable(CAP_SYS_ADMIN))
+       if (cmd == Q_XGETQUOTA) {
+               if (((type == XQM_USRQUOTA && current->euid != id) ||
+                    (type == XQM_GRPQUOTA && !in_egroup_p(id))) &&
+                    !vx_capable(CAP_SYS_ADMIN, VXC_QUOTA_CTL))
                        return -EPERM;
-       }
-       else if (cmd != Q_GETFMT && cmd != Q_SYNC && cmd != Q_GETINFO && cmd != Q_XGETQSTAT)
-               if (!capable(CAP_SYS_ADMIN))
+       } else if (cmd != Q_XGETQSTAT && cmd != Q_XQUOTASYNC) {
+               if (!vx_capable(CAP_SYS_ADMIN, VXC_QUOTA_CTL))
                        return -EPERM;
+       }
+
+       return 0;
+}
+
+static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
+{
+       int error;
 
-       return security_quotactl (cmd, type, id, sb);
+       if (XQM_COMMAND(cmd))
+               error = xqm_quotactl_valid(sb, type, cmd, id);
+       else
+               error = generic_quotactl_valid(sb, type, cmd, id);
+       if (!error)
+               error = security_quotactl(cmd, type, id, sb);
+       return error;
 }
 
-static struct super_block *get_super_to_sync(int type)
+static void quota_sync_sb(struct super_block *sb, int type)
+{
+       int cnt;
+       struct inode *discard[MAXQUOTAS];
+
+       sb->s_qcop->quota_sync(sb, type);
+       /* This is not very clever (and fast) but currently I don't know about
+        * any other simple way of getting quota data to disk and we must get
+        * them there for userspace to be visible... */
+       if (sb->s_op->sync_fs)
+               sb->s_op->sync_fs(sb, 1);
+       sync_blockdev(sb->s_bdev);
+
+       /* Now when everything is written we can discard the pagecache so
+        * that userspace sees the changes. We need i_mutex and so we could
+        * not do it inside dqonoff_mutex. Moreover we need to be carefull
+        * about races with quotaoff() (that is the reason why we have own
+        * reference to inode). */
+       mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               discard[cnt] = NULL;
+               if (type != -1 && cnt != type)
+                       continue;
+               if (!sb_has_quota_enabled(sb, cnt))
+                       continue;
+               discard[cnt] = igrab(sb_dqopt(sb)->files[cnt]);
+       }
+       mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               if (discard[cnt]) {
+                       mutex_lock(&discard[cnt]->i_mutex);
+                       truncate_inode_pages(&discard[cnt]->i_data, 0);
+                       mutex_unlock(&discard[cnt]->i_mutex);
+                       iput(discard[cnt]);
+               }
+       }
+}
+
+void sync_dquots(struct super_block *sb, int type)
 {
-       struct list_head *head;
        int cnt, dirty;
 
-restart:
-       spin_lock(&sb_lock);
-       list_for_each(head, &super_blocks) {
-               struct super_block *sb = list_entry(head, struct super_block, s_list);
+       if (sb) {
+               if (sb->s_qcop->quota_sync)
+                       quota_sync_sb(sb, type);
+               return;
+       }
 
+       spin_lock(&sb_lock);
+restart:
+       list_for_each_entry(sb, &super_blocks, s_list) {
                /* This test just improves performance so it needn't be reliable... */
                for (cnt = 0, dirty = 0; cnt < MAXQUOTAS; cnt++)
                        if ((type == cnt || type == -1) && sb_has_quota_enabled(sb, cnt)
@@ -124,29 +217,14 @@ restart:
                sb->s_count++;
                spin_unlock(&sb_lock);
                down_read(&sb->s_umount);
-               if (!sb->s_root) {
-                       drop_super(sb);
+               if (sb->s_root && sb->s_qcop->quota_sync)
+                       quota_sync_sb(sb, type);
+               up_read(&sb->s_umount);
+               spin_lock(&sb_lock);
+               if (__put_super_and_need_restart(sb))
                        goto restart;
-               }
-               return sb;
        }
        spin_unlock(&sb_lock);
-       return NULL;
-}
-
-void sync_dquots(struct super_block *sb, int type)
-{
-       if (sb) {
-               if (sb->s_qcop->quota_sync)
-                       sb->s_qcop->quota_sync(sb, type);
-       }
-       else {
-               while ((sb = get_super_to_sync(type)) != 0) {
-                       if (sb->s_qcop->quota_sync)
-                               sb->s_qcop->quota_sync(sb, type);
-                       drop_super(sb);
-               }
-       }
 }
 
 /* Copy parameters and call proper function */
@@ -251,6 +329,8 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, void
                                return -EFAULT;
                        return 0;
                }
+               case Q_XQUOTASYNC:
+                       return sb->s_qcop->quota_sync(sb, type);
                /* We never reach here unless validity check is broken */
                default:
                        BUG();
@@ -258,6 +338,90 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, void
        return 0;
 }
 
+#if defined(CONFIG_BLK_DEV_VROOT) || defined(CONFIG_BLK_DEV_VROOT_MODULE)
+
+#include <linux/vroot.h>
+#include <linux/major.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <linux/vserver/debug.h>
+
+static vroot_grb_func *vroot_get_real_bdev = NULL;
+
+static spinlock_t vroot_grb_lock = SPIN_LOCK_UNLOCKED;
+
+int register_vroot_grb(vroot_grb_func *func) {
+       int ret = -EBUSY;
+
+       spin_lock(&vroot_grb_lock);
+       if (!vroot_get_real_bdev) {
+               vroot_get_real_bdev = func;
+               ret = 0;
+       }
+       spin_unlock(&vroot_grb_lock);
+       return ret;
+}
+EXPORT_SYMBOL(register_vroot_grb);
+
+int unregister_vroot_grb(vroot_grb_func *func) {
+       int ret = -EINVAL;
+
+       spin_lock(&vroot_grb_lock);
+       if (vroot_get_real_bdev) {
+               vroot_get_real_bdev = NULL;
+               ret = 0;
+       }
+       spin_unlock(&vroot_grb_lock);
+       return ret;
+}
+EXPORT_SYMBOL(unregister_vroot_grb);
+
+#endif
+
+/*
+ * look up a superblock on which quota ops will be performed
+ * - use the name of a block device to find the superblock thereon
+ */
+static inline struct super_block *quotactl_block(const char __user *special)
+{
+#ifdef CONFIG_BLOCK
+       struct block_device *bdev;
+       struct super_block *sb;
+       char *tmp = getname(special);
+
+       if (IS_ERR(tmp))
+               return ERR_PTR(PTR_ERR(tmp));
+       bdev = lookup_bdev(tmp);
+       putname(tmp);
+       if (IS_ERR(bdev))
+               return ERR_PTR(PTR_ERR(bdev));
+#if defined(CONFIG_BLK_DEV_VROOT) || defined(CONFIG_BLK_DEV_VROOT_MODULE)
+       if (bdev && bdev->bd_inode &&
+                       imajor(bdev->bd_inode) == VROOT_MAJOR) {
+               struct block_device *bdnew = (void *)-EINVAL;
+
+               if (vroot_get_real_bdev)
+                       bdnew = vroot_get_real_bdev(bdev);
+               else
+                       vxdprintk(VXD_CBIT(misc, 0),
+                                       "vroot_get_real_bdev not set");
+               bdput(bdev);
+               if (IS_ERR(bdnew))
+                       return ERR_PTR(PTR_ERR(bdnew));
+               bdev = bdnew;
+       }
+#endif
+       sb = get_super(bdev);
+       bdput(bdev);
+       if (!sb)
+               return ERR_PTR(-ENODEV);
+
+       return sb;
+#else
+       return ERR_PTR(-ENODEV);
+#endif
+}
+
 /*
  * This is the system call interface. This communicates with
  * the user-level programs. Currently this only supports diskquota
@@ -268,25 +432,15 @@ asmlinkage long sys_quotactl(unsigned int cmd, const char __user *special, qid_t
 {
        uint cmds, type;
        struct super_block *sb = NULL;
-       struct block_device *bdev;
-       char *tmp;
        int ret;
 
        cmds = cmd >> SUBCMDSHIFT;
        type = cmd & SUBCMDMASK;
 
        if (cmds != Q_SYNC || special) {
-               tmp = getname(special);
-               if (IS_ERR(tmp))
-                       return PTR_ERR(tmp);
-               bdev = lookup_bdev(tmp);
-               putname(tmp);
-               if (IS_ERR(bdev))
-                       return PTR_ERR(bdev);
-               sb = get_super(bdev);
-               bdput(bdev);
-               if (!sb)
-                       return -ENODEV;
+               sb = quotactl_block(special);
+               if (IS_ERR(sb))
+                       return PTR_ERR(sb);
        }
 
        ret = check_quotactl_valid(sb, type, cmds, id);