block: remove i_bdev
[linux-2.6-microblaze.git] / fs / quota / quota.c
index 47f9e15..6d16b2b 100644 (file)
@@ -19,6 +19,8 @@
 #include <linux/types.h>
 #include <linux/writeback.h>
 #include <linux/nospec.h>
+#include "compat.h"
+#include "../internal.h"
 
 static int check_quotactl_permission(struct super_block *sb, int type, int cmd,
                                     qid_t id)
@@ -211,8 +213,18 @@ static int quota_getquota(struct super_block *sb, int type, qid_t id,
        if (ret)
                return ret;
        copy_to_if_dqblk(&idq, &fdq);
-       if (copy_to_user(addr, &idq, sizeof(idq)))
-               return -EFAULT;
+
+       if (compat_need_64bit_alignment_fixup()) {
+               struct compat_if_dqblk __user *compat_dqblk = addr;
+
+               if (copy_to_user(compat_dqblk, &idq, sizeof(*compat_dqblk)))
+                       return -EFAULT;
+               if (put_user(idq.dqb_valid, &compat_dqblk->dqb_valid))
+                       return -EFAULT;
+       } else {
+               if (copy_to_user(addr, &idq, sizeof(idq)))
+                       return -EFAULT;
+       }
        return 0;
 }
 
@@ -277,8 +289,16 @@ static int quota_setquota(struct super_block *sb, int type, qid_t id,
        struct if_dqblk idq;
        struct kqid qid;
 
-       if (copy_from_user(&idq, addr, sizeof(idq)))
-               return -EFAULT;
+       if (compat_need_64bit_alignment_fixup()) {
+               struct compat_if_dqblk __user *compat_dqblk = addr;
+
+               if (copy_from_user(&idq, compat_dqblk, sizeof(*compat_dqblk)) ||
+                   get_user(idq.dqb_valid, &compat_dqblk->dqb_valid))
+                       return -EFAULT;
+       } else {
+               if (copy_from_user(&idq, addr, sizeof(idq)))
+                       return -EFAULT;
+       }
        if (!sb->s_qcop->set_dqblk)
                return -ENOSYS;
        qid = make_kqid(current_user_ns(), type, id);
@@ -382,6 +402,33 @@ static int quota_getstate(struct super_block *sb, int type,
        return 0;
 }
 
+static int compat_copy_fs_qfilestat(struct compat_fs_qfilestat __user *to,
+               struct fs_qfilestat *from)
+{
+       if (copy_to_user(to, from, sizeof(*to)) ||
+           put_user(from->qfs_nextents, &to->qfs_nextents))
+               return -EFAULT;
+       return 0;
+}
+
+static int compat_copy_fs_quota_stat(struct compat_fs_quota_stat __user *to,
+               struct fs_quota_stat *from)
+{
+       if (put_user(from->qs_version, &to->qs_version) ||
+           put_user(from->qs_flags, &to->qs_flags) ||
+           put_user(from->qs_pad, &to->qs_pad) ||
+           compat_copy_fs_qfilestat(&to->qs_uquota, &from->qs_uquota) ||
+           compat_copy_fs_qfilestat(&to->qs_gquota, &from->qs_gquota) ||
+           put_user(from->qs_incoredqs, &to->qs_incoredqs) ||
+           put_user(from->qs_btimelimit, &to->qs_btimelimit) ||
+           put_user(from->qs_itimelimit, &to->qs_itimelimit) ||
+           put_user(from->qs_rtbtimelimit, &to->qs_rtbtimelimit) ||
+           put_user(from->qs_bwarnlimit, &to->qs_bwarnlimit) ||
+           put_user(from->qs_iwarnlimit, &to->qs_iwarnlimit))
+               return -EFAULT;
+       return 0;
+}
+
 static int quota_getxstate(struct super_block *sb, int type, void __user *addr)
 {
        struct fs_quota_stat fqs;
@@ -390,9 +437,14 @@ static int quota_getxstate(struct super_block *sb, int type, void __user *addr)
        if (!sb->s_qcop->get_state)
                return -ENOSYS;
        ret = quota_getstate(sb, type, &fqs);
-       if (!ret && copy_to_user(addr, &fqs, sizeof(fqs)))
+       if (ret)
+               return ret;
+
+       if (compat_need_64bit_alignment_fixup())
+               return compat_copy_fs_quota_stat(addr, &fqs);
+       if (copy_to_user(addr, &fqs, sizeof(fqs)))
                return -EFAULT;
-       return ret;
+       return 0;
 }
 
 static int quota_getstatev(struct super_block *sb, int type,
@@ -481,6 +533,14 @@ static inline u64 quota_btobb(u64 bytes)
        return (bytes + (1 << XFS_BB_SHIFT) - 1) >> XFS_BB_SHIFT;
 }
 
+static inline s64 copy_from_xfs_dqblk_ts(const struct fs_disk_quota *d,
+               __s32 timer, __s8 timer_hi)
+{
+       if (d->d_fieldmask & FS_DQ_BIGTIME)
+               return (u32)timer | (s64)timer_hi << 32;
+       return timer;
+}
+
 static void copy_from_xfs_dqblk(struct qc_dqblk *dst, struct fs_disk_quota *src)
 {
        dst->d_spc_hardlimit = quota_bbtob(src->d_blk_hardlimit);
@@ -489,14 +549,17 @@ static void copy_from_xfs_dqblk(struct qc_dqblk *dst, struct fs_disk_quota *src)
        dst->d_ino_softlimit = src->d_ino_softlimit;
        dst->d_space = quota_bbtob(src->d_bcount);
        dst->d_ino_count = src->d_icount;
-       dst->d_ino_timer = src->d_itimer;
-       dst->d_spc_timer = src->d_btimer;
+       dst->d_ino_timer = copy_from_xfs_dqblk_ts(src, src->d_itimer,
+                                                 src->d_itimer_hi);
+       dst->d_spc_timer = copy_from_xfs_dqblk_ts(src, src->d_btimer,
+                                                 src->d_btimer_hi);
        dst->d_ino_warns = src->d_iwarns;
        dst->d_spc_warns = src->d_bwarns;
        dst->d_rt_spc_hardlimit = quota_bbtob(src->d_rtb_hardlimit);
        dst->d_rt_spc_softlimit = quota_bbtob(src->d_rtb_softlimit);
        dst->d_rt_space = quota_bbtob(src->d_rtbcount);
-       dst->d_rt_spc_timer = src->d_rtbtimer;
+       dst->d_rt_spc_timer = copy_from_xfs_dqblk_ts(src, src->d_rtbtimer,
+                                                    src->d_rtbtimer_hi);
        dst->d_rt_spc_warns = src->d_rtbwarns;
        dst->d_fieldmask = 0;
        if (src->d_fieldmask & FS_DQ_ISOFT)
@@ -588,10 +651,26 @@ static int quota_setxquota(struct super_block *sb, int type, qid_t id,
        return sb->s_qcop->set_dqblk(sb, qid, &qdq);
 }
 
+static inline void copy_to_xfs_dqblk_ts(const struct fs_disk_quota *d,
+               __s32 *timer_lo, __s8 *timer_hi, s64 timer)
+{
+       *timer_lo = timer;
+       if (d->d_fieldmask & FS_DQ_BIGTIME)
+               *timer_hi = timer >> 32;
+}
+
+static inline bool want_bigtime(s64 timer)
+{
+       return timer > S32_MAX || timer < S32_MIN;
+}
+
 static void copy_to_xfs_dqblk(struct fs_disk_quota *dst, struct qc_dqblk *src,
                              int type, qid_t id)
 {
        memset(dst, 0, sizeof(*dst));
+       if (want_bigtime(src->d_ino_timer) || want_bigtime(src->d_spc_timer) ||
+           want_bigtime(src->d_rt_spc_timer))
+               dst->d_fieldmask |= FS_DQ_BIGTIME;
        dst->d_version = FS_DQUOT_VERSION;
        dst->d_id = id;
        if (type == USRQUOTA)
@@ -606,14 +685,17 @@ static void copy_to_xfs_dqblk(struct fs_disk_quota *dst, struct qc_dqblk *src,
        dst->d_ino_softlimit = src->d_ino_softlimit;
        dst->d_bcount = quota_btobb(src->d_space);
        dst->d_icount = src->d_ino_count;
-       dst->d_itimer = src->d_ino_timer;
-       dst->d_btimer = src->d_spc_timer;
+       copy_to_xfs_dqblk_ts(dst, &dst->d_itimer, &dst->d_itimer_hi,
+                            src->d_ino_timer);
+       copy_to_xfs_dqblk_ts(dst, &dst->d_btimer, &dst->d_btimer_hi,
+                            src->d_spc_timer);
        dst->d_iwarns = src->d_ino_warns;
        dst->d_bwarns = src->d_spc_warns;
        dst->d_rtb_hardlimit = quota_btobb(src->d_rt_spc_hardlimit);
        dst->d_rtb_softlimit = quota_btobb(src->d_rt_spc_softlimit);
        dst->d_rtbcount = quota_btobb(src->d_rt_space);
-       dst->d_rtbtimer = src->d_rt_spc_timer;
+       copy_to_xfs_dqblk_ts(dst, &dst->d_rtbtimer, &dst->d_rtbtimer_hi,
+                            src->d_rt_spc_timer);
        dst->d_rtbwarns = src->d_rt_spc_warns;
 }
 
@@ -784,27 +866,42 @@ static bool quotactl_cmd_onoff(int cmd)
 static struct super_block *quotactl_block(const char __user *special, int cmd)
 {
 #ifdef CONFIG_BLOCK
-       struct block_device *bdev;
        struct super_block *sb;
        struct filename *tmp = getname(special);
+       bool excl = false, thawed = false;
+       int error;
+       dev_t dev;
 
        if (IS_ERR(tmp))
                return ERR_CAST(tmp);
-       bdev = lookup_bdev(tmp->name);
+       error = lookup_bdev(tmp->name, &dev);
        putname(tmp);
-       if (IS_ERR(bdev))
-               return ERR_CAST(bdev);
-       if (quotactl_cmd_onoff(cmd))
-               sb = get_super_exclusive_thawed(bdev);
-       else if (quotactl_cmd_write(cmd))
-               sb = get_super_thawed(bdev);
-       else
-               sb = get_super(bdev);
-       bdput(bdev);
+       if (error)
+               return ERR_PTR(error);
+
+       if (quotactl_cmd_onoff(cmd)) {
+               excl = true;
+               thawed = true;
+       } else if (quotactl_cmd_write(cmd)) {
+               thawed = true;
+       }
+
+retry:
+       sb = user_get_super(dev, excl);
        if (!sb)
                return ERR_PTR(-ENODEV);
-
+       if (thawed && sb->s_writers.frozen != SB_UNFROZEN) {
+               if (excl)
+                       up_write(&sb->s_umount);
+               else
+                       up_read(&sb->s_umount);
+               wait_event(sb->s_writers.wait_unfrozen,
+                          sb->s_writers.frozen == SB_UNFROZEN);
+               put_super(sb);
+               goto retry;
+       }
        return sb;
+
 #else
        return ERR_PTR(-ENODEV);
 #endif
@@ -816,8 +913,8 @@ static struct super_block *quotactl_block(const char __user *special, int cmd)
  * calls. Maybe we need to add the process quotas etc. in the future,
  * but we probably should use rlimits for that.
  */
-int kernel_quotactl(unsigned int cmd, const char __user *special,
-                   qid_t id, void __user *addr)
+SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special,
+               qid_t, id, void __user *, addr)
 {
        uint cmds, type;
        struct super_block *sb = NULL;
@@ -871,9 +968,3 @@ out:
                path_put(pathp);
        return ret;
 }
-
-SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special,
-               qid_t, id, void __user *, addr)
-{
-       return kernel_quotactl(cmd, special, id, addr);
-}