quota: simplify the quotactl compat handling
authorChristoph Hellwig <hch@lst.de>
Thu, 17 Sep 2020 07:41:59 +0000 (09:41 +0200)
committerAl Viro <viro@zeniv.linux.org.uk>
Thu, 17 Sep 2020 17:00:46 +0000 (13:00 -0400)
Fold the misaligned u64 workarounds into the main quotactl flow instead
of implementing a separate compat syscall handler.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Acked-by: Jan Kara <jack@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
arch/x86/entry/syscalls/syscall_32.tbl
fs/quota/Kconfig
fs/quota/Makefile
fs/quota/compat.c [deleted file]
fs/quota/compat.h [new file with mode: 0644]
fs/quota/quota.c
include/linux/quotaops.h
kernel/sys_ni.c

index 9d11028..3db3d88 100644 (file)
 128    i386    init_module             sys_init_module
 129    i386    delete_module           sys_delete_module
 130    i386    get_kernel_syms
-131    i386    quotactl                sys_quotactl                    compat_sys_quotactl32
+131    i386    quotactl                sys_quotactl
 132    i386    getpgid                 sys_getpgid
 133    i386    fchdir                  sys_fchdir
 134    i386    bdflush                 sys_bdflush
index d1ceb76..b59cd17 100644 (file)
@@ -70,8 +70,3 @@ config QFMT_V2
 config QUOTACTL
        bool
        default n
-
-config QUOTACTL_COMPAT
-       bool
-       depends on QUOTACTL && COMPAT_FOR_U64_ALIGNMENT
-       default y
index f2b49d0..9160639 100644 (file)
@@ -4,5 +4,4 @@ obj-$(CONFIG_QFMT_V1)           += quota_v1.o
 obj-$(CONFIG_QFMT_V2)          += quota_v2.o
 obj-$(CONFIG_QUOTA_TREE)       += quota_tree.o
 obj-$(CONFIG_QUOTACTL)         += quota.o kqid.o
-obj-$(CONFIG_QUOTACTL_COMPAT)  += compat.o
 obj-$(CONFIG_QUOTA_NETLINK_INTERFACE)  += netlink.o
diff --git a/fs/quota/compat.c b/fs/quota/compat.c
deleted file mode 100644 (file)
index c305728..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <linux/syscalls.h>
-#include <linux/compat.h>
-#include <linux/quotaops.h>
-
-/*
- * 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;
-};
-
-COMPAT_SYSCALL_DEFINE4(quotactl32, 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 = kernel_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 = kernel_quotactl(cmd, special, id, dqblk);
-               break;
-       case Q_XGETQSTAT:
-               fsqstat = compat_alloc_user_space(sizeof(struct fs_quota_stat));
-               compat_fsqstat = addr;
-               ret = kernel_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 = kernel_quotactl(cmd, special, id, addr);
-       }
-       return ret;
-}
diff --git a/fs/quota/compat.h b/fs/quota/compat.h
new file mode 100644 (file)
index 0000000..ef7d1e1
--- /dev/null
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/compat.h>
+
+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;
+};
+
+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;
+};
index 5444d3c..e1e9d05 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/types.h>
 #include <linux/writeback.h>
 #include <linux/nospec.h>
+#include "compat.h"
 
 static int check_quotactl_permission(struct super_block *sb, int type, int cmd,
                                     qid_t id)
@@ -211,8 +212,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 +288,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 +401,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 +436,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,
@@ -816,8 +867,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 +922,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);
-}
index 9cf0cd3..a0f6668 100644 (file)
@@ -27,9 +27,6 @@ static inline bool is_quota_modification(struct inode *inode, struct iattr *ia)
                (ia->ia_valid & ATTR_GID && !gid_eq(ia->ia_gid, inode->i_gid));
 }
 
-int kernel_quotactl(unsigned int cmd, const char __user *special,
-                   qid_t id, void __user *addr);
-
 #if defined(CONFIG_QUOTA)
 
 #define quota_error(sb, fmt, args...) \
index 4d59775..c925d1e 100644 (file)
@@ -369,7 +369,6 @@ COND_SYSCALL_COMPAT(fanotify_mark);
 /* x86 */
 COND_SYSCALL(vm86old);
 COND_SYSCALL(modify_ldt);
-COND_SYSCALL_COMPAT(quotactl32);
 COND_SYSCALL(vm86);
 COND_SYSCALL(kexec_file_load);