Commit b716395e2b8e450e294537de0c91476ded2f0395

Authored by Vasily Tarasov
Committed by Linus Torvalds
1 parent 4b7775870b

diskquota: 32bit quota tools on 64bit architectures

OpenVZ Linux kernel team has discovered the problem with 32bit quota tools
working on 64bit architectures.  In 2.6.10 kernel sys32_quotactl() function
was replaced by sys_quotactl() with the comment "sys_quotactl seems to be
32/64bit clean, enable it for 32bit" However this isn't right.  Look at
if_dqblk structure:

struct if_dqblk {
        __u64 dqb_bhardlimit;
        __u64 dqb_bsoftlimit;
        __u64 dqb_curspace;
        __u64 dqb_ihardlimit;
        __u64 dqb_isoftlimit;
        __u64 dqb_curinodes;
        __u64 dqb_btime;
        __u64 dqb_itime;
        __u32 dqb_valid;
};

For 32 bit quota tools sizeof(if_dqblk) == 0x44.
But for 64 bit kernel its size is 0x48, 'cause of alignment!
Thus we got a problem. Attached patch reintroduce sys32_quotactl() function,
that handles this and related situations.

[michal.k.k.piotrowski@gmail.com: build fix]
[akpm@linux-foundation.org: Make it link with CONFIG_QUOTA=n]
Signed-off-by: Vasily Tarasov <vtaras@openvz.org>
Cc: Andi Kleen <ak@suse.de>
Cc: "Luck, Tony" <tony.luck@intel.com>
Cc: Jan Kara <jack@ucw.cz>
Cc: <linux-arch@vger.kernel.org>
Signed-off-by: Michal Piotrowski <michal.k.k.piotrowski@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 4 changed files with 121 additions and 2 deletions Side-by-side Diff

arch/ia64/ia32/ia32_entry.S
... ... @@ -304,7 +304,7 @@
304 304 data8 sys_ni_syscall /* init_module */
305 305 data8 sys_ni_syscall /* delete_module */
306 306 data8 sys_ni_syscall /* get_kernel_syms */ /* 130 */
307   - data8 sys_quotactl
  307 + data8 sys32_quotactl
308 308 data8 sys_getpgid
309 309 data8 sys_fchdir
310 310 data8 sys_ni_syscall /* sys_bdflush */
arch/x86_64/ia32/ia32entry.S
... ... @@ -526,7 +526,7 @@
526 526 .quad sys_init_module
527 527 .quad sys_delete_module
528 528 .quad quiet_ni_syscall /* 130 get_kernel_syms */
529   - .quad sys_quotactl
  529 + .quad sys32_quotactl
530 530 .quad sys_getpgid
531 531 .quad sys_fchdir
532 532 .quad quiet_ni_syscall /* bdflush */
... ... @@ -10,12 +10,14 @@
10 10 #include <linux/slab.h>
11 11 #include <asm/current.h>
12 12 #include <asm/uaccess.h>
  13 +#include <linux/compat.h>
13 14 #include <linux/kernel.h>
14 15 #include <linux/security.h>
15 16 #include <linux/syscalls.h>
16 17 #include <linux/buffer_head.h>
17 18 #include <linux/capability.h>
18 19 #include <linux/quotaops.h>
  20 +#include <linux/types.h>
19 21  
20 22 /* Check validity of generic quotactl commands */
21 23 static int generic_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
... ... @@ -384,4 +386,120 @@
384 386  
385 387 return ret;
386 388 }
  389 +
  390 +#if defined(CONFIG_X86_64) || defined(CONFIG_IA64)
  391 +/*
  392 + * This code works only for 32 bit quota tools over 64 bit OS (x86_64, ia64)
  393 + * and is necessary due to alignment problems.
  394 + */
  395 +struct compat_if_dqblk {
  396 + compat_u64 dqb_bhardlimit;
  397 + compat_u64 dqb_bsoftlimit;
  398 + compat_u64 dqb_curspace;
  399 + compat_u64 dqb_ihardlimit;
  400 + compat_u64 dqb_isoftlimit;
  401 + compat_u64 dqb_curinodes;
  402 + compat_u64 dqb_btime;
  403 + compat_u64 dqb_itime;
  404 + compat_uint_t dqb_valid;
  405 +};
  406 +
  407 +/* XFS structures */
  408 +struct compat_fs_qfilestat {
  409 + compat_u64 dqb_bhardlimit;
  410 + compat_u64 qfs_nblks;
  411 + compat_uint_t qfs_nextents;
  412 +};
  413 +
  414 +struct compat_fs_quota_stat {
  415 + __s8 qs_version;
  416 + __u16 qs_flags;
  417 + __s8 qs_pad;
  418 + struct compat_fs_qfilestat qs_uquota;
  419 + struct compat_fs_qfilestat qs_gquota;
  420 + compat_uint_t qs_incoredqs;
  421 + compat_int_t qs_btimelimit;
  422 + compat_int_t qs_itimelimit;
  423 + compat_int_t qs_rtbtimelimit;
  424 + __u16 qs_bwarnlimit;
  425 + __u16 qs_iwarnlimit;
  426 +};
  427 +
  428 +asmlinkage long sys32_quotactl(unsigned int cmd, const char __user *special,
  429 + qid_t id, void __user *addr)
  430 +{
  431 + unsigned int cmds;
  432 + struct if_dqblk __user *dqblk;
  433 + struct compat_if_dqblk __user *compat_dqblk;
  434 + struct fs_quota_stat __user *fsqstat;
  435 + struct compat_fs_quota_stat __user *compat_fsqstat;
  436 + compat_uint_t data;
  437 + u16 xdata;
  438 + long ret;
  439 +
  440 + cmds = cmd >> SUBCMDSHIFT;
  441 +
  442 + switch (cmds) {
  443 + case Q_GETQUOTA:
  444 + dqblk = compat_alloc_user_space(sizeof(struct if_dqblk));
  445 + compat_dqblk = addr;
  446 + ret = sys_quotactl(cmd, special, id, dqblk);
  447 + if (ret)
  448 + break;
  449 + if (copy_in_user(compat_dqblk, dqblk, sizeof(*compat_dqblk)) ||
  450 + get_user(data, &dqblk->dqb_valid) ||
  451 + put_user(data, &compat_dqblk->dqb_valid))
  452 + ret = -EFAULT;
  453 + break;
  454 + case Q_SETQUOTA:
  455 + dqblk = compat_alloc_user_space(sizeof(struct if_dqblk));
  456 + compat_dqblk = addr;
  457 + ret = -EFAULT;
  458 + if (copy_in_user(dqblk, compat_dqblk, sizeof(*compat_dqblk)) ||
  459 + get_user(data, &compat_dqblk->dqb_valid) ||
  460 + put_user(data, &dqblk->dqb_valid))
  461 + break;
  462 + ret = sys_quotactl(cmd, special, id, dqblk);
  463 + break;
  464 + case Q_XGETQSTAT:
  465 + fsqstat = compat_alloc_user_space(sizeof(struct fs_quota_stat));
  466 + compat_fsqstat = addr;
  467 + ret = sys_quotactl(cmd, special, id, fsqstat);
  468 + if (ret)
  469 + break;
  470 + ret = -EFAULT;
  471 + /* Copying qs_version, qs_flags, qs_pad */
  472 + if (copy_in_user(compat_fsqstat, fsqstat,
  473 + offsetof(struct compat_fs_quota_stat, qs_uquota)))
  474 + break;
  475 + /* Copying qs_uquota */
  476 + if (copy_in_user(&compat_fsqstat->qs_uquota,
  477 + &fsqstat->qs_uquota,
  478 + sizeof(compat_fsqstat->qs_uquota)) ||
  479 + get_user(data, &fsqstat->qs_uquota.qfs_nextents) ||
  480 + put_user(data, &compat_fsqstat->qs_uquota.qfs_nextents))
  481 + break;
  482 + /* Copying qs_gquota */
  483 + if (copy_in_user(&compat_fsqstat->qs_gquota,
  484 + &fsqstat->qs_gquota,
  485 + sizeof(compat_fsqstat->qs_gquota)) ||
  486 + get_user(data, &fsqstat->qs_gquota.qfs_nextents) ||
  487 + put_user(data, &compat_fsqstat->qs_gquota.qfs_nextents))
  488 + break;
  489 + /* Copying the rest */
  490 + if (copy_in_user(&compat_fsqstat->qs_incoredqs,
  491 + &fsqstat->qs_incoredqs,
  492 + sizeof(struct compat_fs_quota_stat) -
  493 + offsetof(struct compat_fs_quota_stat, qs_incoredqs)) ||
  494 + get_user(xdata, &fsqstat->qs_iwarnlimit) ||
  495 + put_user(xdata, &compat_fsqstat->qs_iwarnlimit))
  496 + break;
  497 + ret = 0;
  498 + break;
  499 + default:
  500 + ret = sys_quotactl(cmd, special, id, addr);
  501 + }
  502 + return ret;
  503 +}
  504 +#endif
... ... @@ -14,6 +14,7 @@
14 14  
15 15 cond_syscall(sys_nfsservctl);
16 16 cond_syscall(sys_quotactl);
  17 +cond_syscall(sys32_quotactl);
17 18 cond_syscall(sys_acct);
18 19 cond_syscall(sys_lookup_dcookie);
19 20 cond_syscall(sys_swapon);