Index: linux-2.6.9/fs/ext3/inode.c =================================================================== --- linux-2.6.9.orig/fs/ext3/inode.c 2006-08-25 16:39:10.000000000 +0800 +++ linux-2.6.9/fs/ext3/inode.c 2006-09-14 11:44:29.000000000 +0800 @@ -1028,7 +1028,7 @@ return ret; } -static int +int ext3_journal_dirty_data(handle_t *handle, struct buffer_head *bh) { int err = journal_dirty_data(handle, bh); Index: linux-2.6.9/fs/ext3/super.c =================================================================== --- linux-2.6.9.orig/fs/ext3/super.c 2006-08-25 16:39:48.000000000 +0800 +++ linux-2.6.9/fs/ext3/super.c 2006-09-14 11:51:48.000000000 +0800 @@ -529,7 +529,10 @@ static int ext3_write_info(struct super_block *sb, int type); static int ext3_quota_on(struct super_block *sb, int type, int format_id, char *path); static int ext3_quota_on_mount(struct super_block *sb, int type); -static int ext3_quota_off_mount(struct super_block *sb, int type); +static ssize_t ext3_quota_read(struct super_block *sb, int type, char *data, + size_t len, loff_t off); +static ssize_t ext3_quota_write(struct super_block *sb, int type, + const char *data, size_t len, loff_t off); static struct dquot_operations ext3_quota_operations = { .initialize = ext3_dquot_initialize, @@ -572,6 +575,10 @@ .statfs = ext3_statfs, .remount_fs = ext3_remount, .clear_inode = ext3_clear_inode, +#ifdef CONFIG_QUOTA + .quota_read = ext3_quota_read, + .quota_write = ext3_quota_write, +#endif }; static struct dentry *ext3_get_dentry(struct super_block *sb, void *vobjp) @@ -713,6 +720,7 @@ int option; #ifdef CONFIG_QUOTA int qtype; + char *qname; #endif if (!options) @@ -891,19 +899,22 @@ "quota options when quota turned on.\n"); return 0; } - if (sbi->s_qf_names[qtype]) { + qname = match_strdup(&args[0]); + if (!qname) { printk(KERN_ERR - "EXT3-fs: %s quota file already " - "specified.\n", QTYPE2NAME(qtype)); + "EXT3-fs: not enough memory for " + "storing quotafile name.\n"); return 0; } - sbi->s_qf_names[qtype] = match_strdup(&args[0]); - if (!sbi->s_qf_names[qtype]) { + if (sbi->s_qf_names[qtype] && + strcmp(sbi->s_qf_names[qtype], qname)) { printk(KERN_ERR - "EXT3-fs: not enough memory for " - "storing quotafile name.\n"); + "EXT3-fs: %s quota file already " + "specified.\n", QTYPE2NAME(qtype)); + kfree(qname); return 0; } + sbi->s_qf_names[qtype] = qname; if (strchr(sbi->s_qf_names[qtype], '/')) { printk(KERN_ERR "EXT3-fs: quotafile must be on " @@ -1223,7 +1234,7 @@ /* Turn quotas off */ for (i = 0; i < MAXQUOTAS; i++) { if (sb_dqopt(sb)->files[i]) - ext3_quota_off_mount(sb, i); + vfs_quota_off(sb, i); } #endif sb->s_flags = s_flags; /* Restore MS_RDONLY status */ @@ -2240,7 +2251,7 @@ static inline struct inode *dquot_to_inode(struct dquot *dquot) { - return sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]->f_dentry->d_inode; + return sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]; } static int ext3_dquot_initialize(struct inode *inode, int type) @@ -2279,8 +2290,10 @@ { int ret, err; handle_t *handle; + struct inode *inode; - handle = ext3_journal_start(dquot_to_inode(dquot), + inode = dquot_to_inode(dquot); + handle = ext3_journal_start(inode, EXT3_QUOTA_TRANS_BLOCKS); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2367,22 +2380,9 @@ if (IS_ERR(dentry)) return PTR_ERR(dentry); err = vfs_quota_on_mount(type, EXT3_SB(sb)->s_jquota_fmt, dentry); - if (err) - dput(dentry); - /* We keep the dentry reference if everything went ok - we drop it - * on quota_off time */ - return err; -} - -/* Turn quotas off during mount time */ -static int ext3_quota_off_mount(struct super_block *sb, int type) -{ - int err; - struct dentry *dentry; - - dentry = sb_dqopt(sb)->files[type]->f_dentry; - err = vfs_quota_off_mount(sb, type); - /* We invalidate dentry - it has at least wrong hash... */ + /* Now invalidate and put the dentry - quota got its own reference + * to inode and dentry has at least wrong hash so we had better + * throw it away */ d_invalidate(dentry); dput(dentry); return err; @@ -2405,20 +2405,121 @@ if (err) return err; /* Quotafile not on the same filesystem? */ - if (nd.mnt->mnt_sb != sb) + if (nd.mnt->mnt_sb != sb) { + path_release(&nd); return -EXDEV; + } /* Quotafile not of fs root? */ if (nd.dentry->d_parent->d_inode != sb->s_root->d_inode) printk(KERN_WARNING "EXT3-fs: Quota file not on filesystem root. " "Journalled quota will not work.\n"); - if (!ext3_should_journal_data(nd.dentry->d_inode)) - printk(KERN_WARNING "EXT3-fs: Quota file does not have " - "data-journalling. Journalled quota will not work.\n"); path_release(&nd); return vfs_quota_on(sb, type, format_id, path); } +/* Read data from quotafile - avoid pagecache and such because we cannot afford + * acquiring the locks... As quota files are never truncated and quota code + * itself serializes the operations (and noone else should touch the files) + * we don't have to be afraid of races */ +static ssize_t ext3_quota_read(struct super_block *sb, int type, char *data, + size_t len, loff_t off) +{ + struct inode *inode = sb_dqopt(sb)->files[type]; + sector_t blk = off >> EXT3_BLOCK_SIZE_BITS(sb); + int err = 0; + int offset = off & (sb->s_blocksize - 1); + int tocopy; + size_t toread; + struct buffer_head *bh; + loff_t i_size = i_size_read(inode); + + if (off > i_size) + return 0; + if (off+len > i_size) + len = i_size-off; + toread = len; + while (toread > 0) { + tocopy = sb->s_blocksize - offset < toread ? + sb->s_blocksize - offset : toread; + bh = ext3_bread(NULL, inode, blk, 0, &err); + if (err) + return err; + if (!bh) /* A hole? */ + memset(data, 0, tocopy); + else + memcpy(data, bh->b_data+offset, tocopy); + brelse(bh); + offset = 0; + toread -= tocopy; + data += tocopy; + blk++; + } + return len; +} + +/* Write to quotafile (we know the transaction is already started and has + * enough credits) */ +static ssize_t ext3_quota_write(struct super_block *sb, int type, + const char *data, size_t len, loff_t off) +{ + struct inode *inode = sb_dqopt(sb)->files[type]; + sector_t blk = off >> EXT3_BLOCK_SIZE_BITS(sb); + int err = 0; + int offset = off & (sb->s_blocksize - 1); + int tocopy; + int journal_quota = EXT3_SB(sb)->s_qf_names[type] != NULL; + size_t towrite = len; + struct buffer_head *bh; + handle_t *handle = journal_current_handle(); + + down(&inode->i_sem); + while (towrite > 0) { + tocopy = sb->s_blocksize - offset < towrite ? + sb->s_blocksize - offset : towrite; + bh = ext3_bread(handle, inode, blk, 1, &err); + if (!bh) + goto out; + if (journal_quota) { + err = ext3_journal_get_write_access(handle, bh); + if (err) { + brelse(bh); + goto out; + } + } + lock_buffer(bh); + memcpy(bh->b_data+offset, data, tocopy); + flush_dcache_page(bh->b_page); + unlock_buffer(bh); + if (journal_quota) + err = ext3_journal_dirty_metadata(handle, bh); + else { + /* Always do at least ordered writes for quotas */ + err = ext3_journal_dirty_data(handle, bh); + mark_buffer_dirty(bh); + } + brelse(bh); + if (err) + goto out; + offset = 0; + towrite -= tocopy; + data += tocopy; + blk++; + } +out: + if (len == towrite) + return err; + if (inode->i_size < off+len-towrite) { + i_size_write(inode, off+len-towrite); + EXT3_I(inode)->i_disksize = inode->i_size; + } + inode->i_version++; + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + ext3_mark_inode_dirty(handle, inode); + up(&inode->i_sem); + return len - towrite; +} + #endif static struct super_block *ext3_get_sb(struct file_system_type *fs_type, Index: linux-2.6.9/include/linux/ext3_jbd.h =================================================================== --- linux-2.6.9.orig/include/linux/ext3_jbd.h 2006-08-25 16:39:09.000000000 +0800 +++ linux-2.6.9/include/linux/ext3_jbd.h 2006-09-14 11:44:29.000000000 +0800 @@ -193,6 +193,8 @@ #define ext3_journal_forget(handle, bh) \ __ext3_journal_forget(__FUNCTION__, (handle), (bh)) +int ext3_journal_dirty_data(handle_t *handle, struct buffer_head *bh); + handle_t *ext3_journal_start_sb(struct super_block *sb, int nblocks); int __ext3_journal_stop(const char *where, handle_t *handle);