aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2011-05-03 11:12:58 -0400
committerTheodore Ts'o <tytso@mit.edu>2011-05-03 11:12:58 -0400
commitdf5e6223407e3e645065c4bd968fee007f0e0287 (patch)
treed5027dc9645dea4a5536819ac98f4299757fff22 /fs
parent7ad8e4e6ae2a7c95445ee1715b1714106fb95037 (diff)
ext4: fix deadlock in ext4_symlink() in ENOSPC conditions
ext4_symlink() cannot call __page_symlink() with transaction open. __page_symlink() calls ext4_write_begin() which can wait for transaction commit if we are running out of space thus causing a deadlock. Also error recovery in ext4_truncate_failed_write() does not count with the transaction being already started (although I'm not aware of any particular deadlock here). 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: Jan Kara <jack@suse.cz> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs')
-rw-r--r--fs/ext4/namei.c66
1 files changed, 55 insertions, 11 deletions
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index cadf04b924aa..3c7a06e58469 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2250,6 +2250,7 @@ static int ext4_symlink(struct inode *dir,
2250 handle_t *handle; 2250 handle_t *handle;
2251 struct inode *inode; 2251 struct inode *inode;
2252 int l, err, retries = 0; 2252 int l, err, retries = 0;
2253 int credits;
2253 2254
2254 l = strlen(symname)+1; 2255 l = strlen(symname)+1;
2255 if (l > dir->i_sb->s_blocksize) 2256 if (l > dir->i_sb->s_blocksize)
@@ -2257,10 +2258,26 @@ static int ext4_symlink(struct inode *dir,
2257 2258
2258 dquot_initialize(dir); 2259 dquot_initialize(dir);
2259 2260
2261 if (l > EXT4_N_BLOCKS * 4) {
2262 /*
2263 * For non-fast symlinks, we just allocate inode and put it on
2264 * orphan list in the first transaction => we need bitmap,
2265 * group descriptor, sb, inode block, quota blocks.
2266 */
2267 credits = 4 + EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb);
2268 } else {
2269 /*
2270 * Fast symlink. We have to add entry to directory
2271 * (EXT4_DATA_TRANS_BLOCKS + EXT4_INDEX_EXTRA_TRANS_BLOCKS),
2272 * allocate new inode (bitmap, group descriptor, inode block,
2273 * quota blocks, sb is already counted in previous macros).
2274 */
2275 credits = EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
2276 EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
2277 EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb);
2278 }
2260retry: 2279retry:
2261 handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + 2280 handle = ext4_journal_start(dir, credits);
2262 EXT4_INDEX_EXTRA_TRANS_BLOCKS + 5 +
2263 EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb));
2264 if (IS_ERR(handle)) 2281 if (IS_ERR(handle))
2265 return PTR_ERR(handle); 2282 return PTR_ERR(handle);
2266 2283
@@ -2273,21 +2290,44 @@ retry:
2273 if (IS_ERR(inode)) 2290 if (IS_ERR(inode))
2274 goto out_stop; 2291 goto out_stop;
2275 2292
2276 if (l > sizeof(EXT4_I(inode)->i_data)) { 2293 if (l > EXT4_N_BLOCKS * 4) {
2277 inode->i_op = &ext4_symlink_inode_operations; 2294 inode->i_op = &ext4_symlink_inode_operations;
2278 ext4_set_aops(inode); 2295 ext4_set_aops(inode);
2279 /* 2296 /*
2280 * page_symlink() calls into ext4_prepare/commit_write. 2297 * We cannot call page_symlink() with transaction started
2281 * We have a transaction open. All is sweetness. It also sets 2298 * because it calls into ext4_write_begin() which can wait
2282 * i_size in generic_commit_write(). 2299 * for transaction commit if we are running out of space
2300 * and thus we deadlock. So we have to stop transaction now
2301 * and restart it when symlink contents is written.
2302 *
2303 * To keep fs consistent in case of crash, we have to put inode
2304 * to orphan list in the mean time.
2283 */ 2305 */
2306 drop_nlink(inode);
2307 err = ext4_orphan_add(handle, inode);
2308 ext4_journal_stop(handle);
2309 if (err)
2310 goto err_drop_inode;
2284 err = __page_symlink(inode, symname, l, 1); 2311 err = __page_symlink(inode, symname, l, 1);
2312 if (err)
2313 goto err_drop_inode;
2314 /*
2315 * Now inode is being linked into dir (EXT4_DATA_TRANS_BLOCKS
2316 * + EXT4_INDEX_EXTRA_TRANS_BLOCKS), inode is also modified
2317 */
2318 handle = ext4_journal_start(dir,
2319 EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
2320 EXT4_INDEX_EXTRA_TRANS_BLOCKS + 1);
2321 if (IS_ERR(handle)) {
2322 err = PTR_ERR(handle);
2323 goto err_drop_inode;
2324 }
2325 inc_nlink(inode);
2326 err = ext4_orphan_del(handle, inode);
2285 if (err) { 2327 if (err) {
2328 ext4_journal_stop(handle);
2286 clear_nlink(inode); 2329 clear_nlink(inode);
2287 unlock_new_inode(inode); 2330 goto err_drop_inode;
2288 ext4_mark_inode_dirty(handle, inode);
2289 iput(inode);
2290 goto out_stop;
2291 } 2331 }
2292 } else { 2332 } else {
2293 /* clear the extent format for fast symlink */ 2333 /* clear the extent format for fast symlink */
@@ -2303,6 +2343,10 @@ out_stop:
2303 if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) 2343 if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
2304 goto retry; 2344 goto retry;
2305 return err; 2345 return err;
2346err_drop_inode:
2347 unlock_new_inode(inode);
2348 iput(inode);
2349 return err;
2306} 2350}
2307 2351
2308static int ext4_link(struct dentry *old_dentry, 2352static int ext4_link(struct dentry *old_dentry,