]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
d68772b7 MF |
2 | /* |
3 | * Copyright (C) 2012 Red Hat, Inc. | |
4 | * Copyright (C) 2012 Jeremy Kerr <[email protected]> | |
d68772b7 MF |
5 | */ |
6 | ||
7 | #include <linux/efi.h> | |
bef3efbe | 8 | #include <linux/delay.h> |
d68772b7 | 9 | #include <linux/fs.h> |
20b4fb48 | 10 | #include <linux/slab.h> |
ed8b0de5 | 11 | #include <linux/mount.h> |
d68772b7 MF |
12 | |
13 | #include "internal.h" | |
14 | ||
d68772b7 MF |
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); | |
aca32b57 | 23 | ssize_t bytes; |
d68772b7 MF |
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 | ||
aca32b57 GB |
35 | data = memdup_user(userbuf + sizeof(attributes), datasize); |
36 | if (IS_ERR(data)) | |
37 | return PTR_ERR(data); | |
d68772b7 MF |
38 | |
39 | bytes = efivar_entry_set_get_size(var, attributes, &datasize, | |
40 | data, &set); | |
3fab70c1 LX |
41 | if (!set && bytes) { |
42 | if (bytes == -ENOENT) | |
43 | bytes = -EIO; | |
d68772b7 | 44 | goto out; |
3fab70c1 | 45 | } |
d68772b7 MF |
46 | |
47 | if (bytes == -ENOENT) { | |
48 | drop_nlink(inode); | |
b583043e AV |
49 | d_delete(file->f_path.dentry); |
50 | dput(file->f_path.dentry); | |
d68772b7 | 51 | } else { |
5955102c | 52 | inode_lock(inode); |
d68772b7 | 53 | i_size_write(inode, datasize + sizeof(attributes)); |
da06c204 | 54 | inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); |
5955102c | 55 | inode_unlock(inode); |
d68772b7 MF |
56 | } |
57 | ||
58 | bytes = count; | |
59 | ||
60 | out: | |
61 | kfree(data); | |
62 | ||
63 | return bytes; | |
64 | } | |
65 | ||
66 | static 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 | ||
4353f033 TL |
76 | while (!__ratelimit(&file->f_cred->user->ratelimit)) |
77 | msleep(50); | |
bef3efbe | 78 | |
d68772b7 | 79 | err = efivar_entry_size(var, &datasize); |
3fab70c1 LX |
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) | |
d68772b7 MF |
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)); | |
103 | out_free: | |
104 | kfree(data); | |
105 | ||
106 | return size; | |
107 | } | |
108 | ||
109 | const struct file_operations efivarfs_file_operations = { | |
f53f292e | 110 | .open = simple_open, |
d68772b7 MF |
111 | .read = efivarfs_file_read, |
112 | .write = efivarfs_file_write, | |
113 | .llseek = no_llseek, | |
114 | }; |