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

Reply via email to