diff options
Diffstat (limited to 'fs/ext3/inode.c')
| -rw-r--r-- | fs/ext3/inode.c | 59 |
1 files changed, 45 insertions, 14 deletions
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 339ba65333c1..e0ad97fe3712 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -32,7 +32,6 @@ #include <linux/quotaops.h> #include <linux/module.h> - /* * SEARCH_FROM_ZERO forces each block allocation to search from the start * of the filesystem. This is to force rapid reallocation of recently-freed @@ -715,6 +714,8 @@ err_out: * reachable from inode. * * akpm: `handle' can be NULL if create == 0. + * + * The BKL may not be held on entry here. Be sure to take it early. */ static int ext3_get_block_handle(handle_t *handle, struct inode *inode, @@ -1018,13 +1019,26 @@ static int ext3_prepare_write(struct file *file, struct page *page, ret = PTR_ERR(handle); goto out; } + unlock_kernel(); ret = block_prepare_write(page, from, to, ext3_get_block); + lock_kernel(); if (ret != 0) goto prepare_write_failed; - if (ext3_should_journal_data(inode)) + if (ext3_should_journal_data(inode)) { ret = walk_page_buffers(handle, page->buffers, from, to, NULL, do_journal_get_write_access); + if (ret) { + /* + * We're going to fail this prepare_write(), + * so commit_write() will not be called. + * We need to undo block_prepare_write()'s kmap(). + * AKPM: Do we need to clear PageUptodate? I don't + * think so. + */ + kunmap(page); + } + } prepare_write_failed: if (ret) ext3_journal_stop(handle, inode); @@ -1092,7 +1106,7 @@ static int ext3_commit_write(struct file *file, struct page *page, kunmap(page); if (pos > inode->i_size) inode->i_size = pos; - set_bit(EXT3_STATE_JDATA, &inode->u.ext3_i.i_state); + EXT3_I(inode)->i_state |= EXT3_STATE_JDATA; } else { if (ext3_should_order_data(inode)) { ret = walk_page_buffers(handle, page->buffers, @@ -1100,8 +1114,17 @@ static int ext3_commit_write(struct file *file, struct page *page, } /* Be careful here if generic_commit_write becomes a * required invocation after block_prepare_write. */ - if (ret == 0) + if (ret == 0) { ret = generic_commit_write(file, page, from, to); + } else { + /* + * block_prepare_write() was called, but we're not + * going to call generic_commit_write(). So we + * need to perform generic_commit_write()'s kunmap + * by hand. + */ + kunmap(page); + } } if (inode->i_size > inode->u.ext3_i.i_disksize) { inode->u.ext3_i.i_disksize = inode->i_size; @@ -1136,7 +1159,7 @@ static int ext3_bmap(struct address_space *mapping, long block) journal_t *journal; int err; - if (test_and_clear_bit(EXT3_STATE_JDATA, &inode->u.ext3_i.i_state)) { + if (EXT3_I(inode)->i_state & EXT3_STATE_JDATA) { /* * This is a REALLY heavyweight approach, but the use of * bmap on dirty files is expected to be extremely rare: @@ -1155,6 +1178,7 @@ static int ext3_bmap(struct address_space *mapping, long block) * everything they get. */ + EXT3_I(inode)->i_state &= ~EXT3_STATE_JDATA; journal = EXT3_JOURNAL(inode); journal_lock_updates(journal); err = journal_flush(journal); @@ -2195,7 +2219,7 @@ static int ext3_do_update_inode(handle_t *handle, /* If we are not tracking these fields in the in-memory inode, * then preserve them on disk, but still initialise them to zero * for new inodes. */ - if (inode->u.ext3_i.i_state & EXT3_STATE_NEW) { + if (EXT3_I(inode)->i_state & EXT3_STATE_NEW) { raw_inode->i_faddr = 0; raw_inode->i_frag = 0; raw_inode->i_fsize = 0; @@ -2241,7 +2265,7 @@ static int ext3_do_update_inode(handle_t *handle, rc = ext3_journal_dirty_metadata(handle, bh); if (!err) err = rc; - inode->u.ext3_i.i_state &= ~EXT3_STATE_NEW; + EXT3_I(inode)->i_state &= ~EXT3_STATE_NEW; out_brelse: brelse (bh); @@ -2325,12 +2349,20 @@ void ext3_write_inode(struct inode *inode, int wait) int ext3_setattr(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; - int error, rc; + int error, rc = 0; + const unsigned int ia_valid = attr->ia_valid; error = inode_change_ok(inode, attr); if (error) return error; - + + if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || + (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) { + error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0; + if (error) + return error; + } + if (attr->ia_valid & ATTR_SIZE && attr->ia_size < inode->i_size) { handle_t *handle; @@ -2348,7 +2380,7 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr) ext3_journal_stop(handle, inode); } - inode_setattr(inode, attr); + rc = inode_setattr(inode, attr); /* If inode_setattr's call to ext3_truncate failed to get a * transaction handle at all, we need to clean up the in-core @@ -2358,7 +2390,9 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr) err_out: ext3_std_error(inode->i_sb, error); - return 0; + if (!error) + error = rc; + return error; } @@ -2659,6 +2693,3 @@ int ext3_change_inode_journal_flag(struct inode *inode, int val) * here, in ext3_aops_journal_start() to ensure that the forthcoming "see if we * need to extend" test in ext3_prepare_write() succeeds. */ - - -MODULE_LICENSE("GPL"); |
