fs: unset MNT_WRITE_HOLD on failure

After mnt_hold_writers() has been called we will always have set MNT_WRITE_HOLD
and consequently we always need to pair mnt_hold_writers() with
mnt_unhold_writers(). After the recent cleanup in [1] where Al switched from a
do-while to a for loop the cleanup currently fails to unset MNT_WRITE_HOLD for
the first mount that was changed. Fix this and make sure that the first mount
will be cleaned up and add some comments to make it more obvious.

Link: https://lore.kernel.org/lkml/0000000000007cc21d05dd0432b8@google.com
Link: https://lore.kernel.org/lkml/00000000000080e10e05dd043247@google.com
Link: https://lore.kernel.org/r/20220420131925.2464685-1-brauner@kernel.org
Fixes: e257039f0f ("mount_setattr(): clean the control flow and calling conventions") [1]
Cc: Hillf Danton <hdanton@sina.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Reported-by: syzbot+10a16d1c43580983f6a2@syzkaller.appspotmail.com
Reported-by: syzbot+306090cfa3294f0bbfb3@syzkaller.appspotmail.com
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Christian Brauner (Microsoft) <brauner@kernel.org>
This commit is contained in:
Christian Brauner 2022-04-20 15:19:25 +02:00 committed by Christian Brauner (Microsoft)
parent b2d229d4dd
commit 0014edaedf
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
1 changed files with 13 additions and 1 deletions

View File

@ -4058,10 +4058,22 @@ static int mount_setattr_prepare(struct mount_kattr *kattr, struct mount *mnt)
if (err) {
struct mount *p;
for (p = mnt; p != m; p = next_mnt(p, mnt)) {
/*
* If we had to call mnt_hold_writers() MNT_WRITE_HOLD will
* be set in @mnt_flags. The loop unsets MNT_WRITE_HOLD for all
* mounts and needs to take care to include the first mount.
*/
for (p = mnt; p; p = next_mnt(p, mnt)) {
/* If we had to hold writers unblock them. */
if (p->mnt.mnt_flags & MNT_WRITE_HOLD)
mnt_unhold_writers(p);
/*
* We're done once the first mount we changed got
* MNT_WRITE_HOLD unset.
*/
if (p == m)
break;
}
}
return err;