efivarfs: Add uid/gid mount options
[linux-2.6-block.git] / fs / efivarfs / super.c
CommitLineData
d2912cb1 1// SPDX-License-Identifier: GPL-2.0-only
d68772b7
MF
2/*
3 * Copyright (C) 2012 Red Hat, Inc.
4 * Copyright (C) 2012 Jeremy Kerr <jeremy.kerr@canonical.com>
d68772b7
MF
5 */
6
7#include <linux/ctype.h>
8#include <linux/efi.h>
9#include <linux/fs.h>
47999745 10#include <linux/fs_context.h>
5329aa51 11#include <linux/fs_parser.h>
d68772b7
MF
12#include <linux/module.h>
13#include <linux/pagemap.h>
a614e192 14#include <linux/ucs2_string.h>
20b4fb48
LT
15#include <linux/slab.h>
16#include <linux/magic.h>
d86ff333 17#include <linux/statfs.h>
d68772b7
MF
18
19#include "internal.h"
20
21LIST_HEAD(efivarfs_list);
22
23static void efivarfs_evict_inode(struct inode *inode)
24{
25 clear_inode(inode);
26}
27
5329aa51
JZ
28static int efivarfs_show_options(struct seq_file *m, struct dentry *root)
29{
30 struct super_block *sb = root->d_sb;
31 struct efivarfs_fs_info *sbi = sb->s_fs_info;
32 struct efivarfs_mount_opts *opts = &sbi->mount_opts;
33
34 if (!uid_eq(opts->uid, GLOBAL_ROOT_UID))
35 seq_printf(m, ",uid=%u",
36 from_kuid_munged(&init_user_ns, opts->uid));
37 if (!gid_eq(opts->gid, GLOBAL_ROOT_GID))
38 seq_printf(m, ",gid=%u",
39 from_kgid_munged(&init_user_ns, opts->gid));
40 return 0;
41}
42
d86ff333
AA
43static int efivarfs_statfs(struct dentry *dentry, struct kstatfs *buf)
44{
45 const u32 attr = EFI_VARIABLE_NON_VOLATILE |
46 EFI_VARIABLE_BOOTSERVICE_ACCESS |
47 EFI_VARIABLE_RUNTIME_ACCESS;
48 u64 storage_space, remaining_space, max_variable_size;
49 efi_status_t status;
50
51 status = efivar_query_variable_info(attr, &storage_space, &remaining_space,
52 &max_variable_size);
53 if (status != EFI_SUCCESS)
54 return efi_status_to_err(status);
55
56 /*
57 * This is not a normal filesystem, so no point in pretending it has a block
58 * size; we declare f_bsize to 1, so that we can then report the exact value
59 * sent by EFI QueryVariableInfo in f_blocks and f_bfree
60 */
61 buf->f_bsize = 1;
62 buf->f_namelen = NAME_MAX;
63 buf->f_blocks = storage_space;
64 buf->f_bfree = remaining_space;
65 buf->f_type = dentry->d_sb->s_magic;
66
67 /*
68 * In f_bavail we declare the free space that the kernel will allow writing
69 * when the storage_paranoia x86 quirk is active. To use more, users
70 * should boot the kernel with efi_no_storage_paranoia.
71 */
72 if (remaining_space > efivar_reserved_space())
73 buf->f_bavail = remaining_space - efivar_reserved_space();
74 else
75 buf->f_bavail = 0;
76
77 return 0;
78}
d68772b7 79static const struct super_operations efivarfs_ops = {
d86ff333 80 .statfs = efivarfs_statfs,
d68772b7
MF
81 .drop_inode = generic_delete_inode,
82 .evict_inode = efivarfs_evict_inode,
5329aa51 83 .show_options = efivarfs_show_options,
d68772b7
MF
84};
85
d68772b7
MF
86/*
87 * Compare two efivarfs file names.
88 *
89 * An efivarfs filename is composed of two parts,
90 *
91 * 1. A case-sensitive variable name
92 * 2. A case-insensitive GUID
93 *
94 * So we need to perform a case-sensitive match on part 1 and a
95 * case-insensitive match on part 2.
96 */
6fa67e70 97static int efivarfs_d_compare(const struct dentry *dentry,
d68772b7
MF
98 unsigned int len, const char *str,
99 const struct qstr *name)
100{
101 int guid = len - EFI_VARIABLE_GUID_LEN;
102
103 if (name->len != len)
104 return 1;
105
106 /* Case-sensitive compare for the variable name */
107 if (memcmp(str, name->name, guid))
108 return 1;
109
110 /* Case-insensitive compare for the GUID */
111 return strncasecmp(name->name + guid, str + guid, EFI_VARIABLE_GUID_LEN);
112}
113
da53be12 114static int efivarfs_d_hash(const struct dentry *dentry, struct qstr *qstr)
d68772b7 115{
8387ff25 116 unsigned long hash = init_name_hash(dentry);
d68772b7
MF
117 const unsigned char *s = qstr->name;
118 unsigned int len = qstr->len;
119
120 if (!efivarfs_valid_name(s, len))
121 return -EINVAL;
122
123 while (len-- > EFI_VARIABLE_GUID_LEN)
124 hash = partial_name_hash(*s++, hash);
125
126 /* GUID is case-insensitive. */
127 while (len--)
128 hash = partial_name_hash(tolower(*s++), hash);
129
130 qstr->hash = end_name_hash(hash);
131 return 0;
132}
133
e37dcbfb 134static const struct dentry_operations efivarfs_d_ops = {
d68772b7
MF
135 .d_compare = efivarfs_d_compare,
136 .d_hash = efivarfs_d_hash,
b26d4cd3 137 .d_delete = always_delete_dentry,
d68772b7
MF
138};
139
140static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
141{
142 struct dentry *d;
143 struct qstr q;
144 int err;
145
146 q.name = name;
147 q.len = strlen(name);
148
8387ff25 149 err = efivarfs_d_hash(parent, &q);
d68772b7
MF
150 if (err)
151 return ERR_PTR(err);
152
153 d = d_alloc(parent, &q);
154 if (d)
155 return d;
156
157 return ERR_PTR(-ENOMEM);
158}
159
160static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
161 unsigned long name_size, void *data)
162{
163 struct super_block *sb = (struct super_block *)data;
164 struct efivar_entry *entry;
165 struct inode *inode = NULL;
166 struct dentry *dentry, *root = sb->s_root;
167 unsigned long size = 0;
168 char *name;
e0d64e6a 169 int len;
d68772b7 170 int err = -ENOMEM;
ed8b0de5 171 bool is_removable = false;
d68772b7 172
63ffb573
JD
173 if (guid_equal(&vendor, &LINUX_EFI_RANDOM_SEED_TABLE_GUID))
174 return 0;
175
c57dcb56 176 entry = kzalloc(sizeof(*entry), GFP_KERNEL);
d68772b7
MF
177 if (!entry)
178 return err;
179
180 memcpy(entry->var.VariableName, name16, name_size);
181 memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t));
182
e0d64e6a 183 len = ucs2_utf8size(entry->var.VariableName);
d68772b7
MF
184
185 /* name, plus '-', plus GUID, plus NUL*/
186 name = kmalloc(len + 1 + EFI_VARIABLE_GUID_LEN + 1, GFP_KERNEL);
187 if (!name)
188 goto fail;
189
e0d64e6a 190 ucs2_as_utf8(name, entry->var.VariableName, len);
d68772b7 191
ed8b0de5
PJ
192 if (efivar_variable_is_removable(entry->var.VendorGuid, name, len))
193 is_removable = true;
194
d68772b7
MF
195 name[len] = '-';
196
26e02272 197 efi_guid_to_str(&entry->var.VendorGuid, name + len + 1);
d68772b7
MF
198
199 name[len + EFI_VARIABLE_GUID_LEN+1] = '\0';
200
336af6a4
MS
201 /* replace invalid slashes like kobject_set_name_vargs does for /sys/firmware/efi/vars. */
202 strreplace(name, '/', '!');
203
ed8b0de5
PJ
204 inode = efivarfs_get_inode(sb, d_inode(root), S_IFREG | 0644, 0,
205 is_removable);
d68772b7
MF
206 if (!inode)
207 goto fail_name;
208
209 dentry = efivarfs_alloc_dentry(root, name);
210 if (IS_ERR(dentry)) {
211 err = PTR_ERR(dentry);
212 goto fail_inode;
213 }
214
ec3507b2
AB
215 __efivar_entry_get(entry, NULL, &size, NULL);
216 __efivar_entry_add(entry, &efivarfs_list);
d68772b7 217
22c2b77f
MF
218 /* copied by the above to local storage in the dentry. */
219 kfree(name);
220
5955102c 221 inode_lock(inode);
d68772b7
MF
222 inode->i_private = entry;
223 i_size_write(inode, size + sizeof(entry->var.Attributes));
5955102c 224 inode_unlock(inode);
d68772b7
MF
225 d_add(dentry, inode);
226
227 return 0;
228
229fail_inode:
230 iput(inode);
231fail_name:
232 kfree(name);
233fail:
234 kfree(entry);
235 return err;
236}
237
238static int efivarfs_destroy(struct efivar_entry *entry, void *data)
239{
3a75f9f2 240 efivar_entry_remove(entry);
d68772b7
MF
241 kfree(entry);
242 return 0;
243}
244
5329aa51
JZ
245enum {
246 Opt_uid, Opt_gid,
247};
248
249static const struct fs_parameter_spec efivarfs_parameters[] = {
250 fsparam_u32("uid", Opt_uid),
251 fsparam_u32("gid", Opt_gid),
252 {},
253};
254
255static int efivarfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
256{
257 struct efivarfs_fs_info *sbi = fc->s_fs_info;
258 struct efivarfs_mount_opts *opts = &sbi->mount_opts;
259 struct fs_parse_result result;
260 int opt;
261
262 opt = fs_parse(fc, efivarfs_parameters, param, &result);
263 if (opt < 0)
264 return opt;
265
266 switch (opt) {
267 case Opt_uid:
268 opts->uid = make_kuid(current_user_ns(), result.uint_32);
269 if (!uid_valid(opts->uid))
270 return -EINVAL;
271 break;
272 case Opt_gid:
273 opts->gid = make_kgid(current_user_ns(), result.uint_32);
274 if (!gid_valid(opts->gid))
275 return -EINVAL;
276 break;
277 default:
278 return -EINVAL;
279 }
280
281 return 0;
282}
283
47999745 284static int efivarfs_fill_super(struct super_block *sb, struct fs_context *fc)
d68772b7
MF
285{
286 struct inode *inode = NULL;
287 struct dentry *root;
288 int err;
289
301de9a2
JH
290 if (!efivar_is_available())
291 return -EOPNOTSUPP;
292
d68772b7 293 sb->s_maxbytes = MAX_LFS_FILESIZE;
09cbfeaf
KS
294 sb->s_blocksize = PAGE_SIZE;
295 sb->s_blocksize_bits = PAGE_SHIFT;
d68772b7
MF
296 sb->s_magic = EFIVARFS_MAGIC;
297 sb->s_op = &efivarfs_ops;
298 sb->s_d_op = &efivarfs_d_ops;
299 sb->s_time_gran = 1;
300
f88814cc
AB
301 if (!efivar_supports_writes())
302 sb->s_flags |= SB_RDONLY;
303
ed8b0de5 304 inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0, true);
d68772b7
MF
305 if (!inode)
306 return -ENOMEM;
307 inode->i_op = &efivarfs_dir_inode_operations;
308
309 root = d_make_root(inode);
310 sb->s_root = root;
311 if (!root)
312 return -ENOMEM;
313
314 INIT_LIST_HEAD(&efivarfs_list);
315
1cfd6316 316 err = efivar_init(efivarfs_callback, (void *)sb, true, &efivarfs_list);
d68772b7 317 if (err)
3a75f9f2 318 efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL);
d68772b7
MF
319
320 return err;
321}
322
47999745
DH
323static int efivarfs_get_tree(struct fs_context *fc)
324{
325 return get_tree_single(fc, efivarfs_fill_super);
326}
327
328static const struct fs_context_operations efivarfs_context_ops = {
329 .get_tree = efivarfs_get_tree,
5329aa51 330 .parse_param = efivarfs_parse_param,
47999745
DH
331};
332
333static int efivarfs_init_fs_context(struct fs_context *fc)
d68772b7 334{
5329aa51
JZ
335 struct efivarfs_fs_info *sfi;
336
337 sfi = kzalloc(sizeof(*sfi), GFP_KERNEL);
338 if (!sfi)
339 return -ENOMEM;
340
341 sfi->mount_opts.uid = GLOBAL_ROOT_UID;
342 sfi->mount_opts.gid = GLOBAL_ROOT_GID;
343
344 fc->s_fs_info = sfi;
47999745
DH
345 fc->ops = &efivarfs_context_ops;
346 return 0;
d68772b7
MF
347}
348
349static void efivarfs_kill_sb(struct super_block *sb)
350{
351 kill_litter_super(sb);
d68772b7 352
301de9a2
JH
353 if (!efivar_is_available())
354 return;
355
d68772b7 356 /* Remove all entries and destroy */
3a75f9f2 357 efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL);
d68772b7
MF
358}
359
360static struct file_system_type efivarfs_type = {
af5a29ae 361 .owner = THIS_MODULE,
d68772b7 362 .name = "efivarfs",
47999745 363 .init_fs_context = efivarfs_init_fs_context,
d68772b7 364 .kill_sb = efivarfs_kill_sb,
5329aa51 365 .parameters = efivarfs_parameters,
d68772b7
MF
366};
367
368static __init int efivarfs_init(void)
369{
d68772b7
MF
370 return register_filesystem(&efivarfs_type);
371}
372
af5a29ae
MK
373static __exit void efivarfs_exit(void)
374{
375 unregister_filesystem(&efivarfs_type);
376}
377
d68772b7
MF
378MODULE_AUTHOR("Matthew Garrett, Jeremy Kerr");
379MODULE_DESCRIPTION("EFI Variable Filesystem");
380MODULE_LICENSE("GPL");
381MODULE_ALIAS_FS("efivarfs");
382
383module_init(efivarfs_init);
af5a29ae 384module_exit(efivarfs_exit);