diff options
author | Jan Kara <jack@suse.cz> | 2005-01-03 04:12:49 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-01-03 04:12:49 -0800 |
commit | 6b394613da900cbbf87493a09450c105042ffdc8 (patch) | |
tree | e68d84db12acd12e5e75a98143d845c7437a4b24 /fs | |
parent | 84f308c2eb1d08e80c8b896037e0c4dde3ced11f (diff) | |
download | history-6b394613da900cbbf87493a09450c105042ffdc8.tar.gz |
[PATCH] Fix of quota deadlock on pagelock: ext2
Implementation of quota reading and writing functions for ext2.
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext2/ext2.h | 1 | ||||
-rw-r--r-- | fs/ext2/inode.c | 2 | ||||
-rw-r--r-- | fs/ext2/super.c | 114 |
3 files changed, 116 insertions, 1 deletions
diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 1294f3869b49d2..9f1a40e7945cd3 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -119,6 +119,7 @@ extern int ext2_write_inode (struct inode *, int); extern void ext2_delete_inode (struct inode *); extern int ext2_sync_inode (struct inode *); extern void ext2_discard_prealloc (struct inode *); +extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int); extern void ext2_truncate (struct inode *); extern int ext2_setattr (struct dentry *, struct iattr *); extern void ext2_set_inode_flags(struct inode *inode); diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index e09c3e13248cc8..8b61170e7cc80f 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -524,7 +524,7 @@ changed: * reachable from inode. */ -static int ext2_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) +int ext2_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { int err = -EIO; int offsets[4]; diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 1bb1e0885bfe50..cf44dc288490c1 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -203,6 +203,11 @@ static void ext2_clear_inode(struct inode *inode) } +#ifdef CONFIG_QUOTA +static ssize_t ext2_quota_read(struct super_block *sb, int type, char *data, size_t len, loff_t off); +static ssize_t ext2_quota_write(struct super_block *sb, int type, const char *data, size_t len, loff_t off); +#endif + static struct super_operations ext2_sops = { .alloc_inode = ext2_alloc_inode, .destroy_inode = ext2_destroy_inode, @@ -214,6 +219,10 @@ static struct super_operations ext2_sops = { .statfs = ext2_statfs, .remount_fs = ext2_remount, .clear_inode = ext2_clear_inode, +#ifdef CONFIG_QUOTA + .quota_read = ext2_quota_read, + .quota_write = ext2_quota_write, +#endif }; /* Yes, most of these are left as NULL!! @@ -1004,6 +1013,111 @@ static struct super_block *ext2_get_sb(struct file_system_type *fs_type, return get_sb_bdev(fs_type, flags, dev_name, data, ext2_fill_super); } +#ifdef CONFIG_QUOTA + +/* 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 ext2_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 >> EXT2_BLOCK_SIZE_BITS(sb); + int err = 0; + int offset = off & (sb->s_blocksize - 1); + int tocopy; + size_t toread; + struct buffer_head tmp_bh; + 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; + + tmp_bh.b_state = 0; + err = ext2_get_block(inode, blk, &tmp_bh, 0); + if (err) + return err; + if (!buffer_mapped(&tmp_bh)) /* A hole? */ + memset(data, 0, tocopy); + else { + bh = sb_bread(sb, tmp_bh.b_blocknr); + if (!bh) + return -EIO; + memcpy(data, bh->b_data+offset, tocopy); + brelse(bh); + } + offset = 0; + toread -= tocopy; + data += tocopy; + blk++; + } + return len; +} + +/* Write to quotafile */ +static ssize_t ext2_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 >> EXT2_BLOCK_SIZE_BITS(sb); + int err = 0; + int offset = off & (sb->s_blocksize - 1); + int tocopy; + size_t towrite = len; + struct buffer_head tmp_bh; + struct buffer_head *bh; + + down(&inode->i_sem); + while (towrite > 0) { + tocopy = sb->s_blocksize - offset < towrite ? + sb->s_blocksize - offset : towrite; + + tmp_bh.b_state = 0; + err = ext2_get_block(inode, blk, &tmp_bh, 1); + if (err) + goto out; + if (offset || tocopy != EXT2_BLOCK_SIZE(sb)) + bh = sb_bread(sb, tmp_bh.b_blocknr); + else + bh = sb_getblk(sb, tmp_bh.b_blocknr); + if (!bh) { + err = -EIO; + goto out; + } + lock_buffer(bh); + memcpy(bh->b_data+offset, data, tocopy); + flush_dcache_page(bh->b_page); + set_buffer_uptodate(bh); + mark_buffer_dirty(bh); + unlock_buffer(bh); + brelse(bh); + 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); + inode->i_version++; + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + up(&inode->i_sem); + return len - towrite; +} + +#endif + static struct file_system_type ext2_fs_type = { .owner = THIS_MODULE, .name = "ext2", |