Commit ae54870a authored by Jan Kara's avatar Jan Kara

ext3: Fix lock inversion in ext3_symlink()

ext3_symlink() cannot call __page_symlink() with transaction open.
__page_symlink() calls ext3_write_begin() which gets page lock which ranks
above transaction start (thus lock ordering is violated) and and also
ext3_write_begin() waits for a transaction commit when we run out of space
which never happens if we hold transaction open.

Fix the problem by stopping a transaction before calling __page_symlink()
(we have to be careful and put inode to orphan list so that it gets deleted
in case of crash) and starting another one after __page_symlink() returns
for addition of symlink into a directory.
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent fafc9929
...@@ -2189,6 +2189,7 @@ static int ext3_symlink (struct inode * dir, ...@@ -2189,6 +2189,7 @@ static int ext3_symlink (struct inode * dir,
handle_t *handle; handle_t *handle;
struct inode * inode; struct inode * inode;
int l, err, retries = 0; int l, err, retries = 0;
int credits;
l = strlen(symname)+1; l = strlen(symname)+1;
if (l > dir->i_sb->s_blocksize) if (l > dir->i_sb->s_blocksize)
...@@ -2196,10 +2197,26 @@ static int ext3_symlink (struct inode * dir, ...@@ -2196,10 +2197,26 @@ static int ext3_symlink (struct inode * dir,
dquot_initialize(dir); dquot_initialize(dir);
if (l > EXT3_N_BLOCKS * 4) {
/*
* For non-fast symlinks, we just allocate inode and put it on
* orphan list in the first transaction => we need bitmap,
* group descriptor, sb, inode block, quota blocks.
*/
credits = 4 + EXT3_MAXQUOTAS_INIT_BLOCKS(dir->i_sb);
} else {
/*
* Fast symlink. We have to add entry to directory
* (EXT3_DATA_TRANS_BLOCKS + EXT3_INDEX_EXTRA_TRANS_BLOCKS),
* allocate new inode (bitmap, group descriptor, inode block,
* quota blocks, sb is already counted in previous macros).
*/
credits = EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 +
EXT3_MAXQUOTAS_INIT_BLOCKS(dir->i_sb);
}
retry: retry:
handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) + handle = ext3_journal_start(dir, credits);
EXT3_INDEX_EXTRA_TRANS_BLOCKS + 5 +
EXT3_MAXQUOTAS_INIT_BLOCKS(dir->i_sb));
if (IS_ERR(handle)) if (IS_ERR(handle))
return PTR_ERR(handle); return PTR_ERR(handle);
...@@ -2211,21 +2228,45 @@ static int ext3_symlink (struct inode * dir, ...@@ -2211,21 +2228,45 @@ static int ext3_symlink (struct inode * dir,
if (IS_ERR(inode)) if (IS_ERR(inode))
goto out_stop; goto out_stop;
if (l > sizeof (EXT3_I(inode)->i_data)) { if (l > EXT3_N_BLOCKS * 4) {
inode->i_op = &ext3_symlink_inode_operations; inode->i_op = &ext3_symlink_inode_operations;
ext3_set_aops(inode); ext3_set_aops(inode);
/* /*
* page_symlink() calls into ext3_prepare/commit_write. * We cannot call page_symlink() with transaction started
* We have a transaction open. All is sweetness. It also sets * because it calls into ext3_write_begin() which acquires page
* i_size in generic_commit_write(). * lock which ranks below transaction start (and it can also
* wait for journal commit if we are running out of space). So
* we have to stop transaction now and restart it when symlink
* contents is written.
*
* To keep fs consistent in case of crash, we have to put inode
* to orphan list in the mean time.
*/ */
drop_nlink(inode);
err = ext3_orphan_add(handle, inode);
ext3_journal_stop(handle);
if (err)
goto err_drop_inode;
err = __page_symlink(inode, symname, l, 1); err = __page_symlink(inode, symname, l, 1);
if (err)
goto err_drop_inode;
/*
* Now inode is being linked into dir (EXT3_DATA_TRANS_BLOCKS
* + EXT3_INDEX_EXTRA_TRANS_BLOCKS), inode is also modified
*/
handle = ext3_journal_start(dir,
EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
EXT3_INDEX_EXTRA_TRANS_BLOCKS + 1);
if (IS_ERR(handle)) {
err = PTR_ERR(handle);
goto err_drop_inode;
}
inc_nlink(inode);
err = ext3_orphan_del(handle, inode);
if (err) { if (err) {
ext3_journal_stop(handle);
drop_nlink(inode); drop_nlink(inode);
unlock_new_inode(inode); goto err_drop_inode;
ext3_mark_inode_dirty(handle, inode);
iput (inode);
goto out_stop;
} }
} else { } else {
inode->i_op = &ext3_fast_symlink_inode_operations; inode->i_op = &ext3_fast_symlink_inode_operations;
...@@ -2239,6 +2280,10 @@ static int ext3_symlink (struct inode * dir, ...@@ -2239,6 +2280,10 @@ static int ext3_symlink (struct inode * dir,
if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries)) if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries))
goto retry; goto retry;
return err; return err;
err_drop_inode:
unlock_new_inode(inode);
iput (inode);
return err;
} }
static int ext3_link (struct dentry * old_dentry, static int ext3_link (struct dentry * old_dentry,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment