Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[patch] vfs: fix lookup on deleted directory

Lookup can install a child dentry for a deleted directory. This keeps
the directory dentry alive, and the inode pinned in the cache and on
disk, even after all external references have gone away.

This isn't a big problem normally, since memory pressure or umount
will clear out the directory dentry and its children, releasing the
inode. But for UBIFS this causes problems because its orphan area can
overflow.

Fix this by returning ENOENT for all lookups on a S_DEAD directory
before creating a child dentry.

Thanks to Zoltan Sogor for noticing this while testing UBIFS, and
Artem for the excellent analysis of the problem and testing.

Reported-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Tested-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

authored by

Miklos Szeredi and committed by
Al Viro
d70b67c8 a048d3af

+17 -2
+17 -2
fs/namei.c
··· 519 519 */ 520 520 result = d_lookup(parent, name); 521 521 if (!result) { 522 - struct dentry * dentry = d_alloc(parent, name); 522 + struct dentry *dentry; 523 + 524 + /* Don't create child dentry for a dead directory. */ 525 + result = ERR_PTR(-ENOENT); 526 + if (IS_DEADDIR(dir)) 527 + goto out_unlock; 528 + 529 + dentry = d_alloc(parent, name); 523 530 result = ERR_PTR(-ENOMEM); 524 531 if (dentry) { 525 532 result = dir->i_op->lookup(dir, dentry, nd); ··· 535 528 else 536 529 result = dentry; 537 530 } 531 + out_unlock: 538 532 mutex_unlock(&dir->i_mutex); 539 533 return result; 540 534 } ··· 1325 1317 1326 1318 dentry = cached_lookup(base, name, nd); 1327 1319 if (!dentry) { 1328 - struct dentry *new = d_alloc(base, name); 1320 + struct dentry *new; 1321 + 1322 + /* Don't create child dentry for a dead directory. */ 1323 + dentry = ERR_PTR(-ENOENT); 1324 + if (IS_DEADDIR(inode)) 1325 + goto out; 1326 + 1327 + new = d_alloc(base, name); 1329 1328 dentry = ERR_PTR(-ENOMEM); 1330 1329 if (!new) 1331 1330 goto out;