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->i_mtime = current_time(inode);
  55                inode_unlock(inode);
  56        }
  57
  58        bytes = count;
  59
  60out:
  61        kfree(data);
  62
  63        return bytes;
  64}
  65
  66static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
  67                size_t count, loff_t *ppos)
  68{
  69        struct efivar_entry *var = file->private_data;
  70        unsigned long datasize = 0;
  71        u32 attributes;
  72        void *data;
  73        ssize_t size = 0;
  74        int err;
  75
  76        while (!__ratelimit(&file->f_cred->user->ratelimit))
  77                msleep(50);
  78
  79        err = efivar_entry_size(var, &datasize);
  80
  81        /*
  82         * efivarfs represents uncommitted variables with
  83         * zero-length files. Reading them should return EOF.
  84         */
  85        if (err == -ENOENT)
  86                return 0;
  87        else if (err)
  88                return err;
  89
  90        data = kmalloc(datasize + sizeof(attributes), GFP_KERNEL);
  91
  92        if (!data)
  93                return -ENOMEM;
  94
  95        size = efivar_entry_get(var, &attributes, &datasize,
  96                                data + sizeof(attributes));
  97        if (size)
  98                goto out_free;
  99
 100        memcpy(data, &attributes, sizeof(attributes));
 101        size = simple_read_from_buffer(userbuf, count, ppos,
 102                                       data, datasize + sizeof(attributes));
 103out_free:
 104        kfree(data);
 105
 106        return size;
 107}
 108
 109static inline unsigned int efivarfs_getflags(struct inode *inode)
 110{
 111        unsigned int i_flags;
 112        unsigned int flags = 0;
 113
 114        i_flags = inode->i_flags;
 115        if (i_flags & S_IMMUTABLE)
 116                flags |= FS_IMMUTABLE_FL;
 117        return flags;
 118}
 119
 120static int
 121efivarfs_ioc_getxflags(struct file *file, void __user *arg)
 122{
 123        struct inode *inode = file->f_mapping->host;
 124        unsigned int flags = efivarfs_getflags(inode);
 125
 126        if (copy_to_user(arg, &flags, sizeof(flags)))
 127                return -EFAULT;
 128        return 0;
 129}
 130
 131static int
 132efivarfs_ioc_setxflags(struct file *file, void __user *arg)
 133{
 134        struct inode *inode = file->f_mapping->host;
 135        unsigned int flags;
 136        unsigned int i_flags = 0;
 137        unsigned int oldflags = efivarfs_getflags(inode);
 138        int error;
 139
 140        if (!inode_owner_or_capable(inode))
 141                return -EACCES;
 142
 143        if (copy_from_user(&flags, arg, sizeof(flags)))
 144                return -EFAULT;
 145
 146        if (flags & ~FS_IMMUTABLE_FL)
 147                return -EOPNOTSUPP;
 148
 149        if (flags & FS_IMMUTABLE_FL)
 150                i_flags |= S_IMMUTABLE;
 151
 152
 153        error = mnt_want_write_file(file);
 154        if (error)
 155                return error;
 156
 157        inode_lock(inode);
 158
 159        error = vfs_ioc_setflags_prepare(inode, oldflags, flags);
 160        if (error)
 161                goto out;
 162
 163        inode_set_flags(inode, i_flags, S_IMMUTABLE);
 164out:
 165        inode_unlock(inode);
 166        mnt_drop_write_file(file);
 167        return error;
 168}
 169
 170static long
 171efivarfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long p)
 172{
 173        void __user *arg = (void __user *)p;
 174
 175        switch (cmd) {
 176        case FS_IOC_GETFLAGS:
 177                return efivarfs_ioc_getxflags(file, arg);
 178        case FS_IOC_SETFLAGS:
 179                return efivarfs_ioc_setxflags(file, arg);
 180        }
 181
 182        return -ENOTTY;
 183}
 184
 185const struct file_operations efivarfs_file_operations = {
 186        .open   = simple_open,
 187        .read   = efivarfs_file_read,
 188        .write  = efivarfs_file_write,
 189        .llseek = no_llseek,
 190        .unlocked_ioctl = efivarfs_file_ioctl,
 191};
 192