diff options
| author | Jan Kara <jack@suse.cz> | 2011-05-03 11:12:58 -0400 |
|---|---|---|
| committer | Theodore Ts'o <tytso@mit.edu> | 2011-05-03 11:12:58 -0400 |
| commit | df5e6223407e3e645065c4bd968fee007f0e0287 (patch) | |
| tree | d5027dc9645dea4a5536819ac98f4299757fff22 | |
| parent | 7ad8e4e6ae2a7c95445ee1715b1714106fb95037 (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>
| -rw-r--r-- | fs/ext4/namei.c | 66 |
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 | } | ||
| 2260 | retry: | 2279 | retry: |
| 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; |
| 2346 | err_drop_inode: | ||
| 2347 | unlock_new_inode(inode); | ||
| 2348 | iput(inode); | ||
| 2349 | return err; | ||
| 2306 | } | 2350 | } |
| 2307 | 2351 | ||
| 2308 | static int ext4_link(struct dentry *old_dentry, | 2352 | static int ext4_link(struct dentry *old_dentry, |
