diff options
author | Vasily Tarasov <vtaras@openvz.org> | 2007-07-16 02:41:12 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-16 12:05:48 -0400 |
commit | b716395e2b8e450e294537de0c91476ded2f0395 (patch) | |
tree | 7f8fd39022c1caca71abb30303a453d77cf4d905 | |
parent | 4b7775870b69129e640ed583c9b362d5cd66159d (diff) |
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>
-rw-r--r-- | arch/ia64/ia32/ia32_entry.S | 2 | ||||
-rw-r--r-- | arch/x86_64/ia32/ia32entry.S | 2 | ||||
-rw-r--r-- | fs/quota.c | 118 | ||||
-rw-r--r-- | kernel/sys_ni.c | 1 |
4 files changed, 121 insertions, 2 deletions
diff --git a/arch/ia64/ia32/ia32_entry.S b/arch/ia64/ia32/ia32_entry.S index 99b665e2b1d5..06efd1f9b800 100644 --- a/arch/ia64/ia32/ia32_entry.S +++ b/arch/ia64/ia32/ia32_entry.S | |||
@@ -304,7 +304,7 @@ ia32_syscall_table: | |||
304 | data8 sys_ni_syscall /* init_module */ | 304 | data8 sys_ni_syscall /* init_module */ |
305 | data8 sys_ni_syscall /* delete_module */ | 305 | data8 sys_ni_syscall /* delete_module */ |
306 | data8 sys_ni_syscall /* get_kernel_syms */ /* 130 */ | 306 | data8 sys_ni_syscall /* get_kernel_syms */ /* 130 */ |
307 | data8 sys_quotactl | 307 | data8 sys32_quotactl |
308 | data8 sys_getpgid | 308 | data8 sys_getpgid |
309 | data8 sys_fchdir | 309 | data8 sys_fchdir |
310 | data8 sys_ni_syscall /* sys_bdflush */ | 310 | data8 sys_ni_syscall /* sys_bdflush */ |
diff --git a/arch/x86_64/ia32/ia32entry.S b/arch/x86_64/ia32/ia32entry.S index 47565c3345d2..782dea819438 100644 --- a/arch/x86_64/ia32/ia32entry.S +++ b/arch/x86_64/ia32/ia32entry.S | |||
@@ -526,7 +526,7 @@ ia32_sys_call_table: | |||
526 | .quad sys_init_module | 526 | .quad sys_init_module |
527 | .quad sys_delete_module | 527 | .quad sys_delete_module |
528 | .quad quiet_ni_syscall /* 130 get_kernel_syms */ | 528 | .quad quiet_ni_syscall /* 130 get_kernel_syms */ |
529 | .quad sys_quotactl | 529 | .quad sys32_quotactl |
530 | .quad sys_getpgid | 530 | .quad sys_getpgid |
531 | .quad sys_fchdir | 531 | .quad sys_fchdir |
532 | .quad quiet_ni_syscall /* bdflush */ | 532 | .quad quiet_ni_syscall /* bdflush */ |
diff --git a/fs/quota.c b/fs/quota.c index 9f237d6182c9..e6577ac15a6c 100644 --- a/fs/quota.c +++ b/fs/quota.c | |||
@@ -10,12 +10,14 @@ | |||
10 | #include <linux/slab.h> | 10 | #include <linux/slab.h> |
11 | #include <asm/current.h> | 11 | #include <asm/current.h> |
12 | #include <asm/uaccess.h> | 12 | #include <asm/uaccess.h> |
13 | #include <linux/compat.h> | ||
13 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
14 | #include <linux/security.h> | 15 | #include <linux/security.h> |
15 | #include <linux/syscalls.h> | 16 | #include <linux/syscalls.h> |
16 | #include <linux/buffer_head.h> | 17 | #include <linux/buffer_head.h> |
17 | #include <linux/capability.h> | 18 | #include <linux/capability.h> |
18 | #include <linux/quotaops.h> | 19 | #include <linux/quotaops.h> |
20 | #include <linux/types.h> | ||
19 | 21 | ||
20 | /* Check validity of generic quotactl commands */ | 22 | /* Check validity of generic quotactl commands */ |
21 | static int generic_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id) | 23 | static int generic_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id) |
@@ -384,3 +386,119 @@ asmlinkage long sys_quotactl(unsigned int cmd, const char __user *special, qid_t | |||
384 | 386 | ||
385 | return ret; | 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 | ||
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 7e11e2c98bf9..b0ec498a18d9 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c | |||
@@ -14,6 +14,7 @@ asmlinkage long sys_ni_syscall(void) | |||
14 | 14 | ||
15 | cond_syscall(sys_nfsservctl); | 15 | cond_syscall(sys_nfsservctl); |
16 | cond_syscall(sys_quotactl); | 16 | cond_syscall(sys_quotactl); |
17 | cond_syscall(sys32_quotactl); | ||
17 | cond_syscall(sys_acct); | 18 | cond_syscall(sys_acct); |
18 | cond_syscall(sys_lookup_dcookie); | 19 | cond_syscall(sys_lookup_dcookie); |
19 | cond_syscall(sys_swapon); | 20 | cond_syscall(sys_swapon); |