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