linux/fs/efivarfs/file.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/efi.h>
   8#include <linux/delay.h>
   9#include <linux/fs.h>
  10#include <linux/slab.h>
  11#include <linux/mount.h>
  12
  13#include "internal.h"
  14
  15static ssize_t efivarfs_file_write(struct file *file,
  16                const char __user *userbuf, size_t count, loff_t *ppos)
  17{
  18        struct efivar_entry *var = file->private_data;
  19        void *data;
  20        u32 attributes;
  21        struct inode *inode = file->f_mapping->host;
  22        unsigned long datasize = count - sizeof(attributes);
  23        ssize_t bytes;
  24        bool set = false;
  25
  26        if (count < sizeof(attributes))
  27                return -EINVAL;
  28
  29        if (copy_from_user(&attributes, userbuf, sizeof(attributes)))
  30                return -EFAULT;
  31
  32        if (attributes & ~(EFI_VARIABLE_MASK))
  33                return -EINVAL;
  34
  35        data = memdup_user(userbuf + sizeof(attributes), datasize);
  36        if (IS_ERR(data))
  37                return PTR_ERR(data);
  38
  39        bytes = efivar_entry_set_get_size(var, attributes, &datasize,
  40                                          data, &set);
  41        if (!set && bytes) {
  42                if (bytes == -ENOENT)
  43                        bytes = -EIO;
  44                goto out;
  45        }
  46
  47        if (bytes == -ENOENT) {
  48                drop_nlink(inode);
  49                d_delete(file->f_path.dentry);
  50                dput(file->f_path.dentry);
  51        } else {
  52                inode_lock(inode);
  53                i_size_write(inode, datasize + sizeof(attributes));
  54                inode_unlock(inode);
  55        }
  56
  57        bytes = count;
  58
  59out:
  60        kfree(data);
  61
  62        return bytes;
  63}
  64
  65static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
  66                size_t count, loff_t *ppos)
  67{
  68        struct efivar_entry *var = file->private_data;
  69        unsigned long datasize = 0;
  70        u32 attributes;
  71        void *data;
  72        ssize_t size = 0;
  73        int err;
  74
  75        while (!__ratelimit(&file->f_cred->user->ratelimit)) {
  76                if (!msleep_interruptible(50))
  77                        return -EINTR;
  78        }
  79
  80        err = efivar_entry_size(var, &datasize);
  81
  82        /*
  83         * efivarfs represents uncommitted variables with
  84         * zero-length files. Reading them should return EOF.
  85         */
  86        if (err == -ENOENT)
  87                return 0;
  88        else if (err)
  89                return err;
  90
  91        data = kmalloc(datasize + sizeof(attributes), GFP_KERNEL);
  92
  93        if (!data)
  94                return -ENOMEM;
  95
  96        size = efivar_entry_get(var, &attributes, &datasize,
  97                                data + sizeof(attributes));
  98        if (size)
  99                goto out_free;
 100
 101        memcpy(data, &attributes, sizeof(attributes));
 102        size = simple_read_from_buffer(userbuf, count, ppos,
 103                                       data, datasize + sizeof(attributes));
 104out_free:
 105        kfree(data);
 106
 107        return size;
 108}
 109
 110static inline unsigned int efivarfs_getflags(struct inode *inode)
 111{
 112        unsigned int i_flags;
 113        unsigned int flags = 0;
 114
 115        i_flags = inode->i_flags;
 116        if (i_flags & S_IMMUTABLE)
 117                flags |= FS_IMMUTABLE_FL;
 118        return flags;
 119}
 120
 121static int
 122efivarfs_ioc_getxflags(struct file *file, void __user *arg)
 123{
 124        struct inode *inode = file->f_mapping->host;
 125        unsigned int flags = efivarfs_getflags(inode);
 126
 127        if (copy_to_user(arg, &flags, sizeof(flags)))
 128                return -EFAULT;
 129        return 0;
 130}
 131
 132static int
 133efivarfs_ioc_setxflags(struct file *file, void __user *arg)
 134{
 135        struct inode *inode = file->f_mapping->host;
 136        unsigned int flags;
 137        unsigned int i_flags = 0;
 138        unsigned int oldflags = efivarfs_getflags(inode);
 139        int error;
 140
 141        if (!inode_owner_or_capable(inode))
 142                return -EACCES;
 143
 144        if (copy_from_user(&flags, arg, sizeof(flags)))
 145                return -EFAULT;
 146
 147        if (flags & ~FS_IMMUTABLE_FL)
 148                return -EOPNOTSUPP;
 149
 150        if (flags & FS_IMMUTABLE_FL)
 151                i_flags |= S_IMMUTABLE;
 152
 153
 154        error = mnt_want_write_file(file);
 155        if (error)
 156                return error;
 157
 158        inode_lock(inode);
 159
 160        error = vfs_ioc_setflags_prepare(inode, oldflags, flags);
 161        if (error)
 162                goto out;
 163
 164        inode_set_flags(inode, i_flags, S_IMMUTABLE);
 165out:
 166        inode_unlock(inode);
 167        mnt_drop_write_file(file);
 168        return error;
 169}
 170
 171static long
 172efivarfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long p)
 173{
 174        void __user *arg = (void __user *)p;
 175
 176        switch (cmd) {
 177        case FS_IOC_GETFLAGS:
 178                return efivarfs_ioc_getxflags(file, arg);
 179        case FS_IOC_SETFLAGS:
 180                return efivarfs_ioc_setxflags(file, arg);
 181        }
 182
 183        return -ENOTTY;
 184}
 185
 186const struct file_operations efivarfs_file_operations = {
 187        .open   = simple_open,
 188        .read   = efivarfs_file_read,
 189        .write  = efivarfs_file_write,
 190        .llseek = no_llseek,
 191        .unlocked_ioctl = efivarfs_file_ioctl,
 192};
 193