diff mbox series

kernel: Add Mount Option For Efivarfs

Message ID 20230831153108.2021554-1-jiaozhou@google.com
State New
Headers show
Series kernel: Add Mount Option For Efivarfs | expand

Commit Message

Jiao Zhou Aug. 31, 2023, 3:31 p.m. UTC
Add uid and gid in efivarfs's mount option, so that
we can mount the file system with ownership. This approach
 is used by a number of other filesystems that don't have
native support for ownership.

TEST=FEATURES=test emerge-reven chromeos-kernel-5_15

Signed-off-by: Jiao Zhou <jiaozhou@google.com>
Reported-by: kernel test robot <oliver.sang@intel.com>
Closes: https://lore.kernel.org/oe-lkp/202308291443.ea96ac66-oliver.sang@intel.com
---
 fs/efivarfs/inode.c    |  4 +++
 fs/efivarfs/internal.h |  9 ++++++
 fs/efivarfs/super.c    | 65 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 78 insertions(+)

Comments

Ard Biesheuvel Aug. 31, 2023, 3:55 p.m. UTC | #1
Hello Jiao Zhou,

This is the second revision of your patch. Please include this fact in
the subject header, i.e., [PATCH v2] so it is clearly visible.

On Thu, 31 Aug 2023 at 17:31, Jiao Zhou <jiaozhou@google.com> wrote:
>
> Add uid and gid in efivarfs's mount option, so that
> we can mount the file system with ownership. This approach
>  is used by a number of other filesystems that don't have
> native support for ownership.
>
> TEST=FEATURES=test emerge-reven chromeos-kernel-5_15

What does this line mean? Please drop it if it has internal relevance only.

>
> Signed-off-by: Jiao Zhou <jiaozhou@google.com>
> Reported-by: kernel test robot <oliver.sang@intel.com>
> Closes: https://lore.kernel.org/oe-lkp/202308291443.ea96ac66-oliver.sang@intel.com

Please drop these lines - the robot found an issue caused by a
proposed patch, and the newest revision should no longer trigger that
issue. So these reports have no long term value, and don't deserve a
mention in the commit log. I understand that the instructions in those
reports are inconsistent with this, but those reports should generally
be taken with a grain of salt in any case.

You also failed to include Matthew Garrett's ack.

> ---

Here (below the ---) is where we generally expect an informal revision
history of the patch.

So something like

v2:
- add missing sentinel entry in fs_parameter_spec[] array
- whatever else you did to fix the issue reported by the robot.

For subsequent revisions, please append (or prepend) to the existing
changelog so the entire revision history is documented.

Please fix all of those issues in a v3. Some more below.

>  fs/efivarfs/inode.c    |  4 +++
>  fs/efivarfs/internal.h |  9 ++++++
>  fs/efivarfs/super.c    | 65 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 78 insertions(+)
>
> diff --git a/fs/efivarfs/inode.c b/fs/efivarfs/inode.c
> index 939e5e242b98..de57fb6c28e1 100644
> --- a/fs/efivarfs/inode.c
> +++ b/fs/efivarfs/inode.c
> @@ -20,9 +20,13 @@ struct inode *efivarfs_get_inode(struct super_block *sb,
>                                 const struct inode *dir, int mode,
>                                 dev_t dev, bool is_removable)
>  {
> +       struct efivarfs_fs_info *fsi = sb->s_fs_info;
>         struct inode *inode = new_inode(sb);
> +       struct efivarfs_mount_opts *opts = &fsi->mount_opts;
>
>         if (inode) {
> +               inode->i_uid = opts->uid;
> +               inode->i_gid = opts->gid;
>                 inode->i_ino = get_next_ino();
>                 inode->i_mode = mode;
>                 inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
> diff --git a/fs/efivarfs/internal.h b/fs/efivarfs/internal.h
> index 30ae44cb7453..57deaf56d8e2 100644
> --- a/fs/efivarfs/internal.h
> +++ b/fs/efivarfs/internal.h
> @@ -8,6 +8,15 @@
>
>  #include <linux/list.h>
>
> +struct efivarfs_mount_opts {
> +       kuid_t uid;
> +       kgid_t gid;
> +};
> +
> +struct efivarfs_fs_info {
> +       struct efivarfs_mount_opts mount_opts;
> +};
> +
>  extern const struct file_operations efivarfs_file_operations;
>  extern const struct inode_operations efivarfs_dir_inode_operations;
>  extern bool efivarfs_valid_name(const char *str, int len);
> diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c
> index 15880a68faad..d67b0d157ff5 100644
> --- a/fs/efivarfs/super.c
> +++ b/fs/efivarfs/super.c
> @@ -8,6 +8,7 @@
>  #include <linux/efi.h>
>  #include <linux/fs.h>
>  #include <linux/fs_context.h>
> +#include <linux/fs_parser.h>
>  #include <linux/module.h>
>  #include <linux/pagemap.h>
>  #include <linux/ucs2_string.h>
> @@ -23,10 +24,27 @@ static void efivarfs_evict_inode(struct inode *inode)
>         clear_inode(inode);
>  }
>
> +static int efivarfs_show_options(struct seq_file *m, struct dentry *root)
> +{
> +       struct super_block *sb = root->d_sb;
> +       struct efivarfs_fs_info *sbi = sb->s_fs_info;
> +       struct efivarfs_mount_opts *opts = &sbi->mount_opts;
> +
> +       /* Show partition info */
> +       if (!uid_eq(opts->uid, GLOBAL_ROOT_UID))
> +               seq_printf(m, ",uid=%u",
> +                               from_kuid_munged(&init_user_ns, opts->uid));
> +       if (!gid_eq(opts->gid, GLOBAL_ROOT_GID))
> +               seq_printf(m, ",gid=%u",
> +                               from_kgid_munged(&init_user_ns, opts->gid));
> +       return 0;
> +}
> +
>  static const struct super_operations efivarfs_ops = {
>         .statfs = simple_statfs,
>         .drop_inode = generic_delete_inode,
>         .evict_inode = efivarfs_evict_inode,
> +       .show_options   = efivarfs_show_options,
>  };
>
>  /*
> @@ -190,6 +208,41 @@ static int efivarfs_destroy(struct efivar_entry *entry, void *data)
>         return 0;
>  }
>
> +enum {
> +       Opt_uid, Opt_gid,
> +};
> +
> +static const struct fs_parameter_spec efivarfs_parameters[] = {
> +       fsparam_u32("uid",                      Opt_uid),
> +       fsparam_u32("gid",                      Opt_gid),
> +       {},
> +};
> +
> +static int efivarfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
> +{
> +       struct efivarfs_fs_info *sbi = fc->s_fs_info;
> +       struct efivarfs_mount_opts *opts = &sbi->mount_opts;
> +       struct fs_parse_result result;
> +       int opt;
> +
> +       opt = fs_parse(fc, efivarfs_parameters, param, &result);
> +       if (opt < 0)
> +               return opt;
> +
> +       switch (opt) {
> +       case Opt_uid:
> +               opts->uid = make_kuid(current_user_ns(), result.uint_32);
> +               break;
> +       case Opt_gid:
> +               opts->gid = make_kgid(current_user_ns(), result.uint_32);
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
>  static int efivarfs_fill_super(struct super_block *sb, struct fs_context *fc)
>  {
>         struct inode *inode = NULL;
> @@ -233,10 +286,21 @@ static int efivarfs_get_tree(struct fs_context *fc)
>
>  static const struct fs_context_operations efivarfs_context_ops = {
>         .get_tree       = efivarfs_get_tree,
> +       .parse_param    = efivarfs_parse_param,
>  };
>
>  static int efivarfs_init_fs_context(struct fs_context *fc)
>  {
> +       struct efivarfs_fs_info *sfi;
> +
> +       sfi = kzalloc(sizeof(struct efivarfs_fs_info), GFP_KERNEL);

Please use sizeof(*sfi) here - this avoids future problems if sfi ever
changes type.

> +       if (!sfi)
> +               return -ENOMEM;
> +
> +       sfi->mount_opts.uid = current_uid();
> +       sfi->mount_opts.gid = current_gid();
> +

Could this affect existing users of efivarfs? IIIUC, this will result
in efivarfs being mounted with the uid/gid of the caller instead of
root/root, right? So if existing scripts use some other user with the
right capabilities to mount efivarfs, the uid/gid will change.

Is this part of the change important to your use case?

> +       fc->s_fs_info = sfi;
>         fc->ops = &efivarfs_context_ops;
>         return 0;
>  }
> @@ -254,6 +318,7 @@ static struct file_system_type efivarfs_type = {
>         .name    = "efivarfs",
>         .init_fs_context = efivarfs_init_fs_context,
>         .kill_sb = efivarfs_kill_sb,
> +       .parameters             = efivarfs_parameters,

Please either drop the indentation, or re-indent the whole block.

>  };
>
>  static __init int efivarfs_init(void)
> --
> 2.42.0.rc2.253.gd59a3bf2b4-goog
>
Christian Brauner Sept. 4, 2023, 12:17 p.m. UTC | #2
On Thu, Aug 31, 2023 at 03:31:07PM +0000, Jiao Zhou wrote:
> Add uid and gid in efivarfs's mount option, so that
> we can mount the file system with ownership. This approach
>  is used by a number of other filesystems that don't have
> native support for ownership.
> 
> TEST=FEATURES=test emerge-reven chromeos-kernel-5_15
> 
> Signed-off-by: Jiao Zhou <jiaozhou@google.com>
> Reported-by: kernel test robot <oliver.sang@intel.com>
> Closes: https://lore.kernel.org/oe-lkp/202308291443.ea96ac66-oliver.sang@intel.com
> ---
>  fs/efivarfs/inode.c    |  4 +++
>  fs/efivarfs/internal.h |  9 ++++++
>  fs/efivarfs/super.c    | 65 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 78 insertions(+)
> 
> diff --git a/fs/efivarfs/inode.c b/fs/efivarfs/inode.c
> index 939e5e242b98..de57fb6c28e1 100644
> --- a/fs/efivarfs/inode.c
> +++ b/fs/efivarfs/inode.c
> @@ -20,9 +20,13 @@ struct inode *efivarfs_get_inode(struct super_block *sb,
>  				const struct inode *dir, int mode,
>  				dev_t dev, bool is_removable)
>  {
> +	struct efivarfs_fs_info *fsi = sb->s_fs_info;
>  	struct inode *inode = new_inode(sb);
> +	struct efivarfs_mount_opts *opts = &fsi->mount_opts;
>  
>  	if (inode) {
> +		inode->i_uid = opts->uid;
> +		inode->i_gid = opts->gid;
>  		inode->i_ino = get_next_ino();
>  		inode->i_mode = mode;
>  		inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
> diff --git a/fs/efivarfs/internal.h b/fs/efivarfs/internal.h
> index 30ae44cb7453..57deaf56d8e2 100644
> --- a/fs/efivarfs/internal.h
> +++ b/fs/efivarfs/internal.h
> @@ -8,6 +8,15 @@
>  
>  #include <linux/list.h>
>  
> +struct efivarfs_mount_opts {
> +	kuid_t uid;
> +	kgid_t gid;
> +};
> +
> +struct efivarfs_fs_info {
> +	struct efivarfs_mount_opts mount_opts;
> +};
> +
>  extern const struct file_operations efivarfs_file_operations;
>  extern const struct inode_operations efivarfs_dir_inode_operations;
>  extern bool efivarfs_valid_name(const char *str, int len);
> diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c
> index 15880a68faad..d67b0d157ff5 100644
> --- a/fs/efivarfs/super.c
> +++ b/fs/efivarfs/super.c
> @@ -8,6 +8,7 @@
>  #include <linux/efi.h>
>  #include <linux/fs.h>
>  #include <linux/fs_context.h>
> +#include <linux/fs_parser.h>
>  #include <linux/module.h>
>  #include <linux/pagemap.h>
>  #include <linux/ucs2_string.h>
> @@ -23,10 +24,27 @@ static void efivarfs_evict_inode(struct inode *inode)
>  	clear_inode(inode);
>  }
>  
> +static int efivarfs_show_options(struct seq_file *m, struct dentry *root)
> +{
> +	struct super_block *sb = root->d_sb;
> +	struct efivarfs_fs_info *sbi = sb->s_fs_info;
> +	struct efivarfs_mount_opts *opts = &sbi->mount_opts;
> +
> +	/* Show partition info */
> +	if (!uid_eq(opts->uid, GLOBAL_ROOT_UID))
> +		seq_printf(m, ",uid=%u",
> +				from_kuid_munged(&init_user_ns, opts->uid));
> +	if (!gid_eq(opts->gid, GLOBAL_ROOT_GID))
> +		seq_printf(m, ",gid=%u",
> +				from_kgid_munged(&init_user_ns, opts->gid));
> +	return 0;
> +}
> +
>  static const struct super_operations efivarfs_ops = {
>  	.statfs = simple_statfs,
>  	.drop_inode = generic_delete_inode,
>  	.evict_inode = efivarfs_evict_inode,
> +	.show_options	= efivarfs_show_options,
>  };
>  
>  /*
> @@ -190,6 +208,41 @@ static int efivarfs_destroy(struct efivar_entry *entry, void *data)
>  	return 0;
>  }
>  
> +enum {
> +	Opt_uid, Opt_gid,
> +};
> +
> +static const struct fs_parameter_spec efivarfs_parameters[] = {
> +	fsparam_u32("uid",			Opt_uid),
> +	fsparam_u32("gid",			Opt_gid),
> +	{},
> +};
> +
> +static int efivarfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
> +{
> +	struct efivarfs_fs_info *sbi = fc->s_fs_info;
> +	struct efivarfs_mount_opts *opts = &sbi->mount_opts;
> +	struct fs_parse_result result;
> +	int opt;
> +
> +	opt = fs_parse(fc, efivarfs_parameters, param, &result);
> +	if (opt < 0)
> +		return opt;
> +
> +	switch (opt) {
> +	case Opt_uid:
> +		opts->uid = make_kuid(current_user_ns(), result.uint_32);
> +		break;
> +	case Opt_gid:
> +		opts->gid = make_kgid(current_user_ns(), result.uint_32);
> +		break;

This will allow the following:

# initial user namespace
fd_fs = fsopen("efivarfs")

# switch to some unprivileged userns
fsconfig(fd_fs, FSCONFIG_SET_STRING, "uid", "1000")
==> This now resolves within the caller's user namespace which might
    have an idmapping where 1000 cannot be resolved causing sb->{g,u}id
    to be set to INVALID_{G,U}ID.

    In fact this is also possible in your patch right now without the
    namespace switching. The caller could just pass -1 and that would
    cause inodes with INVALID_{G,U}ID to be created.
    So you want a check for {g,u}id_valid().

# send fd back to init_user_ns
fsconfig(fd_fs, FSCONFIG_CMD_CREATE)
fd_mnt = fsmount(fd_fs, ...)
move_mount(fd_fs, "", -EBADF, "/somehwere", ...)
Ard Biesheuvel Sept. 6, 2023, 7:06 a.m. UTC | #3
On Tue, 5 Sept 2023 at 20:05, Jiao Zhou <jiaozhou@google.com> wrote:
>
> Hi Christian,
>
> Thanks for your reply, will adding a guid and uid check like the below be sufficient? I saw this is how most file systems check their gid and uid. I am not quite sure about how to send fd back to init_user_ns. Can you please clarify a little bit? Thank you so much.
...

>

Please don't top post and please only send plaintext email to the mailing lists.

Please consult Documentation/process/submitting-patches.rst in the
Linux source tree if you are unfamiliar with the upstream contribution
process (and since you appear to be at Google, you might want to
consult go/kernel-upstream as well)

Thanks,
Ard.


>
>
> On Mon, Sep 4, 2023 at 1:17 PM Christian Brauner <brauner@kernel.org> wrote:
>>
>> On Thu, Aug 31, 2023 at 03:31:07PM +0000, Jiao Zhou wrote:
>> > Add uid and gid in efivarfs's mount option, so that
>> > we can mount the file system with ownership. This approach
>> >  is used by a number of other filesystems that don't have
>> > native support for ownership.
>> >
>> > TEST=FEATURES=test emerge-reven chromeos-kernel-5_15
>> >
>> > Signed-off-by: Jiao Zhou <jiaozhou@google.com>
>> > Reported-by: kernel test robot <oliver.sang@intel.com>
>> > Closes: https://lore.kernel.org/oe-lkp/202308291443.ea96ac66-oliver.sang@intel.com
>> > ---
>> >  fs/efivarfs/inode.c    |  4 +++
>> >  fs/efivarfs/internal.h |  9 ++++++
>> >  fs/efivarfs/super.c    | 65 ++++++++++++++++++++++++++++++++++++++++++
>> >  3 files changed, 78 insertions(+)
>> >
>> > diff --git a/fs/efivarfs/inode.c b/fs/efivarfs/inode.c
>> > index 939e5e242b98..de57fb6c28e1 100644
>> > --- a/fs/efivarfs/inode.c
>> > +++ b/fs/efivarfs/inode.c
>> > @@ -20,9 +20,13 @@ struct inode *efivarfs_get_inode(struct super_block *sb,
>> >                               const struct inode *dir, int mode,
>> >                               dev_t dev, bool is_removable)
>> >  {
>> > +     struct efivarfs_fs_info *fsi = sb->s_fs_info;
>> >       struct inode *inode = new_inode(sb);
>> > +     struct efivarfs_mount_opts *opts = &fsi->mount_opts;
>> >
>> >       if (inode) {
>> > +             inode->i_uid = opts->uid;
>> > +             inode->i_gid = opts->gid;
>> >               inode->i_ino = get_next_ino();
>> >               inode->i_mode = mode;
>> >               inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
>> > diff --git a/fs/efivarfs/internal.h b/fs/efivarfs/internal.h
>> > index 30ae44cb7453..57deaf56d8e2 100644
>> > --- a/fs/efivarfs/internal.h
>> > +++ b/fs/efivarfs/internal.h
>> > @@ -8,6 +8,15 @@
>> >
>> >  #include <linux/list.h>
>> >
>> > +struct efivarfs_mount_opts {
>> > +     kuid_t uid;
>> > +     kgid_t gid;
>> > +};
>> > +
>> > +struct efivarfs_fs_info {
>> > +     struct efivarfs_mount_opts mount_opts;
>> > +};
>> > +
>> >  extern const struct file_operations efivarfs_file_operations;
>> >  extern const struct inode_operations efivarfs_dir_inode_operations;
>> >  extern bool efivarfs_valid_name(const char *str, int len);
>> > diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c
>> > index 15880a68faad..d67b0d157ff5 100644
>> > --- a/fs/efivarfs/super.c
>> > +++ b/fs/efivarfs/super.c
>> > @@ -8,6 +8,7 @@
>> >  #include <linux/efi.h>
>> >  #include <linux/fs.h>
>> >  #include <linux/fs_context.h>
>> > +#include <linux/fs_parser.h>
>> >  #include <linux/module.h>
>> >  #include <linux/pagemap.h>
>> >  #include <linux/ucs2_string.h>
>> > @@ -23,10 +24,27 @@ static void efivarfs_evict_inode(struct inode *inode)
>> >       clear_inode(inode);
>> >  }
>> >
>> > +static int efivarfs_show_options(struct seq_file *m, struct dentry *root)
>> > +{
>> > +     struct super_block *sb = root->d_sb;
>> > +     struct efivarfs_fs_info *sbi = sb->s_fs_info;
>> > +     struct efivarfs_mount_opts *opts = &sbi->mount_opts;
>> > +
>> > +     /* Show partition info */
>> > +     if (!uid_eq(opts->uid, GLOBAL_ROOT_UID))
>> > +             seq_printf(m, ",uid=%u",
>> > +                             from_kuid_munged(&init_user_ns, opts->uid));
>> > +     if (!gid_eq(opts->gid, GLOBAL_ROOT_GID))
>> > +             seq_printf(m, ",gid=%u",
>> > +                             from_kgid_munged(&init_user_ns, opts->gid));
>> > +     return 0;
>> > +}
>> > +
>> >  static const struct super_operations efivarfs_ops = {
>> >       .statfs = simple_statfs,
>> >       .drop_inode = generic_delete_inode,
>> >       .evict_inode = efivarfs_evict_inode,
>> > +     .show_options   = efivarfs_show_options,
>> >  };
>> >
>> >  /*
>> > @@ -190,6 +208,41 @@ static int efivarfs_destroy(struct efivar_entry *entry, void *data)
>> >       return 0;
>> >  }
>> >
>> > +enum {
>> > +     Opt_uid, Opt_gid,
>> > +};
>> > +
>> > +static const struct fs_parameter_spec efivarfs_parameters[] = {
>> > +     fsparam_u32("uid",                      Opt_uid),
>> > +     fsparam_u32("gid",                      Opt_gid),
>> > +     {},
>> > +};
>> > +
>> > +static int efivarfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
>> > +{
>> > +     struct efivarfs_fs_info *sbi = fc->s_fs_info;
>> > +     struct efivarfs_mount_opts *opts = &sbi->mount_opts;
>> > +     struct fs_parse_result result;
>> > +     int opt;
>> > +
>> > +     opt = fs_parse(fc, efivarfs_parameters, param, &result);
>> > +     if (opt < 0)
>> > +             return opt;
>> > +
>> > +     switch (opt) {
>> > +     case Opt_uid:
>> > +             opts->uid = make_kuid(current_user_ns(), result.uint_32);
>> > +             break;
>> > +     case Opt_gid:
>> > +             opts->gid = make_kgid(current_user_ns(), result.uint_32);
>> > +             break;
>>
>> This will allow the following:
>>
>> # initial user namespace
>> fd_fs = fsopen("efivarfs")
>>
>> # switch to some unprivileged userns
>> fsconfig(fd_fs, FSCONFIG_SET_STRING, "uid", "1000")
>> ==> This now resolves within the caller's user namespace which might
>>     have an idmapping where 1000 cannot be resolved causing sb->{g,u}id
>>     to be set to INVALID_{G,U}ID.
>>
>>     In fact this is also possible in your patch right now without the
>>     namespace switching. The caller could just pass -1 and that would
>>     cause inodes with INVALID_{G,U}ID to be created.
>>     So you want a check for {g,u}id_valid().
>>
>> # send fd back to init_user_ns
>> fsconfig(fd_fs, FSCONFIG_CMD_CREATE)
>> fd_mnt = fsmount(fd_fs, ...)
>> move_mount(fd_fs, "", -EBADF, "/somehwere", ...)
diff mbox series

Patch

diff --git a/fs/efivarfs/inode.c b/fs/efivarfs/inode.c
index 939e5e242b98..de57fb6c28e1 100644
--- a/fs/efivarfs/inode.c
+++ b/fs/efivarfs/inode.c
@@ -20,9 +20,13 @@  struct inode *efivarfs_get_inode(struct super_block *sb,
 				const struct inode *dir, int mode,
 				dev_t dev, bool is_removable)
 {
+	struct efivarfs_fs_info *fsi = sb->s_fs_info;
 	struct inode *inode = new_inode(sb);
+	struct efivarfs_mount_opts *opts = &fsi->mount_opts;
 
 	if (inode) {
+		inode->i_uid = opts->uid;
+		inode->i_gid = opts->gid;
 		inode->i_ino = get_next_ino();
 		inode->i_mode = mode;
 		inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
diff --git a/fs/efivarfs/internal.h b/fs/efivarfs/internal.h
index 30ae44cb7453..57deaf56d8e2 100644
--- a/fs/efivarfs/internal.h
+++ b/fs/efivarfs/internal.h
@@ -8,6 +8,15 @@ 
 
 #include <linux/list.h>
 
+struct efivarfs_mount_opts {
+	kuid_t uid;
+	kgid_t gid;
+};
+
+struct efivarfs_fs_info {
+	struct efivarfs_mount_opts mount_opts;
+};
+
 extern const struct file_operations efivarfs_file_operations;
 extern const struct inode_operations efivarfs_dir_inode_operations;
 extern bool efivarfs_valid_name(const char *str, int len);
diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c
index 15880a68faad..d67b0d157ff5 100644
--- a/fs/efivarfs/super.c
+++ b/fs/efivarfs/super.c
@@ -8,6 +8,7 @@ 
 #include <linux/efi.h>
 #include <linux/fs.h>
 #include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/module.h>
 #include <linux/pagemap.h>
 #include <linux/ucs2_string.h>
@@ -23,10 +24,27 @@  static void efivarfs_evict_inode(struct inode *inode)
 	clear_inode(inode);
 }
 
+static int efivarfs_show_options(struct seq_file *m, struct dentry *root)
+{
+	struct super_block *sb = root->d_sb;
+	struct efivarfs_fs_info *sbi = sb->s_fs_info;
+	struct efivarfs_mount_opts *opts = &sbi->mount_opts;
+
+	/* Show partition info */
+	if (!uid_eq(opts->uid, GLOBAL_ROOT_UID))
+		seq_printf(m, ",uid=%u",
+				from_kuid_munged(&init_user_ns, opts->uid));
+	if (!gid_eq(opts->gid, GLOBAL_ROOT_GID))
+		seq_printf(m, ",gid=%u",
+				from_kgid_munged(&init_user_ns, opts->gid));
+	return 0;
+}
+
 static const struct super_operations efivarfs_ops = {
 	.statfs = simple_statfs,
 	.drop_inode = generic_delete_inode,
 	.evict_inode = efivarfs_evict_inode,
+	.show_options	= efivarfs_show_options,
 };
 
 /*
@@ -190,6 +208,41 @@  static int efivarfs_destroy(struct efivar_entry *entry, void *data)
 	return 0;
 }
 
+enum {
+	Opt_uid, Opt_gid,
+};
+
+static const struct fs_parameter_spec efivarfs_parameters[] = {
+	fsparam_u32("uid",			Opt_uid),
+	fsparam_u32("gid",			Opt_gid),
+	{},
+};
+
+static int efivarfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
+{
+	struct efivarfs_fs_info *sbi = fc->s_fs_info;
+	struct efivarfs_mount_opts *opts = &sbi->mount_opts;
+	struct fs_parse_result result;
+	int opt;
+
+	opt = fs_parse(fc, efivarfs_parameters, param, &result);
+	if (opt < 0)
+		return opt;
+
+	switch (opt) {
+	case Opt_uid:
+		opts->uid = make_kuid(current_user_ns(), result.uint_32);
+		break;
+	case Opt_gid:
+		opts->gid = make_kgid(current_user_ns(), result.uint_32);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int efivarfs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	struct inode *inode = NULL;
@@ -233,10 +286,21 @@  static int efivarfs_get_tree(struct fs_context *fc)
 
 static const struct fs_context_operations efivarfs_context_ops = {
 	.get_tree	= efivarfs_get_tree,
+	.parse_param	= efivarfs_parse_param,
 };
 
 static int efivarfs_init_fs_context(struct fs_context *fc)
 {
+	struct efivarfs_fs_info *sfi;
+
+	sfi = kzalloc(sizeof(struct efivarfs_fs_info), GFP_KERNEL);
+	if (!sfi)
+		return -ENOMEM;
+
+	sfi->mount_opts.uid = current_uid();
+	sfi->mount_opts.gid = current_gid();
+
+	fc->s_fs_info = sfi;
 	fc->ops = &efivarfs_context_ops;
 	return 0;
 }
@@ -254,6 +318,7 @@  static struct file_system_type efivarfs_type = {
 	.name    = "efivarfs",
 	.init_fs_context = efivarfs_init_fs_context,
 	.kill_sb = efivarfs_kill_sb,
+	.parameters		= efivarfs_parameters,
 };
 
 static __init int efivarfs_init(void)