Commit 704a6dfc authored by Linus Torvalds's avatar Linus Torvalds

Merge quota update

parents 155aba2e 6a0e8d78
......@@ -322,7 +322,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 sys32_quotactl
data8 sys_quotactl
data8 sys_getpgid
data8 sys_fchdir
data8 sys32_ni_syscall /* sys_bdflush */
......
......@@ -3669,97 +3669,6 @@ 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)
{
......
......@@ -897,97 +897,6 @@ 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;
......
......@@ -586,7 +586,7 @@ sys32_quotactl_wrapper:
llgtr %r3,%r3 # const char *
lgfr %r4,%r4 # int
llgtr %r5,%r5 # caddr_t
jg sys32_quotactl # branch to system call
jg sys_quotactl # branch to system call
.globl sys32_getpgid_wrapper
sys32_getpgid_wrapper:
......
......@@ -889,97 +889,6 @@ 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;
......
......@@ -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 sys32_quotactl, sys_nis_syscall, sys32_mount, sys_ustat, sys_setxattr
.word sys_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
......
......@@ -18,21 +18,6 @@ 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
......
......@@ -7,11 +7,8 @@ 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
if [ "$CONFIG_QUOTA" = "y" ]; then
define_bool CONFIG_QUOTACTL y
fi
tristate 'Kernel automounter support' CONFIG_AUTOFS_FS
tristate 'Kernel automounter version 4 support (also supports v3)' CONFIG_AUTOFS4_FS
......
......@@ -16,7 +16,7 @@ obj-y := open.o read_write.o devices.o file_table.o buffer.o \
namei.o fcntl.o ioctl.o readdir.o select.o fifo.o locks.o \
dcache.o inode.o attr.o bad_inode.o file.o iobuf.o dnotify.o \
filesystems.o namespace.o seq_file.o xattr.o libfs.o \
fs-writeback.o quota.o mpage.o
fs-writeback.o mpage.o
ifneq ($(CONFIG_NFSD),n)
ifneq ($(CONFIG_NFSD),)
......@@ -36,6 +36,7 @@ obj-$(CONFIG_BINFMT_ELF) += binfmt_elf.o
obj-$(CONFIG_QUOTA) += dquot.o
obj-$(CONFIG_QFMT_V1) += quota_v1.o
obj-$(CONFIG_QFMT_V2) += quota_v2.o
obj-$(CONFIG_QUOTACTL) += quota.o
obj-$(CONFIG_PROC_FS) += proc/
obj-y += partitions/
......
......@@ -45,6 +45,10 @@
* Added dynamic quota structure allocation
* Jan Kara <jack@suse.cz> 12/2000
*
* Rewritten quota interface. Implemented new quota format and
* formats registering.
* Jan Kara, <jack@suse.cz>, 2001,2002
*
* (C) Copyright 1994 - 1997 Marco van Wieringen
*/
......@@ -121,7 +125,7 @@ static void put_quota_format(struct quota_format_type *fmt)
* Unused dquots (dq_count == 0) are added to the free_dquots list when freed,
* and this list is searched whenever we need an available dquot. Dquots are
* removed from the list as soon as they are used again, and
* dqstats_array[DQSTATS_FREE] gives the number of dquots on the list. When
* dqstats.free_dquots gives the number of dquots on the list. When
* dquot is invalidated it's completely released from memory.
*
* Dquots with a specific identity (device, type and id) are placed on
......@@ -148,7 +152,7 @@ static LIST_HEAD(inuse_list);
static LIST_HEAD(free_dquots);
static struct list_head dquot_hash[NR_DQHASH];
__u32 dqstats_array[DQSTATS_SIZE];
struct dqstats dqstats;
static void dqput(struct dquot *);
static struct dquot *dqduplicate(struct dquot *);
......@@ -207,14 +211,14 @@ static inline struct dquot *find_dquot(unsigned int hashent, struct super_block
static inline void put_dquot_head(struct dquot *dquot)
{
list_add(&dquot->dq_free, &free_dquots);
++dqstats_array[DQSTATS_FREE];
dqstats.free_dquots++;
}
/* Add a dquot to the tail of the free list */
static inline void put_dquot_last(struct dquot *dquot)
{
list_add(&dquot->dq_free, free_dquots.prev);
++dqstats_array[DQSTATS_FREE];
dqstats.free_dquots++;
}
/* Move dquot to the head of free list (it must be already on it) */
......@@ -230,7 +234,7 @@ static inline void remove_free_dquot(struct dquot *dquot)
return;
list_del(&dquot->dq_free);
INIT_LIST_HEAD(&dquot->dq_free);
--dqstats_array[DQSTATS_FREE];
dqstats.free_dquots--;
}
static inline void put_inuse(struct dquot *dquot)
......@@ -238,12 +242,12 @@ static inline void put_inuse(struct dquot *dquot)
/* We add to the back of inuse list so we don't have to restart
* when traversing this list and we block */
list_add(&dquot->dq_inuse, inuse_list.prev);
++dqstats_array[DQSTATS_ALLOCATED];
dqstats.allocated_dquots++;
}
static inline void remove_inuse(struct dquot *dquot)
{
--dqstats_array[DQSTATS_ALLOCATED];
dqstats.allocated_dquots--;
list_del(&dquot->dq_inuse);
}
......@@ -403,7 +407,7 @@ static int vfs_quota_sync(struct super_block *sb, int type)
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt) && info_dirty(&dqopt->info[cnt]))
dqopt->ops[cnt]->write_file_info(sb, cnt);
++dqstats_array[DQSTATS_SYNCS];
dqstats.syncs++;
return 0;
}
......@@ -491,7 +495,7 @@ int shrink_dqcache_memory(int priority, unsigned int gfp_mask)
int count = 0;
lock_kernel();
count = dqstats_array[DQSTATS_FREE] / priority;
count = dqstats.free_dquots / priority;
prune_dqcache(count);
unlock_kernel();
kmem_cache_shrink(dquot_cachep);
......@@ -517,7 +521,7 @@ static void dqput(struct dquot *dquot)
}
#endif
++dqstats_array[DQSTATS_DROPS];
dqstats.drops++;
we_slept:
if (dquot->dq_dup_ref && dquot->dq_count - dquot->dq_dup_ref <= 1) { /* Last unduplicated reference? */
__wait_dup_drop(dquot);
......@@ -597,7 +601,7 @@ static struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
if (!dquot->dq_count)
remove_free_dquot(dquot);
get_dquot_ref(dquot);
++dqstats_array[DQSTATS_CACHE_HITS];
dqstats.cache_hits++;
wait_on_dquot(dquot);
if (empty)
dqput(empty);
......@@ -609,7 +613,7 @@ static struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
return NODQUOT;
}
++dquot->dq_referenced;
++dqstats_array[DQSTATS_LOOKUPS];
dqstats.lookups++;
return dquot;
}
......@@ -629,7 +633,7 @@ static struct dquot *dqduplicate(struct dquot *dquot)
printk(KERN_ERR "VFS: dqduplicate(): Locked quota to be duplicated!\n");
get_dquot_dup_ref(dquot);
dquot->dq_referenced++;
++dqstats_array[DQSTATS_LOOKUPS];
dqstats.lookups++;
return dquot;
}
......@@ -645,7 +649,7 @@ static void dqputduplicate(struct dquot *dquot)
if (!dquot->dq_dup_ref)
wake_up(&dquot->dq_wait_free);
put_dquot_ref(dquot);
++dqstats_array[DQSTATS_DROPS];
dqstats.drops++;
}
static int dqinit_needed(struct inode *inode, int type)
......@@ -1295,7 +1299,7 @@ int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path)
int error;
if (!fmt)
return -EINVAL;
return -ESRCH;
if (is_enabled(dqopt, type)) {
error = -EBUSY;
goto out_fmt;
......@@ -1437,7 +1441,7 @@ int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *d
}
/* Generic routine for getting common part of quota file information */
int vfs_get_info(struct super_block *sb, int type, struct if_dqinfo *ii)
int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
{
struct mem_dqinfo *mi = sb_dqopt(sb)->info + type;
......@@ -1449,7 +1453,7 @@ int vfs_get_info(struct super_block *sb, int type, struct if_dqinfo *ii)
}
/* Generic routine for setting common part of quota file information */
int vfs_set_info(struct super_block *sb, int type, struct if_dqinfo *ii)
int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
{
struct mem_dqinfo *mi = sb_dqopt(sb)->info + type;
......@@ -1467,18 +1471,30 @@ struct quotactl_ops vfs_quotactl_ops = {
quota_on: vfs_quota_on,
quota_off: vfs_quota_off,
quota_sync: vfs_quota_sync,
get_info: vfs_get_info,
set_info: vfs_set_info,
get_info: vfs_get_dqinfo,
set_info: vfs_set_dqinfo,
get_dqblk: vfs_get_dqblk,
set_dqblk: vfs_set_dqblk
};
static ctl_table fs_dqstats_table[] = {
{FS_DQ_LOOKUPS, "lookups", &dqstats.lookups, sizeof(int), 0444, NULL, &proc_dointvec},
{FS_DQ_DROPS, "drops", &dqstats.drops, sizeof(int), 0444, NULL, &proc_dointvec},
{FS_DQ_READS, "reads", &dqstats.reads, sizeof(int), 0444, NULL, &proc_dointvec},
{FS_DQ_WRITES, "writes", &dqstats.writes, sizeof(int), 0444, NULL, &proc_dointvec},
{FS_DQ_CACHE_HITS, "cache_hits", &dqstats.cache_hits, sizeof(int), 0444, NULL, &proc_dointvec},
{FS_DQ_ALLOCATED, "allocated_dquots", &dqstats.allocated_dquots, sizeof(int), 0444, NULL, &proc_dointvec},
{FS_DQ_FREE, "free_dquots", &dqstats.free_dquots, sizeof(int), 0444, NULL, &proc_dointvec},
{FS_DQ_SYNCS, "syncs", &dqstats.syncs, sizeof(int), 0444, NULL, &proc_dointvec},
{},
};
static ctl_table fs_table[] = {
{FS_DQSTATS, "dqstats", dqstats_array, sizeof(dqstats_array), 0444, NULL, &proc_dointvec},
{FS_DQSTATS, "quota", NULL, 0, 0555, fs_dqstats_table},
{},
};
static ctl_table dquot_table[] = {
static ctl_table sys_table[] = {
{CTL_FS, "fs", NULL, 0, 0555, fs_table},
{},
};
......@@ -1487,7 +1503,7 @@ static int __init dquot_init(void)
{
int i;
register_sysctl_table(dquot_table, 0);
register_sysctl_table(sys_table, 0);
for (i = 0; i < NR_DQHASH; i++)
INIT_LIST_HEAD(dquot_hash + i);
printk(KERN_NOTICE "VFS: Disk quotas v%s\n", __DQUOT_VERSION__);
......@@ -1498,4 +1514,4 @@ __initcall(dquot_init);
EXPORT_SYMBOL(register_quota_format);
EXPORT_SYMBOL(unregister_quota_format);
EXPORT_SYMBOL(dqstats_array);
EXPORT_SYMBOL(dqstats);
......@@ -6,16 +6,12 @@
*/
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/slab.h>
#include <asm/current.h>
#include <asm/uaccess.h>
#include <linux/kernel.h>
#include <linux/smp_lock.h>
#include <linux/namei.h>
#ifdef CONFIG_QIFACE_COMPAT
#include <linux/quotacompat.h>
#endif
/* Check validity of quotactl */
static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
......@@ -234,381 +230,6 @@ 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_array, sizeof(dqstats_array));
}
#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_array, sizeof(dqstats_array));
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
......@@ -625,25 +246,11 @@ 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);
......
......@@ -57,7 +57,7 @@ static int v1_read_dqblk(struct dquot *dquot)
if (dquot->dq_dqb.dqb_bhardlimit == 0 && dquot->dq_dqb.dqb_bsoftlimit == 0 &&
dquot->dq_dqb.dqb_ihardlimit == 0 && dquot->dq_dqb.dqb_isoftlimit == 0)
dquot->dq_flags |= DQ_FAKE;
++dqstats_array[DQSTATS_READS];
dqstats.reads++;
return 0;
}
......@@ -101,7 +101,7 @@ static int v1_commit_dqblk(struct dquot *dquot)
out:
set_fs(fs);
++dqstats_array[DQSTATS_WRITES];
dqstats.writes++;
return ret;
}
......@@ -234,6 +234,8 @@ static void __exit exit_v1_quota_format(void)
unregister_quota_format(&v1_quota_format);
}
EXPORT_NO_SYMBOLS;
module_init(init_v1_quota_format);
module_exit(exit_v1_quota_format);
......@@ -430,7 +430,7 @@ static int v2_write_dquot(struct dquot *dquot)
}
else
ret = 0;
++dqstats_array[DQSTATS_WRITES];
dqstats.writes++;
return ret;
}
......@@ -645,7 +645,7 @@ static int v2_read_dquot(struct dquot *dquot)
set_fs(fs);
disk2memdqb(&dquot->dq_dqb, &ddquot);
}
++dqstats_array[DQSTATS_READS];
dqstats.reads++;
return ret;
}
......@@ -686,5 +686,7 @@ static void __exit exit_v2_quota_format(void)
unregister_quota_format(&v2_quota_format);
}
EXPORT_NO_SYMBOLS;
module_init(init_v2_quota_format);
module_exit(exit_v2_quota_format);
......@@ -184,22 +184,18 @@ extern inline void mark_info_dirty(struct mem_dqinfo *info)
#define sb_dqopt(sb) (&(sb)->s_dquot)
/*
* Statistics about disc quota.
*/
enum {
DQSTATS_LOOKUPS,
DQSTATS_DROPS,
DQSTATS_READS,
DQSTATS_WRITES,
DQSTATS_CACHE_HITS,
DQSTATS_ALLOCATED,
DQSTATS_FREE,
DQSTATS_SYNCS,
DQSTATS_SIZE
struct dqstats {
int lookups;
int drops;
int reads;
int writes;
int cache_hits;
int allocated_dquots;
int free_dquots;
int syncs;
};
extern __u32 dqstats_array[DQSTATS_SIZE];
extern struct dqstats dqstats;
#define NR_DQHASH 43 /* Just an arbitrary number */
......
/*
* 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
......@@ -544,7 +544,19 @@ enum
FS_LEASES=13, /* int: leases enabled */
FS_DIR_NOTIFY=14, /* int: directory notification enabled */
FS_LEASE_TIME=15, /* int: maximum time to wait for a lease break */
FS_DQSTATS=16, /* int: disc quota suage statistics */
FS_DQSTATS=16, /* disc quota usage statistics */
};
/* /proc/sys/fs/quota/ */
enum {
FS_DQ_LOOKUPS = 1,
FS_DQ_DROPS = 2,
FS_DQ_READS = 3,
FS_DQ_WRITES = 4,
FS_DQ_CACHE_HITS = 5,
FS_DQ_ALLOCATED = 6,
FS_DQ_FREE = 7,
FS_DQ_SYNCS = 8,
};
/* CTL_DEBUG names: */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment