| From 081bb92b9c6508a16640466a0ba6f89ce90ca1d6 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 14 Nov 2019 18:41:03 +0000 |
| Subject: [PATCH] afs: Fix race in commit bulk status fetch |
| |
| commit a28f239e296767ebf4ec4ae8a9ecb57d0d444b3f upstream. |
| |
| When a lookup is done, the afs filesystem will perform a bulk status-fetch |
| operation on the requested vnode (file) plus the next 49 other vnodes from |
| the directory list (in AFS, directory contents are downloaded as blobs and |
| parsed locally). When the results are received, it will speculatively |
| populate the inode cache from the extra data. |
| |
| However, if the lookup races with another lookup on the same directory, but |
| for a different file - one that's in the 49 extra fetches, then if the bulk |
| status-fetch operation finishes first, it will try and update the inode |
| from the other lookup. |
| |
| If this other inode is still in the throes of being created, however, this |
| will cause an assertion failure in afs_apply_status(): |
| |
| BUG_ON(test_bit(AFS_VNODE_UNSET, &vnode->flags)); |
| |
| on or about fs/afs/inode.c:175 because it expects data to be there already |
| that it can compare to. |
| |
| Fix this by skipping the update if the inode is being created as the |
| creator will presumably set up the inode with the same information. |
| |
| Fixes: 39db9815da48 ("afs: Fix application of the results of a inline bulk status fetch") |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| Reviewed-by: Marc Dionne <marc.dionne@auristor.com> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/fs/afs/dir.c b/fs/afs/dir.c |
| index 9bd5c067d55d..e26c901877ea 100644 |
| --- a/fs/afs/dir.c |
| +++ b/fs/afs/dir.c |
| @@ -804,7 +804,12 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry, |
| continue; |
| |
| if (cookie->inodes[i]) { |
| - afs_vnode_commit_status(&fc, AFS_FS_I(cookie->inodes[i]), |
| + struct afs_vnode *iv = AFS_FS_I(cookie->inodes[i]); |
| + |
| + if (test_bit(AFS_VNODE_UNSET, &iv->flags)) |
| + continue; |
| + |
| + afs_vnode_commit_status(&fc, iv, |
| scb->cb_break, NULL, scb); |
| continue; |
| } |
| -- |
| 2.7.4 |
| |