Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2012 Red Hat, Inc.
0004  * Copyright (C) 2012 Jeremy Kerr <jeremy.kerr@canonical.com>
0005  */
0006 
0007 #include <linux/ctype.h>
0008 #include <linux/efi.h>
0009 #include <linux/fs.h>
0010 #include <linux/fs_context.h>
0011 #include <linux/module.h>
0012 #include <linux/pagemap.h>
0013 #include <linux/ucs2_string.h>
0014 #include <linux/slab.h>
0015 #include <linux/magic.h>
0016 
0017 #include "internal.h"
0018 
0019 LIST_HEAD(efivarfs_list);
0020 
0021 static void efivarfs_evict_inode(struct inode *inode)
0022 {
0023     clear_inode(inode);
0024 }
0025 
0026 static const struct super_operations efivarfs_ops = {
0027     .statfs = simple_statfs,
0028     .drop_inode = generic_delete_inode,
0029     .evict_inode = efivarfs_evict_inode,
0030 };
0031 
0032 /*
0033  * Compare two efivarfs file names.
0034  *
0035  * An efivarfs filename is composed of two parts,
0036  *
0037  *  1. A case-sensitive variable name
0038  *  2. A case-insensitive GUID
0039  *
0040  * So we need to perform a case-sensitive match on part 1 and a
0041  * case-insensitive match on part 2.
0042  */
0043 static int efivarfs_d_compare(const struct dentry *dentry,
0044                   unsigned int len, const char *str,
0045                   const struct qstr *name)
0046 {
0047     int guid = len - EFI_VARIABLE_GUID_LEN;
0048 
0049     if (name->len != len)
0050         return 1;
0051 
0052     /* Case-sensitive compare for the variable name */
0053     if (memcmp(str, name->name, guid))
0054         return 1;
0055 
0056     /* Case-insensitive compare for the GUID */
0057     return strncasecmp(name->name + guid, str + guid, EFI_VARIABLE_GUID_LEN);
0058 }
0059 
0060 static int efivarfs_d_hash(const struct dentry *dentry, struct qstr *qstr)
0061 {
0062     unsigned long hash = init_name_hash(dentry);
0063     const unsigned char *s = qstr->name;
0064     unsigned int len = qstr->len;
0065 
0066     if (!efivarfs_valid_name(s, len))
0067         return -EINVAL;
0068 
0069     while (len-- > EFI_VARIABLE_GUID_LEN)
0070         hash = partial_name_hash(*s++, hash);
0071 
0072     /* GUID is case-insensitive. */
0073     while (len--)
0074         hash = partial_name_hash(tolower(*s++), hash);
0075 
0076     qstr->hash = end_name_hash(hash);
0077     return 0;
0078 }
0079 
0080 static const struct dentry_operations efivarfs_d_ops = {
0081     .d_compare = efivarfs_d_compare,
0082     .d_hash = efivarfs_d_hash,
0083     .d_delete = always_delete_dentry,
0084 };
0085 
0086 static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
0087 {
0088     struct dentry *d;
0089     struct qstr q;
0090     int err;
0091 
0092     q.name = name;
0093     q.len = strlen(name);
0094 
0095     err = efivarfs_d_hash(parent, &q);
0096     if (err)
0097         return ERR_PTR(err);
0098 
0099     d = d_alloc(parent, &q);
0100     if (d)
0101         return d;
0102 
0103     return ERR_PTR(-ENOMEM);
0104 }
0105 
0106 static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
0107                  unsigned long name_size, void *data)
0108 {
0109     struct super_block *sb = (struct super_block *)data;
0110     struct efivar_entry *entry;
0111     struct inode *inode = NULL;
0112     struct dentry *dentry, *root = sb->s_root;
0113     unsigned long size = 0;
0114     char *name;
0115     int len;
0116     int err = -ENOMEM;
0117     bool is_removable = false;
0118 
0119     entry = kzalloc(sizeof(*entry), GFP_KERNEL);
0120     if (!entry)
0121         return err;
0122 
0123     memcpy(entry->var.VariableName, name16, name_size);
0124     memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t));
0125 
0126     len = ucs2_utf8size(entry->var.VariableName);
0127 
0128     /* name, plus '-', plus GUID, plus NUL*/
0129     name = kmalloc(len + 1 + EFI_VARIABLE_GUID_LEN + 1, GFP_KERNEL);
0130     if (!name)
0131         goto fail;
0132 
0133     ucs2_as_utf8(name, entry->var.VariableName, len);
0134 
0135     if (efivar_variable_is_removable(entry->var.VendorGuid, name, len))
0136         is_removable = true;
0137 
0138     name[len] = '-';
0139 
0140     efi_guid_to_str(&entry->var.VendorGuid, name + len + 1);
0141 
0142     name[len + EFI_VARIABLE_GUID_LEN+1] = '\0';
0143 
0144     /* replace invalid slashes like kobject_set_name_vargs does for /sys/firmware/efi/vars. */
0145     strreplace(name, '/', '!');
0146 
0147     inode = efivarfs_get_inode(sb, d_inode(root), S_IFREG | 0644, 0,
0148                    is_removable);
0149     if (!inode)
0150         goto fail_name;
0151 
0152     dentry = efivarfs_alloc_dentry(root, name);
0153     if (IS_ERR(dentry)) {
0154         err = PTR_ERR(dentry);
0155         goto fail_inode;
0156     }
0157 
0158     __efivar_entry_get(entry, NULL, &size, NULL);
0159     __efivar_entry_add(entry, &efivarfs_list);
0160 
0161     /* copied by the above to local storage in the dentry. */
0162     kfree(name);
0163 
0164     inode_lock(inode);
0165     inode->i_private = entry;
0166     i_size_write(inode, size + sizeof(entry->var.Attributes));
0167     inode_unlock(inode);
0168     d_add(dentry, inode);
0169 
0170     return 0;
0171 
0172 fail_inode:
0173     iput(inode);
0174 fail_name:
0175     kfree(name);
0176 fail:
0177     kfree(entry);
0178     return err;
0179 }
0180 
0181 static int efivarfs_destroy(struct efivar_entry *entry, void *data)
0182 {
0183     efivar_entry_remove(entry);
0184     kfree(entry);
0185     return 0;
0186 }
0187 
0188 static int efivarfs_fill_super(struct super_block *sb, struct fs_context *fc)
0189 {
0190     struct inode *inode = NULL;
0191     struct dentry *root;
0192     int err;
0193 
0194     sb->s_maxbytes          = MAX_LFS_FILESIZE;
0195     sb->s_blocksize         = PAGE_SIZE;
0196     sb->s_blocksize_bits    = PAGE_SHIFT;
0197     sb->s_magic             = EFIVARFS_MAGIC;
0198     sb->s_op                = &efivarfs_ops;
0199     sb->s_d_op      = &efivarfs_d_ops;
0200     sb->s_time_gran         = 1;
0201 
0202     if (!efivar_supports_writes())
0203         sb->s_flags |= SB_RDONLY;
0204 
0205     inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0, true);
0206     if (!inode)
0207         return -ENOMEM;
0208     inode->i_op = &efivarfs_dir_inode_operations;
0209 
0210     root = d_make_root(inode);
0211     sb->s_root = root;
0212     if (!root)
0213         return -ENOMEM;
0214 
0215     INIT_LIST_HEAD(&efivarfs_list);
0216 
0217     err = efivar_init(efivarfs_callback, (void *)sb, true, &efivarfs_list);
0218     if (err)
0219         efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL);
0220 
0221     return err;
0222 }
0223 
0224 static int efivarfs_get_tree(struct fs_context *fc)
0225 {
0226     return get_tree_single(fc, efivarfs_fill_super);
0227 }
0228 
0229 static const struct fs_context_operations efivarfs_context_ops = {
0230     .get_tree   = efivarfs_get_tree,
0231 };
0232 
0233 static int efivarfs_init_fs_context(struct fs_context *fc)
0234 {
0235     fc->ops = &efivarfs_context_ops;
0236     return 0;
0237 }
0238 
0239 static void efivarfs_kill_sb(struct super_block *sb)
0240 {
0241     kill_litter_super(sb);
0242 
0243     /* Remove all entries and destroy */
0244     efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL);
0245 }
0246 
0247 static struct file_system_type efivarfs_type = {
0248     .owner   = THIS_MODULE,
0249     .name    = "efivarfs",
0250     .init_fs_context = efivarfs_init_fs_context,
0251     .kill_sb = efivarfs_kill_sb,
0252 };
0253 
0254 static __init int efivarfs_init(void)
0255 {
0256     if (!efivars_kobject())
0257         return -ENODEV;
0258 
0259     return register_filesystem(&efivarfs_type);
0260 }
0261 
0262 static __exit void efivarfs_exit(void)
0263 {
0264     unregister_filesystem(&efivarfs_type);
0265 }
0266 
0267 MODULE_AUTHOR("Matthew Garrett, Jeremy Kerr");
0268 MODULE_DESCRIPTION("EFI Variable Filesystem");
0269 MODULE_LICENSE("GPL");
0270 MODULE_ALIAS_FS("efivarfs");
0271 
0272 module_init(efivarfs_init);
0273 module_exit(efivarfs_exit);