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