114 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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_set_mtime_to_ts(inode, inode_set_ctime_current(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 with83 * 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 109const struct file_operations efivarfs_file_operations = {110 .open = simple_open,111 .read = efivarfs_file_read,112 .write = efivarfs_file_write,113};114