]> git.hungrycats.org Git - linux/commitdiff
[PATCH] [12/13] quota-12-compat
authorJan Kara <jack@suse.cz>
Mon, 20 May 2002 02:34:44 +0000 (19:34 -0700)
committerLinus Torvalds <torvalds@penguin.transmeta.com>
Mon, 20 May 2002 02:34:44 +0000 (19:34 -0700)
This patch implements configurable backward compatible quota interface.
Maybe this isn't needed in 2.5 but as some people want to use patches
in 2.4 where it's necessary I have implemented it.

arch/ia64/ia32/ia32_entry.S
arch/ia64/ia32/sys_ia32.c
arch/s390x/kernel/linux32.c
arch/s390x/kernel/wrapper32.S
arch/sparc64/kernel/sys_sparc32.c
arch/sparc64/kernel/systbls.S
fs/Config.help
fs/Config.in
fs/quota.c
include/linux/quotacompat.h [new file with mode: 0644]

index f9595d3f43ed7a9e0699e520beedfc27d85798f8..f3e7e950b7ac0a61b9a2f5cda429946e146dbee8 100644 (file)
@@ -310,7 +310,7 @@ ia32_syscall_table:
        data8 sys32_ni_syscall  /* init_module */
        data8 sys32_ni_syscall  /* delete_module */
        data8 sys32_ni_syscall  /* get_kernel_syms */  /* 130 */
-       data8 sys_quotactl
+       data8 sys32_quotactl
        data8 sys_getpgid
        data8 sys_fchdir
        data8 sys32_ni_syscall  /* sys_bdflush */
index c3852b487a11ff5fb9b292868ffa9a0066d96e81..95b7e49bf51e83987787766bc07fe24b932a392e 100644 (file)
@@ -3669,6 +3669,97 @@ getname32 (const char *filename)
        return result;
 }
 
+extern asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr);
+
+#ifdef CONFIG_QIFACE_COMPAT
+#ifdef CONFIG_QIFACE_V1
+struct user_dqblk32 {
+       __u32 dqb_bhardlimit;
+       __u32 dqb_bsoftlimit;
+       __u32 dqb_curblocks;
+       __u32 dqb_ihardlimit;
+       __u32 dqb_isoftlimit;
+       __u32 dqb_curinodes;
+       __kernel_time_t32 dqb_btime;
+       __kernel_time_t32 dqb_itime;
+};
+typedef struct v1c_mem_dqblk comp_dqblk_t;
+
+#define Q_COMP_GETQUOTA Q_V1_GETQUOTA
+#define Q_COMP_SETQUOTA Q_V1_SETQUOTA
+#define Q_COMP_SETQLIM Q_V1_SETQLIM
+#define Q_COMP_SETUSE Q_V1_SETUSE
+#else
+struct user_dqblk32 {
+       __u32 dqb_ihardlimit;
+       __u32 dqb_isoftlimit;
+       __u32 dqb_curinodes;
+       __u32 dqb_bhardlimit;
+       __u32 dqb_bsoftlimit;
+       __u64 dqb_curspace;
+       __kernel_time_t32 dqb_btime;
+       __kernel_time_t32 dqb_itime;
+};
+typedef struct v2c_mem_dqblk comp_dqblk_t;
+
+#define Q_COMP_GETQUOTA Q_V2_GETQUOTA
+#define Q_COMP_SETQUOTA Q_V2_SETQUOTA
+#define Q_COMP_SETQLIM Q_V2_SETQLIM
+#define Q_COMP_SETUSE Q_V2_SETUSE
+#endif
+
+asmlinkage long sys32_quotactl(int cmd, const char *special, int id, caddr_t addr)
+{
+       int cmds = cmd >> SUBCMDSHIFT;
+       long err;
+       comp_dqblk_t d;
+       mm_segment_t old_fs;
+       char *spec;
+       
+       switch (cmds) {
+               case Q_COMP_GETQUOTA:
+                       break;
+               case Q_COMP_SETQUOTA:
+               case Q_COMP_SETUSE:
+               case Q_COMP_SETQLIM:
+                       if (copy_from_user(&d, (struct user_dqblk32 *)addr,
+                                           sizeof (struct user_dqblk32)))
+                               return -EFAULT;
+                       d.dqb_itime = ((struct user_dqblk32 *)&d)->dqb_itime;
+                       d.dqb_btime = ((struct user_dqblk32 *)&d)->dqb_btime;
+                       break;
+       default:
+               return sys_quotactl(cmd, special, id, (__kernel_caddr_t)addr);
+       }
+       spec = getname (special);
+       err = PTR_ERR(spec);
+       if (IS_ERR(spec)) return err;
+       old_fs = get_fs();
+       set_fs (KERNEL_DS);
+       err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d);
+       set_fs (old_fs);
+       putname (spec);
+       if (err)
+               return err;
+       if (cmds == Q_COMP_GETQUOTA) {
+               __kernel_time_t b = d.dqb_btime, i = d.dqb_itime;
+               ((struct user_dqblk32 *)&d)->dqb_itime = i;
+               ((struct user_dqblk32 *)&d)->dqb_btime = b;
+               if (copy_to_user ((struct user_dqblk32 *)addr, &d,
+                                 sizeof (struct user_dqblk32)))
+                       return -EFAULT;
+       }
+       return 0;
+}
+
+#else
+/* No conversion needed for new interface */
+asmlinkage long sys32_quotactl(int cmd, const char *special, int id, caddr_t addr)
+{
+       return sys_quotactl(cmd, special, id, addr);
+}
+#endif
+
 asmlinkage long
 sys32_sched_rr_get_interval (pid_t pid, struct timespec32 *interval)
 {
index e06f1958dd108dc638d706ca3106dcce4ecb23a5..f9da89b329bb18fc45ea9d8257f9a4932e8bbfdc 100644 (file)
@@ -897,6 +897,97 @@ asmlinkage long sys32_fcntl64(unsigned int fd, unsigned int cmd, unsigned long a
        return sys32_fcntl(fd, cmd, arg);
 }
 
+extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr);
+
+#ifdef CONFIG_QIFACE_COMPAT
+#ifdef CONFIG_QIFACE_V1
+struct user_dqblk32 {
+       __u32 dqb_bhardlimit;
+       __u32 dqb_bsoftlimit;
+       __u32 dqb_curblocks;
+       __u32 dqb_ihardlimit;
+       __u32 dqb_isoftlimit;
+       __u32 dqb_curinodes;
+       __kernel_time_t32 dqb_btime;
+       __kernel_time_t32 dqb_itime;
+};
+typedef struct v1c_mem_dqblk comp_dqblk_t;
+
+#define Q_COMP_GETQUOTA Q_V1_GETQUOTA
+#define Q_COMP_SETQUOTA Q_V1_SETQUOTA
+#define Q_COMP_SETQLIM Q_V1_SETQLIM
+#define Q_COMP_SETUSE Q_V1_SETUSE
+#else
+struct user_dqblk32 {
+       __u32 dqb_ihardlimit;
+       __u32 dqb_isoftlimit;
+       __u32 dqb_curinodes;
+       __u32 dqb_bhardlimit;
+       __u32 dqb_bsoftlimit;
+       __u64 dqb_curspace;
+       __kernel_time_t32 dqb_btime;
+       __kernel_time_t32 dqb_itime;
+};
+typedef struct v2c_mem_dqblk comp_dqblk_t;
+
+#define Q_COMP_GETQUOTA Q_V2_GETQUOTA
+#define Q_COMP_SETQUOTA Q_V2_SETQUOTA
+#define Q_COMP_SETQLIM Q_V2_SETQLIM
+#define Q_COMP_SETUSE Q_V2_SETUSE
+#endif
+
+asmlinkage int sys32_quotactl(int cmd, const char *special, int id, caddr_t addr)
+{
+       int cmds = cmd >> SUBCMDSHIFT;
+       int err;
+       comp_dqblk_t d;
+       mm_segment_t old_fs;
+       char *spec;
+       
+       switch (cmds) {
+               case Q_COMP_GETQUOTA:
+                       break;
+               case Q_COMP_SETQUOTA:
+               case Q_COMP_SETUSE:
+               case Q_COMP_SETQLIM:
+                       if (copy_from_user(&d, (struct user_dqblk32 *)addr,
+                                           sizeof (struct user_dqblk32)))
+                               return -EFAULT;
+                       d.dqb_itime = ((struct user_dqblk32 *)&d)->dqb_itime;
+                       d.dqb_btime = ((struct user_dqblk32 *)&d)->dqb_btime;
+                       break;
+       default:
+               return sys_quotactl(cmd, special, id, (__kernel_caddr_t)addr);
+       }
+       spec = getname (special);
+       err = PTR_ERR(spec);
+       if (IS_ERR(spec)) return err;
+       old_fs = get_fs();
+       set_fs (KERNEL_DS);
+       err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d);
+       set_fs (old_fs);
+       putname (spec);
+       if (err)
+               return err;
+       if (cmds == Q_COMP_GETQUOTA) {
+               __kernel_time_t b = d.dqb_btime, i = d.dqb_itime;
+               ((struct user_dqblk32 *)&d)->dqb_itime = i;
+               ((struct user_dqblk32 *)&d)->dqb_btime = b;
+               if (copy_to_user ((struct user_dqblk32 *)addr, &d,
+                                 sizeof (struct user_dqblk32)))
+                       return -EFAULT;
+       }
+       return 0;
+}
+
+#else
+/* No conversion needed for new interface */
+asmlinkage int sys32_quotactl(int cmd, const char *special, int id, caddr_t addr)
+{
+       return sys_quotactl(cmd, special, id, addr);
+}
+#endif
+
 static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf)
 {
        int err;
index 8a66558332b1fffae5388dcfd876d4a054bf544c..a11ee19b21ffb3691d17c48e45c25ededc3841ae 100644 (file)
@@ -586,7 +586,7 @@ sys32_quotactl_wrapper:
        llgtr   %r3,%r3                 # const char *
        lgfr    %r4,%r4                 # int
        llgtr   %r5,%r5                 # caddr_t
-       jg      sys_quotactl            # branch to system call
+       jg      sys32_quotactl          # branch to system call
 
        .globl  sys32_getpgid_wrapper 
 sys32_getpgid_wrapper:
index 224387833d548b59cce275a5d91f00fd31b55e18..ebf671149c954b7857696098555158f2f41ab344 100644 (file)
@@ -889,6 +889,97 @@ asmlinkage long sys32_fcntl64(unsigned int fd, unsigned int cmd, unsigned long a
        return sys32_fcntl(fd, cmd, arg);
 }
 
+extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr);
+
+#ifdef CONFIG_QIFACE_COMPAT
+#ifdef CONFIG_QIFACE_V1
+struct user_dqblk32 {
+       __u32 dqb_bhardlimit;
+       __u32 dqb_bsoftlimit;
+       __u32 dqb_curblocks;
+       __u32 dqb_ihardlimit;
+       __u32 dqb_isoftlimit;
+       __u32 dqb_curinodes;
+       __kernel_time_t32 dqb_btime;
+       __kernel_time_t32 dqb_itime;
+};
+typedef struct v1c_mem_dqblk comp_dqblk_t;
+
+#define Q_COMP_GETQUOTA Q_V1_GETQUOTA
+#define Q_COMP_SETQUOTA Q_V1_SETQUOTA
+#define Q_COMP_SETQLIM Q_V1_SETQLIM
+#define Q_COMP_SETUSE Q_V1_SETUSE
+#else
+struct user_dqblk32 {
+       __u32 dqb_ihardlimit;
+       __u32 dqb_isoftlimit;
+       __u32 dqb_curinodes;
+       __u32 dqb_bhardlimit;
+       __u32 dqb_bsoftlimit;
+       __u64 dqb_curspace;
+       __kernel_time_t32 dqb_btime;
+       __kernel_time_t32 dqb_itime;
+};
+typedef struct v2c_mem_dqblk comp_dqblk_t;
+
+#define Q_COMP_GETQUOTA Q_V2_GETQUOTA
+#define Q_COMP_SETQUOTA Q_V2_SETQUOTA
+#define Q_COMP_SETQLIM Q_V2_SETQLIM
+#define Q_COMP_SETUSE Q_V2_SETUSE
+#endif
+
+asmlinkage int sys32_quotactl(int cmd, const char *special, int id, caddr_t addr)
+{
+       int cmds = cmd >> SUBCMDSHIFT;
+       int err;
+       comp_dqblk_t d;
+       mm_segment_t old_fs;
+       char *spec;
+       
+       switch (cmds) {
+               case Q_COMP_GETQUOTA:
+                       break;
+               case Q_COMP_SETQUOTA:
+               case Q_COMP_SETUSE:
+               case Q_COMP_SETQLIM:
+                       if (copy_from_user(&d, (struct user_dqblk32 *)addr,
+                                           sizeof (struct user_dqblk32)))
+                               return -EFAULT;
+                       d.dqb_itime = ((struct user_dqblk32 *)&d)->dqb_itime;
+                       d.dqb_btime = ((struct user_dqblk32 *)&d)->dqb_btime;
+                       break;
+       default:
+               return sys_quotactl(cmd, special, id, (__kernel_caddr_t)addr);
+       }
+       spec = getname (special);
+       err = PTR_ERR(spec);
+       if (IS_ERR(spec)) return err;
+       old_fs = get_fs();
+       set_fs (KERNEL_DS);
+       err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d);
+       set_fs (old_fs);
+       putname (spec);
+       if (err)
+               return err;
+       if (cmds == Q_COMP_GETQUOTA) {
+               __kernel_time_t b = d.dqb_btime, i = d.dqb_itime;
+               ((struct user_dqblk32 *)&d)->dqb_itime = i;
+               ((struct user_dqblk32 *)&d)->dqb_btime = b;
+               if (copy_to_user ((struct user_dqblk32 *)addr, &d,
+                                 sizeof (struct user_dqblk32)))
+                       return -EFAULT;
+       }
+       return 0;
+}
+
+#else
+/* No conversion needed for new interface */
+asmlinkage int sys32_quotactl(int cmd, const char *special, int id, caddr_t addr)
+{
+       return sys_quotactl(cmd, special, id, addr);
+}
+#endif
+
 static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf)
 {
        int err;
index 6138ce2fca94c162b693796252e5cc662a3911a5..8efe9229605675b0f15d8a20c9ddedd5aeae0ca9 100644 (file)
@@ -52,7 +52,7 @@ sys_call_table32:
 /*150*/        .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_poll, sys_getdents64
        .word sys32_fcntl64, sys_nis_syscall, sys32_statfs, sys32_fstatfs, sys_oldumount
 /*160*/        .word sys32_sched_setaffinity, sys32_sched_getaffinity, sys_getdomainname, sys_setdomainname, sys_nis_syscall
-       .word sys_quotactl, sys_nis_syscall, sys32_mount, sys_ustat, sys_setxattr
+       .word sys32_quotactl, sys_nis_syscall, sys32_mount, sys_ustat, sys_setxattr
 /*170*/        .word sys_lsetxattr, sys_fsetxattr, sys_getxattr, sys_lgetxattr, sys32_getdents
        .word sys_setsid, sys_fchdir, sys_fgetxattr, sys_listxattr, sys_llistxattr
 /*180*/        .word sys_flistxattr, sys_removexattr, sys_lremovexattr, sys32_sigpending, sys32_query_module
index 94e6693f25d429390fded11f8d4801cdb9aa5eff..58eee1f8ea80b154798982b766718a1b219b48b6 100644 (file)
@@ -18,6 +18,21 @@ CONFIG_QFMT_V2
   need this functionality say Y here. Note that you will need latest
   quota utilities for new quota format with this kernel.
 
+CONFIG_QIFACE_COMPAT
+  This option will enable old quota interface in kernel.
+  If you have old quota tools (version <= 3.04) and you don't want to
+  upgrade them say Y here.
+
+CONFIG_QIFACE_V1
+  This is the oldest quota interface. It was used for old quota format.
+  If you have old quota tools and you use old quota format choose this
+  interface (if unsure, this interface is the best one to choose).
+
+CONFIG_QIFACE_V2
+  This quota interface was used by VFS v0 quota format. If you need
+  support for VFS v0 quota format (eg. you're using quota on ReiserFS)
+  and you don't want to upgrade quota tools, choose this interface.
+
 CONFIG_MINIX_FS
   Minix is a simple operating system used in many classes about OS's.
   The minix file system (method to organize files on a hard disk
index 318e3a9814df924fd70d4eb0a6f55aab9029af6e..e66a3b7e8472c668fa511c4877571daba83645d4 100644 (file)
@@ -7,6 +7,12 @@ comment 'File systems'
 bool 'Quota support' CONFIG_QUOTA
 dep_tristate '  Old quota format support' CONFIG_QFMT_V1 $CONFIG_QUOTA
 dep_tristate '  VFS v0 quota format support' CONFIG_QFMT_V2 $CONFIG_QUOTA
+dep_mbool '  Compatible quota interfaces' CONFIG_QIFACE_COMPAT $CONFIG_QUOTA
+if [ "$CONFIG_QUOTA" = "y" -a "$CONFIG_QIFACE_COMPAT" = "y" ]; then
+   choice '    Compatible quota interfaces' \
+       "Original       CONFIG_QIFACE_V1 \
+        VFSv0          CONFIG_QIFACE_V2" Original
+fi
 tristate 'Kernel automounter support' CONFIG_AUTOFS_FS
 tristate 'Kernel automounter version 4 support (also supports v3)' CONFIG_AUTOFS4_FS
 
index 88d54e8ade7b16313580986d4a515948e6ccef18..45ef892edc5c9db4f4f93452563c0fc551dd12c5 100644 (file)
 #include <asm/uaccess.h>
 #include <linux/kernel.h>
 #include <linux/smp_lock.h>
+#ifdef CONFIG_QIFACE_COMPAT
+#include <linux/quotacompat.h>
+#endif
+
 
 int nr_dquots, nr_free_dquots;
 
@@ -49,7 +53,7 @@ static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t
                case Q_GETQUOTA:
                        if (!sb->s_qcop->get_dqblk)
                                return -ENOSYS;
-               break;
+                       break;
                case Q_SYNC:
                        if (!sb->s_qcop->quota_sync)
                                return -ENOSYS;
@@ -231,6 +235,381 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, cadd
        return 0;
 }
 
+#ifdef CONFIG_QIFACE_COMPAT
+static int check_compat_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
+{
+       if (type >= MAXQUOTAS)
+               return -EINVAL;
+       /* Is operation supported? */
+       /* sb==NULL for GETSTATS calls */
+       if (sb && !sb->s_qcop)
+               return -ENOSYS;
+
+       switch (cmd) {
+               case Q_COMP_QUOTAON:
+                       if (!sb->s_qcop->quota_on)
+                               return -ENOSYS;
+                       break;
+               case Q_COMP_QUOTAOFF:
+                       if (!sb->s_qcop->quota_off)
+                               return -ENOSYS;
+                       break;
+               case Q_COMP_SYNC:
+                       if (!sb->s_qcop->quota_sync)
+                               return -ENOSYS;
+               break;
+#ifdef CONFIG_QIFACE_V2
+               case Q_V2_SETFLAGS:
+               case Q_V2_SETGRACE:
+               case Q_V2_SETINFO:
+                       if (!sb->s_qcop->set_info)
+                               return -ENOSYS;
+                       break;
+               case Q_V2_GETINFO:
+                       if (!sb->s_qcop->get_info)
+                               return -ENOSYS;
+                       break;
+               case Q_V2_SETQLIM:
+               case Q_V2_SETUSE:
+               case Q_V2_SETQUOTA:
+                       if (!sb->s_qcop->set_dqblk)
+                               return -ENOSYS;
+                       break;
+               case Q_V2_GETQUOTA:
+                       if (!sb->s_qcop->get_dqblk)
+                               return -ENOSYS;
+                       break;
+               case Q_V2_GETSTATS:
+                       return 0;       /* GETSTATS need no other checks */
+#endif
+#ifdef CONFIG_QIFACE_V1
+               case Q_V1_SETQLIM:
+               case Q_V1_SETUSE:
+               case Q_V1_SETQUOTA:
+                       if (!sb->s_qcop->set_dqblk)
+                               return -ENOSYS;
+                       break;
+               case Q_V1_GETQUOTA:
+                       if (!sb->s_qcop->get_dqblk)
+                               return -ENOSYS;
+                       break;
+               case Q_V1_RSQUASH:
+                       if (!sb->s_qcop->set_info)
+                               return -ENOSYS;
+                       break;
+               case Q_V1_GETSTATS:
+                       return 0;       /* GETSTATS need no other checks */
+#endif
+               default:
+                       return -EINVAL;
+       }
+
+       /* Is quota turned on for commands which need it? */
+       switch (cmd) {
+               case Q_V2_SETFLAGS:
+               case Q_V2_SETGRACE:
+               case Q_V2_SETINFO:
+               case Q_V2_GETINFO:
+               case Q_COMP_QUOTAOFF:
+               case Q_V1_RSQUASH:
+               case Q_V1_SETQUOTA:
+               case Q_V1_SETQLIM:
+               case Q_V1_SETUSE:
+               case Q_V2_SETQUOTA:
+               /* Q_V2_SETQLIM: collision with Q_V1_SETQLIM */
+               case Q_V2_SETUSE:
+               case Q_V1_GETQUOTA:
+               case Q_V2_GETQUOTA:
+                       if (!sb_has_quota_enabled(sb, type))
+                               return -ESRCH;
+       }
+#ifdef CONFIG_QIFACE_V1
+       if (cmd != Q_COMP_QUOTAON && cmd != Q_COMP_QUOTAOFF && cmd != Q_COMP_SYNC && sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id != QFMT_VFS_OLD)
+#else
+       if (cmd != Q_COMP_QUOTAON && cmd != Q_COMP_QUOTAOFF && cmd != Q_COMP_SYNC && sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id != QFMT_VFS_V0)
+#endif
+               return -ESRCH;
+
+       /* Check privileges */
+       if (cmd == Q_V1_GETQUOTA || cmd == Q_V2_GETQUOTA) {
+               if (((type == USRQUOTA && current->euid != id) ||
+                    (type == GRPQUOTA && !in_egroup_p(id))) &&
+                   !capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+       }
+       else if (cmd != Q_V1_GETSTATS && cmd != Q_V2_GETSTATS && cmd != Q_V2_GETINFO && cmd != Q_COMP_SYNC)
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+       return 0;
+}
+
+#ifdef CONFIG_QIFACE_V1
+static int v1_set_rsquash(struct super_block *sb, int type, int flag)
+{
+       struct if_dqinfo info;
+
+       info.dqi_valid = IIF_FLAGS;
+       info.dqi_flags = flag ? V1_DQF_RSQUASH : 0;
+       return sb->s_qcop->set_info(sb, type, &info);
+}
+
+static int v1_get_dqblk(struct super_block *sb, int type, qid_t id, struct v1c_mem_dqblk *mdq)
+{
+       struct if_dqblk idq;
+       int ret;
+
+       if ((ret = sb->s_qcop->get_dqblk(sb, type, id, &idq)) < 0)
+               return ret;
+       mdq->dqb_ihardlimit = idq.dqb_ihardlimit;
+       mdq->dqb_isoftlimit = idq.dqb_isoftlimit;
+       mdq->dqb_curinodes = idq.dqb_curinodes;
+       mdq->dqb_bhardlimit = idq.dqb_bhardlimit;
+       mdq->dqb_bsoftlimit = idq.dqb_bsoftlimit;
+       mdq->dqb_curblocks = toqb(idq.dqb_curspace);
+       mdq->dqb_itime = idq.dqb_itime;
+       mdq->dqb_btime = idq.dqb_btime;
+       if (id == 0) {  /* Times for id 0 are in fact grace times */
+               struct if_dqinfo info;
+
+               if ((ret = sb->s_qcop->get_info(sb, type, &info)) < 0)
+                       return ret;
+               mdq->dqb_btime = info.dqi_bgrace;
+               mdq->dqb_itime = info.dqi_igrace;
+       }
+       return 0;
+}
+
+static int v1_set_dqblk(struct super_block *sb, int type, int cmd, qid_t id, struct v1c_mem_dqblk *mdq)
+{
+       struct if_dqblk idq;
+       int ret;
+
+       idq.dqb_valid = 0;
+       if (cmd == Q_V1_SETQUOTA || cmd == Q_V1_SETQLIM) {
+               idq.dqb_ihardlimit = mdq->dqb_ihardlimit;
+               idq.dqb_isoftlimit = mdq->dqb_isoftlimit;
+               idq.dqb_bhardlimit = mdq->dqb_bhardlimit;
+               idq.dqb_bsoftlimit = mdq->dqb_bsoftlimit;
+               idq.dqb_valid |= QIF_LIMITS;
+       }
+       if (cmd == Q_V1_SETQUOTA || cmd == Q_V1_SETUSE) {
+               idq.dqb_curinodes = mdq->dqb_curinodes;
+               idq.dqb_curspace = ((qsize_t)mdq->dqb_curblocks) << QUOTABLOCK_BITS;
+               idq.dqb_valid |= QIF_USAGE;
+       }
+       ret = sb->s_qcop->set_dqblk(sb, type, id, &idq);
+       if (!ret && id == 0 && cmd == Q_V1_SETQUOTA) {  /* Times for id 0 are in fact grace times */
+               struct if_dqinfo info;
+
+               info.dqi_bgrace = mdq->dqb_btime;
+               info.dqi_igrace = mdq->dqb_itime;
+               info.dqi_valid = IIF_BGRACE | IIF_IGRACE;
+               ret = sb->s_qcop->set_info(sb, type, &info);
+       }
+       return ret;
+}
+
+static void v1_get_stats(struct v1c_dqstats *dst)
+{
+       memcpy(dst, &dqstats, sizeof(dqstats));
+}
+#endif
+
+#ifdef CONFIG_QIFACE_V2
+static int v2_get_info(struct super_block *sb, int type, struct v2c_mem_dqinfo *oinfo)
+{
+       struct if_dqinfo info;
+       int ret;
+
+       if ((ret = sb->s_qcop->get_info(sb, type, &info)) < 0)
+               return ret;
+       oinfo->dqi_bgrace = info.dqi_bgrace;
+       oinfo->dqi_igrace = info.dqi_igrace;
+       oinfo->dqi_flags = info.dqi_flags;
+       oinfo->dqi_blocks = sb_dqopt(sb)->info[type].u.v2_i.dqi_blocks;
+       oinfo->dqi_free_blk = sb_dqopt(sb)->info[type].u.v2_i.dqi_free_blk;
+       oinfo->dqi_free_entry = sb_dqopt(sb)->info[type].u.v2_i.dqi_free_entry;
+       return 0;
+}
+
+static int v2_set_info(struct super_block *sb, int type, int cmd, struct v2c_mem_dqinfo *oinfo)
+{
+       struct if_dqinfo info;
+
+       info.dqi_valid = 0;
+       if (cmd == Q_V2_SETGRACE || cmd == Q_V2_SETINFO) {
+               info.dqi_bgrace = oinfo->dqi_bgrace;
+               info.dqi_igrace = oinfo->dqi_igrace;
+               info.dqi_valid |= IIF_BGRACE | IIF_IGRACE;
+       }
+       if (cmd == Q_V2_SETFLAGS || cmd == Q_V2_SETINFO) {
+               info.dqi_flags = oinfo->dqi_flags;
+               info.dqi_valid |= IIF_FLAGS;
+       }
+       /* We don't simulate deadly effects of setting other parameters ;-) */
+       return sb->s_qcop->set_info(sb, type, &info);
+}
+
+static int v2_get_dqblk(struct super_block *sb, int type, qid_t id, struct v2c_mem_dqblk *mdq)
+{
+       struct if_dqblk idq;
+       int ret;
+
+       if ((ret = sb->s_qcop->get_dqblk(sb, type, id, &idq)) < 0)
+               return ret;
+       mdq->dqb_ihardlimit = idq.dqb_ihardlimit;
+       mdq->dqb_isoftlimit = idq.dqb_isoftlimit;
+       mdq->dqb_curinodes = idq.dqb_curinodes;
+       mdq->dqb_bhardlimit = idq.dqb_bhardlimit;
+       mdq->dqb_bsoftlimit = idq.dqb_bsoftlimit;
+       mdq->dqb_curspace = idq.dqb_curspace;
+       mdq->dqb_itime = idq.dqb_itime;
+       mdq->dqb_btime = idq.dqb_btime;
+       return 0;
+}
+
+static int v2_set_dqblk(struct super_block *sb, int type, int cmd, qid_t id, struct v2c_mem_dqblk *mdq)
+{
+       struct if_dqblk idq;
+
+       idq.dqb_valid = 0;
+       if (cmd == Q_V2_SETQUOTA || cmd == Q_V2_SETQLIM) {
+               idq.dqb_ihardlimit = mdq->dqb_ihardlimit;
+               idq.dqb_isoftlimit = mdq->dqb_isoftlimit;
+               idq.dqb_bhardlimit = mdq->dqb_bhardlimit;
+               idq.dqb_bsoftlimit = mdq->dqb_bsoftlimit;
+               idq.dqb_valid |= QIF_LIMITS;
+       }
+       if (cmd == Q_V2_SETQUOTA || cmd == Q_V2_SETUSE) {
+               idq.dqb_curinodes = mdq->dqb_curinodes;
+               idq.dqb_curspace = mdq->dqb_curspace;
+               idq.dqb_valid |= QIF_USAGE;
+       }
+       return sb->s_qcop->set_dqblk(sb, type, id, &idq);
+}
+
+static void v2_get_stats(struct v2c_dqstats *dst)
+{
+       memcpy(dst, &dqstats, sizeof(dqstats));
+       dst->version = __DQUOT_NUM_VERSION__;
+}
+#endif
+
+/* Handle requests to old interface */
+static int do_compat_quotactl(struct super_block *sb, int type, int cmd, qid_t id, caddr_t addr)
+{
+       int ret;
+
+       switch (cmd) {
+               case Q_COMP_QUOTAON: {
+                       char *pathname;
+
+                       if (IS_ERR(pathname = getname(addr)))
+                               return PTR_ERR(pathname);
+#ifdef CONFIG_QIFACE_V1
+                       ret = sb->s_qcop->quota_on(sb, type, QFMT_VFS_OLD, pathname);
+#else
+                       ret = sb->s_qcop->quota_on(sb, type, QFMT_VFS_V0, pathname);
+#endif
+                       putname(pathname);
+                       return ret;
+               }
+               case Q_COMP_QUOTAOFF:
+                       return sb->s_qcop->quota_off(sb, type);
+               case Q_COMP_SYNC:
+                       return sb->s_qcop->quota_sync(sb, type);
+#ifdef CONFIG_QIFACE_V1
+               case Q_V1_RSQUASH: {
+                       int flag;
+
+                       if (copy_from_user(&flag, addr, sizeof(flag)))
+                               return -EFAULT;
+                       return v1_set_rsquash(sb, type, flag);
+               }
+               case Q_V1_GETQUOTA: {
+                       struct v1c_mem_dqblk mdq;
+
+                       if ((ret = v1_get_dqblk(sb, type, id, &mdq)))
+                               return ret;
+                       if (copy_to_user(addr, &mdq, sizeof(mdq)))
+                               return -EFAULT;
+                       return 0;
+               }
+               case Q_V1_SETQLIM:
+               case Q_V1_SETUSE:
+               case Q_V1_SETQUOTA: {
+                       struct v1c_mem_dqblk mdq;
+
+                       if (copy_from_user(&mdq, addr, sizeof(mdq)))
+                               return -EFAULT;
+                       return v1_set_dqblk(sb, type, cmd, id, &mdq);
+               }
+               case Q_V1_GETSTATS: {
+                       struct v1c_dqstats dst;
+
+                       v1_get_stats(&dst);
+                       if (copy_to_user(addr, &dst, sizeof(dst)))
+                               return -EFAULT;
+                       return 0;
+               }
+#endif
+#ifdef CONFIG_QIFACE_V2
+               case Q_V2_GETINFO: {
+                       struct v2c_mem_dqinfo info;
+
+                       if ((ret = v2_get_info(sb, type, &info)))
+                               return ret;
+                       if (copy_to_user(addr, &info, sizeof(info)))
+                               return -EFAULT;
+                       return 0;
+               }
+               case Q_V2_SETFLAGS:
+               case Q_V2_SETGRACE:
+               case Q_V2_SETINFO: {
+                       struct v2c_mem_dqinfo info;
+
+                       if (copy_from_user(&info, addr, sizeof(info)))
+                               return -EFAULT;
+                       
+                       return v2_set_info(sb, type, cmd, &info);
+               }
+               case Q_V2_GETQUOTA: {
+                       struct v2c_mem_dqblk mdq;
+
+                       if ((ret = v2_get_dqblk(sb, type, id, &mdq)))
+                               return ret;
+                       if (copy_to_user(addr, &mdq, sizeof(mdq)))
+                               return -EFAULT;
+                       return 0;
+               }
+               case Q_V2_SETUSE:
+               case Q_V2_SETQLIM:
+               case Q_V2_SETQUOTA: {
+                       struct v2c_mem_dqblk mdq;
+
+                       if (copy_from_user(&mdq, addr, sizeof(mdq)))
+                               return -EFAULT;
+                       return v2_set_dqblk(sb, type, cmd, id, &mdq);
+               }
+               case Q_V2_GETSTATS: {
+                       struct v2c_dqstats dst;
+
+                       v2_get_stats(&dst);
+                       if (copy_to_user(addr, &dst, sizeof(dst)))
+                               return -EFAULT;
+                       return 0;
+               }
+#endif
+       }
+       BUG();
+       return 0;
+}
+#endif
+
+/* Macros for short-circuiting the compatibility tests */
+#define NEW_COMMAND(c) ((c) & (0x80 << 16))
+#define XQM_COMMAND(c) (((c) & ('X' << 8)) == ('X' << 8))
+
 /*
  * This is the system call interface. This communicates with
  * the user-level programs. Currently this only supports diskquota
@@ -247,11 +626,25 @@ asmlinkage long sys_quotactl(unsigned int cmd, const char *special, qid_t id, ca
        cmds = cmd >> SUBCMDSHIFT;
        type = cmd & SUBCMDMASK;
 
+#ifdef CONFIG_QIFACE_COMPAT
+       if (cmds != Q_V1_GETSTATS && cmds != Q_V2_GETSTATS && IS_ERR(sb = resolve_dev(special))) {
+               ret = PTR_ERR(sb);
+               sb = NULL;
+               goto out;
+       }
+       if (!NEW_COMMAND(cmds) && !XQM_COMMAND(cmds)) {
+               if ((ret = check_compat_quotactl_valid(sb, type, cmds, id)) < 0)
+                       goto out;
+               ret = do_compat_quotactl(sb, type, cmds, id, addr);
+               goto out;
+       }
+#else
        if (IS_ERR(sb = resolve_dev(special))) {
                ret = PTR_ERR(sb);
                sb = NULL;
                goto out;
        }
+#endif
        if ((ret = check_quotactl_valid(sb, type, cmds, id)) < 0)
                goto out;
        ret = do_quotactl(sb, type, cmds, id, addr);
diff --git a/include/linux/quotacompat.h b/include/linux/quotacompat.h
new file mode 100644 (file)
index 0000000..484aac1
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ *     Definition of symbols used for backward compatible interface
+ */
+
+#ifndef _LINUX_QUOTACOMPAT_
+#define _LINUX_QUOTACOMPAT_
+
+#include <linux/types.h>
+#include <linux/quota.h>
+
+struct v1c_mem_dqblk {
+       __u32 dqb_bhardlimit;   /* absolute limit on disk blks alloc */
+       __u32 dqb_bsoftlimit;   /* preferred limit on disk blks */
+       __u32 dqb_curblocks;    /* current block count */
+       __u32 dqb_ihardlimit;   /* maximum # allocated inodes */
+       __u32 dqb_isoftlimit;   /* preferred inode limit */
+       __u32 dqb_curinodes;    /* current # allocated inodes */
+       time_t dqb_btime;       /* time limit for excessive disk use */
+       time_t dqb_itime;       /* time limit for excessive files */
+};
+
+struct v1c_dqstats {
+       __u32 lookups;
+       __u32 drops;
+       __u32 reads;
+       __u32 writes;
+       __u32 cache_hits;
+       __u32 allocated_dquots;
+       __u32 free_dquots;
+       __u32 syncs;
+};
+
+struct v2c_mem_dqblk {
+       unsigned int dqb_ihardlimit;
+       unsigned int dqb_isoftlimit;
+       unsigned int dqb_curinodes;
+       unsigned int dqb_bhardlimit;
+       unsigned int dqb_bsoftlimit;
+       qsize_t dqb_curspace;
+       __kernel_time_t dqb_btime;
+       __kernel_time_t dqb_itime;
+};
+
+struct v2c_mem_dqinfo {
+       unsigned int dqi_bgrace;
+       unsigned int dqi_igrace;
+       unsigned int dqi_flags;
+       unsigned int dqi_blocks;
+       unsigned int dqi_free_blk;
+       unsigned int dqi_free_entry;
+};
+
+struct v2c_dqstats {
+       __u32 lookups;
+       __u32 drops;
+       __u32 reads;
+       __u32 writes;
+       __u32 cache_hits;
+       __u32 allocated_dquots;
+       __u32 free_dquots;
+       __u32 syncs;
+       __u32 version;
+};
+
+#define Q_COMP_QUOTAON  0x0100 /* enable quotas */
+#define Q_COMP_QUOTAOFF 0x0200 /* disable quotas */
+#define Q_COMP_SYNC     0x0600 /* sync disk copy of a filesystems quotas */
+
+#define Q_V1_GETQUOTA 0x0300   /* get limits and usage */
+#define Q_V1_SETQUOTA 0x0400   /* set limits and usage */
+#define Q_V1_SETUSE   0x0500   /* set usage */
+#define Q_V1_SETQLIM  0x0700   /* set limits */
+#define Q_V1_GETSTATS 0x0800   /* get collected stats */
+#define Q_V1_RSQUASH  0x1000   /* set root_squash option */
+
+#define Q_V2_SETQLIM  0x0700   /* set limits */
+#define Q_V2_GETINFO  0x0900   /* get info about quotas - graces, flags... */
+#define Q_V2_SETINFO  0x0A00   /* set info about quotas */
+#define Q_V2_SETGRACE 0x0B00   /* set inode and block grace */
+#define Q_V2_SETFLAGS 0x0C00   /* set flags for quota */
+#define Q_V2_GETQUOTA 0x0D00   /* get limits and usage */
+#define Q_V2_SETQUOTA 0x0E00   /* set limits and usage */
+#define Q_V2_SETUSE   0x0F00   /* set usage */
+#define Q_V2_GETSTATS 0x1100   /* get collected stats */
+
+#endif