Commit 43a3a37b authored by Linus Torvalds's avatar Linus Torvalds

Merge quota update from Jan Kara

parents 6033f024 ad447df3
...@@ -3669,7 +3669,11 @@ getname32 (const char *filename) ...@@ -3669,7 +3669,11 @@ getname32 (const char *filename)
return result; return result;
} }
struct dqblk32 { 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_bhardlimit;
__u32 dqb_bsoftlimit; __u32 dqb_bsoftlimit;
__u32 dqb_curblocks; __u32 dqb_curblocks;
...@@ -3679,50 +3683,83 @@ struct dqblk32 { ...@@ -3679,50 +3683,83 @@ struct dqblk32 {
__kernel_time_t32 dqb_btime; __kernel_time_t32 dqb_btime;
__kernel_time_t32 dqb_itime; __kernel_time_t32 dqb_itime;
}; };
typedef struct v1c_mem_dqblk comp_dqblk_t;
asmlinkage long #define Q_COMP_GETQUOTA Q_V1_GETQUOTA
sys32_quotactl (int cmd, unsigned int special, int id, struct dqblk32 *addr) #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)
{ {
extern asmlinkage long sys_quotactl (int, const char *, int, caddr_t);
int cmds = cmd >> SUBCMDSHIFT; int cmds = cmd >> SUBCMDSHIFT;
long err;
comp_dqblk_t d;
mm_segment_t old_fs; mm_segment_t old_fs;
struct dqblk d;
char *spec; char *spec;
long err;
switch (cmds) { switch (cmds) {
case Q_GETQUOTA: case Q_COMP_GETQUOTA:
break; break;
case Q_SETQUOTA: case Q_COMP_SETQUOTA:
case Q_SETUSE: case Q_COMP_SETUSE:
case Q_SETQLIM: case Q_COMP_SETQLIM:
if (copy_from_user (&d, addr, sizeof(struct dqblk32))) if (copy_from_user(&d, (struct user_dqblk32 *)addr,
return -EFAULT; sizeof (struct user_dqblk32)))
d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; return -EFAULT;
d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; d.dqb_itime = ((struct user_dqblk32 *)&d)->dqb_itime;
break; d.dqb_btime = ((struct user_dqblk32 *)&d)->dqb_btime;
default: break;
return sys_quotactl(cmd, (void *) A(special), id, (caddr_t) addr); default:
return sys_quotactl(cmd, special, id, (__kernel_caddr_t)addr);
} }
spec = getname32((void *) A(special)); spec = getname (special);
err = PTR_ERR(spec); err = PTR_ERR(spec);
if (IS_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; return err;
old_fs = get_fs (); if (cmds == Q_COMP_GETQUOTA) {
set_fs(KERNEL_DS);
err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d);
set_fs(old_fs);
putname(spec);
if (cmds == Q_GETQUOTA) {
__kernel_time_t b = d.dqb_btime, i = d.dqb_itime; __kernel_time_t b = d.dqb_btime, i = d.dqb_itime;
((struct dqblk32 *)&d)->dqb_itime = i; ((struct user_dqblk32 *)&d)->dqb_itime = i;
((struct dqblk32 *)&d)->dqb_btime = b; ((struct user_dqblk32 *)&d)->dqb_btime = b;
if (copy_to_user(addr, &d, sizeof(struct dqblk32))) if (copy_to_user ((struct user_dqblk32 *)addr, &d,
sizeof (struct user_dqblk32)))
return -EFAULT; return -EFAULT;
} }
return err; 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 asmlinkage long
sys32_sched_rr_get_interval (pid_t pid, struct timespec32 *interval) sys32_sched_rr_get_interval (pid_t pid, struct timespec32 *interval)
{ {
......
...@@ -897,64 +897,97 @@ asmlinkage long sys32_fcntl64(unsigned int fd, unsigned int cmd, unsigned long a ...@@ -897,64 +897,97 @@ asmlinkage long sys32_fcntl64(unsigned int fd, unsigned int cmd, unsigned long a
return sys32_fcntl(fd, cmd, arg); return sys32_fcntl(fd, cmd, arg);
} }
struct mem_dqblk32 { extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr);
__u32 dqb_ihardlimit;
__u32 dqb_isoftlimit; #ifdef CONFIG_QIFACE_COMPAT
__u32 dqb_curinodes; #ifdef CONFIG_QIFACE_V1
__u32 dqb_bhardlimit; struct user_dqblk32 {
__u32 dqb_bsoftlimit; __u32 dqb_bhardlimit;
__u64 dqb_curspace; __u32 dqb_bsoftlimit;
__kernel_time_t32 dqb_btime; __u32 dqb_curblocks;
__kernel_time_t32 dqb_itime; __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;
extern asmlinkage long sys_quotactl(int cmd, const char *special, int id, __kernel_caddr_t addr);
#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;
asmlinkage int sys32_quotactl(int cmd, const char *special, int id, unsigned long addr) #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 cmds = cmd >> SUBCMDSHIFT;
int err; int err;
struct mem_dqblk d; comp_dqblk_t d;
mm_segment_t old_fs; mm_segment_t old_fs;
char *spec; char *spec;
switch (cmds) { switch (cmds) {
case Q_GETQUOTA: case Q_COMP_GETQUOTA:
break; break;
case Q_SETQUOTA: case Q_COMP_SETQUOTA:
case Q_SETUSE: case Q_COMP_SETUSE:
case Q_SETQLIM: case Q_COMP_SETQLIM:
if (copy_from_user (&d, (struct mem_dqblk32 *)addr, if (copy_from_user(&d, (struct user_dqblk32 *)addr,
sizeof (struct mem_dqblk32))) sizeof (struct user_dqblk32)))
return -EFAULT; return -EFAULT;
d.dqb_itime = ((struct mem_dqblk32 *)&d)->dqb_itime; d.dqb_itime = ((struct user_dqblk32 *)&d)->dqb_itime;
d.dqb_btime = ((struct mem_dqblk32 *)&d)->dqb_btime; d.dqb_btime = ((struct user_dqblk32 *)&d)->dqb_btime;
break; break;
default: default:
return sys_quotactl(cmd, special, return sys_quotactl(cmd, special, id, (__kernel_caddr_t)addr);
id, (__kernel_caddr_t)addr);
} }
spec = getname (special); spec = getname (special);
err = PTR_ERR(spec); err = PTR_ERR(spec);
if (IS_ERR(spec)) return err; if (IS_ERR(spec)) return err;
old_fs = get_fs (); old_fs = get_fs();
set_fs (KERNEL_DS); set_fs (KERNEL_DS);
err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d); err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d);
set_fs (old_fs); set_fs (old_fs);
putname (spec); putname (spec);
if (err) if (err)
return err; return err;
if (cmds == Q_GETQUOTA) { if (cmds == Q_COMP_GETQUOTA) {
__kernel_time_t b = d.dqb_btime, i = d.dqb_itime; __kernel_time_t b = d.dqb_btime, i = d.dqb_itime;
((struct mem_dqblk32 *)&d)->dqb_itime = i; ((struct user_dqblk32 *)&d)->dqb_itime = i;
((struct mem_dqblk32 *)&d)->dqb_btime = b; ((struct user_dqblk32 *)&d)->dqb_btime = b;
if (copy_to_user ((struct mem_dqblk32 *)addr, &d, if (copy_to_user ((struct user_dqblk32 *)addr, &d,
sizeof (struct mem_dqblk32))) sizeof (struct user_dqblk32)))
return -EFAULT; return -EFAULT;
} }
return 0; 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) static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf)
{ {
int err; int err;
......
...@@ -889,62 +889,97 @@ asmlinkage long sys32_fcntl64(unsigned int fd, unsigned int cmd, unsigned long a ...@@ -889,62 +889,97 @@ asmlinkage long sys32_fcntl64(unsigned int fd, unsigned int cmd, unsigned long a
return sys32_fcntl(fd, cmd, arg); return sys32_fcntl(fd, cmd, arg);
} }
struct 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;
};
extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr); extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr);
asmlinkage int sys32_quotactl(int cmd, const char *special, int id, unsigned long 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 cmds = cmd >> SUBCMDSHIFT;
int err; int err;
struct dqblk d; comp_dqblk_t d;
mm_segment_t old_fs; mm_segment_t old_fs;
char *spec; char *spec;
switch (cmds) { switch (cmds) {
case Q_GETQUOTA: case Q_COMP_GETQUOTA:
break; break;
case Q_SETQUOTA: case Q_COMP_SETQUOTA:
case Q_SETUSE: case Q_COMP_SETUSE:
case Q_SETQLIM: case Q_COMP_SETQLIM:
if (copy_from_user (&d, (struct dqblk32 *)addr, if (copy_from_user(&d, (struct user_dqblk32 *)addr,
sizeof (struct dqblk32))) sizeof (struct user_dqblk32)))
return -EFAULT; return -EFAULT;
d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; d.dqb_itime = ((struct user_dqblk32 *)&d)->dqb_itime;
d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; d.dqb_btime = ((struct user_dqblk32 *)&d)->dqb_btime;
break; break;
default: default:
return sys_quotactl(cmd, special, return sys_quotactl(cmd, special, id, (__kernel_caddr_t)addr);
id, (caddr_t)addr);
} }
spec = getname (special); spec = getname (special);
err = PTR_ERR(spec); err = PTR_ERR(spec);
if (IS_ERR(spec)) return err; if (IS_ERR(spec)) return err;
old_fs = get_fs (); old_fs = get_fs();
set_fs (KERNEL_DS); set_fs (KERNEL_DS);
err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d);
set_fs (old_fs); set_fs (old_fs);
putname (spec); putname (spec);
if (cmds == Q_GETQUOTA) { if (err)
return err;
if (cmds == Q_COMP_GETQUOTA) {
__kernel_time_t b = d.dqb_btime, i = d.dqb_itime; __kernel_time_t b = d.dqb_btime, i = d.dqb_itime;
((struct dqblk32 *)&d)->dqb_itime = i; ((struct user_dqblk32 *)&d)->dqb_itime = i;
((struct dqblk32 *)&d)->dqb_btime = b; ((struct user_dqblk32 *)&d)->dqb_btime = b;
if (copy_to_user ((struct dqblk32 *)addr, &d, if (copy_to_user ((struct user_dqblk32 *)addr, &d,
sizeof (struct dqblk32))) sizeof (struct user_dqblk32)))
return -EFAULT; return -EFAULT;
} }
return err; 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) static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf)
{ {
int err; int err;
......
...@@ -194,7 +194,7 @@ sunos_sys_table: ...@@ -194,7 +194,7 @@ sunos_sys_table:
.word sunos_getdirentries, sys32_statfs, sys32_fstatfs .word sunos_getdirentries, sys32_statfs, sys32_fstatfs
.word sys_oldumount, sunos_nosys, sunos_nosys .word sys_oldumount, sunos_nosys, sunos_nosys
.word sys_getdomainname, sys_setdomainname .word sys_getdomainname, sys_setdomainname
.word sunos_nosys, sys32_quotactl, sunos_nosys .word sunos_nosys, sys_quotactl, sunos_nosys
.word sunos_mount, sys_ustat, sunos_semsys .word sunos_mount, sys_ustat, sunos_semsys
.word sunos_nosys, sunos_shmsys, sunos_audit .word sunos_nosys, sunos_shmsys, sunos_audit
.word sunos_nosys, sunos_getdents, sys_setsid .word sunos_nosys, sunos_getdents, sys_setsid
......
CONFIG_QUOTA CONFIG_QUOTA
If you say Y here, you will be able to set per user limits for disk If you say Y here, you will be able to set per user limits for disk
usage (also called disk quotas). Currently, it works only for the usage (also called disk quotas). Currently, it works for the
ext2 file system. You need additional software in order to use quota ext2, ext3, and reiserfs file system. You need additional software
support; for details, read the Quota mini-HOWTO, available from in order to use quota support (you can download sources from
<http://www.sf.net/projects/linuxquota/>). For further details, read
the Quota mini-HOWTO, available from
<http://www.linuxdoc.org/docs.html#howto>. Probably the quota <http://www.linuxdoc.org/docs.html#howto>. Probably the quota
support is only useful for multi user systems. If unsure, say N. support is only useful for multi user systems. If unsure, say N.
CONFIG_QFMT_V1
This quota format was (is) used by kernels earlier than 2.4.??. If
you have quota working and you don't want to convert to new quota
format say Y here.
CONFIG_QFMT_V2
This quota format allows using quotas with 32-bit UIDs/GIDs. If you
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 CONFIG_MINIX_FS
Minix is a simple operating system used in many classes about OS's. 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 The minix file system (method to organize files on a hard disk
......
...@@ -5,6 +5,14 @@ mainmenu_option next_comment ...@@ -5,6 +5,14 @@ mainmenu_option next_comment
comment 'File systems' comment 'File systems'
bool 'Quota support' CONFIG_QUOTA 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 support' CONFIG_AUTOFS_FS
tristate 'Kernel automounter version 4 support (also supports v3)' CONFIG_AUTOFS4_FS tristate 'Kernel automounter version 4 support (also supports v3)' CONFIG_AUTOFS4_FS
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
O_TARGET := fs.o O_TARGET := fs.o
export-objs := filesystems.o open.o dcache.o buffer.o bio.o inode.o export-objs := filesystems.o open.o dcache.o buffer.o bio.o inode.o dquot.o
mod-subdirs := nls mod-subdirs := nls
obj-y := open.o read_write.o devices.o file_table.o buffer.o \ obj-y := open.o read_write.o devices.o file_table.o buffer.o \
...@@ -15,7 +15,7 @@ obj-y := open.o read_write.o devices.o file_table.o buffer.o \ ...@@ -15,7 +15,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 \ 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 \ 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 \ filesystems.o namespace.o seq_file.o xattr.o libfs.o \
fs-writeback.o fs-writeback.o quota.o
ifneq ($(CONFIG_NFSD),n) ifneq ($(CONFIG_NFSD),n)
ifneq ($(CONFIG_NFSD),) ifneq ($(CONFIG_NFSD),)
...@@ -82,6 +82,8 @@ obj-y += binfmt_script.o ...@@ -82,6 +82,8 @@ obj-y += binfmt_script.o
obj-$(CONFIG_BINFMT_ELF) += binfmt_elf.o obj-$(CONFIG_BINFMT_ELF) += binfmt_elf.o
obj-$(CONFIG_QUOTA) += dquot.o obj-$(CONFIG_QUOTA) += dquot.o
obj-$(CONFIG_QFMT_V1) += quota_v1.o
obj-$(CONFIG_QFMT_V2) += quota_v2.o
# persistent filesystems # persistent filesystems
obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o)) obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o))
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
* Jan Kara, <jack@suse.cz>, sponsored by SuSE CR, 10-11/99 * Jan Kara, <jack@suse.cz>, sponsored by SuSE CR, 10-11/99
* *
* Used struct list_head instead of own list struct * Used struct list_head instead of own list struct
* Invalidation of dquots with dq_count > 0 no longer possible * Invalidation of referenced dquots is no longer possible
* Improved free_dquots list management * Improved free_dquots list management
* Quota and i_blocks are now updated in one place to avoid races * Quota and i_blocks are now updated in one place to avoid races
* Warnings are now delayed so we won't block in critical section * Warnings are now delayed so we won't block in critical section
...@@ -62,18 +62,50 @@ ...@@ -62,18 +62,50 @@
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#define __DQUOT_VERSION__ "dquot_6.4.0" static char *quotatypes[] = INITQFNAMES;
static struct quota_format_type *quota_formats; /* List of registered formats */
int nr_dquots, nr_free_dquots; int register_quota_format(struct quota_format_type *fmt)
{
lock_kernel();
fmt->qf_next = quota_formats;
quota_formats = fmt;
unlock_kernel();
return 0;
}
static char *quotatypes[] = INITQFNAMES; void unregister_quota_format(struct quota_format_type *fmt)
{
struct quota_format_type **actqf;
lock_kernel();
for (actqf = &quota_formats; *actqf && *actqf != fmt; actqf = &(*actqf)->qf_next);
if (*actqf)
*actqf = (*actqf)->qf_next;
unlock_kernel();
}
static struct quota_format_type *find_quota_format(int id)
{
struct quota_format_type *actqf;
lock_kernel();
for (actqf = quota_formats; actqf && actqf->qf_fmt_id != id; actqf = actqf->qf_next);
if (actqf && !try_inc_mod_count(actqf->qf_owner))
actqf = NULL;
unlock_kernel();
return actqf;
}
static inline struct quota_mount_options *sb_dqopt(struct super_block *sb) static void put_quota_format(struct quota_format_type *fmt)
{ {
return &sb->s_dquot; if (fmt->qf_owner)
__MOD_DEC_USE_COUNT(fmt->qf_owner);
} }
/* /*
...@@ -116,28 +148,32 @@ static LIST_HEAD(inuse_list); ...@@ -116,28 +148,32 @@ static LIST_HEAD(inuse_list);
static LIST_HEAD(free_dquots); static LIST_HEAD(free_dquots);
static struct list_head dquot_hash[NR_DQHASH]; static struct list_head dquot_hash[NR_DQHASH];
static struct dqstats dqstats; struct dqstats dqstats;
static void dqput(struct dquot *); static void dqput(struct dquot *);
static struct dquot *dqduplicate(struct dquot *); static struct dquot *dqduplicate(struct dquot *);
static inline char is_enabled(struct quota_mount_options *dqopt, short type) static inline void get_dquot_ref(struct dquot *dquot)
{ {
switch (type) { dquot->dq_count++;
case USRQUOTA: }
return((dqopt->flags & DQUOT_USR_ENABLED) != 0);
case GRPQUOTA: static inline void put_dquot_ref(struct dquot *dquot)
return((dqopt->flags & DQUOT_GRP_ENABLED) != 0); {
} dquot->dq_count--;
return(0);
} }
static inline char sb_has_quota_enabled(struct super_block *sb, short type) static inline void get_dquot_dup_ref(struct dquot *dquot)
{ {
return is_enabled(sb_dqopt(sb), type); dquot->dq_dup_ref++;
} }
static inline int const hashfn(struct super_block *sb, unsigned int id, short type) static inline void put_dquot_dup_ref(struct dquot *dquot)
{
dquot->dq_dup_ref--;
}
static inline int const hashfn(struct super_block *sb, unsigned int id, int type)
{ {
return((HASHDEV(sb->s_dev) ^ id) * (MAXQUOTAS - type)) % NR_DQHASH; return((HASHDEV(sb->s_dev) ^ id) * (MAXQUOTAS - type)) % NR_DQHASH;
} }
...@@ -154,7 +190,7 @@ static inline void remove_dquot_hash(struct dquot *dquot) ...@@ -154,7 +190,7 @@ static inline void remove_dquot_hash(struct dquot *dquot)
INIT_LIST_HEAD(&dquot->dq_hash); INIT_LIST_HEAD(&dquot->dq_hash);
} }
static inline struct dquot *find_dquot(unsigned int hashent, struct super_block *sb, unsigned int id, short type) static inline struct dquot *find_dquot(unsigned int hashent, struct super_block *sb, unsigned int id, int type)
{ {
struct list_head *head; struct list_head *head;
struct dquot *dquot; struct dquot *dquot;
...@@ -244,6 +280,7 @@ static inline void unlock_dquot(struct dquot *dquot) ...@@ -244,6 +280,7 @@ static inline void unlock_dquot(struct dquot *dquot)
wake_up(&dquot->dq_wait_lock); wake_up(&dquot->dq_wait_lock);
} }
/* Wait for dquot to be unused */
static void __wait_dquot_unused(struct dquot *dquot) static void __wait_dquot_unused(struct dquot *dquot)
{ {
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
...@@ -259,79 +296,50 @@ static void __wait_dquot_unused(struct dquot *dquot) ...@@ -259,79 +296,50 @@ static void __wait_dquot_unused(struct dquot *dquot)
current->state = TASK_RUNNING; current->state = TASK_RUNNING;
} }
/* /* Wait for all duplicated dquot references to be dropped */
* We don't have to be afraid of deadlocks as we never have quotas on quota files... static void __wait_dup_drop(struct dquot *dquot)
*/ {
static void write_dquot(struct dquot *dquot) DECLARE_WAITQUEUE(wait, current);
{
short type = dquot->dq_type;
struct file *filp;
mm_segment_t fs;
loff_t offset;
ssize_t ret;
struct semaphore *sem = &dquot->dq_sb->s_dquot.dqio_sem;
struct dqblk dqbuf;
down(sem);
filp = dquot->dq_sb->s_dquot.files[type];
offset = dqoff(dquot->dq_id);
fs = get_fs();
set_fs(KERNEL_DS);
/* add_wait_queue(&dquot->dq_wait_free, &wait);
* Note: clear the DQ_MOD flag unconditionally, repeat:
* so we don't loop forever on failure. set_current_state(TASK_UNINTERRUPTIBLE);
*/ if (dquot->dq_dup_ref) {
memcpy(&dqbuf, &dquot->dq_dqb, sizeof(struct dqblk)); schedule();
dquot->dq_flags &= ~DQ_MOD; goto repeat;
ret = 0; }
if (filp) remove_wait_queue(&dquot->dq_wait_free, &wait);
ret = filp->f_op->write(filp, (char *)&dqbuf, current->state = TASK_RUNNING;
sizeof(struct dqblk), &offset); }
if (ret != sizeof(struct dqblk))
printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", static int read_dqblk(struct dquot *dquot)
dquot->dq_sb->s_id); {
int ret;
set_fs(fs); struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
up(sem);
dqstats.writes++;
}
static void read_dquot(struct dquot *dquot)
{
short type = dquot->dq_type;
struct file *filp;
mm_segment_t fs;
loff_t offset;
filp = dquot->dq_sb->s_dquot.files[type];
if (filp == (struct file *)NULL)
return;
lock_dquot(dquot); lock_dquot(dquot);
if (!dquot->dq_sb) /* Invalidated quota? */ down(&dqopt->dqio_sem);
goto out_lock; ret = dqopt->ops[dquot->dq_type]->read_dqblk(dquot);
/* Now we are sure filp is valid - the dquot isn't invalidated */ up(&dqopt->dqio_sem);
down(&dquot->dq_sb->s_dquot.dqio_sem);
offset = dqoff(dquot->dq_id);
fs = get_fs();
set_fs(KERNEL_DS);
filp->f_op->read(filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk), &offset);
up(&dquot->dq_sb->s_dquot.dqio_sem);
set_fs(fs);
if (dquot->dq_bhardlimit == 0 && dquot->dq_bsoftlimit == 0 &&
dquot->dq_ihardlimit == 0 && dquot->dq_isoftlimit == 0)
dquot->dq_flags |= DQ_FAKE;
dqstats.reads++;
out_lock:
unlock_dquot(dquot); unlock_dquot(dquot);
return ret;
}
static int commit_dqblk(struct dquot *dquot)
{
int ret;
struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
down(&dqopt->dqio_sem);
ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot);
up(&dqopt->dqio_sem);
return ret;
} }
/* Invalidate all dquots on the list, wait for all users. Note that this function is called /* Invalidate all dquots on the list, wait for all users. Note that this function is called
* after quota is disabled so no new quota might be created. As we only insert to the end of * after quota is disabled so no new quota might be created. As we only insert to the end of
* inuse list, we don't have to restart searching... */ * inuse list, we don't have to restart searching... */
static void invalidate_dquots(struct super_block *sb, short type) static void invalidate_dquots(struct super_block *sb, int type)
{ {
struct dquot *dquot; struct dquot *dquot;
struct list_head *head; struct list_head *head;
...@@ -360,12 +368,13 @@ static void invalidate_dquots(struct super_block *sb, short type) ...@@ -360,12 +368,13 @@ static void invalidate_dquots(struct super_block *sb, short type)
} }
} }
int sync_dquots(struct super_block *sb, short type) static int vfs_quota_sync(struct super_block *sb, int type)
{ {
struct list_head *head; struct list_head *head;
struct dquot *dquot; struct dquot *dquot;
struct quota_info *dqopt = sb_dqopt(sb);
int cnt;
lock_kernel();
restart: restart:
list_for_each(head, &inuse_list) { list_for_each(head, &inuse_list) {
dquot = list_entry(head, struct dquot, dq_inuse); dquot = list_entry(head, struct dquot, dq_inuse);
...@@ -375,22 +384,77 @@ int sync_dquots(struct super_block *sb, short type) ...@@ -375,22 +384,77 @@ int sync_dquots(struct super_block *sb, short type)
continue; continue;
if (!dquot->dq_sb) /* Invalidated? */ if (!dquot->dq_sb) /* Invalidated? */
continue; continue;
if (!(dquot->dq_flags & (DQ_MOD | DQ_LOCKED))) if (!dquot_dirty(dquot) && !(dquot->dq_flags & DQ_LOCKED))
continue; continue;
/* Raise use count so quota won't be invalidated. We can't use dqduplicate() as it does too many tests */ /* Get reference to quota so it won't be invalidated. get_dquot_ref()
dquot->dq_count++; * is enough since if dquot is locked/modified it can't be
* on the free list */
get_dquot_ref(dquot);
if (dquot->dq_flags & DQ_LOCKED) if (dquot->dq_flags & DQ_LOCKED)
wait_on_dquot(dquot); wait_on_dquot(dquot);
if (dquot->dq_flags & DQ_MOD) if (dquot_dirty(dquot))
write_dquot(dquot); commit_dqblk(dquot);
dqput(dquot); dqput(dquot);
goto restart; goto restart;
} }
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt))
dqopt->info[cnt].dqi_flags &= ~DQF_ANY_DQUOT_DIRTY;
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.syncs++; dqstats.syncs++;
unlock_kernel();
return 0; return 0;
} }
static struct super_block *get_super_to_sync(int type)
{
struct list_head *head;
int cnt, dirty;
restart:
spin_lock(&sb_lock);
list_for_each(head, &super_blocks) {
struct super_block *sb = list_entry(head, struct super_block, s_list);
for (cnt = 0, dirty = 0; cnt < MAXQUOTAS; cnt++)
if ((type == cnt || type == -1) && sb_has_quota_enabled(sb, cnt)
&& sb_dqopt(sb)->info[cnt].dqi_flags & DQF_ANY_DQUOT_DIRTY)
dirty = 1;
if (!dirty)
continue;
sb->s_count++;
spin_unlock(&sb_lock);
down_read(&sb->s_umount);
if (!sb->s_root) {
drop_super(sb);
goto restart;
}
return sb;
}
spin_unlock(&sb_lock);
return NULL;
}
void sync_dquots(struct super_block *sb, int type)
{
if (sb) {
lock_kernel();
if (sb->s_qcop->quota_sync)
sb->s_qcop->quota_sync(sb, type);
unlock_kernel();
}
else {
while ((sb = get_super_to_sync(type))) {
lock_kernel();
if (sb->s_qcop->quota_sync)
sb->s_qcop->quota_sync(sb, type);
unlock_kernel();
drop_super(sb);
}
}
}
/* Free unused dquots from cache */ /* Free unused dquots from cache */
static void prune_dqcache(int count) static void prune_dqcache(int count)
{ {
...@@ -433,11 +497,15 @@ int shrink_dqcache_memory(int priority, unsigned int gfp_mask) ...@@ -433,11 +497,15 @@ int shrink_dqcache_memory(int priority, unsigned int gfp_mask)
return 0; return 0;
} }
/* NOTE: If you change this function please check whether dqput_blocks() works right... */ /*
* Put reference to dquot
* NOTE: If you change this function please check whether dqput_blocks() works right...
*/
static void dqput(struct dquot *dquot) static void dqput(struct dquot *dquot)
{ {
if (!dquot) if (!dquot)
return; return;
#ifdef __DQUOT_PARANOIA
if (!dquot->dq_count) { if (!dquot->dq_count) {
printk("VFS: dqput: trying to free free dquot\n"); printk("VFS: dqput: trying to free free dquot\n");
printk("VFS: device %s, dquot of %s %d\n", printk("VFS: device %s, dquot of %s %d\n",
...@@ -446,33 +514,38 @@ static void dqput(struct dquot *dquot) ...@@ -446,33 +514,38 @@ static void dqput(struct dquot *dquot)
dquot->dq_id); dquot->dq_id);
return; return;
} }
#endif
dqstats.drops++; dqstats.drops++;
we_slept: we_slept:
if (dquot->dq_dup_ref && dquot->dq_count - dquot->dq_dup_ref <= 1) { /* Last unduplicated reference? */
__wait_dup_drop(dquot);
goto we_slept;
}
if (dquot->dq_count > 1) { if (dquot->dq_count > 1) {
/* We have more than one user... We can simply decrement use count */ /* We have more than one user... We can simply decrement use count */
dquot->dq_count--; put_dquot_ref(dquot);
return; return;
} }
if (dquot->dq_flags & DQ_MOD) { if (dquot_dirty(dquot)) {
write_dquot(dquot); commit_dqblk(dquot);
goto we_slept; goto we_slept;
} }
/* sanity check */ /* sanity check */
if (!list_empty(&dquot->dq_free)) { if (!list_empty(&dquot->dq_free)) {
printk(KERN_ERR "dqput: dquot already on free list??\n"); printk(KERN_ERR "dqput: dquot already on free list??\n");
dquot->dq_count--; /* J.K. Just decrementing use count seems safer... */ put_dquot_ref(dquot);
return; return;
} }
dquot->dq_count--; put_dquot_ref(dquot);
/* If dquot is going to be invalidated invalidate_dquots() is going to free it so */ /* If dquot is going to be invalidated invalidate_dquots() is going to free it so */
if (!(dquot->dq_flags & DQ_INVAL)) if (!(dquot->dq_flags & DQ_INVAL))
put_dquot_last(dquot); /* Place at end of LRU free queue */ put_dquot_last(dquot); /* Place at end of LRU free queue */
wake_up(&dquot->dq_wait_free); wake_up(&dquot->dq_wait_free);
} }
static struct dquot *get_empty_dquot(void) static struct dquot *get_empty_dquot(struct super_block *sb, int type)
{ {
struct dquot *dquot; struct dquot *dquot;
...@@ -486,6 +559,8 @@ static struct dquot *get_empty_dquot(void) ...@@ -486,6 +559,8 @@ static struct dquot *get_empty_dquot(void)
INIT_LIST_HEAD(&dquot->dq_free); INIT_LIST_HEAD(&dquot->dq_free);
INIT_LIST_HEAD(&dquot->dq_inuse); INIT_LIST_HEAD(&dquot->dq_inuse);
INIT_LIST_HEAD(&dquot->dq_hash); INIT_LIST_HEAD(&dquot->dq_hash);
dquot->dq_sb = sb;
dquot->dq_type = type;
dquot->dq_count = 1; dquot->dq_count = 1;
/* all dquots go on the inuse_list */ /* all dquots go on the inuse_list */
put_inuse(dquot); put_inuse(dquot);
...@@ -493,11 +568,11 @@ static struct dquot *get_empty_dquot(void) ...@@ -493,11 +568,11 @@ static struct dquot *get_empty_dquot(void)
return dquot; return dquot;
} }
static struct dquot *dqget(struct super_block *sb, unsigned int id, short type) static struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
{ {
unsigned int hashent = hashfn(sb, id, type); unsigned int hashent = hashfn(sb, id, type);
struct dquot *dquot, *empty = NODQUOT; struct dquot *dquot, *empty = NODQUOT;
struct quota_mount_options *dqopt = sb_dqopt(sb); struct quota_info *dqopt = sb_dqopt(sb);
we_slept: we_slept:
if (!is_enabled(dqopt, type)) { if (!is_enabled(dqopt, type)) {
...@@ -508,20 +583,19 @@ static struct dquot *dqget(struct super_block *sb, unsigned int id, short type) ...@@ -508,20 +583,19 @@ static struct dquot *dqget(struct super_block *sb, unsigned int id, short type)
if ((dquot = find_dquot(hashent, sb, id, type)) == NODQUOT) { if ((dquot = find_dquot(hashent, sb, id, type)) == NODQUOT) {
if (empty == NODQUOT) { if (empty == NODQUOT) {
if ((empty = get_empty_dquot()) == NODQUOT) if ((empty = get_empty_dquot(sb, type)) == NODQUOT)
schedule(); /* Try to wait for a moment... */ schedule(); /* Try to wait for a moment... */
goto we_slept; goto we_slept;
} }
dquot = empty; dquot = empty;
dquot->dq_id = id; dquot->dq_id = id;
dquot->dq_type = type;
dquot->dq_sb = sb;
/* hash it first so it can be found */ /* hash it first so it can be found */
insert_dquot_hash(dquot); insert_dquot_hash(dquot);
read_dquot(dquot); read_dqblk(dquot);
} else { } else {
if (!dquot->dq_count++) if (!dquot->dq_count)
remove_free_dquot(dquot); remove_free_dquot(dquot);
get_dquot_ref(dquot);
dqstats.cache_hits++; dqstats.cache_hits++;
wait_on_dquot(dquot); wait_on_dquot(dquot);
if (empty) if (empty)
...@@ -539,24 +613,40 @@ static struct dquot *dqget(struct super_block *sb, unsigned int id, short type) ...@@ -539,24 +613,40 @@ static struct dquot *dqget(struct super_block *sb, unsigned int id, short type)
return dquot; return dquot;
} }
/* Duplicate reference to dquot got from inode */
static struct dquot *dqduplicate(struct dquot *dquot) static struct dquot *dqduplicate(struct dquot *dquot)
{ {
if (dquot == NODQUOT) if (dquot == NODQUOT)
return NODQUOT; return NODQUOT;
dquot->dq_count++; get_dquot_ref(dquot);
if (!dquot->dq_sb) { if (!dquot->dq_sb) {
printk(KERN_ERR "VFS: dqduplicate(): Invalidated quota to be duplicated!\n"); printk(KERN_ERR "VFS: dqduplicate(): Invalidated quota to be duplicated!\n");
dquot->dq_count--; put_dquot_ref(dquot);
return NODQUOT; return NODQUOT;
} }
if (dquot->dq_flags & DQ_LOCKED) if (dquot->dq_flags & DQ_LOCKED)
printk(KERN_ERR "VFS: dqduplicate(): Locked quota to be duplicated!\n"); printk(KERN_ERR "VFS: dqduplicate(): Locked quota to be duplicated!\n");
get_dquot_dup_ref(dquot);
dquot->dq_referenced++; dquot->dq_referenced++;
dqstats.lookups++; dqstats.lookups++;
return dquot; return dquot;
} }
static int dqinit_needed(struct inode *inode, short type) /* Put duplicated reference */
static void dqputduplicate(struct dquot *dquot)
{
if (!dquot->dq_dup_ref) {
printk(KERN_ERR "VFS: dqputduplicate(): Duplicated dquot put without duplicate reference.\n");
return;
}
put_dquot_dup_ref(dquot);
if (!dquot->dq_dup_ref)
wake_up(&dquot->dq_wait_free);
put_dquot_ref(dquot);
dqstats.drops++;
}
static int dqinit_needed(struct inode *inode, int type)
{ {
int cnt; int cnt;
...@@ -570,13 +660,10 @@ static int dqinit_needed(struct inode *inode, short type) ...@@ -570,13 +660,10 @@ static int dqinit_needed(struct inode *inode, short type)
return 0; return 0;
} }
static void add_dquot_ref(struct super_block *sb, short type) static void add_dquot_ref(struct super_block *sb, int type)
{ {
struct list_head *p; struct list_head *p;
if (!sb->dq_op)
return; /* nothing to do */
restart: restart:
file_list_lock(); file_list_lock();
list_for_each(p, &sb->s_files) { list_for_each(p, &sb->s_files) {
...@@ -599,13 +686,15 @@ static void add_dquot_ref(struct super_block *sb, short type) ...@@ -599,13 +686,15 @@ static void add_dquot_ref(struct super_block *sb, short type)
/* Return 0 if dqput() won't block (note that 1 doesn't necessarily mean blocking) */ /* Return 0 if dqput() won't block (note that 1 doesn't necessarily mean blocking) */
static inline int dqput_blocks(struct dquot *dquot) static inline int dqput_blocks(struct dquot *dquot)
{ {
if (dquot->dq_count == 1) if (dquot->dq_dup_ref && dquot->dq_count - dquot->dq_dup_ref <= 1)
return 1;
if (dquot->dq_count <= 1 && dquot->dq_flags & DQ_MOD)
return 1; return 1;
return 0; return 0;
} }
/* Remove references to dquots from inode - add dquot to list for freeing if needed */ /* Remove references to dquots from inode - add dquot to list for freeing if needed */
int remove_inode_dquot_ref(struct inode *inode, short type, struct list_head *tofree_head) int remove_inode_dquot_ref(struct inode *inode, int type, struct list_head *tofree_head)
{ {
struct dquot *dquot = inode->i_dquot[type]; struct dquot *dquot = inode->i_dquot[type];
int cnt; int cnt;
...@@ -652,38 +741,38 @@ void put_dquot_list(struct list_head *tofree_head) ...@@ -652,38 +741,38 @@ void put_dquot_list(struct list_head *tofree_head)
static inline void dquot_incr_inodes(struct dquot *dquot, unsigned long number) static inline void dquot_incr_inodes(struct dquot *dquot, unsigned long number)
{ {
dquot->dq_curinodes += number; dquot->dq_dqb.dqb_curinodes += number;
dquot->dq_flags |= DQ_MOD; mark_dquot_dirty(dquot);
} }
static inline void dquot_incr_blocks(struct dquot *dquot, unsigned long number) static inline void dquot_incr_space(struct dquot *dquot, qsize_t number)
{ {
dquot->dq_curblocks += number; dquot->dq_dqb.dqb_curspace += number;
dquot->dq_flags |= DQ_MOD; mark_dquot_dirty(dquot);
} }
static inline void dquot_decr_inodes(struct dquot *dquot, unsigned long number) static inline void dquot_decr_inodes(struct dquot *dquot, unsigned long number)
{ {
if (dquot->dq_curinodes > number) if (dquot->dq_dqb.dqb_curinodes > number)
dquot->dq_curinodes -= number; dquot->dq_dqb.dqb_curinodes -= number;
else else
dquot->dq_curinodes = 0; dquot->dq_dqb.dqb_curinodes = 0;
if (dquot->dq_curinodes < dquot->dq_isoftlimit) if (dquot->dq_dqb.dqb_curinodes < dquot->dq_dqb.dqb_isoftlimit)
dquot->dq_itime = (time_t) 0; dquot->dq_dqb.dqb_itime = (time_t) 0;
dquot->dq_flags &= ~DQ_INODES; dquot->dq_flags &= ~DQ_INODES;
dquot->dq_flags |= DQ_MOD; mark_dquot_dirty(dquot);
} }
static inline void dquot_decr_blocks(struct dquot *dquot, unsigned long number) static inline void dquot_decr_space(struct dquot *dquot, qsize_t number)
{ {
if (dquot->dq_curblocks > number) if (dquot->dq_dqb.dqb_curspace > number)
dquot->dq_curblocks -= number; dquot->dq_dqb.dqb_curspace -= number;
else else
dquot->dq_curblocks = 0; dquot->dq_dqb.dqb_curspace = 0;
if (dquot->dq_curblocks < dquot->dq_bsoftlimit) if (toqb(dquot->dq_dqb.dqb_curspace) < dquot->dq_dqb.dqb_bsoftlimit)
dquot->dq_btime = (time_t) 0; dquot->dq_dqb.dqb_btime = (time_t) 0;
dquot->dq_flags &= ~DQ_BLKS; dquot->dq_flags &= ~DQ_BLKS;
dquot->dq_flags |= DQ_MOD; mark_dquot_dirty(dquot);
} }
static inline int need_print_warning(struct dquot *dquot, int flag) static inline int need_print_warning(struct dquot *dquot, int flag)
...@@ -756,7 +845,10 @@ static inline void flush_warnings(struct dquot **dquots, char *warntype) ...@@ -756,7 +845,10 @@ static inline void flush_warnings(struct dquot **dquots, char *warntype)
static inline char ignore_hardlimit(struct dquot *dquot) static inline char ignore_hardlimit(struct dquot *dquot)
{ {
return capable(CAP_SYS_RESOURCE) && !dquot->dq_sb->s_dquot.rsquash[dquot->dq_type]; struct mem_dqinfo *info = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_type];
return capable(CAP_SYS_RESOURCE) &&
(info->dqi_format->qf_fmt_id != QFMT_VFS_OLD || !(info->dqi_flags & V1_DQF_RSQUASH));
} }
static int check_idq(struct dquot *dquot, ulong inodes, char *warntype) static int check_idq(struct dquot *dquot, ulong inodes, char *warntype)
...@@ -765,60 +857,60 @@ static int check_idq(struct dquot *dquot, ulong inodes, char *warntype) ...@@ -765,60 +857,60 @@ static int check_idq(struct dquot *dquot, ulong inodes, char *warntype)
if (inodes <= 0 || dquot->dq_flags & DQ_FAKE) if (inodes <= 0 || dquot->dq_flags & DQ_FAKE)
return QUOTA_OK; return QUOTA_OK;
if (dquot->dq_ihardlimit && if (dquot->dq_dqb.dqb_ihardlimit &&
(dquot->dq_curinodes + inodes) > dquot->dq_ihardlimit && (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_ihardlimit &&
!ignore_hardlimit(dquot)) { !ignore_hardlimit(dquot)) {
*warntype = IHARDWARN; *warntype = IHARDWARN;
return NO_QUOTA; return NO_QUOTA;
} }
if (dquot->dq_isoftlimit && if (dquot->dq_dqb.dqb_isoftlimit &&
(dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit && (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_isoftlimit &&
dquot->dq_itime && CURRENT_TIME >= dquot->dq_itime && dquot->dq_dqb.dqb_itime && CURRENT_TIME >= dquot->dq_dqb.dqb_itime &&
!ignore_hardlimit(dquot)) { !ignore_hardlimit(dquot)) {
*warntype = ISOFTLONGWARN; *warntype = ISOFTLONGWARN;
return NO_QUOTA; return NO_QUOTA;
} }
if (dquot->dq_isoftlimit && if (dquot->dq_dqb.dqb_isoftlimit &&
(dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit && (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_isoftlimit &&
dquot->dq_itime == 0) { dquot->dq_dqb.dqb_itime == 0) {
*warntype = ISOFTWARN; *warntype = ISOFTWARN;
dquot->dq_itime = CURRENT_TIME + dquot->dq_sb->s_dquot.inode_expire[dquot->dq_type]; dquot->dq_dqb.dqb_itime = CURRENT_TIME + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace;
} }
return QUOTA_OK; return QUOTA_OK;
} }
static int check_bdq(struct dquot *dquot, ulong blocks, char prealloc, char *warntype) static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *warntype)
{ {
*warntype = 0; *warntype = 0;
if (blocks <= 0 || dquot->dq_flags & DQ_FAKE) if (space <= 0 || dquot->dq_flags & DQ_FAKE)
return QUOTA_OK; return QUOTA_OK;
if (dquot->dq_bhardlimit && if (dquot->dq_dqb.dqb_bhardlimit &&
(dquot->dq_curblocks + blocks) > dquot->dq_bhardlimit && toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bhardlimit &&
!ignore_hardlimit(dquot)) { !ignore_hardlimit(dquot)) {
if (!prealloc) if (!prealloc)
*warntype = BHARDWARN; *warntype = BHARDWARN;
return NO_QUOTA; return NO_QUOTA;
} }
if (dquot->dq_bsoftlimit && if (dquot->dq_dqb.dqb_bsoftlimit &&
(dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit && toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit &&
dquot->dq_btime && CURRENT_TIME >= dquot->dq_btime && dquot->dq_dqb.dqb_btime && CURRENT_TIME >= dquot->dq_dqb.dqb_btime &&
!ignore_hardlimit(dquot)) { !ignore_hardlimit(dquot)) {
if (!prealloc) if (!prealloc)
*warntype = BSOFTLONGWARN; *warntype = BSOFTLONGWARN;
return NO_QUOTA; return NO_QUOTA;
} }
if (dquot->dq_bsoftlimit && if (dquot->dq_dqb.dqb_bsoftlimit &&
(dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit && toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit &&
dquot->dq_btime == 0) { dquot->dq_dqb.dqb_btime == 0) {
if (!prealloc) { if (!prealloc) {
*warntype = BSOFTWARN; *warntype = BSOFTWARN;
dquot->dq_btime = CURRENT_TIME + dquot->dq_sb->s_dquot.block_expire[dquot->dq_type]; dquot->dq_dqb.dqb_btime = CURRENT_TIME + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_bgrace;
} }
else else
/* /*
...@@ -831,149 +923,16 @@ static int check_bdq(struct dquot *dquot, ulong blocks, char prealloc, char *war ...@@ -831,149 +923,16 @@ static int check_bdq(struct dquot *dquot, ulong blocks, char prealloc, char *war
return QUOTA_OK; return QUOTA_OK;
} }
/*
* Initialize a dquot-struct with new quota info. This is used by the
* system call interface functions.
*/
static int set_dqblk(struct super_block *sb, int id, short type, int flags, struct dqblk *dqblk)
{
struct dquot *dquot;
int error = -EFAULT;
struct dqblk dq_dqblk;
if (copy_from_user(&dq_dqblk, dqblk, sizeof(struct dqblk)))
return error;
if (sb && (dquot = dqget(sb, id, type)) != NODQUOT) {
/* We can't block while changing quota structure... */
if (id > 0 && ((flags & SET_QUOTA) || (flags & SET_QLIMIT))) {
dquot->dq_bhardlimit = dq_dqblk.dqb_bhardlimit;
dquot->dq_bsoftlimit = dq_dqblk.dqb_bsoftlimit;
dquot->dq_ihardlimit = dq_dqblk.dqb_ihardlimit;
dquot->dq_isoftlimit = dq_dqblk.dqb_isoftlimit;
}
if ((flags & SET_QUOTA) || (flags & SET_USE)) {
if (dquot->dq_isoftlimit &&
dquot->dq_curinodes < dquot->dq_isoftlimit &&
dq_dqblk.dqb_curinodes >= dquot->dq_isoftlimit)
dquot->dq_itime = CURRENT_TIME + dquot->dq_sb->s_dquot.inode_expire[type];
dquot->dq_curinodes = dq_dqblk.dqb_curinodes;
if (dquot->dq_curinodes < dquot->dq_isoftlimit)
dquot->dq_flags &= ~DQ_INODES;
if (dquot->dq_bsoftlimit &&
dquot->dq_curblocks < dquot->dq_bsoftlimit &&
dq_dqblk.dqb_curblocks >= dquot->dq_bsoftlimit)
dquot->dq_btime = CURRENT_TIME + dquot->dq_sb->s_dquot.block_expire[type];
dquot->dq_curblocks = dq_dqblk.dqb_curblocks;
if (dquot->dq_curblocks < dquot->dq_bsoftlimit)
dquot->dq_flags &= ~DQ_BLKS;
}
if (id == 0) {
dquot->dq_sb->s_dquot.block_expire[type] = dquot->dq_btime = dq_dqblk.dqb_btime;
dquot->dq_sb->s_dquot.inode_expire[type] = dquot->dq_itime = dq_dqblk.dqb_itime;
}
if (dq_dqblk.dqb_bhardlimit == 0 && dq_dqblk.dqb_bsoftlimit == 0 &&
dq_dqblk.dqb_ihardlimit == 0 && dq_dqblk.dqb_isoftlimit == 0)
dquot->dq_flags |= DQ_FAKE;
else
dquot->dq_flags &= ~DQ_FAKE;
dquot->dq_flags |= DQ_MOD;
dqput(dquot);
}
return 0;
}
static int get_quota(struct super_block *sb, int id, short type, struct dqblk *dqblk)
{
struct dquot *dquot;
struct dqblk data;
int error = -ESRCH;
if (!sb || !sb_has_quota_enabled(sb, type))
goto out;
dquot = dqget(sb, id, type);
if (dquot == NODQUOT)
goto out;
memcpy(&data, &dquot->dq_dqb, sizeof(struct dqblk)); /* We copy data to preserve them from changing */
dqput(dquot);
error = -EFAULT;
if (dqblk && !copy_to_user(dqblk, &data, sizeof(struct dqblk)))
error = 0;
out:
return error;
}
static int get_stats(caddr_t addr)
{
int error = -EFAULT;
struct dqstats stats;
dqstats.allocated_dquots = nr_dquots;
dqstats.free_dquots = nr_free_dquots;
/* make a copy, in case we page-fault in user space */
memcpy(&stats, &dqstats, sizeof(struct dqstats));
if (!copy_to_user(addr, &stats, sizeof(struct dqstats)))
error = 0;
return error;
}
static int quota_root_squash(struct super_block *sb, short type, int *addr)
{
int new_value, error;
if (!sb)
return(-ENODEV);
error = -EFAULT;
if (!copy_from_user(&new_value, addr, sizeof(int))) {
sb_dqopt(sb)->rsquash[type] = new_value;
error = 0;
}
return error;
}
#if 0 /* We are not going to support filesystems without i_blocks... */
/*
* This is a simple algorithm that calculates the size of a file in blocks.
* This is only used on filesystems that do not have an i_blocks count.
*/
static u_long isize_to_blocks(loff_t isize, size_t blksize_bits)
{
u_long blocks;
u_long indirect;
if (!blksize_bits)
blksize_bits = BLOCK_SIZE_BITS;
blocks = (isize >> blksize_bits) + ((isize & ~((1 << blksize_bits)-1)) ? 1 : 0);
if (blocks > 10) {
indirect = ((blocks - 11) >> 8) + 1; /* single indirect blocks */
if (blocks > (10 + 256)) {
indirect += ((blocks - 267) >> 16) + 1; /* double indirect blocks */
if (blocks > (10 + 256 + (256 << 8)))
indirect++; /* triple indirect blocks */
}
blocks += indirect;
}
return blocks;
}
#endif
/* /*
* Externally referenced functions through dquot_operations in inode. * Externally referenced functions through dquot_operations in inode.
* *
* Note: this is a blocking operation. * Note: this is a blocking operation.
*/ */
void dquot_initialize(struct inode *inode, short type) void dquot_initialize(struct inode *inode, int type)
{ {
struct dquot *dquot[MAXQUOTAS]; struct dquot *dquot[MAXQUOTAS];
unsigned int id = 0; unsigned int id = 0;
short cnt; int cnt;
if (IS_NOQUOTA(inode)) if (IS_NOQUOTA(inode))
return; return;
...@@ -1019,7 +978,7 @@ void dquot_initialize(struct inode *inode, short type) ...@@ -1019,7 +978,7 @@ void dquot_initialize(struct inode *inode, short type)
void dquot_drop(struct inode *inode) void dquot_drop(struct inode *inode)
{ {
struct dquot *dquot; struct dquot *dquot;
short cnt; int cnt;
inode->i_flags &= ~S_QUOTA; inode->i_flags &= ~S_QUOTA;
for (cnt = 0; cnt < MAXQUOTAS; cnt++) { for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
...@@ -1034,7 +993,7 @@ void dquot_drop(struct inode *inode) ...@@ -1034,7 +993,7 @@ void dquot_drop(struct inode *inode)
/* /*
* This operation can block, but only after everything is updated * This operation can block, but only after everything is updated
*/ */
int dquot_alloc_block(struct inode *inode, unsigned long number, char warn) int dquot_alloc_space(struct inode *inode, qsize_t number, int warn)
{ {
int cnt, ret = NO_QUOTA; int cnt, ret = NO_QUOTA;
struct dquot *dquot[MAXQUOTAS]; struct dquot *dquot[MAXQUOTAS];
...@@ -1056,16 +1015,16 @@ int dquot_alloc_block(struct inode *inode, unsigned long number, char warn) ...@@ -1056,16 +1015,16 @@ int dquot_alloc_block(struct inode *inode, unsigned long number, char warn)
for (cnt = 0; cnt < MAXQUOTAS; cnt++) { for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (dquot[cnt] == NODQUOT) if (dquot[cnt] == NODQUOT)
continue; continue;
dquot_incr_blocks(dquot[cnt], number); dquot_incr_space(dquot[cnt], number);
} }
inode->i_blocks += number << (BLOCK_SIZE_BITS - 9); inode_add_bytes(inode, number);
/* NOBLOCK End */ /* NOBLOCK End */
ret = QUOTA_OK; ret = QUOTA_OK;
warn_put_all: warn_put_all:
flush_warnings(dquot, warntype); flush_warnings(dquot, warntype);
for (cnt = 0; cnt < MAXQUOTAS; cnt++) for (cnt = 0; cnt < MAXQUOTAS; cnt++)
if (dquot[cnt] != NODQUOT) if (dquot[cnt] != NODQUOT)
dqput(dquot[cnt]); dqputduplicate(dquot[cnt]);
unlock_kernel(); unlock_kernel();
return ret; return ret;
} }
...@@ -1104,7 +1063,7 @@ int dquot_alloc_inode(const struct inode *inode, unsigned long number) ...@@ -1104,7 +1063,7 @@ int dquot_alloc_inode(const struct inode *inode, unsigned long number)
flush_warnings(dquot, warntype); flush_warnings(dquot, warntype);
for (cnt = 0; cnt < MAXQUOTAS; cnt++) for (cnt = 0; cnt < MAXQUOTAS; cnt++)
if (dquot[cnt] != NODQUOT) if (dquot[cnt] != NODQUOT)
dqput(dquot[cnt]); dqputduplicate(dquot[cnt]);
unlock_kernel(); unlock_kernel();
return ret; return ret;
} }
...@@ -1112,9 +1071,9 @@ int dquot_alloc_inode(const struct inode *inode, unsigned long number) ...@@ -1112,9 +1071,9 @@ int dquot_alloc_inode(const struct inode *inode, unsigned long number)
/* /*
* This is a non-blocking operation. * This is a non-blocking operation.
*/ */
void dquot_free_block(struct inode *inode, unsigned long number) void dquot_free_space(struct inode *inode, qsize_t number)
{ {
unsigned short cnt; unsigned int cnt;
struct dquot *dquot; struct dquot *dquot;
/* NOBLOCK Start */ /* NOBLOCK Start */
...@@ -1123,10 +1082,10 @@ void dquot_free_block(struct inode *inode, unsigned long number) ...@@ -1123,10 +1082,10 @@ void dquot_free_block(struct inode *inode, unsigned long number)
dquot = dqduplicate(inode->i_dquot[cnt]); dquot = dqduplicate(inode->i_dquot[cnt]);
if (dquot == NODQUOT) if (dquot == NODQUOT)
continue; continue;
dquot_decr_blocks(dquot, number); dquot_decr_space(dquot, number);
dqput(dquot); dqputduplicate(dquot);
} }
inode->i_blocks -= number << (BLOCK_SIZE_BITS - 9); inode_sub_bytes(inode, number);
unlock_kernel(); unlock_kernel();
/* NOBLOCK End */ /* NOBLOCK End */
} }
...@@ -1136,7 +1095,7 @@ void dquot_free_block(struct inode *inode, unsigned long number) ...@@ -1136,7 +1095,7 @@ void dquot_free_block(struct inode *inode, unsigned long number)
*/ */
void dquot_free_inode(const struct inode *inode, unsigned long number) void dquot_free_inode(const struct inode *inode, unsigned long number)
{ {
unsigned short cnt; unsigned int cnt;
struct dquot *dquot; struct dquot *dquot;
/* NOBLOCK Start */ /* NOBLOCK Start */
...@@ -1146,7 +1105,7 @@ void dquot_free_inode(const struct inode *inode, unsigned long number) ...@@ -1146,7 +1105,7 @@ void dquot_free_inode(const struct inode *inode, unsigned long number)
if (dquot == NODQUOT) if (dquot == NODQUOT)
continue; continue;
dquot_decr_inodes(dquot, number); dquot_decr_inodes(dquot, number);
dqput(dquot); dqputduplicate(dquot);
} }
unlock_kernel(); unlock_kernel();
/* NOBLOCK End */ /* NOBLOCK End */
...@@ -1159,7 +1118,7 @@ void dquot_free_inode(const struct inode *inode, unsigned long number) ...@@ -1159,7 +1118,7 @@ void dquot_free_inode(const struct inode *inode, unsigned long number)
*/ */
int dquot_transfer(struct inode *inode, struct iattr *iattr) int dquot_transfer(struct inode *inode, struct iattr *iattr)
{ {
unsigned long blocks; qsize_t space;
struct dquot *transfer_from[MAXQUOTAS]; struct dquot *transfer_from[MAXQUOTAS];
struct dquot *transfer_to[MAXQUOTAS]; struct dquot *transfer_to[MAXQUOTAS];
int cnt, ret = NO_QUOTA, chuid = (iattr->ia_valid & ATTR_UID) && inode->i_uid != iattr->ia_uid, int cnt, ret = NO_QUOTA, chuid = (iattr->ia_valid & ATTR_UID) && inode->i_uid != iattr->ia_uid,
...@@ -1189,7 +1148,7 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) ...@@ -1189,7 +1148,7 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
} }
} }
/* NOBLOCK START: From now on we shouldn't block */ /* NOBLOCK START: From now on we shouldn't block */
blocks = (inode->i_blocks >> 1); space = inode_get_bytes(inode);
/* Build the transfer_from list and check the limits */ /* Build the transfer_from list and check the limits */
for (cnt = 0; cnt < MAXQUOTAS; cnt++) { for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
/* The second test can fail when quotaoff is in progress... */ /* The second test can fail when quotaoff is in progress... */
...@@ -1199,7 +1158,7 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) ...@@ -1199,7 +1158,7 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
if (transfer_from[cnt] == NODQUOT) /* Can happen on quotafiles (quota isn't initialized on them)... */ if (transfer_from[cnt] == NODQUOT) /* Can happen on quotafiles (quota isn't initialized on them)... */
continue; continue;
if (check_idq(transfer_to[cnt], 1, warntype+cnt) == NO_QUOTA || if (check_idq(transfer_to[cnt], 1, warntype+cnt) == NO_QUOTA ||
check_bdq(transfer_to[cnt], blocks, 0, warntype+cnt) == NO_QUOTA) check_bdq(transfer_to[cnt], space, 0, warntype+cnt) == NO_QUOTA)
goto warn_put_all; goto warn_put_all;
} }
...@@ -1214,10 +1173,10 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) ...@@ -1214,10 +1173,10 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
continue; continue;
dquot_decr_inodes(transfer_from[cnt], 1); dquot_decr_inodes(transfer_from[cnt], 1);
dquot_decr_blocks(transfer_from[cnt], blocks); dquot_decr_space(transfer_from[cnt], space);
dquot_incr_inodes(transfer_to[cnt], 1); dquot_incr_inodes(transfer_to[cnt], 1);
dquot_incr_blocks(transfer_to[cnt], blocks); dquot_incr_space(transfer_to[cnt], space);
if (inode->i_dquot[cnt] == NODQUOT) if (inode->i_dquot[cnt] == NODQUOT)
BUG(); BUG();
...@@ -1233,52 +1192,29 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) ...@@ -1233,52 +1192,29 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
warn_put_all: warn_put_all:
flush_warnings(transfer_to, warntype); flush_warnings(transfer_to, warntype);
for (cnt = 0; cnt < MAXQUOTAS; cnt++) { for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
/* First we must put duplicate - otherwise we might deadlock */
if (transfer_to[cnt] != NODQUOT) if (transfer_to[cnt] != NODQUOT)
dqput(transfer_to[cnt]); dqputduplicate(transfer_to[cnt]);
if (transfer_from[cnt] != NODQUOT) if (transfer_from[cnt] != NODQUOT)
dqput(transfer_from[cnt]); dqput(transfer_from[cnt]);
} }
return ret; return ret;
} }
static ctl_table fs_table[] = {
{FS_NRDQUOT, "dquot-nr", &nr_dquots, 2*sizeof(int),
0444, NULL, &proc_dointvec},
{},
};
static ctl_table dquot_table[] = {
{CTL_FS, "fs", NULL, 0, 0555, fs_table},
{},
};
static int __init dquot_init(void)
{
int i;
register_sysctl_table(dquot_table, 0);
for (i = 0; i < NR_DQHASH; i++)
INIT_LIST_HEAD(dquot_hash + i);
printk(KERN_NOTICE "VFS: Diskquotas version %s initialized\n", __DQUOT_VERSION__);
return 0;
}
__initcall(dquot_init);
/* /*
* Definitions of diskquota operations. * Definitions of diskquota operations.
*/ */
struct dquot_operations dquot_operations = { struct dquot_operations dquot_operations = {
initialize: dquot_initialize, /* mandatory */ initialize: dquot_initialize, /* mandatory */
drop: dquot_drop, /* mandatory */ drop: dquot_drop, /* mandatory */
alloc_block: dquot_alloc_block, alloc_space: dquot_alloc_space,
alloc_inode: dquot_alloc_inode, alloc_inode: dquot_alloc_inode,
free_block: dquot_free_block, free_space: dquot_free_space,
free_inode: dquot_free_inode, free_inode: dquot_free_inode,
transfer: dquot_transfer transfer: dquot_transfer
}; };
static inline void set_enable_flags(struct quota_mount_options *dqopt, short type) static inline void set_enable_flags(struct quota_info *dqopt, int type)
{ {
switch (type) { switch (type) {
case USRQUOTA: case USRQUOTA:
...@@ -1290,7 +1226,7 @@ static inline void set_enable_flags(struct quota_mount_options *dqopt, short typ ...@@ -1290,7 +1226,7 @@ static inline void set_enable_flags(struct quota_mount_options *dqopt, short typ
} }
} }
static inline void reset_enable_flags(struct quota_mount_options *dqopt, short type) static inline void reset_enable_flags(struct quota_info *dqopt, int type)
{ {
switch (type) { switch (type) {
case USRQUOTA: case USRQUOTA:
...@@ -1303,16 +1239,15 @@ static inline void reset_enable_flags(struct quota_mount_options *dqopt, short t ...@@ -1303,16 +1239,15 @@ static inline void reset_enable_flags(struct quota_mount_options *dqopt, short t
} }
/* Function in inode.c - remove pointers to dquots in icache */ /* Function in inode.c - remove pointers to dquots in icache */
extern void remove_dquot_ref(struct super_block *, short); extern void remove_dquot_ref(struct super_block *, int);
/* /*
* Turn quota off on a device. type == -1 ==> quotaoff for all types (umount) * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
*/ */
int quota_off(struct super_block *sb, short type) int vfs_quota_off(struct super_block *sb, int type)
{ {
struct file *filp; int cnt;
short cnt; struct quota_info *dqopt = sb_dqopt(sb);
struct quota_mount_options *dqopt = sb_dqopt(sb);
lock_kernel(); lock_kernel();
if (!sb) if (!sb)
...@@ -1330,12 +1265,18 @@ int quota_off(struct super_block *sb, short type) ...@@ -1330,12 +1265,18 @@ int quota_off(struct super_block *sb, short type)
/* Note: these are blocking operations */ /* Note: these are blocking operations */
remove_dquot_ref(sb, cnt); remove_dquot_ref(sb, cnt);
invalidate_dquots(sb, cnt); invalidate_dquots(sb, cnt);
if (info_dirty(&dqopt->info[cnt]))
dqopt->ops[cnt]->write_file_info(sb, cnt);
if (dqopt->ops[cnt]->free_file_info)
dqopt->ops[cnt]->free_file_info(sb, cnt);
put_quota_format(dqopt->info[cnt].dqi_format);
filp = dqopt->files[cnt]; fput(dqopt->files[cnt]);
dqopt->files[cnt] = (struct file *)NULL; dqopt->files[cnt] = (struct file *)NULL;
dqopt->inode_expire[cnt] = 0; dqopt->info[cnt].dqi_flags = 0;
dqopt->block_expire[cnt] = 0; dqopt->info[cnt].dqi_igrace = 0;
fput(filp); dqopt->info[cnt].dqi_bgrace = 0;
dqopt->ops[cnt] = NULL;
} }
up(&dqopt->dqoff_sem); up(&dqopt->dqoff_sem);
out: out:
...@@ -1343,38 +1284,29 @@ int quota_off(struct super_block *sb, short type) ...@@ -1343,38 +1284,29 @@ int quota_off(struct super_block *sb, short type)
return 0; return 0;
} }
static inline int check_quotafile_size(loff_t size) int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path)
{ {
ulong blocks = size >> BLOCK_SIZE_BITS; struct file *f = NULL;
size_t off = size & (BLOCK_SIZE - 1);
return !(((blocks % sizeof(struct dqblk)) * BLOCK_SIZE + off % sizeof(struct dqblk)) % sizeof(struct dqblk));
}
static int quota_on(struct super_block *sb, short type, char *path)
{
struct file *f;
struct inode *inode; struct inode *inode;
struct dquot *dquot; struct quota_info *dqopt = sb_dqopt(sb);
struct quota_mount_options *dqopt = sb_dqopt(sb); struct quota_format_type *fmt = find_quota_format(format_id);
char *tmp;
int error; int error;
if (is_enabled(dqopt, type)) if (!fmt)
return -EBUSY; return -EINVAL;
if (is_enabled(dqopt, type)) {
error = -EBUSY;
goto out_fmt;
}
down(&dqopt->dqoff_sem); down(&dqopt->dqoff_sem);
tmp = getname(path);
error = PTR_ERR(tmp);
if (IS_ERR(tmp))
goto out_lock;
f = filp_open(tmp, O_RDWR, 0600); f = filp_open(path, O_RDWR, 0600);
putname(tmp);
error = PTR_ERR(f); error = PTR_ERR(f);
if (IS_ERR(f)) if (IS_ERR(f))
goto out_lock; goto out_lock;
dqopt->files[type] = f;
error = -EIO; error = -EIO;
if (!f->f_op || !f->f_op->read || !f->f_op->write) if (!f->f_op || !f->f_op->read || !f->f_op->write)
goto out_f; goto out_f;
...@@ -1383,134 +1315,223 @@ static int quota_on(struct super_block *sb, short type, char *path) ...@@ -1383,134 +1315,223 @@ static int quota_on(struct super_block *sb, short type, char *path)
if (!S_ISREG(inode->i_mode)) if (!S_ISREG(inode->i_mode))
goto out_f; goto out_f;
error = -EINVAL; error = -EINVAL;
if (inode->i_size == 0 || !check_quotafile_size(inode->i_size)) if (!fmt->qf_ops->check_quota_file(sb, type))
goto out_f; goto out_f;
/* We don't want quota on quota files */ /* We don't want quota on quota files */
dquot_drop(inode); dquot_drop(inode);
inode->i_flags |= S_NOQUOTA; inode->i_flags |= S_NOQUOTA;
dqopt->files[type] = f; dqopt->ops[type] = fmt->qf_ops;
sb->dq_op = &dquot_operations; dqopt->info[type].dqi_format = fmt;
if ((error = dqopt->ops[type]->read_file_info(sb, type)) < 0)
goto out_f;
set_enable_flags(dqopt, type); set_enable_flags(dqopt, type);
dquot = dqget(sb, 0, type);
dqopt->inode_expire[type] = (dquot != NODQUOT) ? dquot->dq_itime : MAX_IQ_TIME;
dqopt->block_expire[type] = (dquot != NODQUOT) ? dquot->dq_btime : MAX_DQ_TIME;
dqput(dquot);
add_dquot_ref(sb, type); add_dquot_ref(sb, type);
up(&dqopt->dqoff_sem); up(&dqopt->dqoff_sem);
return 0; return 0;
out_f: out_f:
filp_close(f, NULL); if (f)
filp_close(f, NULL);
dqopt->files[type] = NULL;
out_lock: out_lock:
up(&dqopt->dqoff_sem); up(&dqopt->dqoff_sem);
out_fmt:
put_quota_format(fmt);
return error; return error;
} }
/* /* Generic routine for getting common part of quota structure */
* This is the system call interface. This communicates with static void do_get_dqblk(struct dquot *dquot, struct if_dqblk *di)
* the user-level programs. Currently this only supports diskquota
* calls. Maybe we need to add the process quotas etc. in the future,
* but we probably should use rlimits for that.
*/
asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
{ {
int cmds = 0, type = 0, flags = 0; struct mem_dqblk *dm = &dquot->dq_dqb;
kdev_t dev;
struct super_block *sb = NULL; di->dqb_bhardlimit = dm->dqb_bhardlimit;
int ret = -EINVAL; di->dqb_bsoftlimit = dm->dqb_bsoftlimit;
di->dqb_curspace = dm->dqb_curspace;
di->dqb_ihardlimit = dm->dqb_ihardlimit;
di->dqb_isoftlimit = dm->dqb_isoftlimit;
di->dqb_curinodes = dm->dqb_curinodes;
di->dqb_btime = dm->dqb_btime;
di->dqb_itime = dm->dqb_itime;
di->dqb_valid = QIF_ALL;
}
lock_kernel(); int vfs_get_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di)
cmds = cmd >> SUBCMDSHIFT; {
type = cmd & SUBCMDMASK; struct dquot *dquot = dqget(sb, id, type);
if ((u_int) type >= MAXQUOTAS) if (!dquot)
goto out; return -EINVAL;
if (id & ~0xFFFF) do_get_dqblk(dquot, di);
goto out; dqput(dquot);
return 0;
}
ret = -EPERM; /* Generic routine for setting common part of quota structure */
switch (cmds) { static void do_set_dqblk(struct dquot *dquot, struct if_dqblk *di)
case Q_SYNC: {
case Q_GETSTATS: struct mem_dqblk *dm = &dquot->dq_dqb;
break; int check_blim = 0, check_ilim = 0;
case Q_GETQUOTA:
if (((type == USRQUOTA && current->euid != id) ||
(type == GRPQUOTA && !in_egroup_p(id))) &&
!capable(CAP_SYS_ADMIN))
goto out;
break;
default:
if (!capable(CAP_SYS_ADMIN))
goto out;
}
ret = -EINVAL; if (di->dqb_valid & QIF_SPACE) {
dev = NODEV; dm->dqb_curspace = di->dqb_curspace;
if (special != NULL || (cmds != Q_SYNC && cmds != Q_GETSTATS)) { check_blim = 1;
mode_t mode;
struct nameidata nd;
ret = user_path_walk(special, &nd);
if (ret)
goto out;
dev = nd.dentry->d_inode->i_rdev;
mode = nd.dentry->d_inode->i_mode;
path_release(&nd);
ret = -ENOTBLK;
if (!S_ISBLK(mode))
goto out;
ret = -ENODEV;
sb = get_super(dev);
if (!sb)
goto out;
} }
if (di->dqb_valid & QIF_BLIMITS) {
ret = -EINVAL; dm->dqb_bsoftlimit = di->dqb_bsoftlimit;
switch (cmds) { dm->dqb_bhardlimit = di->dqb_bhardlimit;
case Q_QUOTAON: check_blim = 1;
ret = quota_on(sb, type, (char *) addr); }
goto out; if (di->dqb_valid & QIF_INODES) {
case Q_QUOTAOFF: dm->dqb_curinodes = di->dqb_curinodes;
ret = quota_off(sb, type); check_ilim = 1;
goto out; }
case Q_GETQUOTA: if (di->dqb_valid & QIF_ILIMITS) {
ret = get_quota(sb, id, type, (struct dqblk *) addr); dm->dqb_isoftlimit = di->dqb_isoftlimit;
goto out; dm->dqb_ihardlimit = di->dqb_ihardlimit;
case Q_SETQUOTA: check_ilim = 1;
flags |= SET_QUOTA; }
break; if (di->dqb_valid & QIF_BTIME)
case Q_SETUSE: dm->dqb_btime = di->dqb_btime;
flags |= SET_USE; if (di->dqb_valid & QIF_ITIME)
break; dm->dqb_itime = di->dqb_itime;
case Q_SETQLIM:
flags |= SET_QLIMIT; if (check_blim) {
break; if (!dm->dqb_bsoftlimit || toqb(dm->dqb_curspace) < dm->dqb_bsoftlimit) {
case Q_SYNC: dm->dqb_btime = 0;
ret = sync_dquots(sb, type); dquot->dq_flags &= ~DQ_BLKS;
goto out; }
case Q_GETSTATS: else if (!(di->dqb_valid & QIF_BTIME)) /* Set grace only if user hasn't provided his own... */
ret = get_stats(addr); dm->dqb_btime = CURRENT_TIME + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_bgrace;
goto out; }
case Q_RSQUASH: if (check_ilim) {
ret = quota_root_squash(sb, type, (int *) addr); if (!dm->dqb_isoftlimit || dm->dqb_curinodes < dm->dqb_isoftlimit) {
goto out; dm->dqb_itime = 0;
default: dquot->dq_flags &= ~DQ_INODES;
goto out; }
else if (!(di->dqb_valid & QIF_ITIME)) /* Set grace only if user hasn't provided his own... */
dm->dqb_itime = CURRENT_TIME + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace;
} }
if (dm->dqb_bhardlimit || dm->dqb_bsoftlimit || dm->dqb_ihardlimit || dm->dqb_isoftlimit)
dquot->dq_flags &= ~DQ_FAKE;
else
dquot->dq_flags |= DQ_FAKE;
dquot->dq_flags |= DQ_MOD;
}
ret = -ENODEV; int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di)
if (sb && sb_has_quota_enabled(sb, type)) {
ret = set_dqblk(sb, id, type, flags, (struct dqblk *) addr); struct dquot *dquot = dqget(sb, id, type);
out:
if (sb) if (!dquot)
drop_super(sb); return -EINVAL;
do_set_dqblk(dquot, di);
dqput(dquot);
return 0;
}
/* Generic routine for getting common part of quota file information */
int vfs_get_info(struct super_block *sb, int type, struct if_dqinfo *ii)
{
struct mem_dqinfo *mi = sb_dqopt(sb)->info + type;
ii->dqi_bgrace = mi->dqi_bgrace;
ii->dqi_igrace = mi->dqi_igrace;
ii->dqi_flags = mi->dqi_flags & DQF_MASK;
ii->dqi_valid = IIF_ALL;
return 0;
}
/* Generic routine for setting common part of quota file information */
int vfs_set_info(struct super_block *sb, int type, struct if_dqinfo *ii)
{
struct mem_dqinfo *mi = sb_dqopt(sb)->info + type;
if (ii->dqi_valid & IIF_BGRACE)
mi->dqi_bgrace = ii->dqi_bgrace;
if (ii->dqi_valid & IIF_IGRACE)
mi->dqi_igrace = ii->dqi_igrace;
if (ii->dqi_valid & IIF_FLAGS)
mi->dqi_flags = (mi->dqi_flags & ~DQF_MASK) | (ii->dqi_flags & DQF_MASK);
mark_info_dirty(mi);
return 0;
}
#ifdef CONFIG_PROC_FS
static int read_stats(char *buffer, char **start, off_t offset, int count, int *eof, void *data)
{
int len;
struct quota_format_type *actqf;
dqstats.allocated_dquots = nr_dquots;
dqstats.free_dquots = nr_free_dquots;
len = sprintf(buffer, "Version %u\n", __DQUOT_NUM_VERSION__);
len += sprintf(buffer + len, "Formats");
lock_kernel();
for (actqf = quota_formats; actqf; actqf = actqf->qf_next)
len += sprintf(buffer + len, " %u", actqf->qf_fmt_id);
unlock_kernel(); unlock_kernel();
return ret; len += sprintf(buffer + len, "\n%u %u %u %u %u %u %u %u\n",
dqstats.lookups, dqstats.drops,
dqstats.reads, dqstats.writes,
dqstats.cache_hits, dqstats.allocated_dquots,
dqstats.free_dquots, dqstats.syncs);
if (offset >= len) {
*start = buffer;
*eof = 1;
return 0;
}
*start = buffer + offset;
if ((len -= offset) > count)
return count;
*eof = 1;
return len;
}
#endif
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_dqblk: vfs_get_dqblk,
set_dqblk: vfs_set_dqblk
};
static ctl_table fs_table[] = {
{FS_NRDQUOT, "dquot-nr", &nr_dquots, 2*sizeof(int),
0444, NULL, &proc_dointvec},
{},
};
static ctl_table dquot_table[] = {
{CTL_FS, "fs", NULL, 0, 0555, fs_table},
{},
};
static int __init dquot_init(void)
{
int i;
register_sysctl_table(dquot_table, 0);
for (i = 0; i < NR_DQHASH; i++)
INIT_LIST_HEAD(dquot_hash + i);
printk(KERN_NOTICE "VFS: Diskquotas version %s initialized\n", __DQUOT_VERSION__);
#ifdef CONFIG_PROC_FS
create_proc_read_entry("fs/quota", 0, 0, read_stats, NULL);
#endif
return 0;
} }
__initcall(dquot_init);
EXPORT_SYMBOL(register_quota_format);
EXPORT_SYMBOL(unregister_quota_format);
EXPORT_SYMBOL(dqstats);
...@@ -99,6 +99,7 @@ static struct inode *alloc_inode(struct super_block *sb) ...@@ -99,6 +99,7 @@ static struct inode *alloc_inode(struct super_block *sb)
atomic_set(&inode->i_writecount, 0); atomic_set(&inode->i_writecount, 0);
inode->i_size = 0; inode->i_size = 0;
inode->i_blocks = 0; inode->i_blocks = 0;
inode->i_bytes = 0;
inode->i_generation = 0; inode->i_generation = 0;
memset(&inode->i_dquot, 0, sizeof(inode->i_dquot)); memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
inode->i_pipe = NULL; inode->i_pipe = NULL;
...@@ -978,9 +979,9 @@ void update_atime (struct inode *inode) ...@@ -978,9 +979,9 @@ void update_atime (struct inode *inode)
/* Functions back in dquot.c */ /* Functions back in dquot.c */
void put_dquot_list(struct list_head *); void put_dquot_list(struct list_head *);
int remove_inode_dquot_ref(struct inode *, short, struct list_head *); int remove_inode_dquot_ref(struct inode *, int, struct list_head *);
void remove_dquot_ref(struct super_block *sb, short type) void remove_dquot_ref(struct super_block *sb, int type)
{ {
struct inode *inode; struct inode *inode;
struct list_head *act_head; struct list_head *act_head;
......
...@@ -102,6 +102,16 @@ asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) ...@@ -102,6 +102,16 @@ asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
filp->f_flags &= ~FASYNC; filp->f_flags &= ~FASYNC;
break; break;
case FIOQSIZE:
if (S_ISDIR(filp->f_dentry->d_inode->i_mode) ||
S_ISREG(filp->f_dentry->d_inode->i_mode) ||
S_ISLNK(filp->f_dentry->d_inode->i_mode)) {
loff_t res = inode_get_bytes(filp->f_dentry->d_inode);
error = copy_to_user((loff_t *)arg, &res, sizeof(res)) ? -EFAULT : 0;
}
else
error = -ENOTTY;
break;
default: default:
error = -ENOTTY; error = -ENOTTY;
if (S_ISREG(filp->f_dentry->d_inode->i_mode)) if (S_ISREG(filp->f_dentry->d_inode->i_mode))
......
/*
* Quota code necessary even when VFS quota support is not compiled
* into the kernel. The interesting stuff is over in dquot.c, here
* we have symbols for initial quotactl(2) handling, the sysctl(2)
* variables, etc - things needed even when quota support disabled.
*/
#include <linux/fs.h>
#include <linux/slab.h>
#include <asm/current.h>
#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;
/* Check validity of quotactl */
static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
{
if (type >= MAXQUOTAS)
return -EINVAL;
/* Is operation supported? */
if (!sb->s_qcop)
return -ENOSYS;
switch (cmd) {
case Q_GETFMT:
break;
case Q_QUOTAON:
if (!sb->s_qcop->quota_on)
return -ENOSYS;
break;
case Q_QUOTAOFF:
if (!sb->s_qcop->quota_off)
return -ENOSYS;
break;
case Q_SETINFO:
if (!sb->s_qcop->set_info)
return -ENOSYS;
break;
case Q_GETINFO:
if (!sb->s_qcop->get_info)
return -ENOSYS;
break;
case Q_SETQUOTA:
if (!sb->s_qcop->set_dqblk)
return -ENOSYS;
break;
case Q_GETQUOTA:
if (!sb->s_qcop->get_dqblk)
return -ENOSYS;
break;
case Q_SYNC:
if (!sb->s_qcop->quota_sync)
return -ENOSYS;
break;
case Q_XQUOTAON:
case Q_XQUOTAOFF:
case Q_XQUOTARM:
if (!sb->s_qcop->set_xstate)
return -ENOSYS;
break;
case Q_XGETQSTAT:
if (!sb->s_qcop->get_xstate)
return -ENOSYS;
break;
case Q_XSETQLIM:
if (!sb->s_qcop->set_xquota)
return -ENOSYS;
break;
case Q_XGETQUOTA:
if (!sb->s_qcop->get_xquota)
return -ENOSYS;
break;
default:
return -EINVAL;
}
/* Is quota turned on for commands which need it? */
switch (cmd) {
case Q_GETFMT:
case Q_GETINFO:
case Q_QUOTAOFF:
case Q_SETINFO:
case Q_SETQUOTA:
case Q_GETQUOTA:
if (!sb_has_quota_enabled(sb, type))
return -ESRCH;
}
/* Check privileges */
if (cmd == Q_GETQUOTA || cmd == Q_XGETQUOTA) {
if (((type == USRQUOTA && current->euid != id) ||
(type == GRPQUOTA && !in_egroup_p(id))) &&
!capable(CAP_SYS_ADMIN))
return -EPERM;
}
else if (cmd != Q_GETFMT && cmd != Q_SYNC && cmd != Q_GETINFO && cmd != Q_XGETQSTAT)
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
return 0;
}
/* Resolve device pathname to superblock */
static struct super_block *resolve_dev(const char *path)
{
int ret;
mode_t mode;
struct nameidata nd;
kdev_t dev;
struct super_block *sb;
ret = user_path_walk(path, &nd);
if (ret)
goto out;
dev = nd.dentry->d_inode->i_rdev;
mode = nd.dentry->d_inode->i_mode;
path_release(&nd);
ret = -ENOTBLK;
if (!S_ISBLK(mode))
goto out;
ret = -ENODEV;
sb = get_super(dev);
if (!sb)
goto out;
return sb;
out:
return ERR_PTR(ret);
}
/* Copy parameters and call proper function */
static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, caddr_t addr)
{
int ret;
switch (cmd) {
case Q_QUOTAON: {
char *pathname;
if (IS_ERR(pathname = getname(addr)))
return PTR_ERR(pathname);
ret = sb->s_qcop->quota_on(sb, type, id, pathname);
putname(pathname);
return ret;
}
case Q_QUOTAOFF:
return sb->s_qcop->quota_off(sb, type);
case Q_GETFMT: {
__u32 fmt;
fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id;
if (copy_to_user(addr, &fmt, sizeof(fmt)))
return -EFAULT;
return 0;
}
case Q_GETINFO: {
struct if_dqinfo info;
if ((ret = sb->s_qcop->get_info(sb, type, &info)))
return ret;
if (copy_to_user(addr, &info, sizeof(info)))
return -EFAULT;
return 0;
}
case Q_SETINFO: {
struct if_dqinfo info;
if (copy_from_user(&info, addr, sizeof(info)))
return -EFAULT;
return sb->s_qcop->set_info(sb, type, &info);
}
case Q_GETQUOTA: {
struct if_dqblk idq;
if ((ret = sb->s_qcop->get_dqblk(sb, type, id, &idq)))
return ret;
if (copy_to_user(addr, &idq, sizeof(idq)))
return -EFAULT;
return 0;
}
case Q_SETQUOTA: {
struct if_dqblk idq;
if (copy_from_user(&idq, addr, sizeof(idq)))
return -EFAULT;
return sb->s_qcop->set_dqblk(sb, type, id, &idq);
}
case Q_SYNC:
return sb->s_qcop->quota_sync(sb, type);
case Q_XQUOTAON:
case Q_XQUOTAOFF:
case Q_XQUOTARM: {
__u32 flags;
if (copy_from_user(&flags, addr, sizeof(flags)))
return -EFAULT;
return sb->s_qcop->set_xstate(sb, flags, cmd);
}
case Q_XGETQSTAT: {
struct fs_quota_stat fqs;
if ((ret = sb->s_qcop->get_xstate(sb, &fqs)))
return ret;
if (copy_to_user(addr, &fqs, sizeof(fqs)))
return -EFAULT;
return 0;
}
case Q_XSETQLIM: {
struct fs_disk_quota fdq;
if (copy_from_user(&fdq, addr, sizeof(fdq)))
return -EFAULT;
return sb->s_qcop->set_xquota(sb, type, id, &fdq);
}
case Q_XGETQUOTA: {
struct fs_disk_quota fdq;
if ((ret = sb->s_qcop->get_xquota(sb, type, id, &fdq)))
return ret;
if (copy_to_user(addr, &fdq, sizeof(fdq)))
return -EFAULT;
return 0;
}
/* We never reach here unless validity check is broken */
default:
BUG();
}
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
* calls. Maybe we need to add the process quotas etc. in the future,
* but we probably should use rlimits for that.
*/
asmlinkage long sys_quotactl(unsigned int cmd, const char *special, qid_t id, caddr_t addr)
{
uint cmds, type;
struct super_block *sb = NULL;
int ret = -EINVAL;
lock_kernel();
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);
out:
if (sb)
drop_super(sb);
unlock_kernel();
return ret;
}
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/quota.h>
#include <linux/dqblk_v1.h>
#include <linux/quotaio_v1.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
static void v1_disk2mem_dqblk(struct mem_dqblk *m, struct v1_disk_dqblk *d)
{
m->dqb_ihardlimit = d->dqb_ihardlimit;
m->dqb_isoftlimit = d->dqb_isoftlimit;
m->dqb_curinodes = d->dqb_curinodes;
m->dqb_bhardlimit = d->dqb_bhardlimit;
m->dqb_bsoftlimit = d->dqb_bsoftlimit;
m->dqb_curspace = d->dqb_curblocks << QUOTABLOCK_BITS;
m->dqb_itime = d->dqb_itime;
m->dqb_btime = d->dqb_btime;
}
static void v1_mem2disk_dqblk(struct v1_disk_dqblk *d, struct mem_dqblk *m)
{
d->dqb_ihardlimit = m->dqb_ihardlimit;
d->dqb_isoftlimit = m->dqb_isoftlimit;
d->dqb_curinodes = m->dqb_curinodes;
d->dqb_bhardlimit = m->dqb_bhardlimit;
d->dqb_bsoftlimit = m->dqb_bsoftlimit;
d->dqb_curblocks = toqb(m->dqb_curspace);
d->dqb_itime = m->dqb_itime;
d->dqb_btime = m->dqb_btime;
}
static int v1_read_dqblk(struct dquot *dquot)
{
int type = dquot->dq_type;
struct file *filp;
mm_segment_t fs;
loff_t offset;
struct v1_disk_dqblk dqblk;
filp = sb_dqopt(dquot->dq_sb)->files[type];
if (filp == (struct file *)NULL)
return -EINVAL;
/* Now we are sure filp is valid */
offset = v1_dqoff(dquot->dq_id);
fs = get_fs();
set_fs(KERNEL_DS);
filp->f_op->read(filp, (char *)&dqblk, sizeof(struct v1_disk_dqblk), &offset);
set_fs(fs);
v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk);
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.reads++;
return 0;
}
static int v1_commit_dqblk(struct dquot *dquot)
{
short type = dquot->dq_type;
struct file *filp;
mm_segment_t fs;
loff_t offset;
ssize_t ret;
struct v1_disk_dqblk dqblk;
filp = sb_dqopt(dquot->dq_sb)->files[type];
offset = v1_dqoff(dquot->dq_id);
fs = get_fs();
set_fs(KERNEL_DS);
/*
* Note: clear the DQ_MOD flag unconditionally,
* so we don't loop forever on failure.
*/
v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb);
dquot->dq_flags &= ~DQ_MOD;
if (dquot->dq_id == 0) {
dqblk.dqb_btime = sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace;
dqblk.dqb_itime = sb_dqopt(dquot->dq_sb)->info[type].dqi_igrace;
}
ret = 0;
if (filp)
ret = filp->f_op->write(filp, (char *)&dqblk,
sizeof(struct v1_disk_dqblk), &offset);
if (ret != sizeof(struct v1_disk_dqblk)) {
printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
kdevname(dquot->dq_sb->s_dev));
if (ret >= 0)
ret = -EIO;
goto out;
}
ret = 0;
out:
set_fs(fs);
dqstats.writes++;
return ret;
}
/* Magics of new quota format */
#define V2_INITQMAGICS {\
0xd9c01f11, /* USRQUOTA */\
0xd9c01927 /* GRPQUOTA */\
}
/* Header of new quota format */
struct v2_disk_dqheader {
__u32 dqh_magic; /* Magic number identifying file */
__u32 dqh_version; /* File version */
};
static int v1_check_quota_file(struct super_block *sb, int type)
{
struct file *f = sb_dqopt(sb)->files[type];
struct inode *inode = f->f_dentry->d_inode;
ulong blocks;
size_t off;
struct v2_disk_dqheader dqhead;
mm_segment_t fs;
ssize_t size;
loff_t offset = 0;
static const uint quota_magics[] = V2_INITQMAGICS;
if (!inode->i_size)
return 0;
blocks = inode->i_size >> BLOCK_SIZE_BITS;
off = inode->i_size & (BLOCK_SIZE - 1);
if ((blocks % sizeof(struct v1_disk_dqblk) * BLOCK_SIZE + off) % sizeof(struct v1_disk_dqblk))
return 0;
/* Doublecheck whether we didn't get file with new format - with old quotactl() this could happen */
fs = get_fs();
set_fs(KERNEL_DS);
size = f->f_op->read(f, (char *)&dqhead, sizeof(struct v2_disk_dqheader), &offset);
set_fs(fs);
if (size != sizeof(struct v2_disk_dqheader))
return 1; /* Probably not new format */
if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type])
return 1; /* Definitely not new format */
printk(KERN_INFO "VFS: %s: Refusing to turn on old quota format on given file. It probably contains newer quota format.\n", kdevname(sb->s_dev));
return 0; /* Seems like a new format file -> refuse it */
}
static int v1_read_file_info(struct super_block *sb, int type)
{
struct quota_info *dqopt = sb_dqopt(sb);
mm_segment_t fs;
loff_t offset;
struct file *filp = dqopt->files[type];
struct v1_disk_dqblk dqblk;
int ret;
down(&dqopt->dqio_sem);
offset = v1_dqoff(0);
fs = get_fs();
set_fs(KERNEL_DS);
if ((ret = filp->f_op->read(filp, (char *)&dqblk, sizeof(struct v1_disk_dqblk), &offset)) != sizeof(struct v1_disk_dqblk)) {
if (ret >= 0)
ret = -EIO;
goto out;
}
ret = 0;
dqopt->info[type].dqi_igrace = dqblk.dqb_itime ? dqblk.dqb_itime : MAX_IQ_TIME;
dqopt->info[type].dqi_bgrace = dqblk.dqb_btime ? dqblk.dqb_btime : MAX_DQ_TIME;
out:
up(&dqopt->dqio_sem);
set_fs(fs);
return ret;
}
static int v1_write_file_info(struct super_block *sb, int type)
{
struct quota_info *dqopt = sb_dqopt(sb);
mm_segment_t fs;
struct file *filp = dqopt->files[type];
struct v1_disk_dqblk dqblk;
loff_t offset;
int ret;
down(&dqopt->dqio_sem);
dqopt->info[type].dqi_flags &= ~DQF_INFO_DIRTY;
offset = v1_dqoff(0);
fs = get_fs();
set_fs(KERNEL_DS);
if ((ret = filp->f_op->read(filp, (char *)&dqblk, sizeof(struct v1_disk_dqblk), &offset)) != sizeof(struct v1_disk_dqblk)) {
if (ret >= 0)
ret = -EIO;
goto out;
}
dqblk.dqb_itime = dqopt->info[type].dqi_igrace;
dqblk.dqb_btime = dqopt->info[type].dqi_bgrace;
offset = v1_dqoff(0);
ret = filp->f_op->write(filp, (char *)&dqblk, sizeof(struct v1_disk_dqblk), &offset);
if (ret == sizeof(struct v1_disk_dqblk))
ret = 0;
else if (ret > 0)
ret = -EIO;
out:
up(&dqopt->dqio_sem);
set_fs(fs);
return ret;
}
static struct quota_format_ops v1_format_ops = {
check_quota_file: v1_check_quota_file,
read_file_info: v1_read_file_info,
write_file_info: v1_write_file_info,
free_file_info: NULL,
read_dqblk: v1_read_dqblk,
commit_dqblk: v1_commit_dqblk,
};
static struct quota_format_type v1_quota_format = {
qf_fmt_id: QFMT_VFS_OLD,
qf_ops: &v1_format_ops,
qf_owner: THIS_MODULE
};
static int __init init_v1_quota_format(void)
{
return register_quota_format(&v1_quota_format);
}
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);
/*
* vfsv0 quota IO operations on file
*/
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/dqblk_v2.h>
#include <linux/quotaio_v2.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <asm/byteorder.h>
#include <asm/uaccess.h>
#define __QUOTA_V2_PARANOIA
typedef char *dqbuf_t;
#define GETIDINDEX(id, depth) (((id) >> ((V2_DQTREEDEPTH-(depth)-1)*8)) & 0xff)
#define GETENTRIES(buf) ((struct v2_disk_dqblk *)(((char *)buf)+sizeof(struct v2_disk_dqdbheader)))
/* Check whether given file is really vfsv0 quotafile */
static int v2_check_quota_file(struct super_block *sb, int type)
{
struct v2_disk_dqheader dqhead;
struct file *f = sb_dqopt(sb)->files[type];
mm_segment_t fs;
ssize_t size;
loff_t offset = 0;
static const uint quota_magics[] = V2_INITQMAGICS;
static const uint quota_versions[] = V2_INITQVERSIONS;
fs = get_fs();
set_fs(KERNEL_DS);
size = f->f_op->read(f, (char *)&dqhead, sizeof(struct v2_disk_dqheader), &offset);
set_fs(fs);
if (size != sizeof(struct v2_disk_dqheader))
return 0;
if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] ||
le32_to_cpu(dqhead.dqh_version) != quota_versions[type])
return 0;
return 1;
}
/* Read information header from quota file */
static int v2_read_file_info(struct super_block *sb, int type)
{
mm_segment_t fs;
struct v2_disk_dqinfo dinfo;
struct mem_dqinfo *info = sb_dqopt(sb)->info+type;
struct file *f = sb_dqopt(sb)->files[type];
ssize_t size;
loff_t offset = V2_DQINFOOFF;
fs = get_fs();
set_fs(KERNEL_DS);
size = f->f_op->read(f, (char *)&dinfo, sizeof(struct v2_disk_dqinfo), &offset);
set_fs(fs);
if (size != sizeof(struct v2_disk_dqinfo)) {
printk(KERN_WARNING "Can't read info structure on device %s.\n",
kdevname(f->f_dentry->d_sb->s_dev));
return -1;
}
info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace);
info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace);
info->dqi_flags = le32_to_cpu(dinfo.dqi_flags);
info->u.v2_i.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks);
info->u.v2_i.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk);
info->u.v2_i.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry);
return 0;
}
/* Write information header to quota file */
static int v2_write_file_info(struct super_block *sb, int type)
{
mm_segment_t fs;
struct v2_disk_dqinfo dinfo;
struct mem_dqinfo *info = sb_dqopt(sb)->info+type;
struct file *f = sb_dqopt(sb)->files[type];
ssize_t size;
loff_t offset = V2_DQINFOOFF;
info->dqi_flags &= ~DQF_INFO_DIRTY;
dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace);
dinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace);
dinfo.dqi_flags = cpu_to_le32(info->dqi_flags & DQF_MASK);
dinfo.dqi_blocks = cpu_to_le32(info->u.v2_i.dqi_blocks);
dinfo.dqi_free_blk = cpu_to_le32(info->u.v2_i.dqi_free_blk);
dinfo.dqi_free_entry = cpu_to_le32(info->u.v2_i.dqi_free_entry);
fs = get_fs();
set_fs(KERNEL_DS);
size = f->f_op->write(f, (char *)&dinfo, sizeof(struct v2_disk_dqinfo), &offset);
set_fs(fs);
if (size != sizeof(struct v2_disk_dqinfo)) {
printk(KERN_WARNING "Can't write info structure on device %s.\n",
kdevname(f->f_dentry->d_sb->s_dev));
return -1;
}
return 0;
}
static void disk2memdqb(struct mem_dqblk *m, struct v2_disk_dqblk *d)
{
m->dqb_ihardlimit = le32_to_cpu(d->dqb_ihardlimit);
m->dqb_isoftlimit = le32_to_cpu(d->dqb_isoftlimit);
m->dqb_curinodes = le32_to_cpu(d->dqb_curinodes);
m->dqb_itime = le64_to_cpu(d->dqb_itime);
m->dqb_bhardlimit = le32_to_cpu(d->dqb_bhardlimit);
m->dqb_bsoftlimit = le32_to_cpu(d->dqb_bsoftlimit);
m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
m->dqb_btime = le64_to_cpu(d->dqb_btime);
}
static void mem2diskdqb(struct v2_disk_dqblk *d, struct mem_dqblk *m, qid_t id)
{
d->dqb_ihardlimit = cpu_to_le32(m->dqb_ihardlimit);
d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit);
d->dqb_curinodes = cpu_to_le32(m->dqb_curinodes);
d->dqb_itime = cpu_to_le64(m->dqb_itime);
d->dqb_bhardlimit = cpu_to_le32(m->dqb_bhardlimit);
d->dqb_bsoftlimit = cpu_to_le32(m->dqb_bsoftlimit);
d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
d->dqb_btime = cpu_to_le64(m->dqb_btime);
d->dqb_id = cpu_to_le32(id);
}
static dqbuf_t getdqbuf(void)
{
dqbuf_t buf = kmalloc(V2_DQBLKSIZE, GFP_KERNEL);
if (!buf)
printk(KERN_WARNING "VFS: Not enough memory for quota buffers.\n");
return buf;
}
static inline void freedqbuf(dqbuf_t buf)
{
kfree(buf);
}
static ssize_t read_blk(struct file *filp, uint blk, dqbuf_t buf)
{
mm_segment_t fs;
ssize_t ret;
loff_t offset = blk<<V2_DQBLKSIZE_BITS;
memset(buf, 0, V2_DQBLKSIZE);
fs = get_fs();
set_fs(KERNEL_DS);
ret = filp->f_op->read(filp, (char *)buf, V2_DQBLKSIZE, &offset);
set_fs(fs);
return ret;
}
static ssize_t write_blk(struct file *filp, uint blk, dqbuf_t buf)
{
mm_segment_t fs;
ssize_t ret;
loff_t offset = blk<<V2_DQBLKSIZE_BITS;
fs = get_fs();
set_fs(KERNEL_DS);
ret = filp->f_op->write(filp, (char *)buf, V2_DQBLKSIZE, &offset);
set_fs(fs);
return ret;
}
/* Remove empty block from list and return it */
static int get_free_dqblk(struct file *filp, struct mem_dqinfo *info)
{
dqbuf_t buf = getdqbuf();
struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
int ret, blk;
if (!buf)
return -ENOMEM;
if (info->u.v2_i.dqi_free_blk) {
blk = info->u.v2_i.dqi_free_blk;
if ((ret = read_blk(filp, blk, buf)) < 0)
goto out_buf;
info->u.v2_i.dqi_free_blk = le32_to_cpu(dh->dqdh_next_free);
}
else {
memset(buf, 0, V2_DQBLKSIZE);
if ((ret = write_blk(filp, info->u.v2_i.dqi_blocks, buf)) < 0) /* Assure block allocation... */
goto out_buf;
blk = info->u.v2_i.dqi_blocks++;
}
mark_info_dirty(info);
ret = blk;
out_buf:
freedqbuf(buf);
return ret;
}
/* Insert empty block to the list */
static int put_free_dqblk(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk)
{
struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
int err;
dh->dqdh_next_free = cpu_to_le32(info->u.v2_i.dqi_free_blk);
dh->dqdh_prev_free = cpu_to_le32(0);
dh->dqdh_entries = cpu_to_le16(0);
info->u.v2_i.dqi_free_blk = blk;
mark_info_dirty(info);
if ((err = write_blk(filp, blk, buf)) < 0) /* Some strange block. We had better leave it... */
return err;
return 0;
}
/* Remove given block from the list of blocks with free entries */
static int remove_free_dqentry(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk)
{
dqbuf_t tmpbuf = getdqbuf();
struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
uint nextblk = le32_to_cpu(dh->dqdh_next_free), prevblk = le32_to_cpu(dh->dqdh_prev_free);
int err;
if (!tmpbuf)
return -ENOMEM;
if (nextblk) {
if ((err = read_blk(filp, nextblk, tmpbuf)) < 0)
goto out_buf;
((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = dh->dqdh_prev_free;
if ((err = write_blk(filp, nextblk, tmpbuf)) < 0)
goto out_buf;
}
if (prevblk) {
if ((err = read_blk(filp, prevblk, tmpbuf)) < 0)
goto out_buf;
((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_next_free = dh->dqdh_next_free;
if ((err = write_blk(filp, prevblk, tmpbuf)) < 0)
goto out_buf;
}
else {
info->u.v2_i.dqi_free_entry = nextblk;
mark_info_dirty(info);
}
freedqbuf(tmpbuf);
dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0);
if (write_blk(filp, blk, buf) < 0) /* No matter whether write succeeds block is out of list */
printk(KERN_ERR "VFS: Can't write block (%u) with free entries.\n", blk);
return 0;
out_buf:
freedqbuf(tmpbuf);
return err;
}
/* Insert given block to the beginning of list with free entries */
static int insert_free_dqentry(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk)
{
dqbuf_t tmpbuf = getdqbuf();
struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
int err;
if (!tmpbuf)
return -ENOMEM;
dh->dqdh_next_free = cpu_to_le32(info->u.v2_i.dqi_free_entry);
dh->dqdh_prev_free = cpu_to_le32(0);
if ((err = write_blk(filp, blk, buf)) < 0)
goto out_buf;
if (info->u.v2_i.dqi_free_entry) {
if ((err = read_blk(filp, info->u.v2_i.dqi_free_entry, tmpbuf)) < 0)
goto out_buf;
((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = cpu_to_le32(blk);
if ((err = write_blk(filp, info->u.v2_i.dqi_free_entry, tmpbuf)) < 0)
goto out_buf;
}
freedqbuf(tmpbuf);
info->u.v2_i.dqi_free_entry = blk;
mark_info_dirty(info);
return 0;
out_buf:
freedqbuf(tmpbuf);
return err;
}
/* Find space for dquot */
static uint find_free_dqentry(struct dquot *dquot, int *err)
{
struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info+dquot->dq_type;
uint blk, i;
struct v2_disk_dqdbheader *dh;
struct v2_disk_dqblk *ddquot;
struct v2_disk_dqblk fakedquot;
dqbuf_t buf;
*err = 0;
if (!(buf = getdqbuf())) {
*err = -ENOMEM;
return 0;
}
dh = (struct v2_disk_dqdbheader *)buf;
ddquot = GETENTRIES(buf);
if (info->u.v2_i.dqi_free_entry) {
blk = info->u.v2_i.dqi_free_entry;
if ((*err = read_blk(filp, blk, buf)) < 0)
goto out_buf;
}
else {
blk = get_free_dqblk(filp, info);
if ((int)blk < 0) {
*err = blk;
return 0;
}
memset(buf, 0, V2_DQBLKSIZE);
info->u.v2_i.dqi_free_entry = blk; /* This is enough as block is already zeroed and entry list is empty... */
mark_info_dirty(info);
}
if (le16_to_cpu(dh->dqdh_entries)+1 >= V2_DQSTRINBLK) /* Block will be full? */
if ((*err = remove_free_dqentry(filp, info, buf, blk)) < 0) {
printk(KERN_ERR "VFS: find_free_dqentry(): Can't remove block (%u) from entry free list.\n", blk);
goto out_buf;
}
dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries)+1);
memset(&fakedquot, 0, sizeof(struct v2_disk_dqblk));
/* Find free structure in block */
for (i = 0; i < V2_DQSTRINBLK && memcmp(&fakedquot, ddquot+i, sizeof(struct v2_disk_dqblk)); i++);
#ifdef __QUOTA_V2_PARANOIA
if (i == V2_DQSTRINBLK) {
printk(KERN_ERR "VFS: find_free_dqentry(): Data block full but it shouldn't.\n");
*err = -EIO;
goto out_buf;
}
#endif
if ((*err = write_blk(filp, blk, buf)) < 0) {
printk(KERN_ERR "VFS: find_free_dqentry(): Can't write quota data block %u.\n", blk);
goto out_buf;
}
dquot->dq_off = (blk<<V2_DQBLKSIZE_BITS)+sizeof(struct v2_disk_dqdbheader)+i*sizeof(struct v2_disk_dqblk);
freedqbuf(buf);
return blk;
out_buf:
freedqbuf(buf);
return 0;
}
/* Insert reference to structure into the trie */
static int do_insert_tree(struct dquot *dquot, uint *treeblk, int depth)
{
struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type;
dqbuf_t buf;
int ret = 0, newson = 0, newact = 0;
u32 *ref;
uint newblk;
if (!(buf = getdqbuf()))
return -ENOMEM;
if (!*treeblk) {
ret = get_free_dqblk(filp, info);
if (ret < 0)
goto out_buf;
*treeblk = ret;
memset(buf, 0, V2_DQBLKSIZE);
newact = 1;
}
else {
if ((ret = read_blk(filp, *treeblk, buf)) < 0) {
printk(KERN_ERR "VFS: Can't read tree quota block %u.\n", *treeblk);
goto out_buf;
}
}
ref = (u32 *)buf;
newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
if (!newblk)
newson = 1;
if (depth == V2_DQTREEDEPTH-1) {
#ifdef __QUOTA_V2_PARANOIA
if (newblk) {
printk(KERN_ERR "VFS: Inserting already present quota entry (block %u).\n", ref[GETIDINDEX(dquot->dq_id, depth)]);
ret = -EIO;
goto out_buf;
}
#endif
newblk = find_free_dqentry(dquot, &ret);
}
else
ret = do_insert_tree(dquot, &newblk, depth+1);
if (newson && ret >= 0) {
ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(newblk);
ret = write_blk(filp, *treeblk, buf);
}
else if (newact && ret < 0)
put_free_dqblk(filp, info, buf, *treeblk);
out_buf:
freedqbuf(buf);
return ret;
}
/* Wrapper for inserting quota structure into tree */
static inline int dq_insert_tree(struct dquot *dquot)
{
int tmp = V2_DQTREEOFF;
return do_insert_tree(dquot, &tmp, 0);
}
/*
* We don't have to be afraid of deadlocks as we never have quotas on quota files...
*/
static int v2_write_dquot(struct dquot *dquot)
{
int type = dquot->dq_type;
struct file *filp;
mm_segment_t fs;
loff_t offset;
ssize_t ret;
struct v2_disk_dqblk ddquot;
if (!dquot->dq_off)
if ((ret = dq_insert_tree(dquot)) < 0) {
printk(KERN_ERR "VFS: Error %d occured while creating quota.\n", ret);
return ret;
}
filp = sb_dqopt(dquot->dq_sb)->files[type];
offset = dquot->dq_off;
mem2diskdqb(&ddquot, &dquot->dq_dqb, dquot->dq_id);
fs = get_fs();
set_fs(KERNEL_DS);
ret = filp->f_op->write(filp, (char *)&ddquot, sizeof(struct v2_disk_dqblk), &offset);
set_fs(fs);
if (ret != sizeof(struct v2_disk_dqblk)) {
printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", kdevname(dquot->dq_sb->s_dev));
if (ret >= 0)
ret = -ENOSPC;
}
else
ret = 0;
dqstats.writes++;
return ret;
}
/* Free dquot entry in data block */
static int free_dqentry(struct dquot *dquot, uint blk)
{
struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type;
struct v2_disk_dqdbheader *dh;
dqbuf_t buf = getdqbuf();
int ret = 0;
if (!buf)
return -ENOMEM;
if (dquot->dq_off >> V2_DQBLKSIZE_BITS != blk) {
printk(KERN_ERR "VFS: Quota structure has offset to other block (%u) than it should (%u).\n", blk, (uint)(dquot->dq_off >> V2_DQBLKSIZE_BITS));
goto out_buf;
}
if ((ret = read_blk(filp, blk, buf)) < 0) {
printk(KERN_ERR "VFS: Can't read quota data block %u\n", blk);
goto out_buf;
}
dh = (struct v2_disk_dqdbheader *)buf;
dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries)-1);
if (!le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */
if ((ret = remove_free_dqentry(filp, info, buf, blk)) < 0 ||
(ret = put_free_dqblk(filp, info, buf, blk)) < 0) {
printk(KERN_ERR "VFS: Can't move quota data block (%u) to free list.\n", blk);
goto out_buf;
}
}
else {
memset(buf+(dquot->dq_off & ((1 << V2_DQBLKSIZE_BITS)-1)), 0, sizeof(struct v2_disk_dqblk));
if (le16_to_cpu(dh->dqdh_entries) == V2_DQSTRINBLK-1) {
/* Insert will write block itself */
if ((ret = insert_free_dqentry(filp, info, buf, blk)) < 0) {
printk(KERN_ERR "VFS: Can't insert quota data block (%u) to free entry list.\n", blk);
goto out_buf;
}
}
else
if ((ret = write_blk(filp, blk, buf)) < 0) {
printk(KERN_ERR "VFS: Can't write quota data block %u\n", blk);
goto out_buf;
}
}
dquot->dq_off = 0; /* Quota is now unattached */
out_buf:
freedqbuf(buf);
return ret;
}
/* Remove reference to dquot from tree */
static int remove_tree(struct dquot *dquot, uint *blk, int depth)
{
struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type;
dqbuf_t buf = getdqbuf();
int ret = 0;
uint newblk;
u32 *ref = (u32 *)buf;
if (!buf)
return -ENOMEM;
if ((ret = read_blk(filp, *blk, buf)) < 0) {
printk(KERN_ERR "VFS: Can't read quota data block %u\n", *blk);
goto out_buf;
}
newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
if (depth == V2_DQTREEDEPTH-1) {
ret = free_dqentry(dquot, newblk);
newblk = 0;
}
else
ret = remove_tree(dquot, &newblk, depth+1);
if (ret >= 0 && !newblk) {
int i;
ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(0);
for (i = 0; i < V2_DQBLKSIZE && !buf[i]; i++); /* Block got empty? */
if (i == V2_DQBLKSIZE) {
put_free_dqblk(filp, info, buf, *blk);
*blk = 0;
}
else
if ((ret = write_blk(filp, *blk, buf)) < 0)
printk(KERN_ERR "VFS: Can't write quota tree block %u.\n", *blk);
}
out_buf:
freedqbuf(buf);
return ret;
}
/* Delete dquot from tree */
static int v2_delete_dquot(struct dquot *dquot)
{
uint tmp = V2_DQTREEOFF;
if (!dquot->dq_off) /* Even not allocated? */
return 0;
return remove_tree(dquot, &tmp, 0);
}
/* Find entry in block */
static loff_t find_block_dqentry(struct dquot *dquot, uint blk)
{
struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
dqbuf_t buf = getdqbuf();
loff_t ret = 0;
int i;
struct v2_disk_dqblk *ddquot = GETENTRIES(buf);
if (!buf)
return -ENOMEM;
if ((ret = read_blk(filp, blk, buf)) < 0) {
printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
goto out_buf;
}
if (dquot->dq_id)
for (i = 0; i < V2_DQSTRINBLK && le32_to_cpu(ddquot[i].dqb_id) != dquot->dq_id; i++);
else { /* ID 0 as a bit more complicated searching... */
struct v2_disk_dqblk fakedquot;
memset(&fakedquot, 0, sizeof(struct v2_disk_dqblk));
for (i = 0; i < V2_DQSTRINBLK; i++)
if (!le32_to_cpu(ddquot[i].dqb_id) && memcmp(&fakedquot, ddquot+i, sizeof(struct v2_disk_dqblk)))
break;
}
if (i == V2_DQSTRINBLK) {
printk(KERN_ERR "VFS: Quota for id %u referenced but not present.\n", dquot->dq_id);
ret = -EIO;
goto out_buf;
}
else
ret = (blk << V2_DQBLKSIZE_BITS) + sizeof(struct v2_disk_dqdbheader) + i * sizeof(struct v2_disk_dqblk);
out_buf:
freedqbuf(buf);
return ret;
}
/* Find entry for given id in the tree */
static loff_t find_tree_dqentry(struct dquot *dquot, uint blk, int depth)
{
struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
dqbuf_t buf = getdqbuf();
loff_t ret = 0;
u32 *ref = (u32 *)buf;
if (!buf)
return -ENOMEM;
if ((ret = read_blk(filp, blk, buf)) < 0) {
printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
goto out_buf;
}
ret = 0;
blk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
if (!blk) /* No reference? */
goto out_buf;
if (depth < V2_DQTREEDEPTH-1)
ret = find_tree_dqentry(dquot, blk, depth+1);
else
ret = find_block_dqentry(dquot, blk);
out_buf:
freedqbuf(buf);
return ret;
}
/* Find entry for given id in the tree - wrapper function */
static inline loff_t find_dqentry(struct dquot *dquot)
{
return find_tree_dqentry(dquot, V2_DQTREEOFF, 0);
}
static int v2_read_dquot(struct dquot *dquot)
{
int type = dquot->dq_type;
struct file *filp;
mm_segment_t fs;
loff_t offset;
struct v2_disk_dqblk ddquot;
int ret = 0;
filp = sb_dqopt(dquot->dq_sb)->files[type];
#ifdef __QUOTA_V2_PARANOIA
if (!filp || !dquot->dq_sb) { /* Invalidated quota? */
printk(KERN_ERR "VFS: Quota invalidated while reading!\n");
return -EIO;
}
#endif
offset = find_dqentry(dquot);
if (offset <= 0) { /* Entry not present? */
if (offset < 0)
printk(KERN_ERR "VFS: Can't read quota structure for id %u.\n", dquot->dq_id);
dquot->dq_off = 0;
dquot->dq_flags |= DQ_FAKE;
memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
ret = offset;
}
else {
dquot->dq_off = offset;
fs = get_fs();
set_fs(KERNEL_DS);
if ((ret = filp->f_op->read(filp, (char *)&ddquot, sizeof(struct v2_disk_dqblk), &offset)) != sizeof(struct v2_disk_dqblk)) {
if (ret >= 0)
ret = -EIO;
printk(KERN_ERR "VFS: Error while reading quota structure for id %u.\n", dquot->dq_id);
memset(&ddquot, 0, sizeof(struct v2_disk_dqblk));
}
else
ret = 0;
set_fs(fs);
disk2memdqb(&dquot->dq_dqb, &ddquot);
}
dqstats.reads++;
return ret;
}
/* Commit changes of dquot to disk - it might also mean deleting it when quota became fake one and user has no blocks... */
static int v2_commit_dquot(struct dquot *dquot)
{
/* We clear the flag everytime so we don't loop when there was an IO error... */
dquot->dq_flags &= ~DQ_MOD;
if (dquot->dq_flags & DQ_FAKE && !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace))
return v2_delete_dquot(dquot);
else
return v2_write_dquot(dquot);
}
static struct quota_format_ops v2_format_ops = {
check_quota_file: v2_check_quota_file,
read_file_info: v2_read_file_info,
write_file_info: v2_write_file_info,
free_file_info: NULL,
read_dqblk: v2_read_dquot,
commit_dqblk: v2_commit_dquot,
};
static struct quota_format_type v2_quota_format = {
qf_fmt_id: QFMT_VFS_V0,
qf_ops: &v2_format_ops,
qf_owner: THIS_MODULE
};
static int __init init_v2_quota_format(void)
{
return register_quota_format(&v2_quota_format);
}
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);
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/devfs_fs_kernel.h> #include <linux/devfs_fs_kernel.h>
#include <linux/acct.h> #include <linux/acct.h>
#include <linux/blkdev.h> #include <linux/blkdev.h>
#include <linux/quotaops.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
void get_filesystem(struct file_system_type *fs); void get_filesystem(struct file_system_type *fs);
...@@ -62,6 +63,8 @@ static struct super_block *alloc_super(void) ...@@ -62,6 +63,8 @@ static struct super_block *alloc_super(void)
sema_init(&s->s_dquot.dqio_sem, 1); sema_init(&s->s_dquot.dqio_sem, 1);
sema_init(&s->s_dquot.dqoff_sem, 1); sema_init(&s->s_dquot.dqoff_sem, 1);
s->s_maxbytes = MAX_NON_LFS; s->s_maxbytes = MAX_NON_LFS;
s->dq_op = sb_dquot_ops;
s->s_qcop = sb_quotactl_ops;
} }
return s; return s;
} }
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#define FIONBIO _IOW('f', 126, int) #define FIONBIO _IOW('f', 126, int)
#define FIONREAD _IOR('f', 127, int) #define FIONREAD _IOR('f', 127, int)
#define TIOCINQ FIONREAD #define TIOCINQ FIONREAD
#define FIOQSIZE _IOR('f', 128, loff_t)
#define TIOCGETP _IOR('t', 8, struct sgttyb) #define TIOCGETP _IOR('t', 8, struct sgttyb)
#define TIOCSETP _IOW('t', 9, struct sgttyb) #define TIOCSETP _IOW('t', 9, struct sgttyb)
......
...@@ -69,6 +69,7 @@ ...@@ -69,6 +69,7 @@
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ #define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
#define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */ #define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
#define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */ #define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
#define FIOQSIZE 0x5460
/* Used for packet mode */ /* Used for packet mode */
#define TIOCPKT_DATA 0 #define TIOCPKT_DATA 0
......
...@@ -67,6 +67,7 @@ ...@@ -67,6 +67,7 @@
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ #define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
#define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */ #define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
#define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */ #define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
#define FIOQSIZE 0x5460
/* Used for packet mode */ /* Used for packet mode */
#define TIOCPKT_DATA 0 #define TIOCPKT_DATA 0
......
...@@ -72,6 +72,7 @@ ...@@ -72,6 +72,7 @@
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ #define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
#define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */ #define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
#define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */ #define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
#define FIOQSIZE 0x5460
/* Used for packet mode */ /* Used for packet mode */
#define TIOCPKT_DATA 0 #define TIOCPKT_DATA 0
......
...@@ -65,6 +65,7 @@ ...@@ -65,6 +65,7 @@
#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */ #define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ #define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
#define FIOQSIZE 0x545E
/* Used for packet mode */ /* Used for packet mode */
#define TIOCPKT_DATA 0 #define TIOCPKT_DATA 0
......
...@@ -67,6 +67,7 @@ ...@@ -67,6 +67,7 @@
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ #define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
#define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */ #define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
#define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */ #define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
#define FIOQSIZE 0x5460 /* Get exact space used by quota */
/* Used for packet mode */ /* Used for packet mode */
#define TIOCPKT_DATA 0 #define TIOCPKT_DATA 0
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#define FIONBIO _IOW('f', 126, int) #define FIONBIO _IOW('f', 126, int)
#define FIONREAD _IOR('f', 127, int) #define FIONREAD _IOR('f', 127, int)
#define TIOCINQ FIONREAD #define TIOCINQ FIONREAD
#define FIOQSIZE _IOR('f', 128, loff_t)
#define TCGETS 0x5401 #define TCGETS 0x5401
#define TCSETS 0x5402 #define TCSETS 0x5402
......
...@@ -86,6 +86,7 @@ ...@@ -86,6 +86,7 @@
#define FIONBIO _IOW('f', 126, int) #define FIONBIO _IOW('f', 126, int)
#define FIONREAD _IOR('f', 127, int) #define FIONREAD _IOR('f', 127, int)
#define TIOCINQ FIONREAD #define TIOCINQ FIONREAD
#define FIOQSIZE _IOR('f', 128, loff_t)
/* SCARY Rutgers local SunOS kernel hackery, perhaps I will support it /* SCARY Rutgers local SunOS kernel hackery, perhaps I will support it
* someday. This is completely bogus, I know... * someday. This is completely bogus, I know...
......
...@@ -87,6 +87,7 @@ ...@@ -87,6 +87,7 @@
#define FIONBIO _IOW('f', 126, int) #define FIONBIO _IOW('f', 126, int)
#define FIONREAD _IOR('f', 127, int) #define FIONREAD _IOR('f', 127, int)
#define TIOCINQ FIONREAD #define TIOCINQ FIONREAD
#define FIOQSIZE _IOR('f', 128, loff_t)
/* SCARY Rutgers local SunOS kernel hackery, perhaps I will support it /* SCARY Rutgers local SunOS kernel hackery, perhaps I will support it
* someday. This is completely bogus, I know... * someday. This is completely bogus, I know...
......
/*
* File with in-memory structures of old quota format
*/
#ifndef _LINUX_DQBLK_V1_H
#define _LINUX_DQBLK_V1_H
/* Id of quota format */
#define QFMT_VFS_OLD 1
/* Root squash turned on */
#define V1_DQF_RSQUASH 1
/* Special information about quotafile */
struct v1_mem_dqinfo {
};
#endif /* _LINUX_DQBLK_V1_H */
/*
* Definitions of structures for vfsv0 quota format
*/
#ifndef _LINUX_DQBLK_V2_H
#define _LINUX_DQBLK_V2_H
#include <linux/types.h>
/* id numbers of quota format */
#define QFMT_VFS_V0 2
/* Inmemory copy of version specific information */
struct v2_mem_dqinfo {
unsigned int dqi_blocks;
unsigned int dqi_free_blk;
unsigned int dqi_free_entry;
};
#endif /* _LINUX_DQBLK_V2_H */
...@@ -368,6 +368,7 @@ struct inode { ...@@ -368,6 +368,7 @@ struct inode {
unsigned long i_blksize; unsigned long i_blksize;
unsigned long i_blocks; unsigned long i_blocks;
unsigned long i_version; unsigned long i_version;
unsigned short i_bytes;
struct semaphore i_sem; struct semaphore i_sem;
struct inode_operations *i_op; struct inode_operations *i_op;
struct file_operations *i_fop; /* former ->i_op->default_file_ops */ struct file_operations *i_fop; /* former ->i_op->default_file_ops */
...@@ -425,6 +426,39 @@ struct fown_struct { ...@@ -425,6 +426,39 @@ struct fown_struct {
int signum; /* posix.1b rt signal to be delivered on IO */ int signum; /* posix.1b rt signal to be delivered on IO */
}; };
static inline void inode_add_bytes(struct inode *inode, loff_t bytes)
{
inode->i_blocks += bytes >> 9;
bytes &= 511;
inode->i_bytes += bytes;
if (inode->i_bytes >= 512) {
inode->i_blocks++;
inode->i_bytes -= 512;
}
}
static inline void inode_sub_bytes(struct inode *inode, loff_t bytes)
{
inode->i_blocks -= bytes >> 9;
bytes &= 511;
if (inode->i_bytes < bytes) {
inode->i_blocks--;
inode->i_bytes += 512;
}
inode->i_bytes -= bytes;
}
static inline loff_t inode_get_bytes(struct inode *inode)
{
return (((loff_t)inode->i_blocks) << 9) + inode->i_bytes;
}
static inline void inode_set_bytes(struct inode *inode, loff_t bytes)
{
inode->i_blocks = bytes >> 9;
inode->i_bytes = bytes & 511;
}
/* /*
* Track a single file's readahead state * Track a single file's readahead state
*/ */
...@@ -581,20 +615,6 @@ struct nameidata { ...@@ -581,20 +615,6 @@ struct nameidata {
struct vfsmount *old_mnt; struct vfsmount *old_mnt;
}; };
#define DQUOT_USR_ENABLED 0x01 /* User diskquotas enabled */
#define DQUOT_GRP_ENABLED 0x02 /* Group diskquotas enabled */
struct quota_mount_options
{
unsigned int flags; /* Flags for diskquotas on this device */
struct semaphore dqio_sem; /* lock device while I/O in progress */
struct semaphore dqoff_sem; /* serialize quota_off() and quota_on() on device */
struct file *files[MAXQUOTAS]; /* fp's to quotafiles */
time_t inode_expire[MAXQUOTAS]; /* expiretime for inode-quota */
time_t block_expire[MAXQUOTAS]; /* expiretime for block-quota */
char rsquash[MAXQUOTAS]; /* for quotas threat root as any other user */
};
/* /*
* Umount options * Umount options
*/ */
...@@ -624,6 +644,7 @@ struct super_block { ...@@ -624,6 +644,7 @@ struct super_block {
struct file_system_type *s_type; struct file_system_type *s_type;
struct super_operations *s_op; struct super_operations *s_op;
struct dquot_operations *dq_op; struct dquot_operations *dq_op;
struct quotactl_ops *s_qcop;
struct export_operations *s_export_op; struct export_operations *s_export_op;
unsigned long s_flags; unsigned long s_flags;
unsigned long s_magic; unsigned long s_magic;
...@@ -642,7 +663,7 @@ struct super_block { ...@@ -642,7 +663,7 @@ struct super_block {
struct block_device *s_bdev; struct block_device *s_bdev;
struct list_head s_instances; struct list_head s_instances;
struct quota_mount_options s_dquot; /* Diskquota specific options */ struct quota_info s_dquot; /* Diskquota specific options */
char s_id[32]; /* Informational name */ char s_id[32]; /* Informational name */
...@@ -809,16 +830,6 @@ static inline void mark_inode_dirty_sync(struct inode *inode) ...@@ -809,16 +830,6 @@ static inline void mark_inode_dirty_sync(struct inode *inode)
__mark_inode_dirty(inode, I_DIRTY_SYNC); __mark_inode_dirty(inode, I_DIRTY_SYNC);
} }
struct dquot_operations {
void (*initialize) (struct inode *, short);
void (*drop) (struct inode *);
int (*alloc_block) (struct inode *, unsigned long, char);
int (*alloc_inode) (const struct inode *, unsigned long);
void (*free_block) (struct inode *, unsigned long);
void (*free_inode) (const struct inode *, unsigned long);
int (*transfer) (struct inode *, struct iattr *);
};
/** /**
* &export_operations - for nfsd to communicate with file systems * &export_operations - for nfsd to communicate with file systems
......
...@@ -40,30 +40,22 @@ ...@@ -40,30 +40,22 @@
#define _LINUX_QUOTA_ #define _LINUX_QUOTA_
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h>
/* #define __DQUOT_VERSION__ "dquot_6.5.1"
* Convert diskblocks to blocks and the other way around. #define __DQUOT_NUM_VERSION__ 6*10000+5*100+1
*/
#define dbtob(num) (num << BLOCK_SIZE_BITS)
#define btodb(num) (num >> BLOCK_SIZE_BITS)
/* typedef __kernel_uid32_t qid_t; /* Type in which we store ids in memory */
* Convert count of filesystem blocks to diskquota blocks, meant typedef __u64 qsize_t; /* Type in which we store sizes */
* for filesystems where i_blksize != BLOCK_SIZE
*/
#define fs_to_dq_blocks(num, blksize) (((num) * (blksize)) / BLOCK_SIZE)
/* /* Size of blocks in which are counted size limits */
* Definitions for disk quotas imposed on the average user #define QUOTABLOCK_BITS 10
* (big brother finally hits Linux). #define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
*
* The following constants define the amount of time given a user /* Conversion routines from and to quota blocks */
* before the soft limits are treated as hard limits (usually resulting #define qb2kb(x) ((x) << (QUOTABLOCK_BITS-10))
* in an allocation failure). The timer is started when the user crosses #define kb2qb(x) ((x) >> (QUOTABLOCK_BITS-10))
* their soft limit, it is reset when they go below their soft limit. #define toqb(x) (((x) + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS)
*/
#define MAX_IQ_TIME 604800 /* (7*24*60*60) 1 week */
#define MAX_DQ_TIME 604800 /* (7*24*60*60) 1 week */
#define MAXQUOTAS 2 #define MAXQUOTAS 2
#define USRQUOTA 0 /* element used for user quotas */ #define USRQUOTA 0 /* element used for user quotas */
...@@ -78,9 +70,6 @@ ...@@ -78,9 +70,6 @@
"undefined", \ "undefined", \
}; };
#define QUOTAFILENAME "quota"
#define QUOTAGROUP "staff"
/* /*
* Command definitions for the 'quotactl' system call. * Command definitions for the 'quotactl' system call.
* The commands are broken into a main command defined below * The commands are broken into a main command defined below
...@@ -91,45 +80,111 @@ ...@@ -91,45 +80,111 @@
#define SUBCMDSHIFT 8 #define SUBCMDSHIFT 8
#define QCMD(cmd, type) (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK)) #define QCMD(cmd, type) (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK))
#define Q_QUOTAON 0x0100 /* enable quotas */ #define Q_SYNC 0x800001 /* sync disk copy of a filesystems quotas */
#define Q_QUOTAOFF 0x0200 /* disable quotas */ #define Q_QUOTAON 0x800002 /* turn quotas on */
#define Q_GETQUOTA 0x0300 /* get limits and usage */ #define Q_QUOTAOFF 0x800003 /* turn quotas off */
#define Q_SETQUOTA 0x0400 /* set limits and usage */ #define Q_GETFMT 0x800004 /* get quota format used on given filesystem */
#define Q_SETUSE 0x0500 /* set usage */ #define Q_GETINFO 0x800005 /* get information about quota files */
#define Q_SYNC 0x0600 /* sync disk copy of a filesystems quotas */ #define Q_SETINFO 0x800006 /* set information about quota files */
#define Q_SETQLIM 0x0700 /* set limits */ #define Q_GETQUOTA 0x800007 /* get user quota structure */
#define Q_GETSTATS 0x0800 /* get collected stats */ #define Q_SETQUOTA 0x800008 /* set user quota structure */
#define Q_RSQUASH 0x1000 /* set root_squash option */
/*
* Quota structure used for communication with userspace via quotactl
* Following flags are used to specify which fields are valid
*/
#define QIF_BLIMITS 1
#define QIF_SPACE 2
#define QIF_ILIMITS 4
#define QIF_INODES 8
#define QIF_BTIME 16
#define QIF_ITIME 32
#define QIF_LIMITS (QIF_BLIMITS | QIF_ILIMITS)
#define QIF_USAGE (QIF_SPACE | QIF_INODES)
#define QIF_TIMES (QIF_BTIME | QIF_ITIME)
#define QIF_ALL (QIF_LIMITS | QIF_USAGE | QIF_TIMES)
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;
};
/* /*
* The following structure defines the format of the disk quota file * Structure used for setting quota information about file via quotactl
* (as it appears on disk) - the file is an array of these structures * Following flags are used to specify which fields are valid
* indexed by user or group number.
*/ */
struct dqblk { #define IIF_BGRACE 1
#define IIF_IGRACE 2
#define IIF_FLAGS 4
#define IIF_ALL (IIF_BGRACE | IIF_IGRACE | IIF_FLAGS)
struct if_dqinfo {
__u64 dqi_bgrace;
__u64 dqi_igrace;
__u32 dqi_flags;
__u32 dqi_valid;
};
#ifdef __KERNEL__
#include <linux/xqm.h>
#include <linux/dqblk_v1.h>
#include <linux/dqblk_v2.h>
/*
* Data for one user/group kept in memory
*/
struct mem_dqblk {
__u32 dqb_bhardlimit; /* absolute limit on disk blks alloc */ __u32 dqb_bhardlimit; /* absolute limit on disk blks alloc */
__u32 dqb_bsoftlimit; /* preferred limit on disk blks */ __u32 dqb_bsoftlimit; /* preferred limit on disk blks */
__u32 dqb_curblocks; /* current block count */ qsize_t dqb_curspace; /* current used space */
__u32 dqb_ihardlimit; /* absolute limit on allocated inodes */ __u32 dqb_ihardlimit; /* absolute limit on allocated inodes */
__u32 dqb_isoftlimit; /* preferred inode limit */ __u32 dqb_isoftlimit; /* preferred inode limit */
__u32 dqb_curinodes; /* current # allocated inodes */ __u32 dqb_curinodes; /* current # allocated inodes */
time_t dqb_btime; /* time limit for excessive disk use */ time_t dqb_btime; /* time limit for excessive disk use */
time_t dqb_itime; /* time limit for excessive inode use */ time_t dqb_itime; /* time limit for excessive inode use */
}; };
/* /*
* Shorthand notation. * Data for one quotafile kept in memory
*/ */
#define dq_bhardlimit dq_dqb.dqb_bhardlimit struct quota_format_type;
#define dq_bsoftlimit dq_dqb.dqb_bsoftlimit
#define dq_curblocks dq_dqb.dqb_curblocks struct mem_dqinfo {
#define dq_ihardlimit dq_dqb.dqb_ihardlimit struct quota_format_type *dqi_format;
#define dq_isoftlimit dq_dqb.dqb_isoftlimit int dqi_flags;
#define dq_curinodes dq_dqb.dqb_curinodes unsigned int dqi_bgrace;
#define dq_btime dq_dqb.dqb_btime unsigned int dqi_igrace;
#define dq_itime dq_dqb.dqb_itime union {
struct v1_mem_dqinfo v1_i;
struct v2_mem_dqinfo v2_i;
} u;
};
#define DQF_MASK 0xffff /* Mask for format specific flags */
#define DQF_INFO_DIRTY 0x10000 /* Is info dirty? */
#define DQF_ANY_DQUOT_DIRTY 0x20000 /* Is any dquot dirty? */
extern inline void mark_info_dirty(struct mem_dqinfo *info)
{
info->dqi_flags |= DQF_INFO_DIRTY;
}
#define dqoff(UID) ((loff_t)((UID) * sizeof (struct dqblk))) #define info_dirty(info) ((info)->dqi_flags & DQF_INFO_DIRTY)
#define info_any_dirty(info) ((info)->dqi_flags & DQF_INFO_DIRTY ||\
(info)->dqi_flags & DQF_ANY_DQUOT_DIRTY)
#define sb_dqopt(sb) (&(sb)->s_dquot)
extern int nr_dquots, nr_free_dquots;
struct dqstats { struct dqstats {
__u32 lookups; __u32 lookups;
...@@ -142,9 +197,7 @@ struct dqstats { ...@@ -142,9 +197,7 @@ struct dqstats {
__u32 syncs; __u32 syncs;
}; };
#ifdef __KERNEL__ extern struct dqstats dqstats;
extern int dquot_root_squash;
#define NR_DQHASH 43 /* Just an arbitrary number */ #define NR_DQHASH 43 /* Just an arbitrary number */
...@@ -161,36 +214,112 @@ struct dquot { ...@@ -161,36 +214,112 @@ struct dquot {
struct list_head dq_free; /* Free list element */ struct list_head dq_free; /* Free list element */
wait_queue_head_t dq_wait_lock; /* Pointer to waitqueue on dquot lock */ wait_queue_head_t dq_wait_lock; /* Pointer to waitqueue on dquot lock */
wait_queue_head_t dq_wait_free; /* Pointer to waitqueue for quota to be unused */ wait_queue_head_t dq_wait_free; /* Pointer to waitqueue for quota to be unused */
int dq_count; /* Reference count */ int dq_count; /* Use count */
int dq_dup_ref; /* Number of duplicated refences */
/* fields after this point are cleared when invalidating */ /* fields after this point are cleared when invalidating */
struct super_block *dq_sb; /* superblock this applies to */ struct super_block *dq_sb; /* superblock this applies to */
unsigned int dq_id; /* ID this applies to (uid, gid) */ unsigned int dq_id; /* ID this applies to (uid, gid) */
loff_t dq_off; /* Offset of dquot on disk */
short dq_type; /* Type of quota */ short dq_type; /* Type of quota */
short dq_flags; /* See DQ_* */ short dq_flags; /* See DQ_* */
unsigned long dq_referenced; /* Number of times this dquot was unsigned long dq_referenced; /* Number of times this dquot was
referenced during its lifetime */ referenced during its lifetime */
struct dqblk dq_dqb; /* Diskquota usage */ struct mem_dqblk dq_dqb; /* Diskquota usage */
}; };
#define NODQUOT (struct dquot *)NULL #define NODQUOT (struct dquot *)NULL
/*
* Flags used for set_dqblk.
*/
#define SET_QUOTA 0x02
#define SET_USE 0x04
#define SET_QLIMIT 0x08
#define QUOTA_OK 0 #define QUOTA_OK 0
#define NO_QUOTA 1 #define NO_QUOTA 1
/* Operations which must be implemented by each quota format */
struct quota_format_ops {
int (*check_quota_file)(struct super_block *sb, int type); /* Detect whether file is in our format */
int (*read_file_info)(struct super_block *sb, int type); /* Read main info about file - called on quotaon() */
int (*write_file_info)(struct super_block *sb, int type); /* Write main info about file */
int (*free_file_info)(struct super_block *sb, int type); /* Called on quotaoff() */
int (*read_dqblk)(struct dquot *dquot); /* Read structure for one user */
int (*commit_dqblk)(struct dquot *dquot); /* Write (or delete) structure for one user */
};
/* Operations working with dquots */
struct dquot_operations {
void (*initialize) (struct inode *, int);
void (*drop) (struct inode *);
int (*alloc_space) (struct inode *, qsize_t, int);
int (*alloc_inode) (const struct inode *, unsigned long);
void (*free_space) (struct inode *, qsize_t);
void (*free_inode) (const struct inode *, unsigned long);
int (*transfer) (struct inode *, struct iattr *);
};
/* Operations handling requests from userspace */
struct quotactl_ops {
int (*quota_on)(struct super_block *, int, int, char *);
int (*quota_off)(struct super_block *, int);
int (*quota_sync)(struct super_block *, int);
int (*get_info)(struct super_block *, int, struct if_dqinfo *);
int (*set_info)(struct super_block *, int, struct if_dqinfo *);
int (*get_dqblk)(struct super_block *, int, qid_t, struct if_dqblk *);
int (*set_dqblk)(struct super_block *, int, qid_t, struct if_dqblk *);
int (*get_xstate)(struct super_block *, struct fs_quota_stat *);
int (*set_xstate)(struct super_block *, unsigned int, int);
int (*get_xquota)(struct super_block *, int, qid_t, struct fs_disk_quota *);
int (*set_xquota)(struct super_block *, int, qid_t, struct fs_disk_quota *);
};
struct quota_format_type {
int qf_fmt_id; /* Quota format id */
struct quota_format_ops *qf_ops; /* Operations of format */
struct module *qf_owner; /* Module implementing quota format */
struct quota_format_type *qf_next;
};
#define DQUOT_USR_ENABLED 0x01 /* User diskquotas enabled */
#define DQUOT_GRP_ENABLED 0x02 /* Group diskquotas enabled */
struct quota_info {
unsigned int flags; /* Flags for diskquotas on this device */
struct semaphore dqio_sem; /* lock device while I/O in progress */
struct semaphore dqoff_sem; /* serialize quota_off() and quota_on() on device */
struct file *files[MAXQUOTAS]; /* fp's to quotafiles */
struct mem_dqinfo info[MAXQUOTAS]; /* Information for each quota type */
struct quota_format_ops *ops[MAXQUOTAS]; /* Operations for each type */
};
/* Inline would be better but we need to dereference super_block which is not defined yet */
#define mark_dquot_dirty(dquot) do {\
dquot->dq_flags |= DQ_MOD;\
sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_flags |= DQF_ANY_DQUOT_DIRTY;\
} while (0)
#define dquot_dirty(dquot) ((dquot)->dq_flags & DQ_MOD)
static inline int is_enabled(struct quota_info *dqopt, int type)
{
switch (type) {
case USRQUOTA:
return dqopt->flags & DQUOT_USR_ENABLED;
case GRPQUOTA:
return dqopt->flags & DQUOT_GRP_ENABLED;
}
return 0;
}
#define sb_any_quota_enabled(sb) (is_enabled(sb_dqopt(sb), USRQUOTA) | is_enabled(sb_dqopt(sb), GRPQUOTA))
#define sb_has_quota_enabled(sb, type) (is_enabled(sb_dqopt(sb), type))
int register_quota_format(struct quota_format_type *fmt);
void unregister_quota_format(struct quota_format_type *fmt);
#else #else
# /* nodep */ include <sys/cdefs.h> # /* nodep */ include <sys/cdefs.h>
__BEGIN_DECLS __BEGIN_DECLS
long quotactl __P ((int, const char *, int, caddr_t)); long quotactl __P ((unsigned int, const char *, int, caddr_t));
__END_DECLS __END_DECLS
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
/*
* 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
#ifndef _LINUX_QUOTAIO_V1_H
#define _LINUX_QUOTAIO_V1_H
#include <linux/types.h>
/*
* The following constants define the amount of time given a user
* before the soft limits are treated as hard limits (usually resulting
* in an allocation failure). The timer is started when the user crosses
* their soft limit, it is reset when they go below their soft limit.
*/
#define MAX_IQ_TIME 604800 /* (7*24*60*60) 1 week */
#define MAX_DQ_TIME 604800 /* (7*24*60*60) 1 week */
/*
* The following structure defines the format of the disk quota file
* (as it appears on disk) - the file is an array of these structures
* indexed by user or group number.
*/
struct v1_disk_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; /* absolute limit on 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 inode use */
};
#define v1_dqoff(UID) ((loff_t)((UID) * sizeof (struct v1_disk_dqblk)))
#endif /* _LINUX_QUOTAIO_V1_H */
/*
* Definitions of structures for vfsv0 quota format
*/
#ifndef _LINUX_QUOTAIO_V2_H
#define _LINUX_QUOTAIO_V2_H
#include <linux/types.h>
#include <linux/quota.h>
/*
* Definitions of magics and versions of current quota files
*/
#define V2_INITQMAGICS {\
0xd9c01f11, /* USRQUOTA */\
0xd9c01927 /* GRPQUOTA */\
}
#define V2_INITQVERSIONS {\
0, /* USRQUOTA */\
0 /* GRPQUOTA */\
}
/*
* The following structure defines the format of the disk quota file
* (as it appears on disk) - the file is a radix tree whose leaves point
* to blocks of these structures.
*/
struct v2_disk_dqblk {
__u32 dqb_id; /* id this quota applies to */
__u32 dqb_ihardlimit; /* absolute limit on allocated inodes */
__u32 dqb_isoftlimit; /* preferred inode limit */
__u32 dqb_curinodes; /* current # allocated inodes */
__u32 dqb_bhardlimit; /* absolute limit on disk space (in QUOTABLOCK_SIZE) */
__u32 dqb_bsoftlimit; /* preferred limit on disk space (in QUOTABLOCK_SIZE) */
__u64 dqb_curspace; /* current space occupied (in bytes) */
__u64 dqb_btime; /* time limit for excessive disk use */
__u64 dqb_itime; /* time limit for excessive inode use */
};
/*
* Here are header structures as written on disk and their in-memory copies
*/
/* First generic header */
struct v2_disk_dqheader {
__u32 dqh_magic; /* Magic number identifying file */
__u32 dqh_version; /* File version */
};
/* Header with type and version specific information */
struct v2_disk_dqinfo {
__u32 dqi_bgrace; /* Time before block soft limit becomes hard limit */
__u32 dqi_igrace; /* Time before inode soft limit becomes hard limit */
__u32 dqi_flags; /* Flags for quotafile (DQF_*) */
__u32 dqi_blocks; /* Number of blocks in file */
__u32 dqi_free_blk; /* Number of first free block in the list */
__u32 dqi_free_entry; /* Number of block with at least one free entry */
};
/*
* Structure of header of block with quota structures. It is padded to 16 bytes so
* there will be space for exactly 18 quota-entries in a block
*/
struct v2_disk_dqdbheader {
__u32 dqdh_next_free; /* Number of next block with free entry */
__u32 dqdh_prev_free; /* Number of previous block with free entry */
__u16 dqdh_entries; /* Number of valid entries in block */
__u16 dqdh_pad1;
__u32 dqdh_pad2;
};
#define V2_DQINFOOFF sizeof(struct v2_disk_dqheader) /* Offset of info header in file */
#define V2_DQBLKSIZE_BITS 10
#define V2_DQBLKSIZE (1 << V2_DQBLKSIZE_BITS) /* Size of block with quota structures */
#define V2_DQTREEOFF 1 /* Offset of tree in file in blocks */
#define V2_DQTREEDEPTH 4 /* Depth of quota tree */
#define V2_DQSTRINBLK ((V2_DQBLKSIZE - sizeof(struct v2_disk_dqdbheader)) / sizeof(struct v2_disk_dqblk)) /* Number of entries in one blocks */
#endif /* _LINUX_QUOTAIO_V2_H */
...@@ -20,15 +20,15 @@ ...@@ -20,15 +20,15 @@
/* /*
* declaration of quota_function calls in kernel. * declaration of quota_function calls in kernel.
*/ */
extern void dquot_initialize(struct inode *inode, short type); extern void sync_dquots(struct super_block *sb, int type);
extern void dquot_initialize(struct inode *inode, int type);
extern void dquot_drop(struct inode *inode); extern void dquot_drop(struct inode *inode);
extern int quota_off(struct super_block *sb, short type);
extern int sync_dquots(struct super_block *sb, short type);
extern int dquot_alloc_block(struct inode *inode, unsigned long number, char prealloc); extern int dquot_alloc_space(struct inode *inode, qsize_t number, int prealloc);
extern int dquot_alloc_inode(const struct inode *inode, unsigned long number); extern int dquot_alloc_inode(const struct inode *inode, unsigned long number);
extern void dquot_free_block(struct inode *inode, unsigned long number); extern void dquot_free_space(struct inode *inode, qsize_t number);
extern void dquot_free_inode(const struct inode *inode, unsigned long number); extern void dquot_free_inode(const struct inode *inode, unsigned long number);
extern int dquot_transfer(struct inode *inode, struct iattr *iattr); extern int dquot_transfer(struct inode *inode, struct iattr *iattr);
...@@ -36,7 +36,11 @@ extern int dquot_transfer(struct inode *inode, struct iattr *iattr); ...@@ -36,7 +36,11 @@ extern int dquot_transfer(struct inode *inode, struct iattr *iattr);
/* /*
* Operations supported for diskquotas. * Operations supported for diskquotas.
*/ */
#define sb_any_quota_enabled(sb) ((sb)->s_dquot.flags & (DQUOT_USR_ENABLED | DQUOT_GRP_ENABLED)) extern struct dquot_operations dquot_operations;
extern struct quotactl_ops vfs_quotactl_ops;
#define sb_dquot_ops (&dquot_operations)
#define sb_quotactl_ops (&vfs_quotactl_ops)
static __inline__ void DQUOT_INIT(struct inode *inode) static __inline__ void DQUOT_INIT(struct inode *inode)
{ {
...@@ -59,50 +63,50 @@ static __inline__ void DQUOT_DROP(struct inode *inode) ...@@ -59,50 +63,50 @@ static __inline__ void DQUOT_DROP(struct inode *inode)
unlock_kernel(); unlock_kernel();
} }
static __inline__ int DQUOT_PREALLOC_BLOCK_NODIRTY(struct inode *inode, int nr) static __inline__ int DQUOT_PREALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
{ {
lock_kernel(); lock_kernel();
if (sb_any_quota_enabled(inode->i_sb)) { if (sb_any_quota_enabled(inode->i_sb)) {
/* Number of used blocks is updated in alloc_block() */ /* Used space is updated in alloc_space() */
if (inode->i_sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize), 1) == NO_QUOTA) { if (inode->i_sb->dq_op->alloc_space(inode, nr, 1) == NO_QUOTA) {
unlock_kernel(); unlock_kernel();
return 1; return 1;
} }
} }
else else
inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9); inode_add_bytes(inode, nr);
unlock_kernel(); unlock_kernel();
return 0; return 0;
} }
static __inline__ int DQUOT_PREALLOC_BLOCK(struct inode *inode, int nr) static __inline__ int DQUOT_PREALLOC_SPACE(struct inode *inode, qsize_t nr)
{ {
int ret; int ret;
if (!(ret = DQUOT_PREALLOC_BLOCK_NODIRTY(inode, nr))) if (!(ret = DQUOT_PREALLOC_SPACE_NODIRTY(inode, nr)))
mark_inode_dirty(inode); mark_inode_dirty(inode);
return ret; return ret;
} }
static __inline__ int DQUOT_ALLOC_BLOCK_NODIRTY(struct inode *inode, int nr) static __inline__ int DQUOT_ALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
{ {
lock_kernel(); lock_kernel();
if (sb_any_quota_enabled(inode->i_sb)) { if (sb_any_quota_enabled(inode->i_sb)) {
/* Number of used blocks is updated in alloc_block() */ /* Used space is updated in alloc_space() */
if (inode->i_sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize), 0) == NO_QUOTA) { if (inode->i_sb->dq_op->alloc_space(inode, nr, 0) == NO_QUOTA) {
unlock_kernel(); unlock_kernel();
return 1; return 1;
} }
} }
else else
inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9); inode_add_bytes(inode, nr);
unlock_kernel(); unlock_kernel();
return 0; return 0;
} }
static __inline__ int DQUOT_ALLOC_BLOCK(struct inode *inode, int nr) static __inline__ int DQUOT_ALLOC_SPACE(struct inode *inode, qsize_t nr)
{ {
int ret; int ret;
if (!(ret = DQUOT_ALLOC_BLOCK_NODIRTY(inode, nr))) if (!(ret = DQUOT_ALLOC_SPACE_NODIRTY(inode, nr)))
mark_inode_dirty(inode); mark_inode_dirty(inode);
return ret; return ret;
} }
...@@ -121,19 +125,19 @@ static __inline__ int DQUOT_ALLOC_INODE(struct inode *inode) ...@@ -121,19 +125,19 @@ static __inline__ int DQUOT_ALLOC_INODE(struct inode *inode)
return 0; return 0;
} }
static __inline__ void DQUOT_FREE_BLOCK_NODIRTY(struct inode *inode, int nr) static __inline__ void DQUOT_FREE_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
{ {
lock_kernel(); lock_kernel();
if (sb_any_quota_enabled(inode->i_sb)) if (sb_any_quota_enabled(inode->i_sb))
inode->i_sb->dq_op->free_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize)); inode->i_sb->dq_op->free_space(inode, nr);
else else
inode->i_blocks -= nr << (inode->i_sb->s_blocksize_bits - 9); inode_sub_bytes(inode, nr);
unlock_kernel(); unlock_kernel();
} }
static __inline__ void DQUOT_FREE_BLOCK(struct inode *inode, int nr) static __inline__ void DQUOT_FREE_SPACE(struct inode *inode, qsize_t nr)
{ {
DQUOT_FREE_BLOCK_NODIRTY(inode, nr); DQUOT_FREE_SPACE_NODIRTY(inode, nr);
mark_inode_dirty(inode); mark_inode_dirty(inode);
} }
...@@ -160,13 +164,25 @@ static __inline__ int DQUOT_TRANSFER(struct inode *inode, struct iattr *iattr) ...@@ -160,13 +164,25 @@ static __inline__ int DQUOT_TRANSFER(struct inode *inode, struct iattr *iattr)
} }
#define DQUOT_SYNC(sb) sync_dquots(sb, -1) #define DQUOT_SYNC(sb) sync_dquots(sb, -1)
#define DQUOT_OFF(sb) quota_off(sb, -1)
static __inline__ int DQUOT_OFF(struct super_block *sb)
{
int ret = -ENOSYS;
lock_kernel();
if (sb->s_qcop && sb->s_qcop->quota_off)
ret = sb->s_qcop->quota_off(sb, -1);
unlock_kernel();
return ret;
}
#else #else
/* /*
* NO-OP when quota not configured. * NO-OP when quota not configured.
*/ */
#define sb_dquot_ops (NULL)
#define sb_quotactl_ops (NULL)
#define DQUOT_INIT(inode) do { } while(0) #define DQUOT_INIT(inode) do { } while(0)
#define DQUOT_DROP(inode) do { } while(0) #define DQUOT_DROP(inode) do { } while(0)
#define DQUOT_ALLOC_INODE(inode) (0) #define DQUOT_ALLOC_INODE(inode) (0)
...@@ -174,48 +190,56 @@ static __inline__ int DQUOT_TRANSFER(struct inode *inode, struct iattr *iattr) ...@@ -174,48 +190,56 @@ static __inline__ int DQUOT_TRANSFER(struct inode *inode, struct iattr *iattr)
#define DQUOT_SYNC(sb) do { } while(0) #define DQUOT_SYNC(sb) do { } while(0)
#define DQUOT_OFF(sb) do { } while(0) #define DQUOT_OFF(sb) do { } while(0)
#define DQUOT_TRANSFER(inode, iattr) (0) #define DQUOT_TRANSFER(inode, iattr) (0)
extern __inline__ int DQUOT_PREALLOC_BLOCK_NODIRTY(struct inode *inode, int nr) extern __inline__ int DQUOT_PREALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
{ {
lock_kernel(); lock_kernel();
inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9); inode_add_bytes(inode, nr);
unlock_kernel(); unlock_kernel();
return 0; return 0;
} }
extern __inline__ int DQUOT_PREALLOC_BLOCK(struct inode *inode, int nr) extern __inline__ int DQUOT_PREALLOC_SPACE(struct inode *inode, qsize_t nr)
{ {
DQUOT_PREALLOC_BLOCK_NODIRTY(inode, nr); DQUOT_PREALLOC_SPACE_NODIRTY(inode, nr);
mark_inode_dirty(inode); mark_inode_dirty(inode);
return 0; return 0;
} }
extern __inline__ int DQUOT_ALLOC_BLOCK_NODIRTY(struct inode *inode, int nr) extern __inline__ int DQUOT_ALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
{ {
lock_kernel(); lock_kernel();
inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9); inode_add_bytes(inode, nr);
unlock_kernel(); unlock_kernel();
return 0; return 0;
} }
extern __inline__ int DQUOT_ALLOC_BLOCK(struct inode *inode, int nr) extern __inline__ int DQUOT_ALLOC_SPACE(struct inode *inode, qsize_t nr)
{ {
DQUOT_ALLOC_BLOCK_NODIRTY(inode, nr); DQUOT_ALLOC_SPACE_NODIRTY(inode, nr);
mark_inode_dirty(inode); mark_inode_dirty(inode);
return 0; return 0;
} }
extern __inline__ void DQUOT_FREE_BLOCK_NODIRTY(struct inode *inode, int nr) extern __inline__ void DQUOT_FREE_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
{ {
lock_kernel(); lock_kernel();
inode->i_blocks -= nr << (inode->i_sb->s_blocksize_bits - 9); inode_sub_bytes(inode, nr);
unlock_kernel(); unlock_kernel();
} }
extern __inline__ void DQUOT_FREE_BLOCK(struct inode *inode, int nr) extern __inline__ void DQUOT_FREE_SPACE(struct inode *inode, qsize_t nr)
{ {
DQUOT_FREE_BLOCK_NODIRTY(inode, nr); DQUOT_FREE_SPACE_NODIRTY(inode, nr);
mark_inode_dirty(inode); mark_inode_dirty(inode);
} }
#endif /* CONFIG_QUOTA */ #endif /* CONFIG_QUOTA */
#define DQUOT_PREALLOC_BLOCK_NODIRTY(inode, nr) DQUOT_PREALLOC_SPACE_NODIRTY(inode, ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits)
#define DQUOT_PREALLOC_BLOCK(inode, nr) DQUOT_PREALLOC_SPACE(inode, ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits)
#define DQUOT_ALLOC_BLOCK_NODIRTY(inode, nr) DQUOT_ALLOC_SPACE_NODIRTY(inode, ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits)
#define DQUOT_ALLOC_BLOCK(inode, nr) DQUOT_ALLOC_SPACE(inode, ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits)
#define DQUOT_FREE_BLOCK_NODIRTY(inode, nr) DQUOT_FREE_SPACE_NODIRTY(inode, ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits)
#define DQUOT_FREE_BLOCK(inode, nr) DQUOT_FREE_SPACE(inode, ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits)
#endif /* _LINUX_QUOTAOPS_ */ #endif /* _LINUX_QUOTAOPS_ */
/*
* Copyright (c) 1995-2001 Silicon Graphics, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2.1 of the GNU Lesser General Public License
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307,
* USA.
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
*/
#ifndef _LINUX_XQM_H
#define _LINUX_XQM_H
#include <linux/types.h>
/*
* Disk quota - quotactl(2) commands for the XFS Quota Manager (XQM).
*/
#define XQM_CMD(x) (('X'<<8)+(x)) /* note: forms first QCMD argument */
#define Q_XQUOTAON XQM_CMD(0x1) /* enable accounting/enforcement */
#define Q_XQUOTAOFF XQM_CMD(0x2) /* disable accounting/enforcement */
#define Q_XGETQUOTA XQM_CMD(0x3) /* get disk limits and usage */
#define Q_XSETQLIM XQM_CMD(0x4) /* set disk limits */
#define Q_XGETQSTAT XQM_CMD(0x5) /* get quota subsystem status */
#define Q_XQUOTARM XQM_CMD(0x6) /* free disk space used by dquots */
/*
* fs_disk_quota structure:
*
* This contains the current quota information regarding a user/proj/group.
* It is 64-bit aligned, and all the blk units are in BBs (Basic Blocks) of
* 512 bytes.
*/
#define FS_DQUOT_VERSION 1 /* fs_disk_quota.d_version */
typedef struct fs_disk_quota {
__s8 d_version; /* version of this structure */
__s8 d_flags; /* XFS_{USER,PROJ,GROUP}_QUOTA */
__u16 d_fieldmask; /* field specifier */
__u32 d_id; /* user, project, or group ID */
__u64 d_blk_hardlimit;/* absolute limit on disk blks */
__u64 d_blk_softlimit;/* preferred limit on disk blks */
__u64 d_ino_hardlimit;/* maximum # allocated inodes */
__u64 d_ino_softlimit;/* preferred inode limit */
__u64 d_bcount; /* # disk blocks owned by the user */
__u64 d_icount; /* # inodes owned by the user */
__s32 d_itimer; /* zero if within inode limits */
/* if not, we refuse service */
__s32 d_btimer; /* similar to above; for disk blocks */
__u16 d_iwarns; /* # warnings issued wrt num inodes */
__u16 d_bwarns; /* # warnings issued wrt disk blocks */
__s32 d_padding2; /* padding2 - for future use */
__u64 d_rtb_hardlimit;/* absolute limit on realtime blks */
__u64 d_rtb_softlimit;/* preferred limit on RT disk blks */
__u64 d_rtbcount; /* # realtime blocks owned */
__s32 d_rtbtimer; /* similar to above; for RT disk blks */
__u16 d_rtbwarns; /* # warnings issued wrt RT disk blks */
__s16 d_padding3; /* padding3 - for future use */
char d_padding4[8]; /* yet more padding */
} fs_disk_quota_t;
/*
* These fields are sent to Q_XSETQLIM to specify fields that need to change.
*/
#define FS_DQ_ISOFT (1<<0)
#define FS_DQ_IHARD (1<<1)
#define FS_DQ_BSOFT (1<<2)
#define FS_DQ_BHARD (1<<3)
#define FS_DQ_RTBSOFT (1<<4)
#define FS_DQ_RTBHARD (1<<5)
#define FS_DQ_LIMIT_MASK (FS_DQ_ISOFT | FS_DQ_IHARD | FS_DQ_BSOFT | \
FS_DQ_BHARD | FS_DQ_RTBSOFT | FS_DQ_RTBHARD)
/*
* These timers can only be set in super user's dquot. For others, timers are
* automatically started and stopped. Superusers timer values set the limits
* for the rest. In case these values are zero, the DQ_{F,B}TIMELIMIT values
* defined below are used.
* These values also apply only to the d_fieldmask field for Q_XSETQLIM.
*/
#define FS_DQ_BTIMER (1<<6)
#define FS_DQ_ITIMER (1<<7)
#define FS_DQ_RTBTIMER (1<<8)
#define FS_DQ_TIMER_MASK (FS_DQ_BTIMER | FS_DQ_ITIMER | FS_DQ_RTBTIMER)
/*
* The following constants define the default amount of time given a user
* before the soft limits are treated as hard limits (usually resulting
* in an allocation failure). These may be modified by the quotactl(2)
* system call with the Q_XSETQLIM command.
*/
#define DQ_FTIMELIMIT (7 * 24*60*60) /* 1 week */
#define DQ_BTIMELIMIT (7 * 24*60*60) /* 1 week */
/*
* Various flags related to quotactl(2). Only relevant to XFS filesystems.
*/
#define XFS_QUOTA_UDQ_ACCT (1<<0) /* user quota accounting */
#define XFS_QUOTA_UDQ_ENFD (1<<1) /* user quota limits enforcement */
#define XFS_QUOTA_GDQ_ACCT (1<<2) /* group quota accounting */
#define XFS_QUOTA_GDQ_ENFD (1<<3) /* group quota limits enforcement */
#define XFS_USER_QUOTA (1<<0) /* user quota type */
#define XFS_PROJ_QUOTA (1<<1) /* (IRIX) project quota type */
#define XFS_GROUP_QUOTA (1<<2) /* group quota type */
/*
* fs_quota_stat is the struct returned in Q_XGETQSTAT for a given file system.
* Provides a centralized way to get meta infomation about the quota subsystem.
* eg. space taken up for user and group quotas, number of dquots currently
* incore.
*/
#define FS_QSTAT_VERSION 1 /* fs_quota_stat.qs_version */
/*
* Some basic infomation about 'quota files'.
*/
typedef struct fs_qfilestat {
__u64 qfs_ino; /* inode number */
__u64 qfs_nblks; /* number of BBs 512-byte-blks */
__u32 qfs_nextents; /* number of extents */
} fs_qfilestat_t;
typedef struct fs_quota_stat {
__s8 qs_version; /* version number for future changes */
__u16 qs_flags; /* XFS_QUOTA_{U,P,G}DQ_{ACCT,ENFD} */
__s8 qs_pad; /* unused */
fs_qfilestat_t qs_uquota; /* user quota storage information */
fs_qfilestat_t qs_gquota; /* group quota storage information */
__u32 qs_incoredqs; /* number of dquots incore */
__s32 qs_btimelimit; /* limit for blks timer */
__s32 qs_itimelimit; /* limit for inodes timer */
__s32 qs_rtbtimelimit;/* limit for rt blks timer */
__u16 qs_bwarnlimit; /* limit for num warnings */
__u16 qs_iwarnlimit; /* limit for num warnings */
} fs_quota_stat_t;
#endif /* _LINUX_XQM_H */
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