Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/fs/efivarfs/file.c
26285 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Copyright (C) 2012 Red Hat, Inc.
4
* Copyright (C) 2012 Jeremy Kerr <[email protected]>
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
15
static 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
inode_lock(inode);
40
if (var->removed) {
41
/*
42
* file got removed; don't allow a set. Caused by an
43
* unsuccessful create or successful delete write
44
* racing with us.
45
*/
46
bytes = -EIO;
47
goto out;
48
}
49
50
bytes = efivar_entry_set_get_size(var, attributes, &datasize,
51
data, &set);
52
if (!set) {
53
if (bytes == -ENOENT)
54
bytes = -EIO;
55
goto out;
56
}
57
58
if (bytes == -ENOENT) {
59
/*
60
* zero size signals to release that the write deleted
61
* the variable
62
*/
63
i_size_write(inode, 0);
64
} else {
65
i_size_write(inode, datasize + sizeof(attributes));
66
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
67
}
68
69
bytes = count;
70
71
out:
72
inode_unlock(inode);
73
74
kfree(data);
75
76
return bytes;
77
}
78
79
static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
80
size_t count, loff_t *ppos)
81
{
82
struct efivar_entry *var = file->private_data;
83
unsigned long datasize = 0;
84
u32 attributes;
85
void *data;
86
ssize_t size = 0;
87
int err;
88
89
while (!__ratelimit(&file->f_cred->user->ratelimit))
90
msleep(50);
91
92
err = efivar_entry_size(var, &datasize);
93
94
/*
95
* efivarfs represents uncommitted variables with
96
* zero-length files. Reading them should return EOF.
97
*/
98
if (err == -ENOENT)
99
return 0;
100
else if (err)
101
return err;
102
103
data = kmalloc(datasize + sizeof(attributes), GFP_KERNEL);
104
105
if (!data)
106
return -ENOMEM;
107
108
size = efivar_entry_get(var, &attributes, &datasize,
109
data + sizeof(attributes));
110
if (size)
111
goto out_free;
112
113
memcpy(data, &attributes, sizeof(attributes));
114
size = simple_read_from_buffer(userbuf, count, ppos,
115
data, datasize + sizeof(attributes));
116
out_free:
117
kfree(data);
118
119
return size;
120
}
121
122
static int efivarfs_file_release(struct inode *inode, struct file *file)
123
{
124
struct efivar_entry *var = inode->i_private;
125
126
inode_lock(inode);
127
var->removed = (--var->open_count == 0 && i_size_read(inode) == 0);
128
inode_unlock(inode);
129
130
if (var->removed)
131
simple_recursive_removal(file->f_path.dentry, NULL);
132
133
return 0;
134
}
135
136
static int efivarfs_file_open(struct inode *inode, struct file *file)
137
{
138
struct efivar_entry *entry = inode->i_private;
139
140
file->private_data = entry;
141
142
inode_lock(inode);
143
entry->open_count++;
144
inode_unlock(inode);
145
146
return 0;
147
}
148
149
const struct file_operations efivarfs_file_operations = {
150
.open = efivarfs_file_open,
151
.read = efivarfs_file_read,
152
.write = efivarfs_file_write,
153
.release = efivarfs_file_release,
154
};
155
156