|
@@ -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,
|
|
|
|
|
|
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) +
|
|
|
|
- EXT3_INDEX_EXTRA_TRANS_BLOCKS + 5 +
|
|
|
|
- EXT3_MAXQUOTAS_INIT_BLOCKS(dir->i_sb));
|
|
|
|
|
|
+ handle = ext3_journal_start(dir, credits);
|
|
if (IS_ERR(handle))
|
|
if (IS_ERR(handle))
|
|
return PTR_ERR(handle);
|
|
return PTR_ERR(handle);
|
|
|
|
|
|
@@ -2211,21 +2228,45 @@ retry:
|
|
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 have a transaction open. All is sweetness. It also sets
|
|
|
|
- * i_size in generic_commit_write().
|
|
|
|
|
|
+ * We cannot call page_symlink() with transaction started
|
|
|
|
+ * because it calls into ext3_write_begin() which acquires page
|
|
|
|
+ * 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);
|
|
|
|
- ext3_mark_inode_dirty(handle, inode);
|
|
|
|
- iput (inode);
|
|
|
|
- goto out_stop;
|
|
|
|
|
|
+ goto err_drop_inode;
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
inode->i_op = &ext3_fast_symlink_inode_operations;
|
|
inode->i_op = &ext3_fast_symlink_inode_operations;
|
|
@@ -2239,6 +2280,10 @@ out_stop:
|
|
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,
|