Intel FB: more interlaced mode support
[safe/jmp/linux-2.6] / fs / quota.c
index 1df7832..99b24b5 100644 (file)
 #include <linux/slab.h>
 #include <asm/current.h>
 #include <asm/uaccess.h>
+#include <linux/compat.h>
 #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/types.h>
 
 /* Check validity of generic quotactl commands */
 static int generic_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
@@ -156,7 +159,6 @@ static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t
 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
@@ -166,29 +168,21 @@ static void quota_sync_sb(struct super_block *sb, int type)
                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_sem and so we could
-        * not do it inside dqonoff_sem. Moreover we need to be carefull
-        * about races with quotaoff() (that is the reason why we have own
-        * reference to inode). */
-       down(&sb_dqopt(sb)->dqonoff_sem);
+       /*
+        * Now when everything is written we can discard the pagecache so
+        * that userspace sees the changes.
+        */
+       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]);
-       }
-       up(&sb_dqopt(sb)->dqonoff_sem);
-       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
-               if (discard[cnt]) {
-                       down(&discard[cnt]->i_sem);
-                       truncate_inode_pages(&discard[cnt]->i_data, 0);
-                       up(&discard[cnt]->i_sem);
-                       iput(discard[cnt]);
-               }
+               mutex_lock_nested(&sb_dqopt(sb)->files[cnt]->i_mutex, I_MUTEX_QUOTA);
+               truncate_inode_pages(&sb_dqopt(sb)->files[cnt]->i_data, 0);
+               mutex_unlock(&sb_dqopt(sb)->files[cnt]->i_mutex);
        }
+       mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
 }
 
 void sync_dquots(struct super_block *sb, int type)
@@ -336,6 +330,34 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, void
 }
 
 /*
+ * 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));
+       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
  * calls. Maybe we need to add the process quotas etc. in the future,
@@ -345,25 +367,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);
@@ -374,3 +386,119 @@ asmlinkage long sys_quotactl(unsigned int cmd, const char __user *special, qid_t
 
        return ret;
 }
+
+#if defined(CONFIG_COMPAT_FOR_U64_ALIGNMENT)
+/*
+ * This code works only for 32 bit quota tools over 64 bit OS (x86_64, ia64)
+ * and is necessary due to alignment problems.
+ */
+struct compat_if_dqblk {
+       compat_u64 dqb_bhardlimit;
+       compat_u64 dqb_bsoftlimit;
+       compat_u64 dqb_curspace;
+       compat_u64 dqb_ihardlimit;
+       compat_u64 dqb_isoftlimit;
+       compat_u64 dqb_curinodes;
+       compat_u64 dqb_btime;
+       compat_u64 dqb_itime;
+       compat_uint_t dqb_valid;
+};
+
+/* XFS structures */
+struct compat_fs_qfilestat {
+       compat_u64 dqb_bhardlimit;
+       compat_u64 qfs_nblks;
+       compat_uint_t qfs_nextents;
+};
+
+struct compat_fs_quota_stat {
+       __s8            qs_version;
+       __u16           qs_flags;
+       __s8            qs_pad;
+       struct compat_fs_qfilestat      qs_uquota;
+       struct compat_fs_qfilestat      qs_gquota;
+       compat_uint_t   qs_incoredqs;
+       compat_int_t    qs_btimelimit;
+       compat_int_t    qs_itimelimit;
+       compat_int_t    qs_rtbtimelimit;
+       __u16           qs_bwarnlimit;
+       __u16           qs_iwarnlimit;
+};
+
+asmlinkage long sys32_quotactl(unsigned int cmd, const char __user *special,
+                                               qid_t id, void __user *addr)
+{
+       unsigned int cmds;
+       struct if_dqblk __user *dqblk;
+       struct compat_if_dqblk __user *compat_dqblk;
+       struct fs_quota_stat __user *fsqstat;
+       struct compat_fs_quota_stat __user *compat_fsqstat;
+       compat_uint_t data;
+       u16 xdata;
+       long ret;
+
+       cmds = cmd >> SUBCMDSHIFT;
+
+       switch (cmds) {
+       case Q_GETQUOTA:
+               dqblk = compat_alloc_user_space(sizeof(struct if_dqblk));
+               compat_dqblk = addr;
+               ret = sys_quotactl(cmd, special, id, dqblk);
+               if (ret)
+                       break;
+               if (copy_in_user(compat_dqblk, dqblk, sizeof(*compat_dqblk)) ||
+                       get_user(data, &dqblk->dqb_valid) ||
+                       put_user(data, &compat_dqblk->dqb_valid))
+                       ret = -EFAULT;
+               break;
+       case Q_SETQUOTA:
+               dqblk = compat_alloc_user_space(sizeof(struct if_dqblk));
+               compat_dqblk = addr;
+               ret = -EFAULT;
+               if (copy_in_user(dqblk, compat_dqblk, sizeof(*compat_dqblk)) ||
+                       get_user(data, &compat_dqblk->dqb_valid) ||
+                       put_user(data, &dqblk->dqb_valid))
+                       break;
+               ret = sys_quotactl(cmd, special, id, dqblk);
+               break;
+       case Q_XGETQSTAT:
+               fsqstat = compat_alloc_user_space(sizeof(struct fs_quota_stat));
+               compat_fsqstat = addr;
+               ret = sys_quotactl(cmd, special, id, fsqstat);
+               if (ret)
+                       break;
+               ret = -EFAULT;
+               /* Copying qs_version, qs_flags, qs_pad */
+               if (copy_in_user(compat_fsqstat, fsqstat,
+                       offsetof(struct compat_fs_quota_stat, qs_uquota)))
+                       break;
+               /* Copying qs_uquota */
+               if (copy_in_user(&compat_fsqstat->qs_uquota,
+                       &fsqstat->qs_uquota,
+                       sizeof(compat_fsqstat->qs_uquota)) ||
+                       get_user(data, &fsqstat->qs_uquota.qfs_nextents) ||
+                       put_user(data, &compat_fsqstat->qs_uquota.qfs_nextents))
+                       break;
+               /* Copying qs_gquota */
+               if (copy_in_user(&compat_fsqstat->qs_gquota,
+                       &fsqstat->qs_gquota,
+                       sizeof(compat_fsqstat->qs_gquota)) ||
+                       get_user(data, &fsqstat->qs_gquota.qfs_nextents) ||
+                       put_user(data, &compat_fsqstat->qs_gquota.qfs_nextents))
+                       break;
+               /* Copying the rest */
+               if (copy_in_user(&compat_fsqstat->qs_incoredqs,
+                       &fsqstat->qs_incoredqs,
+                       sizeof(struct compat_fs_quota_stat) -
+                       offsetof(struct compat_fs_quota_stat, qs_incoredqs)) ||
+                       get_user(xdata, &fsqstat->qs_iwarnlimit) ||
+                       put_user(xdata, &compat_fsqstat->qs_iwarnlimit))
+                       break;
+               ret = 0;
+               break;
+       default:
+               ret = sys_quotactl(cmd, special, id, addr);
+       }
+       return ret;
+}
+#endif