diff -urNp x-ref/Documentation/Configure.help x/Documentation/Configure.help
--- x-ref/Documentation/Configure.help 2002-12-13 03:58:42.000000000 +0100
+++ x/Documentation/Configure.help 2002-12-13 03:58:50.000000000 +0100
@@ -12708,12 +12708,44 @@ CONFIG_VIOCD
Quota support
CONFIG_QUOTA
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
- ext2 file system. You need additional software in order to use quota
- support; for details, read the Quota mini-HOWTO, available from
+ usage (also called disk quotas). Currently, it works for the
+ ext2, ext3, and reiserfs file system. You need additional software
+ in order to use quota support (you can download sources from
+ ). For further details, read
+ the Quota mini-HOWTO, available from
. Probably the quota
support is only useful for multi user systems. If unsure, say N.
+Old quota format support
+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.
+
+VFS v0 quota format support
+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.
+
+Compatible quota interfaces
+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.
+
+Original quota interface
+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).
+
+VFS v0 quota interface
+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.
+
Memory Technology Device (MTD) support
CONFIG_MTD
Memory Technology Devices are flash, RAM and similar chips, often
diff -urNp x-ref/arch/ia64/ia32/sys_ia32.c x/arch/ia64/ia32/sys_ia32.c
--- x-ref/arch/ia64/ia32/sys_ia32.c 2002-08-09 14:52:06.000000000 +0200
+++ x/arch/ia64/ia32/sys_ia32.c 2002-12-13 03:58:50.000000000 +0100
@@ -3693,7 +3693,11 @@ getname32 (const char *filename)
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_bsoftlimit;
__u32 dqb_curblocks;
@@ -3703,49 +3707,82 @@ struct dqblk32 {
__kernel_time_t32 dqb_btime;
__kernel_time_t32 dqb_itime;
};
+typedef struct v1c_mem_dqblk comp_dqblk_t;
-asmlinkage long
-sys32_quotactl (int cmd, unsigned int special, int id, struct dqblk32 *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;
+
+#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;
+ long err;
+ comp_dqblk_t d;
mm_segment_t old_fs;
- struct dqblk d;
char *spec;
- long err;
-
+
switch (cmds) {
- case Q_GETQUOTA:
- break;
- case Q_SETQUOTA:
- case Q_SETUSE:
- case Q_SETQLIM:
- if (copy_from_user (&d, addr, sizeof(struct dqblk32)))
- return -EFAULT;
- d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime;
- d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime;
- break;
- default:
- return sys_quotactl(cmd, (void *) A(special), id, (caddr_t) addr);
+ case Q_COMP_GETQUOTA:
+ break;
+ case Q_COMP_SETQUOTA:
+ case Q_COMP_SETUSE:
+ case Q_COMP_SETQLIM:
+ if (copy_from_user(&d, (struct user_dqblk32 *)addr,
+ sizeof (struct user_dqblk32)))
+ return -EFAULT;
+ d.dqb_itime = ((struct user_dqblk32 *)&d)->dqb_itime;
+ d.dqb_btime = ((struct user_dqblk32 *)&d)->dqb_btime;
+ break;
+ default:
+ return sys_quotactl(cmd, special, id, (__kernel_caddr_t)addr);
}
- spec = getname32((void *) A(special));
+ spec = getname (special);
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;
- old_fs = get_fs ();
- 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) {
+ if (cmds == Q_COMP_GETQUOTA) {
__kernel_time_t b = d.dqb_btime, i = d.dqb_itime;
- ((struct dqblk32 *)&d)->dqb_itime = i;
- ((struct dqblk32 *)&d)->dqb_btime = b;
- if (copy_to_user(addr, &d, sizeof(struct dqblk32)))
+ ((struct user_dqblk32 *)&d)->dqb_itime = i;
+ ((struct user_dqblk32 *)&d)->dqb_btime = b;
+ if (copy_to_user ((struct user_dqblk32 *)addr, &d,
+ sizeof (struct user_dqblk32)))
return -EFAULT;
}
- return 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
sys32_sched_rr_get_interval (pid_t pid, struct timespec32 *interval)
diff -urNp x-ref/arch/s390x/kernel/linux32.c x/arch/s390x/kernel/linux32.c
--- x-ref/arch/s390x/kernel/linux32.c 2002-08-09 14:52:08.000000000 +0200
+++ x/arch/s390x/kernel/linux32.c 2002-12-13 03:58:50.000000000 +0100
@@ -912,64 +912,97 @@ asmlinkage long sys32_fcntl64(unsigned i
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);
-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 err;
- struct dqblk d;
+ comp_dqblk_t d;
mm_segment_t old_fs;
char *spec;
switch (cmds) {
- case Q_GETQUOTA:
- break;
- case Q_SETQUOTA:
- case Q_SETUSE:
- case Q_SETQLIM:
- if (copy_from_user (&d, (struct dqblk32 *)addr,
- sizeof (struct dqblk32)))
- return -EFAULT;
- d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime;
- d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime;
- break;
+ case Q_COMP_GETQUOTA:
+ break;
+ case Q_COMP_SETQUOTA:
+ case Q_COMP_SETUSE:
+ case Q_COMP_SETQLIM:
+ if (copy_from_user(&d, (struct user_dqblk32 *)addr,
+ sizeof (struct user_dqblk32)))
+ return -EFAULT;
+ d.dqb_itime = ((struct user_dqblk32 *)&d)->dqb_itime;
+ d.dqb_btime = ((struct user_dqblk32 *)&d)->dqb_btime;
+ break;
default:
- return sys_quotactl(cmd, special,
- id, (caddr_t)addr);
+ return sys_quotactl(cmd, special, id, (__kernel_caddr_t)addr);
}
spec = getname (special);
err = PTR_ERR(spec);
if (IS_ERR(spec)) return err;
- old_fs = get_fs ();
+ old_fs = get_fs();
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);
putname (spec);
if (err)
return err;
- if (cmds == Q_GETQUOTA) {
+ if (cmds == Q_COMP_GETQUOTA) {
__kernel_time_t b = d.dqb_btime, i = d.dqb_itime;
- ((struct dqblk32 *)&d)->dqb_itime = i;
- ((struct dqblk32 *)&d)->dqb_btime = b;
- if (copy_to_user ((struct dqblk32 *)addr, &d,
- sizeof (struct dqblk32)))
+ ((struct user_dqblk32 *)&d)->dqb_itime = i;
+ ((struct user_dqblk32 *)&d)->dqb_btime = b;
+ if (copy_to_user ((struct user_dqblk32 *)addr, &d,
+ sizeof (struct user_dqblk32)))
return -EFAULT;
}
return 0;
}
+#else
+/* No conversion needed for new interface */
+asmlinkage int sys32_quotactl(int cmd, const char *special, int id, caddr_t addr)
+{
+ return sys_quotactl(cmd, special, id, addr);
+}
+#endif
+
static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf)
{
int err;
diff -urNp x-ref/arch/sparc64/kernel/sys_sparc32.c x/arch/sparc64/kernel/sys_sparc32.c
--- x-ref/arch/sparc64/kernel/sys_sparc32.c 2002-12-11 02:12:20.000000000 +0100
+++ x/arch/sparc64/kernel/sys_sparc32.c 2002-12-13 03:58:50.000000000 +0100
@@ -888,62 +888,97 @@ asmlinkage long sys32_fcntl64(unsigned i
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);
-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 err;
- struct dqblk d;
+ comp_dqblk_t d;
mm_segment_t old_fs;
char *spec;
switch (cmds) {
- case Q_GETQUOTA:
- break;
- case Q_SETQUOTA:
- case Q_SETUSE:
- case Q_SETQLIM:
- if (copy_from_user (&d, (struct dqblk32 *)addr,
- sizeof (struct dqblk32)))
- return -EFAULT;
- d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime;
- d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime;
- break;
+ case Q_COMP_GETQUOTA:
+ break;
+ case Q_COMP_SETQUOTA:
+ case Q_COMP_SETUSE:
+ case Q_COMP_SETQLIM:
+ if (copy_from_user(&d, (struct user_dqblk32 *)addr,
+ sizeof (struct user_dqblk32)))
+ return -EFAULT;
+ d.dqb_itime = ((struct user_dqblk32 *)&d)->dqb_itime;
+ d.dqb_btime = ((struct user_dqblk32 *)&d)->dqb_btime;
+ break;
default:
- return sys_quotactl(cmd, special,
- id, (caddr_t)addr);
+ return sys_quotactl(cmd, special, id, (__kernel_caddr_t)addr);
}
spec = getname (special);
err = PTR_ERR(spec);
if (IS_ERR(spec)) return err;
- old_fs = get_fs ();
+ old_fs = get_fs();
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);
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;
- ((struct dqblk32 *)&d)->dqb_itime = i;
- ((struct dqblk32 *)&d)->dqb_btime = b;
- if (copy_to_user ((struct dqblk32 *)addr, &d,
- sizeof (struct dqblk32)))
+ ((struct user_dqblk32 *)&d)->dqb_itime = i;
+ ((struct user_dqblk32 *)&d)->dqb_btime = b;
+ if (copy_to_user ((struct user_dqblk32 *)addr, &d,
+ sizeof (struct user_dqblk32)))
return -EFAULT;
}
- return 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)
{
int err;
diff -urNp x-ref/arch/sparc64/kernel/systbls.S x/arch/sparc64/kernel/systbls.S
--- x-ref/arch/sparc64/kernel/systbls.S 2002-08-09 14:52:09.000000000 +0200
+++ x/arch/sparc64/kernel/systbls.S 2002-12-13 03:58:50.000000000 +0100
@@ -194,7 +194,7 @@ sunos_sys_table:
.word sunos_getdirentries, sys32_statfs, sys32_fstatfs
.word sys_oldumount, sunos_nosys, sunos_nosys
.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_nosys, sunos_shmsys, sunos_audit
.word sunos_nosys, sunos_getdents, sys_setsid
diff -urNp x-ref/fs/Config.in x/fs/Config.in
--- x-ref/fs/Config.in 2002-12-13 03:58:42.000000000 +0100
+++ x/fs/Config.in 2002-12-13 03:58:50.000000000 +0100
@@ -5,6 +5,14 @@ mainmenu_option next_comment
comment 'File systems'
bool 'Quota support' CONFIG_QUOTA
+dep_tristate ' Old quota format support' CONFIG_QFMT_V1 $CONFIG_QUOTA
+dep_tristate ' VFS v0 quota format support' CONFIG_QFMT_V2 $CONFIG_QUOTA
+dep_mbool ' Compatible quota interfaces' CONFIG_QIFACE_COMPAT $CONFIG_QUOTA
+if [ "$CONFIG_QUOTA" = "y" -a "$CONFIG_QIFACE_COMPAT" = "y" ]; then
+ choice ' Compatible quota interfaces' \
+ "Original CONFIG_QIFACE_V1 \
+ VFSv0 CONFIG_QIFACE_V2" Original
+fi
tristate 'Kernel automounter support' CONFIG_AUTOFS_FS
tristate 'Kernel automounter version 4 support (also supports v3)' CONFIG_AUTOFS4_FS
diff -urNp x-ref/fs/Makefile x/fs/Makefile
--- x-ref/fs/Makefile 2002-11-29 02:23:15.000000000 +0100
+++ x/fs/Makefile 2002-12-13 03:58:50.000000000 +0100
@@ -7,20 +7,18 @@
O_TARGET := fs.o
-export-objs := filesystems.o open.o dcache.o buffer.o
+export-objs := filesystems.o open.o dcache.o buffer.o dquot.o
mod-subdirs := nls
obj-y := open.o read_write.o devices.o file_table.o buffer.o \
super.o block_dev.o char_dev.o stat.o exec.o pipe.o namei.o \
fcntl.o ioctl.o readdir.o select.o fifo.o locks.o \
dcache.o inode.o attr.o bad_inode.o file.o iobuf.o dnotify.o \
- filesystems.o namespace.o seq_file.o xattr.o
+ filesystems.o namespace.o seq_file.o xattr.o quota.o
-ifeq ($(CONFIG_QUOTA),y)
-obj-y += dquot.o
-else
-obj-y += noquot.o
-endif
+obj-$(CONFIG_QUOTA) += dquot.o
+obj-$(CONFIG_QFMT_V1) += quota_v1.o
+obj-$(CONFIG_QFMT_V2) += quota_v2.o
subdir-$(CONFIG_PROC_FS) += proc
subdir-y += partitions
diff -urNp x-ref/fs/buffer.c x/fs/buffer.c
--- x-ref/fs/buffer.c 2002-12-13 03:58:46.000000000 +0100
+++ x/fs/buffer.c 2002-12-13 03:59:13.000000000 +0100
@@ -364,7 +364,7 @@ int fsync_super(struct super_block *sb)
lock_kernel();
sync_inodes_sb(sb);
- DQUOT_SYNC(dev);
+ DQUOT_SYNC_SB(sb);
lock_super(sb);
if (sb->s_dirt && sb->s_op && sb->s_op->write_super)
sb->s_op->write_super(sb);
@@ -388,7 +388,7 @@ int fsync_dev(kdev_t dev)
lock_kernel();
sync_inodes(dev);
- DQUOT_SYNC(dev);
+ DQUOT_SYNC_DEV(dev);
sync_supers(dev, 1);
unlock_kernel();
diff -urNp x-ref/fs/dquot.c x/fs/dquot.c
--- x-ref/fs/dquot.c 2002-08-09 14:52:20.000000000 +0200
+++ x/fs/dquot.c 2002-12-13 03:58:50.000000000 +0100
@@ -35,7 +35,7 @@
* Jan Kara, , sponsored by SuSE CR, 10-11/99
*
* 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
* 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
@@ -45,6 +45,10 @@
* Added dynamic quota structure allocation
* Jan Kara 12/2000
*
+ * Rewritten quota interface. Implemented new quota format and
+ * formats registering.
+ * Jan Kara, , 2001,2002
+ *
* (C) Copyright 1994 - 1997 Marco van Wieringen
*/
@@ -59,20 +63,53 @@
#include
#include
#include
+#include
#include
#include
+#include
+#include
#include
-#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;
-static inline struct quota_mount_options *sb_dqopt(struct super_block *sb)
+ lock_kernel();
+ for (actqf = "a_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)
{
- return &sb->s_dquot;
+ 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 void put_quota_format(struct quota_format_type *fmt)
+{
+ if (fmt->qf_owner)
+ __MOD_DEC_USE_COUNT(fmt->qf_owner);
}
/*
@@ -85,11 +122,11 @@ static inline struct quota_mount_options
* list is used for the sync and invalidate operations, which must look
* at every dquot.
*
- * Unused dquots (dq_count == 0) are added to the free_dquots list when
- * freed, and this list is searched whenever we need an available dquot.
- * Dquots are removed from the list as soon as they are used again, and
- * nr_free_dquots gives the number of dquots on the list. When dquot is
- * invalidated it's completely released from memory.
+ * Unused dquots (dq_count == 0) are added to the free_dquots list when freed,
+ * and this list is searched whenever we need an available dquot. Dquots are
+ * removed from the list as soon as they are used again, and
+ * dqstats.free_dquots gives the number of dquots on the list. When
+ * dquot is invalidated it's completely released from memory.
*
* Dquots with a specific identity (device, type and id) are placed on
* one of the dquot_hash[] hash chains. The provides an efficient search
@@ -115,35 +152,39 @@ static LIST_HEAD(inuse_list);
static LIST_HEAD(free_dquots);
static struct list_head dquot_hash[NR_DQHASH];
-static struct dqstats dqstats;
+struct dqstats dqstats;
static void dqput(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) {
- case USRQUOTA:
- return((dqopt->flags & DQUOT_USR_ENABLED) != 0);
- case GRPQUOTA:
- return((dqopt->flags & DQUOT_GRP_ENABLED) != 0);
- }
- return(0);
+ dquot->dq_count++;
+}
+
+static inline void put_dquot_ref(struct dquot *dquot)
+{
+ dquot->dq_count--;
+}
+
+static inline void get_dquot_dup_ref(struct dquot *dquot)
+{
+ dquot->dq_dup_ref++;
}
-static inline char sb_has_quota_enabled(struct super_block *sb, short type)
+static inline void put_dquot_dup_ref(struct dquot *dquot)
{
- return is_enabled(sb_dqopt(sb), type);
+ dquot->dq_dup_ref--;
}
-static inline int const hashfn(kdev_t dev, unsigned int id, short type)
+static inline int const hashfn(struct super_block *sb, unsigned int id, int type)
{
- return((HASHDEV(dev) ^ id) * (MAXQUOTAS - type)) % NR_DQHASH;
+ return((((unsigned long)sb>>L1_CACHE_SHIFT) ^ id) * (MAXQUOTAS - type)) % NR_DQHASH;
}
static inline void insert_dquot_hash(struct dquot *dquot)
{
- struct list_head *head = dquot_hash + hashfn(dquot->dq_dev, dquot->dq_id, dquot->dq_type);
+ struct list_head *head = dquot_hash + hashfn(dquot->dq_sb, dquot->dq_id, dquot->dq_type);
list_add(&dquot->dq_hash, head);
}
@@ -153,14 +194,14 @@ static inline void remove_dquot_hash(str
INIT_LIST_HEAD(&dquot->dq_hash);
}
-static inline struct dquot *find_dquot(unsigned int hashent, kdev_t dev, 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 dquot *dquot;
for (head = dquot_hash[hashent].next; head != dquot_hash+hashent; head = head->next) {
dquot = list_entry(head, struct dquot, dq_hash);
- if (dquot->dq_dev == dev && dquot->dq_id == id && dquot->dq_type == type)
+ if (dquot->dq_sb == sb && dquot->dq_id == id && dquot->dq_type == type)
return dquot;
}
return NODQUOT;
@@ -170,14 +211,14 @@ static inline struct dquot *find_dquot(u
static inline void put_dquot_head(struct dquot *dquot)
{
list_add(&dquot->dq_free, &free_dquots);
- nr_free_dquots++;
+ dqstats.free_dquots++;
}
/* Add a dquot to the tail of the free list */
static inline void put_dquot_last(struct dquot *dquot)
{
list_add(&dquot->dq_free, free_dquots.prev);
- nr_free_dquots++;
+ dqstats.free_dquots++;
}
/* Move dquot to the head of free list (it must be already on it) */
@@ -193,7 +234,7 @@ static inline void remove_free_dquot(str
return;
list_del(&dquot->dq_free);
INIT_LIST_HEAD(&dquot->dq_free);
- nr_free_dquots--;
+ dqstats.free_dquots--;
}
static inline void put_inuse(struct dquot *dquot)
@@ -201,12 +242,12 @@ static inline void put_inuse(struct dquo
/* We add to the back of inuse list so we don't have to restart
* when traversing this list and we block */
list_add(&dquot->dq_inuse, inuse_list.prev);
- nr_dquots++;
+ dqstats.allocated_dquots++;
}
static inline void remove_inuse(struct dquot *dquot)
{
- nr_dquots--;
+ dqstats.allocated_dquots--;
list_del(&dquot->dq_inuse);
}
@@ -243,6 +284,7 @@ static inline void unlock_dquot(struct d
wake_up(&dquot->dq_wait_lock);
}
+/* Wait for dquot to be unused */
static void __wait_dquot_unused(struct dquot *dquot)
{
DECLARE_WAITQUEUE(wait, current);
@@ -258,85 +300,56 @@ repeat:
current->state = TASK_RUNNING;
}
-/*
- * We don't have to be afraid of deadlocks as we never have quotas on quota files...
- */
-static void write_dquot(struct dquot *dquot)
+/* Wait for all duplicated dquot references to be dropped */
+static void __wait_dup_drop(struct dquot *dquot)
{
- 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);
+ DECLARE_WAITQUEUE(wait, current);
- /*
- * Note: clear the DQ_MOD flag unconditionally,
- * so we don't loop forever on failure.
- */
- memcpy(&dqbuf, &dquot->dq_dqb, sizeof(struct dqblk));
- dquot->dq_flags &= ~DQ_MOD;
- ret = 0;
- if (filp)
- ret = filp->f_op->write(filp, (char *)&dqbuf,
- sizeof(struct dqblk), &offset);
- if (ret != sizeof(struct dqblk))
- printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
- kdevname(dquot->dq_dev));
-
- set_fs(fs);
- 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;
+ add_wait_queue(&dquot->dq_wait_free, &wait);
+repeat:
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (dquot->dq_dup_ref) {
+ schedule();
+ goto repeat;
+ }
+ remove_wait_queue(&dquot->dq_wait_free, &wait);
+ current->state = TASK_RUNNING;
+}
- filp = dquot->dq_sb->s_dquot.files[type];
- if (filp == (struct file *)NULL)
- return;
+static int read_dqblk(struct dquot *dquot)
+{
+ int ret;
+ struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
lock_dquot(dquot);
- if (!dquot->dq_sb) /* Invalidated quota? */
- goto out_lock;
- /* Now we are sure filp is valid - the dquot isn't invalidated */
- 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:
+ down(&dqopt->dqio_sem);
+ ret = dqopt->ops[dquot->dq_type]->read_dqblk(dquot);
+ up(&dqopt->dqio_sem);
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
* 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... */
-static void invalidate_dquots(struct super_block *sb, short type)
+static void invalidate_dquots(struct super_block *sb, int type)
{
struct dquot *dquot;
struct list_head *head;
restart:
- for (head = inuse_list.next; head != &inuse_list; head = head->next) {
+ list_for_each(head, &inuse_list) {
dquot = list_entry(head, struct dquot, dq_inuse);
if (dquot->dq_sb != sb)
continue;
@@ -359,37 +372,107 @@ restart:
}
}
-int sync_dquots(kdev_t dev, short type)
+static int vfs_quota_sync(struct super_block *sb, int type)
{
struct list_head *head;
struct dquot *dquot;
+ struct quota_info *dqopt = sb_dqopt(sb);
+ int cnt;
- lock_kernel();
restart:
- for (head = inuse_list.next; head != &inuse_list; head = head->next) {
+ list_for_each(head, &inuse_list) {
dquot = list_entry(head, struct dquot, dq_inuse);
- if (dev && dquot->dq_dev != dev)
+ if (sb && dquot->dq_sb != sb)
continue;
if (type != -1 && dquot->dq_type != type)
continue;
if (!dquot->dq_sb) /* Invalidated? */
continue;
- if (!(dquot->dq_flags & (DQ_MOD | DQ_LOCKED)))
+ if (!dquot_dirty(dquot) && !(dquot->dq_flags & DQ_LOCKED))
continue;
- /* Raise use count so quota won't be invalidated. We can't use dqduplicate() as it does too many tests */
- dquot->dq_count++;
+ /* Get reference to quota so it won't be invalidated. get_dquot_ref()
+ * 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)
wait_on_dquot(dquot);
- if (dquot->dq_flags & DQ_MOD)
- write_dquot(dquot);
+ if (dquot_dirty(dquot))
+ commit_dqblk(dquot);
dqput(dquot);
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++;
- unlock_kernel();
+
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_dev(kdev_t dev, int type)
+{
+ struct super_block *sb;
+
+ if (dev) {
+ if ((sb = get_super(dev))) {
+ lock_kernel();
+ if (sb->s_qcop->quota_sync)
+ sb->s_qcop->quota_sync(sb, type);
+ unlock_kernel();
+ drop_super(sb);
+ }
+ }
+ 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);
+ }
+ }
+}
+
+void sync_dquots_sb(struct super_block *sb, int type)
+{
+ lock_kernel();
+ if (sb->s_qcop->quota_sync)
+ sb->s_qcop->quota_sync(sb, type);
+ unlock_kernel();
+}
+
/* Free unused dquots from cache */
static void prune_dqcache(int count)
{
@@ -408,19 +491,38 @@ static void prune_dqcache(int count)
}
}
+/*
+ * This is called from kswapd when we think we need some
+ * more memory, but aren't really sure how much. So we
+ * carefully try to free a _bit_ of our dqcache, but not
+ * too much.
+ *
+ * Priority:
+ * 1 - very urgent: shrink everything
+ * ...
+ * 6 - base-level: try to shrink a bit.
+ */
+
int shrink_dqcache_memory(int priority, unsigned int gfp_mask)
{
+ int count = 0;
+
lock_kernel();
- prune_dqcache(nr_free_dquots / (priority + 1));
+ count = dqstats.free_dquots / priority;
+ prune_dqcache(count);
unlock_kernel();
return kmem_cache_shrink(dquot_cachep);
}
-/* 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)
{
if (!dquot)
return;
+#ifdef __DQUOT_PARANOIA
if (!dquot->dq_count) {
printk("VFS: dqput: trying to free free dquot\n");
printk("VFS: device %s, dquot of %s %d\n",
@@ -428,33 +530,38 @@ static void dqput(struct dquot *dquot)
dquot->dq_id);
return;
}
+#endif
dqstats.drops++;
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) {
/* We have more than one user... We can simply decrement use count */
- dquot->dq_count--;
+ put_dquot_ref(dquot);
return;
}
- if (dquot->dq_flags & DQ_MOD) {
- write_dquot(dquot);
+ if (dquot_dirty(dquot)) {
+ commit_dqblk(dquot);
goto we_slept;
}
/* sanity check */
if (!list_empty(&dquot->dq_free)) {
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;
}
- dquot->dq_count--;
+ put_dquot_ref(dquot);
/* If dquot is going to be invalidated invalidate_dquots() is going to free it so */
if (!(dquot->dq_flags & DQ_INVAL))
put_dquot_last(dquot); /* Place at end of LRU free queue */
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;
@@ -468,6 +575,9 @@ static struct dquot *get_empty_dquot(voi
INIT_LIST_HEAD(&dquot->dq_free);
INIT_LIST_HEAD(&dquot->dq_inuse);
INIT_LIST_HEAD(&dquot->dq_hash);
+ dquot->dq_sb = sb;
+ dquot->dq_dev = sb->s_dev;
+ dquot->dq_type = type;
dquot->dq_count = 1;
/* all dquots go on the inuse_list */
put_inuse(dquot);
@@ -475,11 +585,11 @@ static struct dquot *get_empty_dquot(voi
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->s_dev, id, type);
+ unsigned int hashent = hashfn(sb, id, type);
struct dquot *dquot, *empty = NODQUOT;
- struct quota_mount_options *dqopt = sb_dqopt(sb);
+ struct quota_info *dqopt = sb_dqopt(sb);
we_slept:
if (!is_enabled(dqopt, type)) {
@@ -488,23 +598,21 @@ we_slept:
return NODQUOT;
}
- if ((dquot = find_dquot(hashent, sb->s_dev, id, type)) == NODQUOT) {
+ if ((dquot = find_dquot(hashent, sb, id, type)) == 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... */
goto we_slept;
}
dquot = empty;
- dquot->dq_id = id;
- dquot->dq_type = type;
- dquot->dq_dev = sb->s_dev;
- dquot->dq_sb = sb;
+ dquot->dq_id = id;
/* hash it first so it can be found */
insert_dquot_hash(dquot);
- read_dquot(dquot);
+ read_dqblk(dquot);
} else {
- if (!dquot->dq_count++)
+ if (!dquot->dq_count)
remove_free_dquot(dquot);
+ get_dquot_ref(dquot);
dqstats.cache_hits++;
wait_on_dquot(dquot);
if (empty)
@@ -516,30 +624,47 @@ we_slept:
dqput(dquot);
return NODQUOT;
}
- dquot->dq_referenced++;
+ ++dquot->dq_referenced;
dqstats.lookups++;
return dquot;
}
+/* Duplicate reference to dquot got from inode */
static struct dquot *dqduplicate(struct dquot *dquot)
{
if (dquot == NODQUOT)
return NODQUOT;
- dquot->dq_count++;
+ get_dquot_ref(dquot);
if (!dquot->dq_sb) {
printk(KERN_ERR "VFS: dqduplicate(): Invalidated quota to be duplicated!\n");
- dquot->dq_count--;
+ put_dquot_ref(dquot);
return NODQUOT;
}
if (dquot->dq_flags & DQ_LOCKED)
printk(KERN_ERR "VFS: dqduplicate(): Locked quota to be duplicated!\n");
+ get_dquot_dup_ref(dquot);
dquot->dq_referenced++;
dqstats.lookups++;
+
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;
@@ -553,16 +678,13 @@ static int dqinit_needed(struct inode *i
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;
- if (!sb->dq_op)
- return; /* nothing to do */
-
restart:
file_list_lock();
- for (p = sb->s_files.next; p != &sb->s_files; p = p->next) {
+ list_for_each(p, &sb->s_files) {
struct file *filp = list_entry(p, struct file, f_list);
struct inode *inode = filp->f_dentry->d_inode;
if (filp->f_mode & FMODE_WRITE && dqinit_needed(inode, type)) {
@@ -582,13 +704,15 @@ restart:
/* Return 0 if dqput() won't block (note that 1 doesn't necessarily mean blocking) */
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 0;
}
/* 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];
int cnt;
@@ -635,38 +759,38 @@ void put_dquot_list(struct list_head *to
static inline void dquot_incr_inodes(struct dquot *dquot, unsigned long number)
{
- dquot->dq_curinodes += number;
- dquot->dq_flags |= DQ_MOD;
+ dquot->dq_dqb.dqb_curinodes += number;
+ 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_flags |= DQ_MOD;
+ dquot->dq_dqb.dqb_curspace += number;
+ mark_dquot_dirty(dquot);
}
static inline void dquot_decr_inodes(struct dquot *dquot, unsigned long number)
{
- if (dquot->dq_curinodes > number)
- dquot->dq_curinodes -= number;
+ if (dquot->dq_dqb.dqb_curinodes > number)
+ dquot->dq_dqb.dqb_curinodes -= number;
else
- dquot->dq_curinodes = 0;
- if (dquot->dq_curinodes < dquot->dq_isoftlimit)
- dquot->dq_itime = (time_t) 0;
+ dquot->dq_dqb.dqb_curinodes = 0;
+ if (dquot->dq_dqb.dqb_curinodes < dquot->dq_dqb.dqb_isoftlimit)
+ dquot->dq_dqb.dqb_itime = (time_t) 0;
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)
- dquot->dq_curblocks -= number;
+ if (dquot->dq_dqb.dqb_curspace > number)
+ dquot->dq_dqb.dqb_curspace -= number;
else
- dquot->dq_curblocks = 0;
- if (dquot->dq_curblocks < dquot->dq_bsoftlimit)
- dquot->dq_btime = (time_t) 0;
+ dquot->dq_dqb.dqb_curspace = 0;
+ if (toqb(dquot->dq_dqb.dqb_curspace) < dquot->dq_dqb.dqb_bsoftlimit)
+ dquot->dq_dqb.dqb_btime = (time_t) 0;
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)
@@ -739,7 +863,10 @@ static inline void flush_warnings(struct
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)
@@ -748,60 +875,60 @@ static int check_idq(struct dquot *dquot
if (inodes <= 0 || dquot->dq_flags & DQ_FAKE)
return QUOTA_OK;
- if (dquot->dq_ihardlimit &&
- (dquot->dq_curinodes + inodes) > dquot->dq_ihardlimit &&
+ if (dquot->dq_dqb.dqb_ihardlimit &&
+ (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_ihardlimit &&
!ignore_hardlimit(dquot)) {
*warntype = IHARDWARN;
return NO_QUOTA;
}
- if (dquot->dq_isoftlimit &&
- (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit &&
- dquot->dq_itime && CURRENT_TIME >= dquot->dq_itime &&
+ if (dquot->dq_dqb.dqb_isoftlimit &&
+ (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_isoftlimit &&
+ dquot->dq_dqb.dqb_itime && CURRENT_TIME >= dquot->dq_dqb.dqb_itime &&
!ignore_hardlimit(dquot)) {
*warntype = ISOFTLONGWARN;
return NO_QUOTA;
}
- if (dquot->dq_isoftlimit &&
- (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit &&
- dquot->dq_itime == 0) {
+ if (dquot->dq_dqb.dqb_isoftlimit &&
+ (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_isoftlimit &&
+ dquot->dq_dqb.dqb_itime == 0) {
*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;
}
-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;
- if (blocks <= 0 || dquot->dq_flags & DQ_FAKE)
+ if (space <= 0 || dquot->dq_flags & DQ_FAKE)
return QUOTA_OK;
- if (dquot->dq_bhardlimit &&
- (dquot->dq_curblocks + blocks) > dquot->dq_bhardlimit &&
+ if (dquot->dq_dqb.dqb_bhardlimit &&
+ toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bhardlimit &&
!ignore_hardlimit(dquot)) {
if (!prealloc)
*warntype = BHARDWARN;
return NO_QUOTA;
}
- if (dquot->dq_bsoftlimit &&
- (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit &&
- dquot->dq_btime && CURRENT_TIME >= dquot->dq_btime &&
+ if (dquot->dq_dqb.dqb_bsoftlimit &&
+ toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit &&
+ dquot->dq_dqb.dqb_btime && CURRENT_TIME >= dquot->dq_dqb.dqb_btime &&
!ignore_hardlimit(dquot)) {
if (!prealloc)
*warntype = BSOFTLONGWARN;
return NO_QUOTA;
}
- if (dquot->dq_bsoftlimit &&
- (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit &&
- dquot->dq_btime == 0) {
+ if (dquot->dq_dqb.dqb_bsoftlimit &&
+ toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit &&
+ dquot->dq_dqb.dqb_btime == 0) {
if (!prealloc) {
*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
/*
@@ -815,148 +942,15 @@ static int check_bdq(struct dquot *dquot
}
/*
- * 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.
*
* 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];
unsigned int id = 0;
- short cnt;
+ int cnt;
if (IS_NOQUOTA(inode))
return;
@@ -1002,7 +996,7 @@ void dquot_initialize(struct inode *inod
void dquot_drop(struct inode *inode)
{
struct dquot *dquot;
- short cnt;
+ int cnt;
inode->i_flags &= ~S_QUOTA;
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
@@ -1017,7 +1011,7 @@ void dquot_drop(struct inode *inode)
/*
* 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;
struct dquot *dquot[MAXQUOTAS];
@@ -1038,16 +1032,16 @@ int dquot_alloc_block(struct inode *inod
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (dquot[cnt] == NODQUOT)
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 */
ret = QUOTA_OK;
warn_put_all:
flush_warnings(dquot, warntype);
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
if (dquot[cnt] != NODQUOT)
- dqput(dquot[cnt]);
+ dqputduplicate(dquot[cnt]);
return ret;
}
@@ -1084,16 +1078,16 @@ warn_put_all:
flush_warnings(dquot, warntype);
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
if (dquot[cnt] != NODQUOT)
- dqput(dquot[cnt]);
+ dqputduplicate(dquot[cnt]);
return ret;
}
/*
* 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;
/* NOBLOCK Start */
@@ -1101,10 +1095,10 @@ void dquot_free_block(struct inode *inod
dquot = dqduplicate(inode->i_dquot[cnt]);
if (dquot == NODQUOT)
continue;
- dquot_decr_blocks(dquot, number);
- dqput(dquot);
+ dquot_decr_space(dquot, number);
+ dqputduplicate(dquot);
}
- inode->i_blocks -= number << (BLOCK_SIZE_BITS - 9);
+ inode_sub_bytes(inode, number);
/* NOBLOCK End */
}
@@ -1113,7 +1107,7 @@ void dquot_free_block(struct inode *inod
*/
void dquot_free_inode(const struct inode *inode, unsigned long number)
{
- unsigned short cnt;
+ unsigned int cnt;
struct dquot *dquot;
/* NOBLOCK Start */
@@ -1122,7 +1116,7 @@ void dquot_free_inode(const struct inode
if (dquot == NODQUOT)
continue;
dquot_decr_inodes(dquot, number);
- dqput(dquot);
+ dqputduplicate(dquot);
}
/* NOBLOCK End */
}
@@ -1134,7 +1128,7 @@ void dquot_free_inode(const struct inode
*/
int dquot_transfer(struct inode *inode, struct iattr *iattr)
{
- unsigned long blocks;
+ qsize_t space;
struct dquot *transfer_from[MAXQUOTAS];
struct dquot *transfer_to[MAXQUOTAS];
int cnt, ret = NO_QUOTA, chuid = (iattr->ia_valid & ATTR_UID) && inode->i_uid != iattr->ia_uid,
@@ -1164,7 +1158,7 @@ int dquot_transfer(struct inode *inode,
}
}
/* 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 */
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
/* The second test can fail when quotaoff is in progress... */
@@ -1174,7 +1168,7 @@ int dquot_transfer(struct inode *inode,
if (transfer_from[cnt] == NODQUOT) /* Can happen on quotafiles (quota isn't initialized on them)... */
continue;
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;
}
@@ -1189,10 +1183,10 @@ int dquot_transfer(struct inode *inode,
continue;
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_blocks(transfer_to[cnt], blocks);
+ dquot_incr_space(transfer_to[cnt], space);
if (inode->i_dquot[cnt] == NODQUOT)
BUG();
@@ -1208,39 +1202,29 @@ int dquot_transfer(struct inode *inode,
warn_put_all:
flush_warnings(transfer_to, warntype);
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ /* First we must put duplicate - otherwise we might deadlock */
if (transfer_to[cnt] != NODQUOT)
- dqput(transfer_to[cnt]);
+ dqputduplicate(transfer_to[cnt]);
if (transfer_from[cnt] != NODQUOT)
dqput(transfer_from[cnt]);
}
return ret;
}
-static int __init dquot_init(void)
-{
- int i;
-
- 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.
*/
struct dquot_operations dquot_operations = {
- dquot_initialize, /* mandatory */
- dquot_drop, /* mandatory */
- dquot_alloc_block,
- dquot_alloc_inode,
- dquot_free_block,
- dquot_free_inode,
- dquot_transfer
+ initialize: dquot_initialize, /* mandatory */
+ drop: dquot_drop, /* mandatory */
+ alloc_space: dquot_alloc_space,
+ alloc_inode: dquot_alloc_inode,
+ free_space: dquot_free_space,
+ free_inode: dquot_free_inode,
+ 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) {
case USRQUOTA:
@@ -1252,7 +1236,7 @@ static inline void set_enable_flags(stru
}
}
-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) {
case USRQUOTA:
@@ -1265,16 +1249,15 @@ static inline void reset_enable_flags(st
}
/* 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)
*/
-int quota_off(struct super_block *sb, short type)
+int vfs_quota_off(struct super_block *sb, int type)
{
- struct file *filp;
- short cnt;
- struct quota_mount_options *dqopt = sb_dqopt(sb);
+ int cnt;
+ struct quota_info *dqopt = sb_dqopt(sb);
lock_kernel();
if (!sb)
@@ -1292,51 +1275,48 @@ int quota_off(struct super_block *sb, sh
/* Note: these are blocking operations */
remove_dquot_ref(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->inode_expire[cnt] = 0;
- dqopt->block_expire[cnt] = 0;
- fput(filp);
- }
+ dqopt->info[cnt].dqi_flags = 0;
+ dqopt->info[cnt].dqi_igrace = 0;
+ dqopt->info[cnt].dqi_bgrace = 0;
+ dqopt->ops[cnt] = NULL;
+ }
up(&dqopt->dqoff_sem);
out:
unlock_kernel();
return 0;
}
-static inline int check_quotafile_size(loff_t size)
-{
- ulong blocks = size >> BLOCK_SIZE_BITS;
- 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)
+int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path)
{
- struct file *f;
+ struct file *f = NULL;
struct inode *inode;
- struct dquot *dquot;
- struct quota_mount_options *dqopt = sb_dqopt(sb);
- char *tmp;
+ struct quota_info *dqopt = sb_dqopt(sb);
+ struct quota_format_type *fmt = find_quota_format(format_id);
int error;
- if (is_enabled(dqopt, type))
- return -EBUSY;
+ if (!fmt)
+ return -ESRCH;
+ if (is_enabled(dqopt, type)) {
+ error = -EBUSY;
+ goto out_fmt;
+ }
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);
- putname(tmp);
+ f = filp_open(path, O_RDWR, 0600);
error = PTR_ERR(f);
if (IS_ERR(f))
goto out_lock;
+ dqopt->files[type] = f;
error = -EIO;
if (!f->f_op || !f->f_op->read || !f->f_op->write)
goto out_f;
@@ -1345,134 +1325,197 @@ static int quota_on(struct super_block *
if (!S_ISREG(inode->i_mode))
goto out_f;
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;
/* We don't want quota on quota files */
dquot_drop(inode);
inode->i_flags |= S_NOQUOTA;
- dqopt->files[type] = f;
- sb->dq_op = &dquot_operations;
+ dqopt->ops[type] = fmt->qf_ops;
+ 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);
- 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);
up(&dqopt->dqoff_sem);
return 0;
out_f:
- filp_close(f, NULL);
+ if (f)
+ filp_close(f, NULL);
+ dqopt->files[type] = NULL;
out_lock:
up(&dqopt->dqoff_sem);
+out_fmt:
+ put_quota_format(fmt);
return error;
}
-/*
- * 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(int cmd, const char *special, int id, caddr_t addr)
+/* Generic routine for getting common part of quota structure */
+static void do_get_dqblk(struct dquot *dquot, struct if_dqblk *di)
{
- int cmds = 0, type = 0, flags = 0;
- kdev_t dev;
- struct super_block *sb = NULL;
- int ret = -EINVAL;
+ struct mem_dqblk *dm = &dquot->dq_dqb;
- lock_kernel();
- cmds = cmd >> SUBCMDSHIFT;
- type = cmd & SUBCMDMASK;
+ di->dqb_bhardlimit = dm->dqb_bhardlimit;
+ 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;
+}
- if ((u_int) type >= MAXQUOTAS)
- goto out;
- if (id & ~0xFFFF)
- goto out;
+int vfs_get_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di)
+{
+ struct dquot *dquot = dqget(sb, id, type);
- ret = -EPERM;
- switch (cmds) {
- case Q_SYNC:
- case Q_GETSTATS:
- break;
- 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;
- dev = NODEV;
- if (special != NULL || (cmds != Q_SYNC && cmds != Q_GETSTATS)) {
- 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;
- }
-
- ret = -EINVAL;
- switch (cmds) {
- case Q_QUOTAON:
- ret = quota_on(sb, type, (char *) addr);
- goto out;
- case Q_QUOTAOFF:
- ret = quota_off(sb, type);
- goto out;
- case Q_GETQUOTA:
- ret = get_quota(sb, id, type, (struct dqblk *) addr);
- goto out;
- case Q_SETQUOTA:
- flags |= SET_QUOTA;
- break;
- case Q_SETUSE:
- flags |= SET_USE;
- break;
- case Q_SETQLIM:
- flags |= SET_QLIMIT;
- break;
- case Q_SYNC:
- ret = sync_dquots(dev, type);
- goto out;
- case Q_GETSTATS:
- ret = get_stats(addr);
- goto out;
- case Q_RSQUASH:
- ret = quota_root_squash(sb, type, (int *) addr);
- goto out;
- default:
- goto out;
- }
-
- ret = -NODEV;
- if (sb && sb_has_quota_enabled(sb, type))
- ret = set_dqblk(sb, id, type, flags, (struct dqblk *) addr);
-out:
- if (sb)
- drop_super(sb);
- unlock_kernel();
- return ret;
+ if (!dquot)
+ return -EINVAL;
+ do_get_dqblk(dquot, di);
+ dqput(dquot);
+ return 0;
+}
+
+/* Generic routine for setting common part of quota structure */
+static void do_set_dqblk(struct dquot *dquot, struct if_dqblk *di)
+{
+ struct mem_dqblk *dm = &dquot->dq_dqb;
+ int check_blim = 0, check_ilim = 0;
+
+ if (di->dqb_valid & QIF_SPACE) {
+ dm->dqb_curspace = di->dqb_curspace;
+ check_blim = 1;
+ }
+ if (di->dqb_valid & QIF_BLIMITS) {
+ dm->dqb_bsoftlimit = di->dqb_bsoftlimit;
+ dm->dqb_bhardlimit = di->dqb_bhardlimit;
+ check_blim = 1;
+ }
+ if (di->dqb_valid & QIF_INODES) {
+ dm->dqb_curinodes = di->dqb_curinodes;
+ check_ilim = 1;
+ }
+ if (di->dqb_valid & QIF_ILIMITS) {
+ dm->dqb_isoftlimit = di->dqb_isoftlimit;
+ dm->dqb_ihardlimit = di->dqb_ihardlimit;
+ check_ilim = 1;
+ }
+ if (di->dqb_valid & QIF_BTIME)
+ dm->dqb_btime = di->dqb_btime;
+ if (di->dqb_valid & QIF_ITIME)
+ dm->dqb_itime = di->dqb_itime;
+
+ if (check_blim) {
+ if (!dm->dqb_bsoftlimit || toqb(dm->dqb_curspace) < dm->dqb_bsoftlimit) {
+ dm->dqb_btime = 0;
+ dquot->dq_flags &= ~DQ_BLKS;
+ }
+ else if (!(di->dqb_valid & QIF_BTIME)) /* Set grace only if user hasn't provided his own... */
+ dm->dqb_btime = CURRENT_TIME + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_bgrace;
+ }
+ if (check_ilim) {
+ if (!dm->dqb_isoftlimit || dm->dqb_curinodes < dm->dqb_isoftlimit) {
+ dm->dqb_itime = 0;
+ dquot->dq_flags &= ~DQ_INODES;
+ }
+ 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;
+}
+
+int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di)
+{
+ struct dquot *dquot = dqget(sb, id, type);
+
+ if (!dquot)
+ return -EINVAL;
+ do_set_dqblk(dquot, di);
+ dqput(dquot);
+ return 0;
+}
+
+/* Generic routine for getting common part of quota file information */
+int vfs_get_dqinfo(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_dqinfo(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;
+}
+
+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_dqinfo,
+ set_info: vfs_set_dqinfo,
+ get_dqblk: vfs_get_dqblk,
+ set_dqblk: vfs_set_dqblk
+};
+
+static ctl_table fs_dqstats_table[] = {
+ {FS_DQ_LOOKUPS, "lookups", &dqstats.lookups, sizeof(int), 0444, NULL, &proc_dointvec},
+ {FS_DQ_DROPS, "drops", &dqstats.drops, sizeof(int), 0444, NULL, &proc_dointvec},
+ {FS_DQ_READS, "reads", &dqstats.reads, sizeof(int), 0444, NULL, &proc_dointvec},
+ {FS_DQ_WRITES, "writes", &dqstats.writes, sizeof(int), 0444, NULL, &proc_dointvec},
+ {FS_DQ_CACHE_HITS, "cache_hits", &dqstats.cache_hits, sizeof(int), 0444, NULL, &proc_dointvec},
+ {FS_DQ_ALLOCATED, "allocated_dquots", &dqstats.allocated_dquots, sizeof(int), 0444, NULL, &proc_dointvec},
+ {FS_DQ_FREE, "free_dquots", &dqstats.free_dquots, sizeof(int), 0444, NULL, &proc_dointvec},
+ {FS_DQ_SYNCS, "syncs", &dqstats.syncs, sizeof(int), 0444, NULL, &proc_dointvec},
+ {},
+};
+
+static ctl_table fs_table[] = {
+ {FS_DQSTATS, "quota", NULL, 0, 0555, fs_dqstats_table},
+ {},
+};
+
+static ctl_table sys_table[] = {
+ {CTL_FS, "fs", NULL, 0, 0555, fs_table},
+ {},
+};
+
+static int __init dquot_init(void)
+{
+ int i;
+
+ register_sysctl_table(sys_table, 0);
+ for (i = 0; i < NR_DQHASH; i++)
+ INIT_LIST_HEAD(dquot_hash + i);
+ printk(KERN_NOTICE "VFS: Disk quotas v%s\n", __DQUOT_VERSION__);
+
+ return 0;
}
+__initcall(dquot_init);
+
+EXPORT_SYMBOL(register_quota_format);
+EXPORT_SYMBOL(unregister_quota_format);
+EXPORT_SYMBOL(dqstats);
diff -urNp x-ref/fs/inode.c x/fs/inode.c
--- x-ref/fs/inode.c 2002-12-13 03:58:49.000000000 +0100
+++ x/fs/inode.c 2002-12-13 03:58:50.000000000 +0100
@@ -107,6 +107,7 @@ static struct inode *alloc_inode(struct
atomic_set(&inode->i_writecount, 0);
inode->i_size = 0;
inode->i_blocks = 0;
+ inode->i_bytes = 0;
inode->i_generation = 0;
memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
inode->i_pipe = NULL;
@@ -1267,9 +1268,9 @@ void update_atime (struct inode *inode)
/* Functions back in dquot.c */
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 list_head *act_head;
diff -urNp x-ref/fs/ioctl.c x/fs/ioctl.c
--- x-ref/fs/ioctl.c 2002-12-13 03:58:39.000000000 +0100
+++ x/fs/ioctl.c 2002-12-13 03:58:50.000000000 +0100
@@ -105,6 +105,17 @@ asmlinkage long sys_ioctl(unsigned int f
fioasync_out:
up(&inode->i_sem);
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:
diff -urNp x-ref/fs/noquot.c x/fs/noquot.c
--- x-ref/fs/noquot.c 2000-05-12 20:21:20.000000000 +0200
+++ x/fs/noquot.c 1970-01-01 01:00:00.000000000 +0100
@@ -1,15 +0,0 @@
-/* noquot.c: Quota stubs necessary for when quotas are not
- * compiled into the kernel.
- */
-
-#include
-#include
-#include
-
-int nr_dquots, nr_free_dquots;
-int max_dquots;
-
-asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
-{
- return(-ENOSYS);
-}
diff -urNp x-ref/fs/quota.c x/fs/quota.c
--- x-ref/fs/quota.c 1970-01-01 01:00:00.000000000 +0100
+++ x/fs/quota.c 2002-12-13 03:58:50.000000000 +0100
@@ -0,0 +1,654 @@
+/*
+ * 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
+#include
+#include
+#include
+#include
+#include
+#ifdef CONFIG_QIFACE_COMPAT
+#include
+#endif
+
+
+/* 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;
+}
diff -urNp x-ref/fs/quota_v1.c x/fs/quota_v1.c
--- x-ref/fs/quota_v1.c 1970-01-01 01:00:00.000000000 +0100
+++ x/fs/quota_v1.c 2002-12-13 03:58:50.000000000 +0100
@@ -0,0 +1,239 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+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_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);
+
diff -urNp x-ref/fs/quota_v2.c x/fs/quota_v2.c
--- x-ref/fs/quota_v2.c 1970-01-01 01:00:00.000000000 +0100
+++ x/fs/quota_v2.c 2002-12-13 03:58:50.000000000 +0100
@@ -0,0 +1,690 @@
+/*
+ * vfsv0 quota IO operations on file
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#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<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<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<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_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);
diff -urNp x-ref/fs/super.c x/fs/super.c
--- x-ref/fs/super.c 2002-12-13 03:58:37.000000000 +0100
+++ x/fs/super.c 2002-12-13 03:58:50.000000000 +0100
@@ -27,6 +27,7 @@
#include
#include
#include
+#include
#include
@@ -287,6 +288,8 @@ static struct super_block *alloc_super(v
sema_init(&s->s_dquot.dqoff_sem, 1);
s->s_maxbytes = MAX_NON_LFS;
s->s_op = &empty_sops;
+ s->dq_op = sb_dquot_ops;
+ s->s_qcop = sb_quotactl_ops;
}
return s;
}
diff -urNp x-ref/include/asm-alpha/ioctls.h x/include/asm-alpha/ioctls.h
--- x-ref/include/asm-alpha/ioctls.h 2002-11-29 02:23:16.000000000 +0100
+++ x/include/asm-alpha/ioctls.h 2002-12-13 03:58:50.000000000 +0100
@@ -9,6 +9,7 @@
#define FIONBIO _IOW('f', 126, int)
#define FIONREAD _IOR('f', 127, int)
#define TIOCINQ FIONREAD
+#define FIOQSIZE _IOR('f', 128, loff_t)
#define TIOCGETP _IOR('t', 8, struct sgttyb)
#define TIOCSETP _IOW('t', 9, struct sgttyb)
diff -urNp x-ref/include/asm-cris/ioctls.h x/include/asm-cris/ioctls.h
--- x-ref/include/asm-cris/ioctls.h 2002-08-09 14:52:20.000000000 +0200
+++ x/include/asm-cris/ioctls.h 2002-12-13 03:58:50.000000000 +0100
@@ -69,6 +69,7 @@
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
#define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
#define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
+#define FIOQSIZE 0x5460
#define TIOCSERSETRS485 0x5460 /* enable rs-485 */
#define TIOCSERWRRS485 0x5461 /* write rs-485 */
diff -urNp x-ref/include/asm-i386/ioctls.h x/include/asm-i386/ioctls.h
--- x-ref/include/asm-i386/ioctls.h 2002-08-09 14:52:20.000000000 +0200
+++ x/include/asm-i386/ioctls.h 2002-12-13 03:58:50.000000000 +0100
@@ -67,6 +67,7 @@
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
#define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
#define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
+#define FIOQSIZE 0x5460
/* Used for packet mode */
#define TIOCPKT_DATA 0
diff -urNp x-ref/include/asm-ia64/ioctls.h x/include/asm-ia64/ioctls.h
--- x-ref/include/asm-ia64/ioctls.h 2002-11-29 02:23:16.000000000 +0100
+++ x/include/asm-ia64/ioctls.h 2002-12-13 03:58:50.000000000 +0100
@@ -72,6 +72,7 @@
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
#define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
#define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
+#define FIOQSIZE 0x5460
/* Used for packet mode */
#define TIOCPKT_DATA 0
diff -urNp x-ref/include/asm-m68k/ioctls.h x/include/asm-m68k/ioctls.h
--- x-ref/include/asm-m68k/ioctls.h 1998-02-13 01:25:04.000000000 +0100
+++ x/include/asm-m68k/ioctls.h 2002-12-13 03:58:50.000000000 +0100
@@ -65,6 +65,7 @@
#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
+#define FIOQSIZE 0x545E
/* Used for packet mode */
#define TIOCPKT_DATA 0
diff -urNp x-ref/include/asm-sh/ioctls.h x/include/asm-sh/ioctls.h
--- x-ref/include/asm-sh/ioctls.h 1999-11-06 19:40:31.000000000 +0100
+++ x/include/asm-sh/ioctls.h 2002-12-13 03:58:50.000000000 +0100
@@ -9,6 +9,7 @@
#define FIONBIO _IOW('f', 126, int)
#define FIONREAD _IOR('f', 127, int)
#define TIOCINQ FIONREAD
+#define FIOQSIZE _IOR('f', 128, loff_t)
#define TCGETS 0x5401
#define TCSETS 0x5402
diff -urNp x-ref/include/asm-sparc/ioctls.h x/include/asm-sparc/ioctls.h
--- x-ref/include/asm-sparc/ioctls.h 2002-08-09 14:52:28.000000000 +0200
+++ x/include/asm-sparc/ioctls.h 2002-12-13 03:58:50.000000000 +0100
@@ -86,6 +86,7 @@
#define FIONBIO _IOW('f', 126, int)
#define FIONREAD _IOR('f', 127, int)
#define TIOCINQ FIONREAD
+#define FIOQSIZE _IOR('f', 128, loff_t)
/* SCARY Rutgers local SunOS kernel hackery, perhaps I will support it
* someday. This is completely bogus, I know...
diff -urNp x-ref/include/asm-sparc64/ioctls.h x/include/asm-sparc64/ioctls.h
--- x-ref/include/asm-sparc64/ioctls.h 2002-08-09 14:52:28.000000000 +0200
+++ x/include/asm-sparc64/ioctls.h 2002-12-13 03:58:50.000000000 +0100
@@ -87,6 +87,7 @@
#define FIONBIO _IOW('f', 126, int)
#define FIONREAD _IOR('f', 127, int)
#define TIOCINQ FIONREAD
+#define FIOQSIZE _IOR('f', 128, loff_t)
/* SCARY Rutgers local SunOS kernel hackery, perhaps I will support it
* someday. This is completely bogus, I know...
diff -urNp x-ref/include/linux/dqblk_v1.h x/include/linux/dqblk_v1.h
--- x-ref/include/linux/dqblk_v1.h 1970-01-01 01:00:00.000000000 +0100
+++ x/include/linux/dqblk_v1.h 2002-12-13 03:58:50.000000000 +0100
@@ -0,0 +1,18 @@
+/*
+ * 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 */
diff -urNp x-ref/include/linux/dqblk_v2.h x/include/linux/dqblk_v2.h
--- x-ref/include/linux/dqblk_v2.h 1970-01-01 01:00:00.000000000 +0100
+++ x/include/linux/dqblk_v2.h 2002-12-13 03:58:50.000000000 +0100
@@ -0,0 +1,20 @@
+/*
+ * Definitions of structures for vfsv0 quota format
+ */
+
+#ifndef _LINUX_DQBLK_V2_H
+#define _LINUX_DQBLK_V2_H
+
+#include
+
+/* 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 */
diff -urNp x-ref/include/linux/dqblk_xfs.h x/include/linux/dqblk_xfs.h
--- x-ref/include/linux/dqblk_xfs.h 1970-01-01 01:00:00.000000000 +0100
+++ x/include/linux/dqblk_xfs.h 2002-12-13 03:58:50.000000000 +0100
@@ -0,0 +1,159 @@
+/*
+ * 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_DQBLK_XFS_H
+#define _LINUX_DQBLK_XFS_H
+
+#include
+
+/*
+ * 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_DQBLK_XFS_H */
diff -urNp x-ref/include/linux/fs.h x/include/linux/fs.h
--- x-ref/include/linux/fs.h 2002-12-13 03:58:49.000000000 +0100
+++ x/include/linux/fs.h 2002-12-13 03:58:50.000000000 +0100
@@ -458,6 +458,7 @@ struct inode {
unsigned long i_blksize;
unsigned long i_blocks;
unsigned long i_version;
+ unsigned short i_bytes;
struct semaphore i_sem;
struct semaphore i_zombie;
struct inode_operations *i_op;
@@ -520,6 +521,39 @@ struct inode {
} u;
};
+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;
+}
+
struct fown_struct {
int pid; /* pid or -pgrp where SIGIO should be sent */
uid_t uid, euid; /* uid/euid of process setting the owner */
@@ -675,20 +709,6 @@ struct nameidata {
int last_type;
};
-#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
*/
@@ -736,6 +756,7 @@ struct super_block {
struct file_system_type *s_type;
struct super_operations *s_op;
struct dquot_operations *dq_op;
+ struct quotactl_ops *s_qcop;
unsigned long s_flags;
unsigned long s_magic;
struct dentry *s_root;
@@ -750,7 +771,7 @@ struct super_block {
struct block_device *s_bdev;
struct list_head s_instances;
- struct quota_mount_options s_dquot; /* Diskquota specific options */
+ struct quota_info s_dquot; /* Diskquota specific options */
union {
struct minix_sb_info minix_sb;
@@ -975,16 +996,6 @@ static inline void mark_inode_dirty_page
__mark_inode_dirty(inode, I_DIRTY_PAGES);
}
-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 *);
-};
-
struct file_system_type {
const char *name;
int fs_flags;
diff -urNp x-ref/include/linux/quota.h x/include/linux/quota.h
--- x-ref/include/linux/quota.h 2002-10-12 02:21:50.000000000 +0200
+++ x/include/linux/quota.h 2002-12-13 03:58:50.000000000 +0100
@@ -40,30 +40,22 @@
#define _LINUX_QUOTA_
#include
+#include
-/*
- * Convert diskblocks to blocks and the other way around.
- */
-#define dbtob(num) (num << BLOCK_SIZE_BITS)
-#define btodb(num) (num >> BLOCK_SIZE_BITS)
+#define __DQUOT_VERSION__ "dquot_6.5.1"
+#define __DQUOT_NUM_VERSION__ 6*10000+5*100+1
-/*
- * Convert count of filesystem blocks to diskquota blocks, meant
- * for filesystems where i_blksize != BLOCK_SIZE
- */
-#define fs_to_dq_blocks(num, blksize) (((num) * (blksize)) / BLOCK_SIZE)
+typedef __kernel_uid32_t qid_t; /* Type in which we store ids in memory */
+typedef __u64 qsize_t; /* Type in which we store sizes */
-/*
- * Definitions for disk quotas imposed on the average user
- * (big brother finally hits Linux).
- *
- * 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 */
+/* Size of blocks in which are counted size limits */
+#define QUOTABLOCK_BITS 10
+#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
+
+/* Conversion routines from and to quota blocks */
+#define qb2kb(x) ((x) << (QUOTABLOCK_BITS-10))
+#define kb2qb(x) ((x) >> (QUOTABLOCK_BITS-10))
+#define toqb(x) (((x) + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS)
#define MAXQUOTAS 2
#define USRQUOTA 0 /* element used for user quotas */
@@ -78,9 +70,6 @@
"undefined", \
};
-#define QUOTAFILENAME "quota"
-#define QUOTAGROUP "staff"
-
/*
* Command definitions for the 'quotactl' system call.
* The commands are broken into a main command defined below
@@ -91,61 +80,122 @@
#define SUBCMDSHIFT 8
#define QCMD(cmd, type) (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK))
-#define Q_QUOTAON 0x0100 /* enable quotas */
-#define Q_QUOTAOFF 0x0200 /* disable quotas */
-#define Q_GETQUOTA 0x0300 /* get limits and usage */
-#define Q_SETQUOTA 0x0400 /* set limits and usage */
-#define Q_SETUSE 0x0500 /* set usage */
-#define Q_SYNC 0x0600 /* sync disk copy of a filesystems quotas */
-#define Q_SETQLIM 0x0700 /* set limits */
-#define Q_GETSTATS 0x0800 /* get collected stats */
-#define Q_RSQUASH 0x1000 /* set root_squash option */
-
-/*
- * 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.
+#define Q_SYNC 0x800001 /* sync disk copy of a filesystems quotas */
+#define Q_QUOTAON 0x800002 /* turn quotas on */
+#define Q_QUOTAOFF 0x800003 /* turn quotas off */
+#define Q_GETFMT 0x800004 /* get quota format used on given filesystem */
+#define Q_GETINFO 0x800005 /* get information about quota files */
+#define Q_SETINFO 0x800006 /* set information about quota files */
+#define Q_GETQUOTA 0x800007 /* get user quota structure */
+#define Q_SETQUOTA 0x800008 /* set user quota structure */
+
+/*
+ * Quota structure used for communication with userspace via quotactl
+ * Following flags are used to specify which fields are valid
*/
-struct dqblk {
+#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;
+};
+
+/*
+ * Structure used for setting quota information about file via quotactl
+ * Following flags are used to specify which fields are valid
+ */
+#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
+#include
+#include
+
+/*
+ * Data for one user/group kept in memory
+ */
+struct 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 */
+ qsize_t dqb_curspace; /* current used space */
__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 */
+ time_t dqb_btime; /* time limit for excessive disk 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
-#define dq_bsoftlimit dq_dqb.dqb_bsoftlimit
-#define dq_curblocks dq_dqb.dqb_curblocks
-#define dq_ihardlimit dq_dqb.dqb_ihardlimit
-#define dq_isoftlimit dq_dqb.dqb_isoftlimit
-#define dq_curinodes dq_dqb.dqb_curinodes
-#define dq_btime dq_dqb.dqb_btime
-#define dq_itime dq_dqb.dqb_itime
+struct quota_format_type;
+
+struct mem_dqinfo {
+ struct quota_format_type *dqi_format;
+ int dqi_flags;
+ unsigned int dqi_bgrace;
+ unsigned int dqi_igrace;
+ 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? */
-#define dqoff(UID) ((loff_t)((UID) * sizeof (struct dqblk)))
+extern inline void mark_info_dirty(struct mem_dqinfo *info)
+{
+ info->dqi_flags |= DQF_INFO_DIRTY;
+}
+
+#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)
struct dqstats {
- __u32 lookups;
- __u32 drops;
- __u32 reads;
- __u32 writes;
- __u32 cache_hits;
- __u32 allocated_dquots;
- __u32 free_dquots;
- __u32 syncs;
+ int lookups;
+ int drops;
+ int reads;
+ int writes;
+ int cache_hits;
+ int allocated_dquots;
+ int free_dquots;
+ int syncs;
};
-#ifdef __KERNEL__
-
-extern int nr_dquots, nr_free_dquots;
-extern int dquot_root_squash;
+extern struct dqstats dqstats;
#define NR_DQHASH 43 /* Just an arbitrary number */
@@ -162,37 +212,113 @@ struct dquot {
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_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 */
struct super_block *dq_sb; /* superblock this applies to */
unsigned int dq_id; /* ID this applies to (uid, gid) */
kdev_t dq_dev; /* Device this applies to */
+ loff_t dq_off; /* Offset of dquot on disk */
short dq_type; /* Type of quota */
short dq_flags; /* See DQ_* */
unsigned long dq_referenced; /* Number of times this dquot was
referenced during its lifetime */
- struct dqblk dq_dqb; /* Diskquota usage */
+ struct mem_dqblk dq_dqb; /* Diskquota usage */
};
#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 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
# /* nodep */ include
__BEGIN_DECLS
-long quotactl __P ((int, const char *, int, caddr_t));
+long quotactl __P ((unsigned int, const char *, int, caddr_t));
__END_DECLS
#endif /* __KERNEL__ */
diff -urNp x-ref/include/linux/quotacompat.h x/include/linux/quotacompat.h
--- x-ref/include/linux/quotacompat.h 1970-01-01 01:00:00.000000000 +0100
+++ x/include/linux/quotacompat.h 2002-12-13 03:58:50.000000000 +0100
@@ -0,0 +1,86 @@
+/*
+ * Definition of symbols used for backward compatible interface
+ */
+
+#ifndef _LINUX_QUOTACOMPAT_
+#define _LINUX_QUOTACOMPAT_
+
+#include
+#include
+
+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
diff -urNp x-ref/include/linux/quotaio_v1.h x/include/linux/quotaio_v1.h
--- x-ref/include/linux/quotaio_v1.h 1970-01-01 01:00:00.000000000 +0100
+++ x/include/linux/quotaio_v1.h 2002-12-13 03:58:50.000000000 +0100
@@ -0,0 +1,33 @@
+#ifndef _LINUX_QUOTAIO_V1_H
+#define _LINUX_QUOTAIO_V1_H
+
+#include
+
+/*
+ * 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 */
diff -urNp x-ref/include/linux/quotaio_v2.h x/include/linux/quotaio_v2.h
--- x-ref/include/linux/quotaio_v2.h 1970-01-01 01:00:00.000000000 +0100
+++ x/include/linux/quotaio_v2.h 2002-12-13 03:58:50.000000000 +0100
@@ -0,0 +1,79 @@
+/*
+ * Definitions of structures for vfsv0 quota format
+ */
+
+#ifndef _LINUX_QUOTAIO_V2_H
+#define _LINUX_QUOTAIO_V2_H
+
+#include
+#include
+
+/*
+ * 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 */
diff -urNp x-ref/include/linux/quotaops.h x/include/linux/quotaops.h
--- x-ref/include/linux/quotaops.h 2002-11-12 05:12:25.000000000 +0100
+++ x/include/linux/quotaops.h 2002-12-13 03:58:50.000000000 +0100
@@ -20,15 +20,16 @@
/*
* declaration of quota_function calls in kernel.
*/
-extern void dquot_initialize(struct inode *inode, short type);
+extern void sync_dquots_dev(kdev_t dev, int type);
+extern void sync_dquots_sb(struct super_block *sb, int type);
+
+extern void dquot_initialize(struct inode *inode, int type);
extern void dquot_drop(struct inode *inode);
-extern int quota_off(struct super_block *sb, short type);
-extern int sync_dquots(kdev_t dev, 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 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 int dquot_transfer(struct inode *inode, struct iattr *iattr);
@@ -36,7 +37,11 @@ extern int dquot_transfer(struct inode
/*
* 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)
{
@@ -59,50 +64,50 @@ static __inline__ void DQUOT_DROP(struct
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();
if (sb_any_quota_enabled(inode->i_sb)) {
- /* Number of used blocks is updated in alloc_block() */
- if (inode->i_sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize), 1) == NO_QUOTA) {
+ /* Used space is updated in alloc_space() */
+ if (inode->i_sb->dq_op->alloc_space(inode, nr, 1) == NO_QUOTA) {
unlock_kernel();
return 1;
}
}
else
- inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9);
+ inode_add_bytes(inode, nr);
unlock_kernel();
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;
- if (!(ret = DQUOT_PREALLOC_BLOCK_NODIRTY(inode, nr)))
+ if (!(ret = DQUOT_PREALLOC_SPACE_NODIRTY(inode, nr)))
mark_inode_dirty(inode);
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();
if (sb_any_quota_enabled(inode->i_sb)) {
- /* Number of used blocks is updated in alloc_block() */
- if (inode->i_sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize), 0) == NO_QUOTA) {
+ /* Used space is updated in alloc_space() */
+ if (inode->i_sb->dq_op->alloc_space(inode, nr, 0) == NO_QUOTA) {
unlock_kernel();
return 1;
}
}
else
- inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9);
+ inode_add_bytes(inode, nr);
unlock_kernel();
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;
- if (!(ret = DQUOT_ALLOC_BLOCK_NODIRTY(inode, nr)))
+ if (!(ret = DQUOT_ALLOC_SPACE_NODIRTY(inode, nr)))
mark_inode_dirty(inode);
return ret;
}
@@ -121,19 +126,19 @@ static __inline__ int DQUOT_ALLOC_INODE(
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();
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
- inode->i_blocks -= nr << (inode->i_sb->s_blocksize_bits - 9);
+ inode_sub_bytes(inode, nr);
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);
}
@@ -159,63 +164,85 @@ static __inline__ int DQUOT_TRANSFER(str
return 0;
}
-#define DQUOT_SYNC(dev) sync_dquots(dev, -1)
-#define DQUOT_OFF(sb) quota_off(sb, -1)
+#define DQUOT_SYNC_DEV(dev) sync_dquots_dev(dev, -1)
+#define DQUOT_SYNC_SB(sb) sync_dquots_sb(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
/*
* 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_DROP(inode) do { } while(0)
#define DQUOT_ALLOC_INODE(inode) (0)
#define DQUOT_FREE_INODE(inode) do { } while(0)
-#define DQUOT_SYNC(dev) do { } while(0)
+#define DQUOT_SYNC_DEV(dev) do { } while(0)
+#define DQUOT_SYNC_SB(sb) do { } while(0)
#define DQUOT_OFF(sb) do { } while(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();
- inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9);
+ inode_add_bytes(inode, nr);
unlock_kernel();
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);
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();
- inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9);
+ inode_add_bytes(inode, nr);
unlock_kernel();
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);
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();
- inode->i_blocks -= nr << (inode->i_sb->s_blocksize_bits - 9);
+ inode_sub_bytes(inode, nr);
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);
}
#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_ */
diff -urNp x-ref/include/linux/sysctl.h x/include/linux/sysctl.h
--- x-ref/include/linux/sysctl.h 2002-12-13 03:58:44.000000000 +0100
+++ x/include/linux/sysctl.h 2002-12-13 03:58:50.000000000 +0100
@@ -593,7 +593,7 @@ enum
FS_STATINODE=2,
FS_MAXINODE=3, /* int:maximum number of inodes that can be allocated */
FS_NRDQUOT=4, /* int:current number of allocated dquots */
- FS_MAXDQUOT=5, /* int:maximum number of dquots that can be allocated */
+ /* was FS_MAXDQUOT */
FS_NRFILE=6, /* int:current number of allocated filedescriptors */
FS_MAXFILE=7, /* int:maximum number of filedescriptors that can be allocated */
FS_DENTRY=8,
@@ -604,6 +604,19 @@ enum
FS_LEASES=13, /* int: leases enabled */
FS_DIR_NOTIFY=14, /* int: directory notification enabled */
FS_LEASE_TIME=15, /* int: maximum time to wait for a lease break */
+ FS_DQSTATS=16, /* dir: disc quota usage statistics */
+};
+
+/* /proc/sys/fs/quota/ */
+enum {
+ FS_DQ_LOOKUPS = 1,
+ FS_DQ_DROPS = 2,
+ FS_DQ_READS = 3,
+ FS_DQ_WRITES = 4,
+ FS_DQ_CACHE_HITS = 5,
+ FS_DQ_ALLOCATED = 6,
+ FS_DQ_FREE = 7,
+ FS_DQ_SYNCS = 8,
};
/* CTL_DEBUG names: */
diff -urNp x-ref/kernel/sysctl.c x/kernel/sysctl.c
--- x-ref/kernel/sysctl.c 2002-12-13 03:58:39.000000000 +0100
+++ x/kernel/sysctl.c 2002-12-13 03:58:50.000000000 +0100
@@ -308,8 +308,6 @@ static ctl_table fs_table[] = {
0444, NULL, &proc_dointvec},
{FS_MAXFILE, "file-max", &files_stat.max_files, sizeof(int),
0644, NULL, &proc_dointvec},
- {FS_NRDQUOT, "dquot-nr", &nr_dquots, 2*sizeof(int),
- 0444, NULL, &proc_dointvec},
{FS_DENTRY, "dentry-state", &dentry_stat, 6*sizeof(int),
0444, NULL, &proc_dointvec},
{FS_OVERFLOWUID, "overflowuid", &fs_overflowuid, sizeof(int), 0644, NULL,