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        .show_options = generic_show_options,
  33};
  34
  35static 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 */
  48static int efivarfs_d_compare(const struct dentry *parent, const struct inode *pinode,
  49                              const struct dentry *dentry, const struct inode *inode,
  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
  66static int efivarfs_d_hash(const struct dentry *dentry,
  67                           const struct inode *inode, struct qstr *qstr)
  68{
  69        unsigned long hash = init_name_hash();
  70        const unsigned char *s = qstr->name;
  71        unsigned int len = qstr->len;
  72
  73        if (!efivarfs_valid_name(s, len))
  74                return -EINVAL;
  75
  76        while (len-- > EFI_VARIABLE_GUID_LEN)
  77                hash = partial_name_hash(*s++, hash);
  78
  79        /* GUID is case-insensitive. */
  80        while (len--)
  81                hash = partial_name_hash(tolower(*s++), hash);
  82
  83        qstr->hash = end_name_hash(hash);
  84        return 0;
  85}
  86
  87/*
  88 * Retaining negative dentries for an in-memory filesystem just wastes
  89 * memory and lookup time: arrange for them to be deleted immediately.
  90 */
  91static int efivarfs_delete_dentry(const struct dentry *dentry)
  92{
  93        return 1;
  94}
  95
  96static struct dentry_operations efivarfs_d_ops = {
  97        .d_compare = efivarfs_d_compare,
  98        .d_hash = efivarfs_d_hash,
  99        .d_delete = efivarfs_delete_dentry,
 100};
 101
 102static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
 103{
 104        struct dentry *d;
 105        struct qstr q;
 106        int err;
 107
 108        q.name = name;
 109        q.len = strlen(name);
 110
 111        err = efivarfs_d_hash(NULL, NULL, &q);
 112        if (err)
 113                return ERR_PTR(err);
 114
 115        d = d_alloc(parent, &q);
 116        if (d)
 117                return d;
 118
 119        return ERR_PTR(-ENOMEM);
 120}
 121
 122static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
 123                             unsigned long name_size, void *data)
 124{
 125        struct super_block *sb = (struct super_block *)data;
 126        struct efivar_entry *entry;
 127        struct inode *inode = NULL;
 128        struct dentry *dentry, *root = sb->s_root;
 129        unsigned long size = 0;
 130        char *name;
 131        int len, i;
 132        int err = -ENOMEM;
 133
 134        entry = kmalloc(sizeof(*entry), GFP_KERNEL);
 135        if (!entry)
 136                return err;
 137
 138        memcpy(entry->var.VariableName, name16, name_size);
 139        memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t));
 140
 141        len = ucs2_strlen(entry->var.VariableName);
 142
 143        /* name, plus '-', plus GUID, plus NUL*/
 144        name = kmalloc(len + 1 + EFI_VARIABLE_GUID_LEN + 1, GFP_KERNEL);
 145        if (!name)
 146                goto fail;
 147
 148        for (i = 0; i < len; i++)
 149                name[i] = entry->var.VariableName[i] & 0xFF;
 150
 151        name[len] = '-';
 152
 153        efi_guid_unparse(&entry->var.VendorGuid, name + len + 1);
 154
 155        name[len + EFI_VARIABLE_GUID_LEN+1] = '\0';
 156
 157        inode = efivarfs_get_inode(sb, root->d_inode, S_IFREG | 0644, 0);
 158        if (!inode)
 159                goto fail_name;
 160
 161        dentry = efivarfs_alloc_dentry(root, name);
 162        if (IS_ERR(dentry)) {
 163                err = PTR_ERR(dentry);
 164                goto fail_inode;
 165        }
 166
 167        /* copied by the above to local storage in the dentry. */
 168        kfree(name);
 169
 170        efivar_entry_size(entry, &size);
 171        efivar_entry_add(entry, &efivarfs_list);
 172
 173        mutex_lock(&inode->i_mutex);
 174        inode->i_private = entry;
 175        i_size_write(inode, size + sizeof(entry->var.Attributes));
 176        mutex_unlock(&inode->i_mutex);
 177        d_add(dentry, inode);
 178
 179        return 0;
 180
 181fail_inode:
 182        iput(inode);
 183fail_name:
 184        kfree(name);
 185fail:
 186        kfree(entry);
 187        return err;
 188}
 189
 190static int efivarfs_destroy(struct efivar_entry *entry, void *data)
 191{
 192        efivar_entry_remove(entry);
 193        kfree(entry);
 194        return 0;
 195}
 196
 197static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
 198{
 199        struct inode *inode = NULL;
 200        struct dentry *root;
 201        int err;
 202
 203        efivarfs_sb = sb;
 204
 205        sb->s_maxbytes          = MAX_LFS_FILESIZE;
 206        sb->s_blocksize         = PAGE_CACHE_SIZE;
 207        sb->s_blocksize_bits    = PAGE_CACHE_SHIFT;
 208        sb->s_magic             = EFIVARFS_MAGIC;
 209        sb->s_op                = &efivarfs_ops;
 210        sb->s_d_op              = &efivarfs_d_ops;
 211        sb->s_time_gran         = 1;
 212
 213        inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0);
 214        if (!inode)
 215                return -ENOMEM;
 216        inode->i_op = &efivarfs_dir_inode_operations;
 217
 218        root = d_make_root(inode);
 219        sb->s_root = root;
 220        if (!root)
 221                return -ENOMEM;
 222
 223        INIT_LIST_HEAD(&efivarfs_list);
 224
 225        err = efivar_init(efivarfs_callback, (void *)sb, false,
 226                          true, &efivarfs_list);
 227        if (err)
 228                __efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL, NULL);
 229
 230        return err;
 231}
 232
 233static struct dentry *efivarfs_mount(struct file_system_type *fs_type,
 234                                    int flags, const char *dev_name, void *data)
 235{
 236        return mount_single(fs_type, flags, data, efivarfs_fill_super);
 237}
 238
 239static void efivarfs_kill_sb(struct super_block *sb)
 240{
 241        kill_litter_super(sb);
 242        efivarfs_sb = NULL;
 243
 244        /* Remove all entries and destroy */
 245        __efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL, NULL);
 246}
 247
 248static struct file_system_type efivarfs_type = {
 249        .name    = "efivarfs",
 250        .mount   = efivarfs_mount,
 251        .kill_sb = efivarfs_kill_sb,
 252};
 253
 254static __init int efivarfs_init(void)
 255{
 256        if (!efi_enabled(EFI_RUNTIME_SERVICES))
 257                return 0;
 258
 259        if (!efivars_kobject())
 260                return 0;
 261
 262        return register_filesystem(&efivarfs_type);
 263}
 264
 265MODULE_AUTHOR("Matthew Garrett, Jeremy Kerr");
 266MODULE_DESCRIPTION("EFI Variable Filesystem");
 267MODULE_LICENSE("GPL");
 268MODULE_ALIAS_FS("efivarfs");
 269
 270module_init(efivarfs_init);
 271