From: Andrew Perepechko <Andrew.Perepechko@Sun.COM>
To: linux-fsdevel@vger.kernel.org
Cc: Johann Lombardi <Johann.Lombardi@Sun.COM>,
Zhiyong Landen tian <Zhiyong.Tian@Sun.COM>,
Alex Lyashkov <Alexey.Lyashkov@Sun.COM>
Subject: [RFC] quota: 64-bit limits with vfs
Date: Thu, 06 Mar 2008 16:41:11 +0300 [thread overview]
Message-ID: <200803061641.12274.andrew.perepechko@sun.com> (raw)
Hello!
We are in need of large (above 4 TB) block quota limits, but it seems like XFS filesystem
(having its own quota implementation) is the only available fs that supports them. Currently
ext3 supports up to 8 TB of data and forthcoming ext4 will support even more.
Linux kernel has two implementations of quota format modules:
quota_v1 (with QFMT_VFS_OLD id)
quota_v2 (with QFMT_VFS_V0 id)
Either uses 32-bit data types to store quota limits on disk
(see struct v1_disk_dqblk and struct v2_disk_dqblk). Block quota limits
are stored in 1kb units (QUOTABLOCK_SIZE constant) which gives
the largest possible quota limit of (2^32-1)*2^10 bytes ~ 4 TB.
In-memory quota entries representation suffers from the same 4 TB
limitation (see struct mem_dqblk).
The patch below adds a separate quota_v3 module which deals with 64-bit data to solve the problem
(another possible approach is to merge the code into quota_v2 module to reuse some amount of the code -
this won't reuse a lot because there're too many references to disk_dqblk structures and dependent constants).
Could you comment on the patch and the idea behind it in general?
Thank you.
Andrew.
---
fs/Kconfig | 7
fs/Makefile | 1
fs/ext3/super.c | 12
fs/quota_v1.c | 50 ++-
fs/quota_v2.c | 45 +-
fs/quota_v3.c | 739 +++++++++++++++++++++++++++++++++++++++++++++
fs/reiserfs/super.c | 2
include/linux/dqblk_v3.h | 26 +
include/linux/quota.h | 23 -
include/linux/quotaio_v3.h | 81 ++++
10 files changed, 952 insertions(+), 34 deletions(-)
---
diff -rNpu quota.orig/fs/Kconfig quota/fs/Kconfig
--- quota.orig/fs/Kconfig 2008-01-24 14:33:56.000000000 +0300
+++ quota/fs/Kconfig 2008-02-27 16:49:56.108413855 +0300
@@ -488,6 +488,13 @@ config QFMT_V2
This quota format allows using quotas with 32-bit UIDs/GIDs. If you
need this functionality say Y here.
+config QFMT_V3
+ tristate "Quota format v3 support"
+ depends on QUOTA
+ help
+ This quota format allows using quotas with 32-bit UIDs/GIDs and 64-bit
+ limits. If you need this functionality say Y here.
+
config QUOTACTL
bool
depends on XFS_QUOTA || QUOTA
diff -rNpu quota.orig/fs/Makefile quota/fs/Makefile
--- quota.orig/fs/Makefile 2008-01-24 14:33:54.000000000 +0300
+++ quota/fs/Makefile 2008-02-27 16:50:13.436477156 +0300
@@ -40,6 +40,7 @@ obj-$(CONFIG_GENERIC_ACL) += generic_acl
obj-$(CONFIG_QUOTA) += dquot.o
obj-$(CONFIG_QFMT_V1) += quota_v1.o
obj-$(CONFIG_QFMT_V2) += quota_v2.o
+obj-$(CONFIG_QFMT_V3) += quota_v3.o
obj-$(CONFIG_QUOTACTL) += quota.o
obj-$(CONFIG_DMAPI) += dmapi/
diff -rNpu quota.orig/fs/quota_v1.c quota/fs/quota_v1.c
--- quota.orig/fs/quota_v1.c 2006-03-20 08:53:29.000000000 +0300
+++ quota/fs/quota_v1.c 2008-02-29 19:15:23.325159161 +0300
@@ -25,8 +25,16 @@ static void v1_disk2mem_dqblk(struct mem
m->dqb_btime = d->dqb_btime;
}
-static void v1_mem2disk_dqblk(struct v1_disk_dqblk *d, struct mem_dqblk *m)
+static int v1_mem2disk_dqblk(struct v1_disk_dqblk *d, struct mem_dqblk *m)
{
+ __u32 typelimit = ~((__u32)0);
+
+ if (m->dqb_ihardlimit > typelimit ||
+ m->dqb_isoftlimit > typelimit ||
+ m->dqb_bhardlimit > typelimit ||
+ m->dqb_bsoftlimit > typelimit)
+ return -EINVAL;
+
d->dqb_ihardlimit = m->dqb_ihardlimit;
d->dqb_isoftlimit = m->dqb_isoftlimit;
d->dqb_curinodes = m->dqb_curinodes;
@@ -35,6 +43,8 @@ static void v1_mem2disk_dqblk(struct v1_
d->dqb_curblocks = toqb(m->dqb_curspace);
d->dqb_itime = m->dqb_itime;
d->dqb_btime = m->dqb_btime;
+
+ return 0;
}
static int v1_read_dqblk(struct dquot *dquot)
@@ -64,7 +74,10 @@ static int v1_commit_dqblk(struct dquot
ssize_t ret;
struct v1_disk_dqblk dqblk;
- v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb);
+ ret = v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb);
+ if (ret < 0)
+ return ret;
+
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;
@@ -88,7 +101,7 @@ out:
return ret;
}
-/* Magics of new quota format */
+/* Magics of vfsv0 quota format */
#define V2_INITQMAGICS {\
0xd9c01f11, /* USRQUOTA */\
0xd9c01927 /* GRPQUOTA */\
@@ -100,15 +113,29 @@ struct v2_disk_dqheader {
__le32 dqh_version; /* File version */
};
+/* Magics of vfsv1 quota format */
+#define V3_INITQMAGICS {\
+ 0xd9c01f11, /* USRQUOTA */\
+ 0xd9c01927 /* GRPQUOTA */\
+}
+
+/* Header of new quota format */
+struct v3_disk_dqheader {
+ __le32 dqh_magic; /* Magic number identifying file */
+ __le32 dqh_version; /* File version */
+};
+
static int v1_check_quota_file(struct super_block *sb, int type)
{
struct inode *inode = sb_dqopt(sb)->files[type];
ulong blocks;
size_t off;
- struct v2_disk_dqheader dqhead;
- ssize_t size;
+ struct v2_disk_dqheader dqhead_v2;
+ struct v3_disk_dqheader dqhead_v3;
+ ssize_t size_v2, size_v3;
loff_t isize;
- static const uint quota_magics[] = V2_INITQMAGICS;
+ static const uint quota_magics_v2[] = V2_INITQMAGICS,
+ quota_magics_v3[] = V3_INITQMAGICS;
isize = i_size_read(inode);
if (!isize)
@@ -118,10 +145,15 @@ static int v1_check_quota_file(struct su
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 */
- size = sb->s_op->quota_read(sb, type, (char *)&dqhead, sizeof(struct v2_disk_dqheader), 0);
- if (size != sizeof(struct v2_disk_dqheader))
+ size_v2 = sb->s_op->quota_read(sb, type, (char *)&dqhead_v2,
+ sizeof(struct v2_disk_dqheader), 0);
+ size_v3 = sb->s_op->quota_read(sb, type, (char *)&dqhead_v3,
+ sizeof(struct v3_disk_dqheader), 0);
+ if (size_v2 != sizeof(struct v2_disk_dqheader) &&
+ size_v3 != sizeof(struct v3_disk_dqheader))
return 1; /* Probably not new format */
- if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type])
+ if (le32_to_cpu(dqhead_v2.dqh_magic) != quota_magics_v2[type] &&
+ le32_to_cpu(dqhead_v3.dqh_magic) != quota_magics_v3[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", sb->s_id);
return 0; /* Seems like a new format file -> refuse it */
diff -rNpu quota.orig/fs/quota_v2.c quota/fs/quota_v2.c
--- quota.orig/fs/quota_v2.c 2006-03-20 08:53:29.000000000 +0300
+++ quota/fs/quota_v2.c 2008-02-28 10:37:31.067602129 +0300
@@ -106,17 +106,27 @@ static void disk2memdqb(struct mem_dqblk
m->dqb_btime = le64_to_cpu(d->dqb_btime);
}
-static void mem2diskdqb(struct v2_disk_dqblk *d, struct mem_dqblk *m, qid_t id)
+static int 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);
+ __u32 typelimit = ~((__u32)0);
+
+ if (m->dqb_ihardlimit > typelimit ||
+ m->dqb_isoftlimit > typelimit ||
+ m->dqb_bhardlimit > typelimit ||
+ m->dqb_bsoftlimit > typelimit)
+ return -EINVAL;
+
+ d->dqb_ihardlimit = cpu_to_le32((__u32)m->dqb_ihardlimit);
+ d->dqb_isoftlimit = cpu_to_le32((__u32)m->dqb_isoftlimit);
+ d->dqb_curinodes = cpu_to_le32((__u32)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_bhardlimit = cpu_to_le32((__u32)m->dqb_bhardlimit);
+ d->dqb_bsoftlimit = cpu_to_le32((__u32)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);
+
+ return 0;
}
static dqbuf_t getdqbuf(void)
@@ -394,14 +404,12 @@ static int v2_write_dquot(struct dquot *
ssize_t ret;
struct v2_disk_dqblk ddquot, empty;
- /* dq_off is guarded by dqio_sem */
- if (!dquot->dq_off)
- if ((ret = dq_insert_tree(dquot)) < 0) {
- printk(KERN_ERR "VFS: Error %zd occurred while creating quota.\n", ret);
- return ret;
- }
spin_lock(&dq_data_lock);
- mem2diskdqb(&ddquot, &dquot->dq_dqb, dquot->dq_id);
+ ret = mem2diskdqb(&ddquot, &dquot->dq_dqb, dquot->dq_id);
+ if (ret < 0) {
+ spin_unlock(&dq_data_lock);
+ return ret;
+ }
/* Argh... We may need to write structure full of zeroes but that would be
* treated as an empty place by the rest of the code. Format change would
* be definitely cleaner but the problems probably are not worth it */
@@ -409,6 +417,17 @@ static int v2_write_dquot(struct dquot *
if (!memcmp(&empty, &ddquot, sizeof(struct v2_disk_dqblk)))
ddquot.dqb_itime = cpu_to_le64(1);
spin_unlock(&dq_data_lock);
+
+ /* dq_off is guarded by dqio_sem */
+ if (!dquot->dq_off) {
+ ret = dq_insert_tree(dquot);
+ if (ret < 0) {
+ printk(KERN_ERR "VFS: Error %zd occurred "
+ "while creating quota.\n", ret);
+ return ret;
+ }
+ }
+
ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type,
(char *)&ddquot, sizeof(struct v2_disk_dqblk), dquot->dq_off);
if (ret != sizeof(struct v2_disk_dqblk)) {
diff -rNpu quota.orig/fs/quota_v3.c quota/fs/quota_v3.c
--- quota.orig/fs/quota_v3.c 1970-01-01 03:00:00.000000000 +0300
+++ quota/fs/quota_v3.c 2008-02-28 10:55:05.981528558 +0300
@@ -0,0 +1,739 @@
+/*
+ * vfsv1 quota IO operations on file
+ *
+ * adds support for quota limits above 4 TB
+ *
+ * based on quota_v3.c by Jan Kara
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/dqblk_v3.h>
+#include <linux/quotaio_v3.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <asm/byteorder.h>
+
+MODULE_DESCRIPTION("Quota format v3 support");
+MODULE_LICENSE("GPL");
+
+#define __QUOTA_V3_PARANOIA
+
+typedef char *dqbuf_t;
+
+#define GETIDINDEX(id, depth) (((id) >> ((V3_DQTREEDEPTH-(depth)-1)*8)) & 0xff)
+#define GETENTRIES(buf) ((struct v3_disk_dqblk *) \
+ (((char *)buf)+sizeof(struct v3_disk_dqdbheader)))
+
+/* Check whether given file is really vfsv1 quotafile */
+static int v3_check_quota_file(struct super_block *sb, int type)
+{
+ struct v3_disk_dqheader dqhead;
+ ssize_t size;
+ static const uint quota_magics[] = V3_INITQMAGICS;
+ static const uint quota_versions[] = V3_INITQVERSIONS;
+
+ size = sb->s_op->quota_read(sb, type, (char *)&dqhead,
+ sizeof(struct v3_disk_dqheader), 0);
+ if (size != sizeof(struct v3_disk_dqheader)) {
+ printk(KERN_WARNING "quota_v3: failed read expected=%zd, "
+ "got=%zd\n", sizeof(struct v3_disk_dqheader), size);
+ 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 v3_read_file_info(struct super_block *sb, int type)
+{
+ struct v3_disk_dqinfo dinfo;
+ struct mem_dqinfo *info = sb_dqopt(sb)->info+type;
+ ssize_t size;
+
+ size = sb->s_op->quota_read(sb, type, (char *)&dinfo,
+ sizeof(struct v3_disk_dqinfo), V3_DQINFOOFF);
+ if (size != sizeof(struct v3_disk_dqinfo)) {
+ printk(KERN_WARNING "Can't read info structure on device %s.\n",
+ sb->s_id);
+ 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.v3_i.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks);
+ info->u.v3_i.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk);
+ info->u.v3_i.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry);
+ return 0;
+}
+
+/* Write information header to quota file */
+static int v3_write_file_info(struct super_block *sb, int type)
+{
+ struct v3_disk_dqinfo dinfo;
+ struct mem_dqinfo *info = sb_dqopt(sb)->info+type;
+ ssize_t size;
+
+ spin_lock(&dq_data_lock);
+ 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);
+ spin_unlock(&dq_data_lock);
+ dinfo.dqi_blocks = cpu_to_le32(info->u.v3_i.dqi_blocks);
+ dinfo.dqi_free_blk = cpu_to_le32(info->u.v3_i.dqi_free_blk);
+ dinfo.dqi_free_entry = cpu_to_le32(info->u.v3_i.dqi_free_entry);
+ size = sb->s_op->quota_write(sb, type, (char *)&dinfo,
+ sizeof(struct v3_disk_dqinfo), V3_DQINFOOFF);
+ if (size != sizeof(struct v3_disk_dqinfo)) {
+ printk(KERN_WARNING "Can't write info structure "
+ "on device %s.\n", sb->s_id);
+ return -1;
+ }
+ return 0;
+}
+
+static void disk2memdqb(struct mem_dqblk *m, struct v3_disk_dqblk *d)
+{
+ m->dqb_ihardlimit = le64_to_cpu(d->dqb_ihardlimit);
+ m->dqb_isoftlimit = le64_to_cpu(d->dqb_isoftlimit);
+ m->dqb_curinodes = le64_to_cpu(d->dqb_curinodes);
+ m->dqb_itime = le64_to_cpu(d->dqb_itime);
+ m->dqb_bhardlimit = le64_to_cpu(d->dqb_bhardlimit);
+ m->dqb_bsoftlimit = le64_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 v3_disk_dqblk *d, struct mem_dqblk *m, qid_t id)
+{
+ d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);
+ d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
+ d->dqb_curinodes = cpu_to_le64(m->dqb_curinodes);
+ d->dqb_itime = cpu_to_le64(m->dqb_itime);
+ d->dqb_bhardlimit = cpu_to_le64(m->dqb_bhardlimit);
+ d->dqb_bsoftlimit = cpu_to_le64(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(V3_DQBLKSIZE, GFP_NOFS);
+ if (!buf)
+ printk(KERN_WARNING "Not enough memory for quota buffers.\n");
+ return buf;
+}
+
+static inline void freedqbuf(dqbuf_t buf)
+{
+ kfree(buf);
+}
+
+static inline ssize_t read_blk(struct super_block *sb, int type,
+ uint blk, dqbuf_t buf)
+{
+ memset(buf, 0, V3_DQBLKSIZE);
+ return sb->s_op->quota_read(sb, type, (char *)buf,
+ V3_DQBLKSIZE, blk << V3_DQBLKSIZE_BITS);
+}
+
+static inline ssize_t write_blk(struct super_block *sb, int type,
+ uint blk, dqbuf_t buf)
+{
+ return sb->s_op->quota_write(sb, type, (char *)buf,
+ V3_DQBLKSIZE, blk << V3_DQBLKSIZE_BITS);
+}
+
+/* Remove empty block from list and return it */
+static int get_free_dqblk(struct super_block *sb, int type)
+{
+ dqbuf_t buf = getdqbuf();
+ struct mem_dqinfo *info = sb_dqinfo(sb, type);
+ struct v3_disk_dqdbheader *dh = (struct v3_disk_dqdbheader *)buf;
+ int ret, blk;
+
+ if (!buf)
+ return -ENOMEM;
+ if (info->u.v3_i.dqi_free_blk) {
+ blk = info->u.v3_i.dqi_free_blk;
+ ret = read_blk(sb, type, blk, buf);
+ if (ret < 0)
+ goto out_buf;
+ info->u.v3_i.dqi_free_blk = le32_to_cpu(dh->dqdh_next_free);
+ } else {
+ memset(buf, 0, V3_DQBLKSIZE);
+ /* Assure block allocation... */
+ ret = write_blk(sb, type, info->u.v3_i.dqi_blocks, buf);
+ if (ret < 0)
+ goto out_buf;
+ blk = info->u.v3_i.dqi_blocks++;
+ }
+ mark_info_dirty(sb, type);
+ ret = blk;
+out_buf:
+ freedqbuf(buf);
+ return ret;
+}
+
+/* Insert empty block to the list */
+static int put_free_dqblk(struct super_block *sb, int type,
+ dqbuf_t buf, uint blk)
+{
+ struct mem_dqinfo *info = sb_dqinfo(sb, type);
+ struct v3_disk_dqdbheader *dh = (struct v3_disk_dqdbheader *)buf;
+ int err;
+
+ dh->dqdh_next_free = cpu_to_le32(info->u.v3_i.dqi_free_blk);
+ dh->dqdh_prev_free = cpu_to_le32(0);
+ dh->dqdh_entries = cpu_to_le16(0);
+ info->u.v3_i.dqi_free_blk = blk;
+ mark_info_dirty(sb, type);
+ /* Some strange block. We had better leave it... */
+ err = write_blk(sb, type, blk, buf);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/* Remove given block from the list of blocks with free entries */
+static int remove_free_dqentry(struct super_block *sb,
+ int type, dqbuf_t buf, uint blk)
+{
+ dqbuf_t tmpbuf = getdqbuf();
+ struct mem_dqinfo *info = sb_dqinfo(sb, type);
+ struct v3_disk_dqdbheader *dh = (struct v3_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) {
+ err = read_blk(sb, type, nextblk, tmpbuf);
+ if (err < 0)
+ goto out_buf;
+ ((struct v3_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = dh->dqdh_prev_free;
+ err = write_blk(sb, type, nextblk, tmpbuf);
+ if (err < 0)
+ goto out_buf;
+ }
+ if (prevblk) {
+ err = read_blk(sb, type, prevblk, tmpbuf);
+ if (err < 0)
+ goto out_buf;
+ ((struct v3_disk_dqdbheader *)tmpbuf)->dqdh_next_free = dh->dqdh_next_free;
+ err = write_blk(sb, type, prevblk, tmpbuf);
+ if (err < 0)
+ goto out_buf;
+ } else {
+ info->u.v3_i.dqi_free_entry = nextblk;
+ mark_info_dirty(sb, type);
+ }
+ freedqbuf(tmpbuf);
+ dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0);
+ /* No matter whether write succeeds block is out of list */
+ if (write_blk(sb, type, blk, buf) < 0)
+ 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 super_block *sb,
+ int type, dqbuf_t buf, uint blk)
+{
+ dqbuf_t tmpbuf = getdqbuf();
+ struct mem_dqinfo *info = sb_dqinfo(sb, type);
+ struct v3_disk_dqdbheader *dh = (struct v3_disk_dqdbheader *)buf;
+ int err;
+
+ if (!tmpbuf)
+ return -ENOMEM;
+ dh->dqdh_next_free = cpu_to_le32(info->u.v3_i.dqi_free_entry);
+ dh->dqdh_prev_free = cpu_to_le32(0);
+ err = write_blk(sb, type, blk, buf);
+ if (err < 0)
+ goto out_buf;
+ if (info->u.v3_i.dqi_free_entry) {
+ err = read_blk(sb, type, info->u.v3_i.dqi_free_entry, tmpbuf);
+ if (err < 0)
+ goto out_buf;
+ ((struct v3_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = cpu_to_le32(blk);
+ err = write_blk(sb, type, info->u.v3_i.dqi_free_entry, tmpbuf);
+ if (err < 0)
+ goto out_buf;
+ }
+ freedqbuf(tmpbuf);
+ info->u.v3_i.dqi_free_entry = blk;
+ mark_info_dirty(sb, type);
+ return 0;
+out_buf:
+ freedqbuf(tmpbuf);
+ return err;
+}
+
+/* Find space for dquot */
+static uint find_free_dqentry(struct dquot *dquot, int *err)
+{
+ struct super_block *sb = dquot->dq_sb;
+ struct mem_dqinfo *info = sb_dqopt(sb)->info+dquot->dq_type;
+ uint blk, i;
+ struct v3_disk_dqdbheader *dh;
+ struct v3_disk_dqblk *ddquot;
+ struct v3_disk_dqblk fakedquot;
+ dqbuf_t buf;
+
+ *err = 0;
+ buf = getdqbuf();
+ if (!buf) {
+ *err = -ENOMEM;
+ return 0;
+ }
+ dh = (struct v3_disk_dqdbheader *)buf;
+ ddquot = GETENTRIES(buf);
+ if (info->u.v3_i.dqi_free_entry) {
+ blk = info->u.v3_i.dqi_free_entry;
+ *err = read_blk(sb, dquot->dq_type, blk, buf);
+ if (*err < 0)
+ goto out_buf;
+ } else {
+ blk = get_free_dqblk(sb, dquot->dq_type);
+ if ((int)blk < 0) {
+ *err = blk;
+ freedqbuf(buf);
+ return 0;
+ }
+ memset(buf, 0, V3_DQBLKSIZE);
+ /* This is enough as block is already zeroed */
+ /* and entry list is empty... */
+ info->u.v3_i.dqi_free_entry = blk;
+ mark_info_dirty(sb, dquot->dq_type);
+ }
+ if (le16_to_cpu(dh->dqdh_entries)+1 >= V3_DQSTRINBLK) {
+ *err = remove_free_dqentry(sb, dquot->dq_type, buf, blk);
+ if (*err < 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 v3_disk_dqblk));
+ /* Find free structure in block */
+ for (i = 0; i < V3_DQSTRINBLK && memcmp(&fakedquot, ddquot+i,
+ sizeof(struct v3_disk_dqblk)); i++);
+#ifdef __QUOTA_V3_PARANOIA
+ if (i == V3_DQSTRINBLK) {
+ printk(KERN_ERR "VFS: find_free_dqentry(): "
+ "Data block full but it shouldn't.\n");
+ *err = -EIO;
+ goto out_buf;
+ }
+#endif
+ *err = write_blk(sb, dquot->dq_type, blk, buf);
+ if (*err < 0) {
+ printk(KERN_ERR "VFS: find_free_dqentry(): "
+ "Can't write quota data block %u.\n", blk);
+ goto out_buf;
+ }
+ dquot->dq_off = (blk<<V3_DQBLKSIZE_BITS)+
+ sizeof(struct v3_disk_dqdbheader)+
+ i*sizeof(struct v3_disk_dqblk);
+ freedqbuf(buf);
+ return blk;
+out_buf:
+ freedqbuf(buf);
+ return 0;
+}
+
+/* Insert reference to structure into the trie */
+static int do_insert_tree(struct dquot *dquot, uint *treeblk, int depth)
+{
+ struct super_block *sb = dquot->dq_sb;
+ dqbuf_t buf;
+ int ret = 0, newson = 0, newact = 0;
+ __le32 *ref;
+ uint newblk;
+
+ buf = getdqbuf();
+ if (!buf)
+ return -ENOMEM;
+ if (!*treeblk) {
+ ret = get_free_dqblk(sb, dquot->dq_type);
+ if (ret < 0)
+ goto out_buf;
+ *treeblk = ret;
+ memset(buf, 0, V3_DQBLKSIZE);
+ newact = 1;
+ } else {
+ ret = read_blk(sb, dquot->dq_type, *treeblk, buf);
+ if (ret < 0) {
+ printk(KERN_ERR "VFS: Can't read tree quota "
+ "block %u.\n", *treeblk);
+ goto out_buf;
+ }
+ }
+ ref = (__le32 *)buf;
+ newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
+ if (!newblk)
+ newson = 1;
+ if (depth == V3_DQTREEDEPTH-1) {
+#ifdef __QUOTA_V3_PARANOIA
+ if (newblk) {
+ printk(KERN_ERR "VFS: Inserting already present "
+ "quota entry (block %u).\n",
+ le32_to_cpu(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(sb, dquot->dq_type, *treeblk, buf);
+ } else if (newact && ret < 0)
+ put_free_dqblk(sb, dquot->dq_type, 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 = V3_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 v3_write_dquot(struct dquot *dquot)
+{
+ int type = dquot->dq_type;
+ ssize_t ret;
+ struct v3_disk_dqblk ddquot, empty;
+
+ /* dq_off is guarded by dqio_sem */
+ if (!dquot->dq_off) {
+ ret = dq_insert_tree(dquot);
+ if (ret < 0) {
+ printk(KERN_ERR "VFS: Error %zd occurred "
+ "while creating quota.\n", ret);
+ return ret;
+ }
+ }
+ spin_lock(&dq_data_lock);
+ mem2diskdqb(&ddquot, &dquot->dq_dqb, dquot->dq_id);
+ /* Argh... We may need to write structure full of zeroes but that would
+ * be treated as an empty place by the rest of the code. Format change
+ * would be definitely cleaner but the problems are not worth it */
+ memset(&empty, 0, sizeof(struct v3_disk_dqblk));
+ if (!memcmp(&empty, &ddquot, sizeof(struct v3_disk_dqblk)))
+ ddquot.dqb_itime = cpu_to_le64(1);
+ spin_unlock(&dq_data_lock);
+ ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type,
+ (char *)&ddquot, sizeof(struct v3_disk_dqblk), dquot->dq_off);
+ if (ret != sizeof(struct v3_disk_dqblk)) {
+ printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
+ dquot->dq_sb->s_id);
+ 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 super_block *sb = dquot->dq_sb;
+ int type = dquot->dq_type;
+ struct v3_disk_dqdbheader *dh;
+ dqbuf_t buf = getdqbuf();
+ int ret = 0;
+
+ if (!buf)
+ return -ENOMEM;
+ if (dquot->dq_off >> V3_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 >> V3_DQBLKSIZE_BITS));
+ goto out_buf;
+ }
+ ret = read_blk(sb, type, blk, buf);
+ if (ret < 0) {
+ printk(KERN_ERR "VFS: Can't read quota data block %u\n", blk);
+ goto out_buf;
+ }
+ dh = (struct v3_disk_dqdbheader *)buf;
+ dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries)-1);
+ if (!le16_to_cpu(dh->dqdh_entries)) {
+ ret = remove_free_dqentry(sb, type, buf, blk);
+ if (ret < 0 ||
+ (ret = put_free_dqblk(sb, type, 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 << V3_DQBLKSIZE_BITS)-1)), 0,
+ sizeof(struct v3_disk_dqblk));
+ if (le16_to_cpu(dh->dqdh_entries) == V3_DQSTRINBLK-1) {
+ /* Insert will write block itself */
+ ret = insert_free_dqentry(sb, type, buf, blk);
+ if (ret < 0) {
+ printk(KERN_ERR "VFS: Can't insert quota data "
+ "block (%u) to free entry list.\n", blk);
+ goto out_buf;
+ }
+ } else
+ ret = write_blk(sb, type, blk, buf);
+ if (ret < 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 super_block *sb = dquot->dq_sb;
+ int type = dquot->dq_type;
+ dqbuf_t buf = getdqbuf();
+ int ret = 0;
+ uint newblk;
+ __le32 *ref = (__le32 *)buf;
+
+ if (!buf)
+ return -ENOMEM;
+ ret = read_blk(sb, type, *blk, buf);
+ if (ret < 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 == V3_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 < V3_DQBLKSIZE && !buf[i]; i++);
+ /* Don't put the root block into the free block list */
+ if (i == V3_DQBLKSIZE && *blk != V3_DQTREEOFF) {
+ put_free_dqblk(sb, type, buf, *blk);
+ *blk = 0;
+ } else {
+ ret = write_blk(sb, type, *blk, buf);
+ if (ret < 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 v3_delete_dquot(struct dquot *dquot)
+{
+ uint tmp = V3_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)
+{
+ dqbuf_t buf = getdqbuf();
+ loff_t ret = 0;
+ int i;
+ struct v3_disk_dqblk *ddquot = GETENTRIES(buf);
+
+ if (!buf)
+ return -ENOMEM;
+ ret = read_blk(dquot->dq_sb, dquot->dq_type, blk, buf);
+ if (ret < 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 < V3_DQSTRINBLK &&
+ le32_to_cpu(ddquot[i].dqb_id) != dquot->dq_id; i++);
+ else { /* ID 0 as a bit more complicated searching... */
+ struct v3_disk_dqblk fakedquot;
+
+ memset(&fakedquot, 0, sizeof(struct v3_disk_dqblk));
+ for (i = 0; i < V3_DQSTRINBLK; i++)
+ if (!le32_to_cpu(ddquot[i].dqb_id) &&
+ memcmp(&fakedquot, ddquot+i,
+ sizeof(struct v3_disk_dqblk)))
+ break;
+ }
+ if (i == V3_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 << V3_DQBLKSIZE_BITS) + sizeof(struct
+ v3_disk_dqdbheader) + i * sizeof(struct v3_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)
+{
+ dqbuf_t buf = getdqbuf();
+ loff_t ret = 0;
+ __le32 *ref = (__le32 *)buf;
+
+ if (!buf)
+ return -ENOMEM;
+ ret = read_blk(dquot->dq_sb, dquot->dq_type, blk, buf);
+ if (ret < 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 < V3_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, V3_DQTREEOFF, 0);
+}
+
+static int v3_read_dquot(struct dquot *dquot)
+{
+ int type = dquot->dq_type;
+ loff_t offset;
+ struct v3_disk_dqblk ddquot, empty;
+ int ret = 0;
+
+#ifdef __QUOTA_V3_PARANOIA
+ /* Invalidated quota? */
+ if (!dquot->dq_sb || !sb_dqopt(dquot->dq_sb)->files[type]) {
+ printk(KERN_ERR "VFS: Quota invalidated while reading!\n");
+ return -EIO;
+ }
+#endif
+ offset = find_dqentry(dquot);
+ if (offset <= 0) {
+ if (offset < 0)
+ printk(KERN_ERR "VFS: Can't read quota "
+ "structure for id %u.\n", dquot->dq_id);
+ dquot->dq_off = 0;
+ set_bit(DQ_FAKE_B, &dquot->dq_flags);
+ memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
+ ret = offset;
+ } else {
+ dquot->dq_off = offset;
+ ret = dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type,
+ (char *)&ddquot, sizeof(struct v3_disk_dqblk), offset);
+ if (ret != sizeof(struct v3_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 v3_disk_dqblk));
+ } else {
+ ret = 0;
+ /* We need to escape back all-zero structure */
+ memset(&empty, 0, sizeof(struct v3_disk_dqblk));
+ empty.dqb_itime = cpu_to_le64(1);
+ if (!memcmp(&empty, &ddquot,
+ sizeof(struct v3_disk_dqblk)))
+ ddquot.dqb_itime = 0;
+ }
+ disk2memdqb(&dquot->dq_dqb, &ddquot);
+ if (!dquot->dq_dqb.dqb_bhardlimit &&
+ !dquot->dq_dqb.dqb_bsoftlimit &&
+ !dquot->dq_dqb.dqb_ihardlimit &&
+ !dquot->dq_dqb.dqb_isoftlimit)
+ set_bit(DQ_FAKE_B, &dquot->dq_flags);
+ }
+ dqstats.reads++;
+
+ return ret;
+}
+
+/* Check whether dquot should not be deleted. We know we are
+ * the only one operating on dquot (thanks to dq_lock) */
+static int v3_release_dquot(struct dquot *dquot)
+{
+ if (test_bit(DQ_FAKE_B, &dquot->dq_flags) &&
+ !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace))
+ return v3_delete_dquot(dquot);
+ return 0;
+}
+
+static struct quota_format_ops v3_format_ops = {
+ .check_quota_file = v3_check_quota_file,
+ .read_file_info = v3_read_file_info,
+ .write_file_info = v3_write_file_info,
+ .free_file_info = NULL,
+ .read_dqblk = v3_read_dquot,
+ .commit_dqblk = v3_write_dquot,
+ .release_dqblk = v3_release_dquot,
+};
+
+static struct quota_format_type v3_quota_format = {
+ .qf_fmt_id = QFMT_VFS_V1,
+ .qf_ops = &v3_format_ops,
+ .qf_owner = THIS_MODULE
+};
+
+static int __init init_v3_quota_format(void)
+{
+ return register_quota_format(&v3_quota_format);
+}
+
+static void __exit exit_v3_quota_format(void)
+{
+ unregister_quota_format(&v3_quota_format);
+}
+
+module_init(init_v3_quota_format);
+module_exit(exit_v3_quota_format);
diff -rNpu quota.orig/include/linux/dqblk_v3.h quota/include/linux/dqblk_v3.h
--- quota.orig/include/linux/dqblk_v3.h 1970-01-01 03:00:00.000000000 +0300
+++ quota/include/linux/dqblk_v3.h 2008-02-27 16:31:45.964283988 +0300
@@ -0,0 +1,26 @@
+/*
+ * Definitions of structures for vfsv1 quota format
+ */
+
+#ifndef _LINUX_DQBLK_V3_H
+#define _LINUX_DQBLK_V3_H
+
+#include <linux/types.h>
+
+/* id numbers of quota format */
+#define QFMT_VFS_V1 3
+
+/* Numbers of blocks needed for updates */
+#define V3_INIT_ALLOC 4
+#define V3_INIT_REWRITE 2
+#define V3_DEL_ALLOC 0
+#define V3_DEL_REWRITE 6
+
+/* Inmemory copy of version specific information */
+struct v3_mem_dqinfo {
+ unsigned int dqi_blocks;
+ unsigned int dqi_free_blk;
+ unsigned int dqi_free_entry;
+};
+
+#endif /* _LINUX_DQBLK_V3_H */
diff -rNpu quota.orig/include/linux/quota.h quota/include/linux/quota.h
--- quota.orig/include/linux/quota.h 2006-03-20 08:53:29.000000000 +0300
+++ quota/include/linux/quota.h 2008-02-27 16:30:15.306620367 +0300
@@ -136,24 +136,27 @@ struct if_dqinfo {
#include <linux/dqblk_xfs.h>
#include <linux/dqblk_v1.h>
#include <linux/dqblk_v2.h>
+#include <linux/dqblk_v3.h>
/* Maximal numbers of writes for quota operation (insert/delete/update)
* (over VFS all formats) */
-#define DQUOT_INIT_ALLOC max(V1_INIT_ALLOC, V2_INIT_ALLOC)
-#define DQUOT_INIT_REWRITE max(V1_INIT_REWRITE, V2_INIT_REWRITE)
-#define DQUOT_DEL_ALLOC max(V1_DEL_ALLOC, V2_DEL_ALLOC)
-#define DQUOT_DEL_REWRITE max(V1_DEL_REWRITE, V2_DEL_REWRITE)
+#define DQUOT_INIT_ALLOC max(V1_INIT_ALLOC, max(V2_INIT_ALLOC, V3_INIT_ALLOC))
+#define DQUOT_INIT_REWRITE max(max(V2_INIT_REWRITE, V3_INIT_REWRITE),\
+ V1_INIT_REWRITE)
+#define DQUOT_DEL_ALLOC max(V1_DEL_ALLOC, max(V2_DEL_ALLOC, V3_DEL_ALLOC))
+#define DQUOT_DEL_REWRITE max(max(V2_DEL_REWRITE, V3_DEL_REWRITE),\
+ V1_DEL_REWRITE)
/*
* 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 */
+ qsize_t dqb_bhardlimit; /* absolute limit on disk blks alloc */
+ qsize_t dqb_bsoftlimit; /* preferred limit on disk blks */
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 */
+ qsize_t dqb_ihardlimit; /* absolute limit on allocated inodes */
+ qsize_t dqb_isoftlimit; /* preferred inode limit */
+ qsize_t 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 */
};
@@ -172,6 +173,7 @@ struct mem_dqinfo {
union {
struct v1_mem_dqinfo v1_i;
struct v2_mem_dqinfo v2_i;
+ struct v3_mem_dqinfo v3_i;
} u;
};
@@ -315,6 +317,7 @@ struct quota_module_name {
#define INIT_QUOTA_MODULE_NAMES {\
{QFMT_VFS_OLD, "quota_v1"},\
{QFMT_VFS_V0, "quota_v2"},\
+ {QFMT_VFS_V1, "quota_v3"},\
{0, NULL}}
#else
diff -rNpu quota.orig/include/linux/quotaio_v3.h quota/include/linux/quotaio_v3.h
--- quota.orig/include/linux/quotaio_v3.h 1970-01-01 03:00:00.000000000 +0300
+++ quota/include/linux/quotaio_v3.h 2008-02-29 19:16:26.281092724 +0300
@@ -0,0 +1,81 @@
+/*
+ * Definitions of structures for vfsv1quota format
+ */
+
+#ifndef _LINUX_QUOTAIO_V3_H
+#define _LINUX_QUOTAIO_V3_H
+
+#include <linux/types.h>
+#include <linux/quota.h>
+
+/*
+ * Definitions of magics and versions of current quota files
+ */
+#define V3_INITQMAGICS {\
+ 0xd9c01f11, /* USRQUOTA */\
+ 0xd9c01927 /* GRPQUOTA */\
+}
+
+#define V3_INITQVERSIONS {\
+ 1, /* USRQUOTA */\
+ 1 /* 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 v3_disk_dqblk {
+ __le32 dqb_id; /* id this quota applies to */
+ __le32 dqb_padding; /* padding field */
+ __le64 dqb_ihardlimit; /* absolute limit on allocated inodes */
+ __le64 dqb_isoftlimit; /* preferred inode limit */
+ __le64 dqb_curinodes; /* current # allocated inodes */
+ __le64 dqb_bhardlimit; /* absolute limit on disk space (in blocks) */
+ __le64 dqb_bsoftlimit; /* preferred limit on disk space (in blocks) */
+ __le64 dqb_curspace; /* current space occupied (in bytes) */
+ __le64 dqb_btime; /* time limit for excessive disk use */
+ __le64 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 v3_disk_dqheader {
+ __le32 dqh_magic; /* Magic number identifying file */
+ __le32 dqh_version; /* File version */
+};
+
+/* Header with type and version specific information */
+struct v3_disk_dqinfo {
+ __le32 dqi_bgrace; /* Time before block soft limit becomes hard */
+ __le32 dqi_igrace; /* Time before inode soft limit becomes hard */
+ __le32 dqi_flags; /* Flags for quotafile (DQF_*) */
+ __le32 dqi_blocks; /* Number of blocks in file */
+ __le32 dqi_free_blk; /* Number of first free block in the list */
+ __le32 dqi_free_entry; /* Number of block with a free entry */
+};
+
+/*
+ * Structure of header of block with quota structures. It is padded to
+ * 16 bytes so there will be space for exactly 21 quota-entries in a block
+ */
+struct v3_disk_dqdbheader {
+ __le32 dqdh_next_free; /* Number of next block with free entry */
+ __le32 dqdh_prev_free; /* Number of previous block with free entry */
+ __le16 dqdh_entries; /* Number of valid entries in block */
+ __le16 dqdh_pad1;
+ __le32 dqdh_pad2;
+};
+
+#define V3_DQINFOOFF sizeof(struct v3_disk_dqheader)
+#define V3_DQBLKSIZE_BITS 10
+#define V3_DQBLKSIZE (1 << V3_DQBLKSIZE_BITS)
+#define V3_DQTREEOFF 1 /* Offset of tree in file in blocks */
+#define V3_DQTREEDEPTH 4 /* Depth of quota tree */
+#define V3_DQSTRINBLK ((V3_DQBLKSIZE - sizeof(struct v3_disk_dqdbheader)) / \
+ sizeof(struct v3_disk_dqblk))
+
+#endif /* _LINUX_QUOTAIO_V3_H */
diff -rNpu quota.orig/fs/ext3/super.c quota/fs/ext3/super.c
--- quota.orig/fs/ext3/super.c 2008-01-24 14:33:52.000000000 +0300
+++ quota/fs/ext3/super.c 2008-03-01 17:50:26.000000000 +0300
@@ -522,7 +522,8 @@ static inline void ext3_show_quota_optio
if (sbi->s_jquota_fmt)
seq_printf(seq, ",jqfmt=%s",
- (sbi->s_jquota_fmt == QFMT_VFS_OLD) ? "vfsold": "vfsv0");
+ (sbi->s_jquota_fmt == QFMT_VFS_OLD) ? "vfsold":
+ ((sbi->s_jquota_fmt == QFMT_VFS_V0) ? "vfsv0" : "vfsv1"));
if (sbi->s_qf_names[USRQUOTA])
seq_printf(seq, ",usrjquota=%s", sbi->s_qf_names[USRQUOTA]);
@@ -673,7 +674,7 @@ enum {
Opt_commit, Opt_journal_update, Opt_journal_inum, Opt_journal_dev,
Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback,
Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota,
- Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_quota, Opt_noquota,
+ Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_quota,
- Opt_ignore, Opt_barrier, Opt_err, Opt_resize, Opt_usrquota,
+ Opt_ignore, Opt_barrier, Opt_err, Opt_resize, Opt_usrquota, Opt_noquota,
Opt_grpquota
};
@@ -719,6 +720,7 @@ static match_table_t tokens = {
{Opt_grpjquota, "grpjquota=%s"},
{Opt_jqfmt_vfsold, "jqfmt=vfsold"},
{Opt_jqfmt_vfsv0, "jqfmt=vfsv0"},
+ {Opt_jqfmt_vfsv1, "jqfmt=vfsv1"},
{Opt_grpquota, "grpquota"},
{Opt_noquota, "noquota"},
{Opt_quota, "quota"},
@@ -990,6 +992,9 @@ clear_qf_name:
case Opt_jqfmt_vfsv0:
sbi->s_jquota_fmt = QFMT_VFS_V0;
break;
+ case Opt_jqfmt_vfsv1:
+ sbi->s_jquota_fmt = QFMT_VFS_V1;
+ break;
case Opt_quota:
case Opt_usrquota:
set_opt(sbi->s_mount_opt, QUOTA);
@@ -1019,6 +1024,7 @@ clear_qf_name:
case Opt_offgrpjquota:
case Opt_jqfmt_vfsold:
case Opt_jqfmt_vfsv0:
+ case Opt_jqfmt_vfsv1:
printk(KERN_ERR
"EXT3-fs: journalled quota options not "
"supported.\n");
diff -rNpu quota.orig/fs/reiserfs/super.c quota/fs/reiserfs/super.c
--- quota.orig/fs/reiserfs/super.c 2008-01-24 14:33:52.000000000 +0300
+++ quota/fs/reiserfs/super.c 2008-03-01 17:51:12.000000000 +0300
@@ -1021,6 +1021,8 @@ static int reiserfs_parse_options(struct
REISERFS_SB(s)->s_jquota_fmt = QFMT_VFS_OLD;
else if (!strcmp(arg, "vfsv0"))
REISERFS_SB(s)->s_jquota_fmt = QFMT_VFS_V0;
+ else if (!strcmp(arg, "vfsv1"))
+ REISERFS_SB(s)->s_jquota_fmt = QFMT_VFS_V1;
else {
reiserfs_warning(s,
"reiserfs_parse_options: unknown quota format specified.");
Signed-off-by: Andrew Perepechko <andrew.perepechko@sun.com>
next reply other threads:[~2008-03-06 13:50 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-03-06 13:41 Andrew Perepechko [this message]
2008-03-06 14:48 ` [RFC] quota: 64-bit limits with vfs Jan Kara
2008-03-09 22:46 ` Andrew Perepechko
2008-03-10 16:26 ` Jan Kara
2008-03-10 17:13 ` Andrew Perepechko
2008-03-10 17:20 ` Jan Kara
2008-03-14 13:08 ` Andrew Perepechko
2008-03-15 4:23 ` Andreas Dilger
2008-03-15 13:24 ` Andrew Perepechko
2008-03-15 13:32 ` Andrew Perepechko
2008-03-15 14:45 ` Andreas Dilger
2008-03-15 18:58 ` [RFC] quota: 64-bit limits with vfs, updated Andrew Perepechko
2008-03-15 22:47 ` Andreas Dilger
2008-03-16 1:14 ` Andrew Perepechko
2008-03-16 11:21 ` Andrew Perepechko
2008-03-17 14:35 ` Jan Kara
2008-03-20 21:37 ` Andrew Perepechko
2008-03-21 1:04 ` Andreas Dilger
2008-03-21 9:14 ` Andrew Perepechko
2008-03-21 10:24 ` Andrew Perepechko
2008-03-17 14:51 ` Jan Kara
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=200803061641.12274.andrew.perepechko@sun.com \
--to=andrew.perepechko@sun.com \
--cc=Alexey.Lyashkov@Sun.COM \
--cc=Johann.Lombardi@Sun.COM \
--cc=Zhiyong.Tian@Sun.COM \
--cc=linux-fsdevel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).