Commit 15f2e3d6 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'fs.v5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux

Pull mount_setattr updates from Christian Brauner:
 "This contains a few more patches to massage the mount_setattr()
  codepaths and one minor fix to reuse a helper we added some time back.

  The final two patches do similar cleanups in different ways. One patch
  is mine and the other is Al's who was nice enough to give me a branch
  for it.

  Since his came in later and my branch had been sitting in -next for
  quite some time we just put his on top instead of swap them"

* tag 'fs.v5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux:
  mount_setattr(): clean the control flow and calling conventions
  fs: clean up mount_setattr control flow
  fs: don't open-code mnt_hold_writers()
  fs: simplify check in mount_setattr_commit()
  fs: add mnt_allow_writers() and simplify mount_setattr_prepare()
parents ed464352 e257039f
...@@ -563,12 +563,9 @@ int sb_prepare_remount_readonly(struct super_block *sb) ...@@ -563,12 +563,9 @@ int sb_prepare_remount_readonly(struct super_block *sb)
lock_mount_hash(); lock_mount_hash();
list_for_each_entry(mnt, &sb->s_mounts, mnt_instance) { list_for_each_entry(mnt, &sb->s_mounts, mnt_instance) {
if (!(mnt->mnt.mnt_flags & MNT_READONLY)) { if (!(mnt->mnt.mnt_flags & MNT_READONLY)) {
mnt->mnt.mnt_flags |= MNT_WRITE_HOLD; err = mnt_hold_writers(mnt);
smp_mb(); if (err)
if (mnt_get_writers(mnt) > 0) {
err = -EBUSY;
break; break;
}
} }
} }
if (!err && atomic_long_read(&sb->s_remove_count)) if (!err && atomic_long_read(&sb->s_remove_count))
...@@ -4000,46 +3997,57 @@ static int can_idmap_mount(const struct mount_kattr *kattr, struct mount *mnt) ...@@ -4000,46 +3997,57 @@ static int can_idmap_mount(const struct mount_kattr *kattr, struct mount *mnt)
return 0; return 0;
} }
static struct mount *mount_setattr_prepare(struct mount_kattr *kattr, /**
struct mount *mnt, int *err) * mnt_allow_writers() - check whether the attribute change allows writers
* @kattr: the new mount attributes
* @mnt: the mount to which @kattr will be applied
*
* Check whether thew new mount attributes in @kattr allow concurrent writers.
*
* Return: true if writers need to be held, false if not
*/
static inline bool mnt_allow_writers(const struct mount_kattr *kattr,
const struct mount *mnt)
{ {
struct mount *m = mnt, *last = NULL; return !(kattr->attr_set & MNT_READONLY) ||
(mnt->mnt.mnt_flags & MNT_READONLY);
}
if (!is_mounted(&m->mnt)) { static int mount_setattr_prepare(struct mount_kattr *kattr, struct mount *mnt)
*err = -EINVAL; {
goto out; struct mount *m;
} int err;
if (!(mnt_has_parent(m) ? check_mnt(m) : is_anon_ns(m->mnt_ns))) { for (m = mnt; m; m = next_mnt(m, mnt)) {
*err = -EINVAL; if (!can_change_locked_flags(m, recalc_flags(kattr, m))) {
goto out; err = -EPERM;
} break;
}
do { err = can_idmap_mount(kattr, m);
unsigned int flags; if (err)
break;
flags = recalc_flags(kattr, m); if (!mnt_allow_writers(kattr, m)) {
if (!can_change_locked_flags(m, flags)) { err = mnt_hold_writers(m);
*err = -EPERM; if (err)
goto out; break;
} }
*err = can_idmap_mount(kattr, m); if (!kattr->recurse)
if (*err) return 0;
goto out; }
last = m; if (err) {
struct mount *p;
if ((kattr->attr_set & MNT_READONLY) && for (p = mnt; p != m; p = next_mnt(p, mnt)) {
!(m->mnt.mnt_flags & MNT_READONLY)) { /* If we had to hold writers unblock them. */
*err = mnt_hold_writers(m); if (p->mnt.mnt_flags & MNT_WRITE_HOLD)
if (*err) mnt_unhold_writers(p);
goto out;
} }
} while (kattr->recurse && (m = next_mnt(m, mnt))); }
return err;
out:
return last;
} }
static void do_idmap_mount(const struct mount_kattr *kattr, struct mount *mnt) static void do_idmap_mount(const struct mount_kattr *kattr, struct mount *mnt)
...@@ -4067,48 +4075,32 @@ static void do_idmap_mount(const struct mount_kattr *kattr, struct mount *mnt) ...@@ -4067,48 +4075,32 @@ static void do_idmap_mount(const struct mount_kattr *kattr, struct mount *mnt)
put_user_ns(old_mnt_userns); put_user_ns(old_mnt_userns);
} }
static void mount_setattr_commit(struct mount_kattr *kattr, static void mount_setattr_commit(struct mount_kattr *kattr, struct mount *mnt)
struct mount *mnt, struct mount *last,
int err)
{ {
struct mount *m = mnt; struct mount *m;
do { for (m = mnt; m; m = next_mnt(m, mnt)) {
if (!err) { unsigned int flags;
unsigned int flags;
do_idmap_mount(kattr, m); do_idmap_mount(kattr, m);
flags = recalc_flags(kattr, m); flags = recalc_flags(kattr, m);
WRITE_ONCE(m->mnt.mnt_flags, flags); WRITE_ONCE(m->mnt.mnt_flags, flags);
}
/* /* If we had to hold writers unblock them. */
* We either set MNT_READONLY above so make it visible if (m->mnt.mnt_flags & MNT_WRITE_HOLD)
* before ~MNT_WRITE_HOLD or we failed to recursively
* apply mount options.
*/
if ((kattr->attr_set & MNT_READONLY) &&
(m->mnt.mnt_flags & MNT_WRITE_HOLD))
mnt_unhold_writers(m); mnt_unhold_writers(m);
if (!err && kattr->propagation) if (kattr->propagation)
change_mnt_propagation(m, kattr->propagation); change_mnt_propagation(m, kattr->propagation);
if (!kattr->recurse)
/*
* On failure, only cleanup until we found the first mount
* we failed to handle.
*/
if (err && m == last)
break; break;
} while (kattr->recurse && (m = next_mnt(m, mnt))); }
touch_mnt_namespace(mnt->mnt_ns);
if (!err)
touch_mnt_namespace(mnt->mnt_ns);
} }
static int do_mount_setattr(struct path *path, struct mount_kattr *kattr) static int do_mount_setattr(struct path *path, struct mount_kattr *kattr)
{ {
struct mount *mnt = real_mount(path->mnt), *last = NULL; struct mount *mnt = real_mount(path->mnt);
int err = 0; int err = 0;
if (path->dentry != mnt->mnt.mnt_root) if (path->dentry != mnt->mnt.mnt_root)
...@@ -4129,16 +4121,32 @@ static int do_mount_setattr(struct path *path, struct mount_kattr *kattr) ...@@ -4129,16 +4121,32 @@ static int do_mount_setattr(struct path *path, struct mount_kattr *kattr)
} }
} }
err = -EINVAL;
lock_mount_hash(); lock_mount_hash();
/* Ensure that this isn't anything purely vfs internal. */
if (!is_mounted(&mnt->mnt))
goto out;
/* /*
* Get the mount tree in a shape where we can change mount * If this is an attached mount make sure it's located in the callers
* properties without failure. * mount namespace. If it's not don't let the caller interact with it.
* If this is a detached mount make sure it has an anonymous mount
* namespace attached to it, i.e. we've created it via OPEN_TREE_CLONE.
*/ */
last = mount_setattr_prepare(kattr, mnt, &err); if (!(mnt_has_parent(mnt) ? check_mnt(mnt) : is_anon_ns(mnt->mnt_ns)))
if (last) /* Commit all changes or revert to the old state. */ goto out;
mount_setattr_commit(kattr, mnt, last, err);
/*
* First, we get the mount tree in a shape where we can change mount
* properties without failure. If we succeeded to do so we commit all
* changes and if we failed we clean up.
*/
err = mount_setattr_prepare(kattr, mnt);
if (!err)
mount_setattr_commit(kattr, mnt);
out:
unlock_mount_hash(); unlock_mount_hash();
if (kattr->propagation) { if (kattr->propagation) {
......
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