Commit b716395e2b8e450e294537de0c91476ded2f0395
Committed by
Linus Torvalds
1 parent
4b7775870b
Exists in
master
and in
20 other branches
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
fs/quota.c
... | ... | @@ -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 |