Re: [PATCH 10/32] VFS: Implement a filesystem superblock creation/configuration context [ver #8]
From: Eric Biggers
Date: Tue Jul 03 2018 - 14:33:34 EST
On Fri, May 25, 2018 at 01:06:29AM +0100, David Howells wrote:
> +/**
> + * sget_fc - Find or create a superblock
> + * @fc: Filesystem context.
> + * @test: Comparison callback
> + * @set: Setup callback
> + *
> + * Find or create a superblock using the parameters stored in the filesystem
> + * context and the two callback functions.
> + *
> + * If an extant superblock is matched, then that will be returned with an
> + * elevated reference count that the caller must transfer or discard.
> + *
> + * If no match is made, a new superblock will be allocated and basic
> + * initialisation will be performed (s_type, s_fs_info and s_id will be set and
> + * the set() callback will be invoked), the superblock will be published and it
> + * will be returned in a partially constructed state with SB_BORN and SB_ACTIVE
> + * as yet unset.
> + */
> +struct super_block *sget_fc(struct fs_context *fc,
> + int (*test)(struct super_block *, struct fs_context *),
> + int (*set)(struct super_block *, struct fs_context *))
> +{
> + struct super_block *s = NULL;
> + struct super_block *old;
> + int err;
> +
> + if (!(fc->sb_flags & SB_KERNMOUNT) &&
> + fc->purpose != FS_CONTEXT_FOR_SUBMOUNT) {
> + /* Don't allow mounting unless the caller has CAP_SYS_ADMIN
> + * over the namespace.
> + */
> + if (!(fc->fs_type->fs_flags & FS_USERNS_MOUNT) &&
> + !capable(CAP_SYS_ADMIN))
> + return ERR_PTR(-EPERM);
> + else if (!ns_capable(fc->user_ns, CAP_SYS_ADMIN))
> + return ERR_PTR(-EPERM);
> + }
> +
> +retry:
> + spin_lock(&sb_lock);
> + if (test) {
> + hlist_for_each_entry(old, &fc->fs_type->fs_supers, s_instances) {
> + if (!test(old, fc))
> + continue;
> + if (fc->user_ns != old->s_user_ns) {
> + spin_unlock(&sb_lock);
> + if (s) {
> + up_write(&s->s_umount);
> + destroy_unused_super(s);
> + }
->s_umount is released once here and again in destroy_unused_super().
> + return ERR_PTR(-EBUSY);
> + }
> + if (!grab_super(old))
> + goto retry;
> + if (s) {
> + up_write(&s->s_umount);
> + destroy_unused_super(s);
Same bug here.
> + up_write(&s->s_umount);
> + destroy_unused_super(s);
And here.
- Eric