xref: /linux/fs/efivarfs/super.c (revision 01b944fe1cd4e21a2a9ed51adbdbafe2d5e905ba)
1d68772b7SMatt Fleming /*
2d68772b7SMatt Fleming  * Copyright (C) 2012 Red Hat, Inc.
3d68772b7SMatt Fleming  * Copyright (C) 2012 Jeremy Kerr <jeremy.kerr@canonical.com>
4d68772b7SMatt Fleming  *
5d68772b7SMatt Fleming  * This program is free software; you can redistribute it and/or modify
6d68772b7SMatt Fleming  * it under the terms of the GNU General Public License version 2 as
7d68772b7SMatt Fleming  * published by the Free Software Foundation.
8d68772b7SMatt Fleming  */
9d68772b7SMatt Fleming 
10d68772b7SMatt Fleming #include <linux/ctype.h>
11d68772b7SMatt Fleming #include <linux/efi.h>
12d68772b7SMatt Fleming #include <linux/fs.h>
13d68772b7SMatt Fleming #include <linux/module.h>
14d68772b7SMatt Fleming #include <linux/pagemap.h>
15a614e192SMatt Fleming #include <linux/ucs2_string.h>
1620b4fb48SLinus Torvalds #include <linux/slab.h>
1720b4fb48SLinus Torvalds #include <linux/magic.h>
18d68772b7SMatt Fleming 
19d68772b7SMatt Fleming #include "internal.h"
20d68772b7SMatt Fleming 
21d68772b7SMatt Fleming LIST_HEAD(efivarfs_list);
22d68772b7SMatt Fleming 
23d68772b7SMatt Fleming static void efivarfs_evict_inode(struct inode *inode)
24d68772b7SMatt Fleming {
25d68772b7SMatt Fleming 	clear_inode(inode);
26d68772b7SMatt Fleming }
27d68772b7SMatt Fleming 
28d68772b7SMatt Fleming static const struct super_operations efivarfs_ops = {
29d68772b7SMatt Fleming 	.statfs = simple_statfs,
30d68772b7SMatt Fleming 	.drop_inode = generic_delete_inode,
31d68772b7SMatt Fleming 	.evict_inode = efivarfs_evict_inode,
32d68772b7SMatt Fleming 	.show_options = generic_show_options,
33d68772b7SMatt Fleming };
34d68772b7SMatt Fleming 
35d68772b7SMatt Fleming static struct super_block *efivarfs_sb;
36d68772b7SMatt Fleming 
37d68772b7SMatt Fleming /*
38d68772b7SMatt Fleming  * Compare two efivarfs file names.
39d68772b7SMatt Fleming  *
40d68772b7SMatt Fleming  * An efivarfs filename is composed of two parts,
41d68772b7SMatt Fleming  *
42d68772b7SMatt Fleming  *	1. A case-sensitive variable name
43d68772b7SMatt Fleming  *	2. A case-insensitive GUID
44d68772b7SMatt Fleming  *
45d68772b7SMatt Fleming  * So we need to perform a case-sensitive match on part 1 and a
46d68772b7SMatt Fleming  * case-insensitive match on part 2.
47d68772b7SMatt Fleming  */
48da53be12SLinus Torvalds static int efivarfs_d_compare(const struct dentry *parent,
49da53be12SLinus Torvalds 			      const struct dentry *dentry,
50d68772b7SMatt Fleming 			      unsigned int len, const char *str,
51d68772b7SMatt Fleming 			      const struct qstr *name)
52d68772b7SMatt Fleming {
53d68772b7SMatt Fleming 	int guid = len - EFI_VARIABLE_GUID_LEN;
54d68772b7SMatt Fleming 
55d68772b7SMatt Fleming 	if (name->len != len)
56d68772b7SMatt Fleming 		return 1;
57d68772b7SMatt Fleming 
58d68772b7SMatt Fleming 	/* Case-sensitive compare for the variable name */
59d68772b7SMatt Fleming 	if (memcmp(str, name->name, guid))
60d68772b7SMatt Fleming 		return 1;
61d68772b7SMatt Fleming 
62d68772b7SMatt Fleming 	/* Case-insensitive compare for the GUID */
63d68772b7SMatt Fleming 	return strncasecmp(name->name + guid, str + guid, EFI_VARIABLE_GUID_LEN);
64d68772b7SMatt Fleming }
65d68772b7SMatt Fleming 
66da53be12SLinus Torvalds static int efivarfs_d_hash(const struct dentry *dentry, struct qstr *qstr)
67d68772b7SMatt Fleming {
68d68772b7SMatt Fleming 	unsigned long hash = init_name_hash();
69d68772b7SMatt Fleming 	const unsigned char *s = qstr->name;
70d68772b7SMatt Fleming 	unsigned int len = qstr->len;
71d68772b7SMatt Fleming 
72d68772b7SMatt Fleming 	if (!efivarfs_valid_name(s, len))
73d68772b7SMatt Fleming 		return -EINVAL;
74d68772b7SMatt Fleming 
75d68772b7SMatt Fleming 	while (len-- > EFI_VARIABLE_GUID_LEN)
76d68772b7SMatt Fleming 		hash = partial_name_hash(*s++, hash);
77d68772b7SMatt Fleming 
78d68772b7SMatt Fleming 	/* GUID is case-insensitive. */
79d68772b7SMatt Fleming 	while (len--)
80d68772b7SMatt Fleming 		hash = partial_name_hash(tolower(*s++), hash);
81d68772b7SMatt Fleming 
82d68772b7SMatt Fleming 	qstr->hash = end_name_hash(hash);
83d68772b7SMatt Fleming 	return 0;
84d68772b7SMatt Fleming }
85d68772b7SMatt Fleming 
86e37dcbfbSFabian Frederick static const struct dentry_operations efivarfs_d_ops = {
87d68772b7SMatt Fleming 	.d_compare = efivarfs_d_compare,
88d68772b7SMatt Fleming 	.d_hash = efivarfs_d_hash,
89b26d4cd3SAl Viro 	.d_delete = always_delete_dentry,
90d68772b7SMatt Fleming };
91d68772b7SMatt Fleming 
92d68772b7SMatt Fleming static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
93d68772b7SMatt Fleming {
94d68772b7SMatt Fleming 	struct dentry *d;
95d68772b7SMatt Fleming 	struct qstr q;
96d68772b7SMatt Fleming 	int err;
97d68772b7SMatt Fleming 
98d68772b7SMatt Fleming 	q.name = name;
99d68772b7SMatt Fleming 	q.len = strlen(name);
100d68772b7SMatt Fleming 
101da53be12SLinus Torvalds 	err = efivarfs_d_hash(NULL, &q);
102d68772b7SMatt Fleming 	if (err)
103d68772b7SMatt Fleming 		return ERR_PTR(err);
104d68772b7SMatt Fleming 
105d68772b7SMatt Fleming 	d = d_alloc(parent, &q);
106d68772b7SMatt Fleming 	if (d)
107d68772b7SMatt Fleming 		return d;
108d68772b7SMatt Fleming 
109d68772b7SMatt Fleming 	return ERR_PTR(-ENOMEM);
110d68772b7SMatt Fleming }
111d68772b7SMatt Fleming 
112d68772b7SMatt Fleming static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
113d68772b7SMatt Fleming 			     unsigned long name_size, void *data)
114d68772b7SMatt Fleming {
115d68772b7SMatt Fleming 	struct super_block *sb = (struct super_block *)data;
116d68772b7SMatt Fleming 	struct efivar_entry *entry;
117d68772b7SMatt Fleming 	struct inode *inode = NULL;
118d68772b7SMatt Fleming 	struct dentry *dentry, *root = sb->s_root;
119d68772b7SMatt Fleming 	unsigned long size = 0;
120d68772b7SMatt Fleming 	char *name;
121d68772b7SMatt Fleming 	int len, i;
122d68772b7SMatt Fleming 	int err = -ENOMEM;
123d68772b7SMatt Fleming 
124c57dcb56SRoss Lagerwall 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
125d68772b7SMatt Fleming 	if (!entry)
126d68772b7SMatt Fleming 		return err;
127d68772b7SMatt Fleming 
128d68772b7SMatt Fleming 	memcpy(entry->var.VariableName, name16, name_size);
129d68772b7SMatt Fleming 	memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t));
130d68772b7SMatt Fleming 
131a614e192SMatt Fleming 	len = ucs2_strlen(entry->var.VariableName);
132d68772b7SMatt Fleming 
133d68772b7SMatt Fleming 	/* name, plus '-', plus GUID, plus NUL*/
134d68772b7SMatt Fleming 	name = kmalloc(len + 1 + EFI_VARIABLE_GUID_LEN + 1, GFP_KERNEL);
135d68772b7SMatt Fleming 	if (!name)
136d68772b7SMatt Fleming 		goto fail;
137d68772b7SMatt Fleming 
138d68772b7SMatt Fleming 	for (i = 0; i < len; i++)
139d68772b7SMatt Fleming 		name[i] = entry->var.VariableName[i] & 0xFF;
140d68772b7SMatt Fleming 
141d68772b7SMatt Fleming 	name[len] = '-';
142d68772b7SMatt Fleming 
14326e02272SBorislav Petkov 	efi_guid_to_str(&entry->var.VendorGuid, name + len + 1);
144d68772b7SMatt Fleming 
145d68772b7SMatt Fleming 	name[len + EFI_VARIABLE_GUID_LEN+1] = '\0';
146d68772b7SMatt Fleming 
1472b0143b5SDavid Howells 	inode = efivarfs_get_inode(sb, d_inode(root), S_IFREG | 0644, 0);
148d68772b7SMatt Fleming 	if (!inode)
149d68772b7SMatt Fleming 		goto fail_name;
150d68772b7SMatt Fleming 
151d68772b7SMatt Fleming 	dentry = efivarfs_alloc_dentry(root, name);
152d68772b7SMatt Fleming 	if (IS_ERR(dentry)) {
153d68772b7SMatt Fleming 		err = PTR_ERR(dentry);
154d68772b7SMatt Fleming 		goto fail_inode;
155d68772b7SMatt Fleming 	}
156d68772b7SMatt Fleming 
157d68772b7SMatt Fleming 	/* copied by the above to local storage in the dentry. */
158d68772b7SMatt Fleming 	kfree(name);
159d68772b7SMatt Fleming 
160d68772b7SMatt Fleming 	efivar_entry_size(entry, &size);
161d68772b7SMatt Fleming 	efivar_entry_add(entry, &efivarfs_list);
162d68772b7SMatt Fleming 
163d68772b7SMatt Fleming 	mutex_lock(&inode->i_mutex);
164d68772b7SMatt Fleming 	inode->i_private = entry;
165d68772b7SMatt Fleming 	i_size_write(inode, size + sizeof(entry->var.Attributes));
166d68772b7SMatt Fleming 	mutex_unlock(&inode->i_mutex);
167d68772b7SMatt Fleming 	d_add(dentry, inode);
168d68772b7SMatt Fleming 
169d68772b7SMatt Fleming 	return 0;
170d68772b7SMatt Fleming 
171d68772b7SMatt Fleming fail_inode:
172d68772b7SMatt Fleming 	iput(inode);
173d68772b7SMatt Fleming fail_name:
174d68772b7SMatt Fleming 	kfree(name);
175d68772b7SMatt Fleming fail:
176d68772b7SMatt Fleming 	kfree(entry);
177d68772b7SMatt Fleming 	return err;
178d68772b7SMatt Fleming }
179d68772b7SMatt Fleming 
180d68772b7SMatt Fleming static int efivarfs_destroy(struct efivar_entry *entry, void *data)
181d68772b7SMatt Fleming {
182d68772b7SMatt Fleming 	efivar_entry_remove(entry);
183d68772b7SMatt Fleming 	kfree(entry);
184d68772b7SMatt Fleming 	return 0;
185d68772b7SMatt Fleming }
186d68772b7SMatt Fleming 
187d68772b7SMatt Fleming static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
188d68772b7SMatt Fleming {
189d68772b7SMatt Fleming 	struct inode *inode = NULL;
190d68772b7SMatt Fleming 	struct dentry *root;
191d68772b7SMatt Fleming 	int err;
192d68772b7SMatt Fleming 
193d68772b7SMatt Fleming 	efivarfs_sb = sb;
194d68772b7SMatt Fleming 
195d68772b7SMatt Fleming 	sb->s_maxbytes          = MAX_LFS_FILESIZE;
196d68772b7SMatt Fleming 	sb->s_blocksize         = PAGE_CACHE_SIZE;
197d68772b7SMatt Fleming 	sb->s_blocksize_bits    = PAGE_CACHE_SHIFT;
198d68772b7SMatt Fleming 	sb->s_magic             = EFIVARFS_MAGIC;
199d68772b7SMatt Fleming 	sb->s_op                = &efivarfs_ops;
200d68772b7SMatt Fleming 	sb->s_d_op		= &efivarfs_d_ops;
201d68772b7SMatt Fleming 	sb->s_time_gran         = 1;
202d68772b7SMatt Fleming 
203d68772b7SMatt Fleming 	inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0);
204d68772b7SMatt Fleming 	if (!inode)
205d68772b7SMatt Fleming 		return -ENOMEM;
206d68772b7SMatt Fleming 	inode->i_op = &efivarfs_dir_inode_operations;
207d68772b7SMatt Fleming 
208d68772b7SMatt Fleming 	root = d_make_root(inode);
209d68772b7SMatt Fleming 	sb->s_root = root;
210d68772b7SMatt Fleming 	if (!root)
211d68772b7SMatt Fleming 		return -ENOMEM;
212d68772b7SMatt Fleming 
213d68772b7SMatt Fleming 	INIT_LIST_HEAD(&efivarfs_list);
214d68772b7SMatt Fleming 
215d68772b7SMatt Fleming 	err = efivar_init(efivarfs_callback, (void *)sb, false,
216d68772b7SMatt Fleming 			  true, &efivarfs_list);
217d68772b7SMatt Fleming 	if (err)
218d68772b7SMatt Fleming 		__efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL, NULL);
219d68772b7SMatt Fleming 
220d68772b7SMatt Fleming 	return err;
221d68772b7SMatt Fleming }
222d68772b7SMatt Fleming 
223d68772b7SMatt Fleming static struct dentry *efivarfs_mount(struct file_system_type *fs_type,
224d68772b7SMatt Fleming 				    int flags, const char *dev_name, void *data)
225d68772b7SMatt Fleming {
226d68772b7SMatt Fleming 	return mount_single(fs_type, flags, data, efivarfs_fill_super);
227d68772b7SMatt Fleming }
228d68772b7SMatt Fleming 
229d68772b7SMatt Fleming static void efivarfs_kill_sb(struct super_block *sb)
230d68772b7SMatt Fleming {
231d68772b7SMatt Fleming 	kill_litter_super(sb);
232d68772b7SMatt Fleming 	efivarfs_sb = NULL;
233d68772b7SMatt Fleming 
234d68772b7SMatt Fleming 	/* Remove all entries and destroy */
235d68772b7SMatt Fleming 	__efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL, NULL);
236d68772b7SMatt Fleming }
237d68772b7SMatt Fleming 
238d68772b7SMatt Fleming static struct file_system_type efivarfs_type = {
239af5a29aeSMathias Krause 	.owner   = THIS_MODULE,
240d68772b7SMatt Fleming 	.name    = "efivarfs",
241d68772b7SMatt Fleming 	.mount   = efivarfs_mount,
242d68772b7SMatt Fleming 	.kill_sb = efivarfs_kill_sb,
243d68772b7SMatt Fleming };
244d68772b7SMatt Fleming 
245d68772b7SMatt Fleming static __init int efivarfs_init(void)
246d68772b7SMatt Fleming {
247d68772b7SMatt Fleming 	if (!efi_enabled(EFI_RUNTIME_SERVICES))
248af5a29aeSMathias Krause 		return -ENODEV;
249d68772b7SMatt Fleming 
250d68772b7SMatt Fleming 	if (!efivars_kobject())
251af5a29aeSMathias Krause 		return -ENODEV;
252d68772b7SMatt Fleming 
253d68772b7SMatt Fleming 	return register_filesystem(&efivarfs_type);
254d68772b7SMatt Fleming }
255d68772b7SMatt Fleming 
256af5a29aeSMathias Krause static __exit void efivarfs_exit(void)
257af5a29aeSMathias Krause {
258af5a29aeSMathias Krause 	unregister_filesystem(&efivarfs_type);
259af5a29aeSMathias Krause }
260af5a29aeSMathias Krause 
261d68772b7SMatt Fleming MODULE_AUTHOR("Matthew Garrett, Jeremy Kerr");
262d68772b7SMatt Fleming MODULE_DESCRIPTION("EFI Variable Filesystem");
263d68772b7SMatt Fleming MODULE_LICENSE("GPL");
264d68772b7SMatt Fleming MODULE_ALIAS_FS("efivarfs");
265d68772b7SMatt Fleming 
266d68772b7SMatt Fleming module_init(efivarfs_init);
267af5a29aeSMathias Krause module_exit(efivarfs_exit);
268