1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2012 Red Hat, Inc.
4 * Copyright (C) 2012 Jeremy Kerr <jeremy.kerr@canonical.com>
7 #include <linux/ctype.h>
10 #include <linux/fs_context.h>
11 #include <linux/module.h>
12 #include <linux/pagemap.h>
13 #include <linux/ucs2_string.h>
14 #include <linux/slab.h>
15 #include <linux/magic.h>
19 LIST_HEAD(efivarfs_list
);
21 static void efivarfs_evict_inode(struct inode
*inode
)
26 static const struct super_operations efivarfs_ops
= {
27 .statfs
= simple_statfs
,
28 .drop_inode
= generic_delete_inode
,
29 .evict_inode
= efivarfs_evict_inode
,
33 * Compare two efivarfs file names.
35 * An efivarfs filename is composed of two parts,
37 * 1. A case-sensitive variable name
38 * 2. A case-insensitive GUID
40 * So we need to perform a case-sensitive match on part 1 and a
41 * case-insensitive match on part 2.
43 static int efivarfs_d_compare(const struct dentry
*dentry
,
44 unsigned int len
, const char *str
,
45 const struct qstr
*name
)
47 int guid
= len
- EFI_VARIABLE_GUID_LEN
;
52 /* Case-sensitive compare for the variable name */
53 if (memcmp(str
, name
->name
, guid
))
56 /* Case-insensitive compare for the GUID */
57 return strncasecmp(name
->name
+ guid
, str
+ guid
, EFI_VARIABLE_GUID_LEN
);
60 static int efivarfs_d_hash(const struct dentry
*dentry
, struct qstr
*qstr
)
62 unsigned long hash
= init_name_hash(dentry
);
63 const unsigned char *s
= qstr
->name
;
64 unsigned int len
= qstr
->len
;
66 if (!efivarfs_valid_name(s
, len
))
69 while (len
-- > EFI_VARIABLE_GUID_LEN
)
70 hash
= partial_name_hash(*s
++, hash
);
72 /* GUID is case-insensitive. */
74 hash
= partial_name_hash(tolower(*s
++), hash
);
76 qstr
->hash
= end_name_hash(hash
);
80 static const struct dentry_operations efivarfs_d_ops
= {
81 .d_compare
= efivarfs_d_compare
,
82 .d_hash
= efivarfs_d_hash
,
83 .d_delete
= always_delete_dentry
,
86 static struct dentry
*efivarfs_alloc_dentry(struct dentry
*parent
, char *name
)
95 err
= efivarfs_d_hash(parent
, &q
);
99 d
= d_alloc(parent
, &q
);
103 return ERR_PTR(-ENOMEM
);
106 static int efivarfs_callback(efi_char16_t
*name16
, efi_guid_t vendor
,
107 unsigned long name_size
, void *data
)
109 struct super_block
*sb
= (struct super_block
*)data
;
110 struct efivar_entry
*entry
;
111 struct inode
*inode
= NULL
;
112 struct dentry
*dentry
, *root
= sb
->s_root
;
113 unsigned long size
= 0;
117 bool is_removable
= false;
119 entry
= kzalloc(sizeof(*entry
), GFP_KERNEL
);
123 memcpy(entry
->var
.VariableName
, name16
, name_size
);
124 memcpy(&(entry
->var
.VendorGuid
), &vendor
, sizeof(efi_guid_t
));
126 len
= ucs2_utf8size(entry
->var
.VariableName
);
128 /* name, plus '-', plus GUID, plus NUL*/
129 name
= kmalloc(len
+ 1 + EFI_VARIABLE_GUID_LEN
+ 1, GFP_KERNEL
);
133 ucs2_as_utf8(name
, entry
->var
.VariableName
, len
);
135 if (efivar_variable_is_removable(entry
->var
.VendorGuid
, name
, len
))
140 efi_guid_to_str(&entry
->var
.VendorGuid
, name
+ len
+ 1);
142 name
[len
+ EFI_VARIABLE_GUID_LEN
+1] = '\0';
144 inode
= efivarfs_get_inode(sb
, d_inode(root
), S_IFREG
| 0644, 0,
149 dentry
= efivarfs_alloc_dentry(root
, name
);
150 if (IS_ERR(dentry
)) {
151 err
= PTR_ERR(dentry
);
155 efivar_entry_size(entry
, &size
);
156 err
= efivar_entry_add(entry
, &efivarfs_list
);
160 /* copied by the above to local storage in the dentry. */
164 inode
->i_private
= entry
;
165 i_size_write(inode
, size
+ sizeof(entry
->var
.Attributes
));
167 d_add(dentry
, inode
);
180 static int efivarfs_destroy(struct efivar_entry
*entry
, void *data
)
182 int err
= efivar_entry_remove(entry
);
190 static int efivarfs_fill_super(struct super_block
*sb
, struct fs_context
*fc
)
192 struct inode
*inode
= NULL
;
196 sb
->s_maxbytes
= MAX_LFS_FILESIZE
;
197 sb
->s_blocksize
= PAGE_SIZE
;
198 sb
->s_blocksize_bits
= PAGE_SHIFT
;
199 sb
->s_magic
= EFIVARFS_MAGIC
;
200 sb
->s_op
= &efivarfs_ops
;
201 sb
->s_d_op
= &efivarfs_d_ops
;
204 inode
= efivarfs_get_inode(sb
, NULL
, S_IFDIR
| 0755, 0, true);
207 inode
->i_op
= &efivarfs_dir_inode_operations
;
209 root
= d_make_root(inode
);
214 INIT_LIST_HEAD(&efivarfs_list
);
216 err
= efivar_init(efivarfs_callback
, (void *)sb
, true, &efivarfs_list
);
218 __efivar_entry_iter(efivarfs_destroy
, &efivarfs_list
, NULL
, NULL
);
223 static int efivarfs_get_tree(struct fs_context
*fc
)
225 return get_tree_single(fc
, efivarfs_fill_super
);
228 static const struct fs_context_operations efivarfs_context_ops
= {
229 .get_tree
= efivarfs_get_tree
,
232 static int efivarfs_init_fs_context(struct fs_context
*fc
)
234 fc
->ops
= &efivarfs_context_ops
;
238 static void efivarfs_kill_sb(struct super_block
*sb
)
240 kill_litter_super(sb
);
242 /* Remove all entries and destroy */
243 __efivar_entry_iter(efivarfs_destroy
, &efivarfs_list
, NULL
, NULL
);
246 static struct file_system_type efivarfs_type
= {
247 .owner
= THIS_MODULE
,
249 .init_fs_context
= efivarfs_init_fs_context
,
250 .kill_sb
= efivarfs_kill_sb
,
253 static __init
int efivarfs_init(void)
255 if (!efi_enabled(EFI_RUNTIME_SERVICES
))
258 if (!efivars_kobject())
261 return register_filesystem(&efivarfs_type
);
264 static __exit
void efivarfs_exit(void)
266 unregister_filesystem(&efivarfs_type
);
269 MODULE_AUTHOR("Matthew Garrett, Jeremy Kerr");
270 MODULE_DESCRIPTION("EFI Variable Filesystem");
271 MODULE_LICENSE("GPL");
272 MODULE_ALIAS_FS("efivarfs");
274 module_init(efivarfs_init
);
275 module_exit(efivarfs_exit
);