Commit 440fbc3a authored by David Howells's avatar David Howells

afs: Fix unlink

Repeating creation and deletion of a file on an afs mount will run the box
out of memory, e.g.:

	dd if=/dev/zero of=/afs/scratch/m0 bs=$((1024*1024)) count=512
	rm /afs/scratch/m0

The problem seems to be that it's not properly decrementing the nlink count
so that the inode can be scrapped.

Note that this doesn't fix local creation followed by remote deletion.
That's harder to handle and will require a separate patch as we're not told
that the file has been deleted - only that the directory has changed.
Reported-by: default avatarMarc Dionne <marc.dionne@auristor.com>
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent 7888da95
...@@ -895,20 +895,38 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -895,20 +895,38 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
* However, if we didn't have a callback promise outstanding, or it was * However, if we didn't have a callback promise outstanding, or it was
* outstanding on a different server, then it won't break it either... * outstanding on a different server, then it won't break it either...
*/ */
static int afs_dir_remove_link(struct dentry *dentry, struct key *key) static int afs_dir_remove_link(struct dentry *dentry, struct key *key,
unsigned long d_version_before,
unsigned long d_version_after)
{ {
bool dir_valid;
int ret = 0; int ret = 0;
/* There were no intervening changes on the server if the version
* number we got back was incremented by exactly 1.
*/
dir_valid = (d_version_after == d_version_before + 1);
if (d_really_is_positive(dentry)) { if (d_really_is_positive(dentry)) {
struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry)); struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
if (dir_valid) {
drop_nlink(&vnode->vfs_inode);
if (vnode->vfs_inode.i_nlink == 0) {
set_bit(AFS_VNODE_DELETED, &vnode->flags);
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
}
ret = 0;
} else {
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
kdebug("AFS_VNODE_DELETED"); kdebug("AFS_VNODE_DELETED");
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
ret = afs_validate(vnode, key); ret = afs_validate(vnode, key);
if (ret == -ESTALE) if (ret == -ESTALE)
ret = 0; ret = 0;
}
_debug("nlink %d [val %d]", vnode->vfs_inode.i_nlink, ret); _debug("nlink %d [val %d]", vnode->vfs_inode.i_nlink, ret);
} }
...@@ -923,6 +941,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -923,6 +941,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
struct afs_fs_cursor fc; struct afs_fs_cursor fc;
struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode; struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode;
struct key *key; struct key *key;
unsigned long d_version = (unsigned long)dentry->d_fsdata;
int ret; int ret;
_enter("{%x:%u},{%pd}", _enter("{%x:%u},{%pd}",
...@@ -955,7 +974,9 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -955,7 +974,9 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
afs_vnode_commit_status(&fc, dvnode, fc.cb_break); afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
ret = afs_end_vnode_operation(&fc); ret = afs_end_vnode_operation(&fc);
if (ret == 0) if (ret == 0)
ret = afs_dir_remove_link(dentry, key); ret = afs_dir_remove_link(
dentry, key, d_version,
(unsigned long)dvnode->status.data_version);
} }
error_key: error_key:
......
...@@ -377,6 +377,10 @@ int afs_validate(struct afs_vnode *vnode, struct key *key) ...@@ -377,6 +377,10 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
} }
read_sequnlock_excl(&vnode->cb_lock); read_sequnlock_excl(&vnode->cb_lock);
if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
clear_nlink(&vnode->vfs_inode);
if (valid) if (valid)
goto valid; goto valid;
......
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