linux/fs/efivarfs/super.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2012 Red Hat, Inc.
   4 * Copyright (C) 2012 Jeremy Kerr <jeremy.kerr@canonical.com>
   5 */
   6
   7#include <linux/ctype.h>
   8#include <linux/efi.h>
   9#include <linux/fs.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>
  16
  17#include "internal.h"
  18
  19LIST_HEAD(efivarfs_list);
  20
  21static void efivarfs_evict_inode(struct inode *inode)
  22{
  23        clear_inode(inode);
  24}
  25
  26static const struct super_operations efivarfs_ops = {
  27        .statfs = simple_statfs,
  28        .drop_inode = generic_delete_inode,
  29        .evict_inode = efivarfs_evict_inode,
  30};
  31
  32/*
  33 * Compare two efivarfs file names.
  34 *
  35 * An efivarfs filename is composed of two parts,
  36 *
  37 *      1. A case-sensitive variable name
  38 *      2. A case-insensitive GUID
  39 *
  40 * So we need to perform a case-sensitive match on part 1 and a
  41 * case-insensitive match on part 2.
  42 */
  43static int efivarfs_d_compare(const struct dentry *dentry,
  44                              unsigned int len, const char *str,
  45                              const struct qstr *name)
  46{
  47        int guid = len - EFI_VARIABLE_GUID_LEN;
  48
  49        if (name->len != len)
  50                return 1;
  51
  52        /* Case-sensitive compare for the variable name */
  53        if (memcmp(str, name->name, guid))
  54                return 1;
  55
  56        /* Case-insensitive compare for the GUID */
  57        return strncasecmp(name->name + guid, str + guid, EFI_VARIABLE_GUID_LEN);
  58}
  59
  60static int efivarfs_d_hash(const struct dentry *dentry, struct qstr *qstr)
  61{
  62        unsigned long hash = init_name_hash(dentry);
  63        const unsigned char *s = qstr->name;
  64        unsigned int len = qstr->len;
  65
  66        if (!efivarfs_valid_name(s, len))
  67                return -EINVAL;
  68
  69        while (len-- > EFI_VARIABLE_GUID_LEN)
  70                hash = partial_name_hash(*s++, hash);
  71
  72        /* GUID is case-insensitive. */
  73        while (len--)
  74                hash = partial_name_hash(tolower(*s++), hash);
  75
  76        qstr->hash = end_name_hash(hash);
  77        return 0;
  78}
  79
  80static 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,
  84};
  85
  86static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
  87{
  88        struct dentry *d;
  89        struct qstr q;
  90        int err;
  91
  92        q.name = name;
  93        q.len = strlen(name);
  94
  95        err = efivarfs_d_hash(parent, &q);
  96        if (err)
  97                return ERR_PTR(err);
  98
  99        d = d_alloc(parent, &q);
 100        if (d)
 101                return d;
 102
 103        return ERR_PTR(-ENOMEM);
 104}
 105
 106static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
 107                             unsigned long name_size, void *data)
 108{
 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;
 114        char *name;
 115        int len;
 116        int err = -ENOMEM;
 117        bool is_removable = false;
 118
 119        entry = kzalloc(sizeof(*entry), GFP_KERNEL);
 120        if (!entry)
 121                return err;
 122
 123        memcpy(entry->var.VariableName, name16, name_size);
 124        memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t));
 125
 126        len = ucs2_utf8size(entry->var.VariableName);
 127
 128        /* name, plus '-', plus GUID, plus NUL*/
 129        name = kmalloc(len + 1 + EFI_VARIABLE_GUID_LEN + 1, GFP_KERNEL);
 130        if (!name)
 131                goto fail;
 132
 133        ucs2_as_utf8(name, entry->var.VariableName, len);
 134
 135        if (efivar_variable_is_removable(entry->var.VendorGuid, name, len))
 136                is_removable = true;
 137
 138        name[len] = '-';
 139
 140        efi_guid_to_str(&entry->var.VendorGuid, name + len + 1);
 141
 142        name[len + EFI_VARIABLE_GUID_LEN+1] = '\0';
 143
 144        /* replace invalid slashes like kobject_set_name_vargs does for /sys/firmware/efi/vars. */
 145        strreplace(name, '/', '!');
 146
 147        inode = efivarfs_get_inode(sb, d_inode(root), S_IFREG | 0644, 0,
 148                                   is_removable);
 149        if (!inode)
 150                goto fail_name;
 151
 152        dentry = efivarfs_alloc_dentry(root, name);
 153        if (IS_ERR(dentry)) {
 154                err = PTR_ERR(dentry);
 155                goto fail_inode;
 156        }
 157
 158        efivar_entry_size(entry, &size);
 159        err = efivar_entry_add(entry, &efivarfs_list);
 160        if (err)
 161                goto fail_inode;
 162
 163        /* copied by the above to local storage in the dentry. */
 164        kfree(name);
 165
 166        inode_lock(inode);
 167        inode->i_private = entry;
 168        i_size_write(inode, size + sizeof(entry->var.Attributes));
 169        inode_unlock(inode);
 170        d_add(dentry, inode);
 171
 172        return 0;
 173
 174fail_inode:
 175        iput(inode);
 176fail_name:
 177        kfree(name);
 178fail:
 179        kfree(entry);
 180        return err;
 181}
 182
 183static int efivarfs_destroy(struct efivar_entry *entry, void *data)
 184{
 185        int err = efivar_entry_remove(entry);
 186
 187        if (err)
 188                return err;
 189        kfree(entry);
 190        return 0;
 191}
 192
 193static int efivarfs_fill_super(struct super_block *sb, struct fs_context *fc)
 194{
 195        struct inode *inode = NULL;
 196        struct dentry *root;
 197        int err;
 198
 199        sb->s_maxbytes          = MAX_LFS_FILESIZE;
 200        sb->s_blocksize         = PAGE_SIZE;
 201        sb->s_blocksize_bits    = PAGE_SHIFT;
 202        sb->s_magic             = EFIVARFS_MAGIC;
 203        sb->s_op                = &efivarfs_ops;
 204        sb->s_d_op              = &efivarfs_d_ops;
 205        sb->s_time_gran         = 1;
 206
 207        if (!efivar_supports_writes())
 208                sb->s_flags |= SB_RDONLY;
 209
 210        inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0, true);
 211        if (!inode)
 212                return -ENOMEM;
 213        inode->i_op = &efivarfs_dir_inode_operations;
 214
 215        root = d_make_root(inode);
 216        sb->s_root = root;
 217        if (!root)
 218                return -ENOMEM;
 219
 220        INIT_LIST_HEAD(&efivarfs_list);
 221
 222        err = efivar_init(efivarfs_callback, (void *)sb, true, &efivarfs_list);
 223        if (err)
 224                __efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL, NULL);
 225
 226        return err;
 227}
 228
 229static int efivarfs_get_tree(struct fs_context *fc)
 230{
 231        return get_tree_single(fc, efivarfs_fill_super);
 232}
 233
 234static const struct fs_context_operations efivarfs_context_ops = {
 235        .get_tree       = efivarfs_get_tree,
 236};
 237
 238static int efivarfs_init_fs_context(struct fs_context *fc)
 239{
 240        fc->ops = &efivarfs_context_ops;
 241        return 0;
 242}
 243
 244static void efivarfs_kill_sb(struct super_block *sb)
 245{
 246        kill_litter_super(sb);
 247
 248        /* Remove all entries and destroy */
 249        __efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL, NULL);
 250}
 251
 252static struct file_system_type efivarfs_type = {
 253        .owner   = THIS_MODULE,
 254        .name    = "efivarfs",
 255        .init_fs_context = efivarfs_init_fs_context,
 256        .kill_sb = efivarfs_kill_sb,
 257};
 258
 259static __init int efivarfs_init(void)
 260{
 261        if (!efivars_kobject())
 262                return -ENODEV;
 263
 264        return register_filesystem(&efivarfs_type);
 265}
 266
 267static __exit void efivarfs_exit(void)
 268{
 269        unregister_filesystem(&efivarfs_type);
 270}
 271
 272MODULE_AUTHOR("Matthew Garrett, Jeremy Kerr");
 273MODULE_DESCRIPTION("EFI Variable Filesystem");
 274MODULE_LICENSE("GPL");
 275MODULE_ALIAS_FS("efivarfs");
 276
 277module_init(efivarfs_init);
 278module_exit(efivarfs_exit);
 279