1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * fs/epfs/file.c
4 *
5 * Copyright (c) 2022 Huawei Technologies Co., Ltd.
6 * Author: weilongping@huawei.com
7 * Create: 2022-06-10
8 */
9 #include <linux/file.h>
10 #include <linux/fs.h>
11 #include <linux/fs_stack.h>
12 #include <linux/slab.h>
13 #include <linux/uaccess.h>
14
15 #include "internal.h"
16
epfs_set_origin_fd(struct file * file,unsigned long arg)17 long epfs_set_origin_fd(struct file *file, unsigned long arg)
18 {
19 int fd = -1;
20 struct file *origin_file;
21 struct inode *inode = file->f_inode;
22 struct epfs_inode_info *info = epfs_inode_to_private(inode);
23 int ret = 0;
24
25 if (copy_from_user(&fd, (int *)arg, sizeof(fd)))
26 return -EFAULT;
27 if (IS_ENABLED(CONFIG_EPFS_DEBUG))
28 epfs_debug("original fd: %d", fd);
29 origin_file = fget(fd);
30 if (!origin_file) {
31 epfs_err("Original file not exist!");
32 return -EBADF;
33 }
34
35 mutex_lock(&info->lock);
36 if (info->origin_file) {
37 // origin_file had been set.
38 ret = -EEXIST;
39 fput(origin_file);
40 } else if (file_inode(origin_file) == inode) {
41 epfs_err("Could not set itself as origin_file!");
42 fput(origin_file);
43 ret = -EINVAL;
44 } else {
45 info->origin_file = origin_file;
46 fsstack_copy_attr_all(inode, file_inode(origin_file));
47 fsstack_copy_inode_size(inode, file_inode(origin_file));
48 }
49 mutex_unlock(&info->lock);
50 return ret;
51 }
52
check_range(struct epfs_range * range)53 int check_range(struct epfs_range *range)
54 {
55 __u64 index;
56
57 if (range->range[0].begin >= range->range[0].end) {
58 epfs_err("Invalid range: [%llu, %llu)", range->range[0].begin,
59 range->range[0].end);
60 return -EINVAL;
61 }
62
63 for (index = 1; index < range->num; index++) {
64 if ((range->range[index].begin >= range->range[index].end) ||
65 (range->range[index].begin < range->range[index - 1].end)) {
66 epfs_err("Invalid range: [%llu, %llu), [%llu, %llu)",
67 range->range[index - 1].begin,
68 range->range[index - 1].end,
69 range->range[index].begin,
70 range->range[index].end);
71 return -EINVAL;
72 }
73 }
74 if (IS_ENABLED(CONFIG_EPFS_DEBUG)) {
75 epfs_debug("epfs_range recv %llu ranges:", range->num);
76 for (index = 0; index < range->num; index++) {
77 epfs_debug("range:[%llu %llu)",
78 range->range[index].begin,
79 range->range[index].end);
80 }
81 epfs_debug("\n");
82 }
83 return 0;
84 }
85
epfs_set_range(struct file * file,unsigned long arg)86 long epfs_set_range(struct file *file, unsigned long arg)
87 {
88 struct inode *inode = file->f_inode;
89 struct inode *origin_inode;
90 struct epfs_inode_info *info = epfs_inode_to_private(inode);
91 int ret = 0;
92 struct epfs_range *range;
93 struct epfs_range header;
94
95 mutex_lock(&info->lock);
96 if (!info->origin_file) {
97 epfs_err("origin file not exist!");
98 ret = -EBADF;
99 goto out_set_range;
100 }
101 origin_inode = info->origin_file->f_inode;
102 if (!in_group_p(origin_inode->i_gid)) {
103 epfs_err("Only group member can set range: %u",
104 i_gid_read(origin_inode));
105 ret = -EACCES;
106 goto out_set_range;
107 }
108
109 if (copy_from_user(&header, (struct epfs_range *)arg,
110 sizeof(header))) {
111 ret = -EFAULT;
112 epfs_err("get header failed!");
113 goto out_set_range;
114 }
115
116 if (header.num > EPFS_MAX_RANGES || header.num == 0) {
117 ret = -EINVAL;
118 epfs_err("illegal num: %llu", header.num);
119 goto out_set_range;
120 }
121
122 range = kzalloc(sizeof(header) + sizeof(header.range[0]) * header.num,
123 GFP_KERNEL);
124 if (!range) {
125 ret = -ENOMEM;
126 goto out_set_range;
127 }
128
129 if (copy_from_user(range, (struct epfs_range *)arg,
130 sizeof(header) + sizeof(header.range[0]) * header.num)) {
131 ret = -EFAULT;
132 epfs_err("Failed to get range! num: %llu", header.num);
133 kfree(range);
134 goto out_set_range;
135 }
136
137 ret = check_range(range);
138 if (ret) {
139 kfree(range);
140 goto out_set_range;
141 }
142
143 info->range = range;
144 out_set_range:
145 mutex_unlock(&info->lock);
146 return ret;
147 }
148
__epfs_ioctl(struct file * file,unsigned int cmd,unsigned long arg)149 static long __epfs_ioctl(struct file *file, unsigned int cmd,
150 unsigned long arg)
151 {
152 long rc = -ENOTTY;
153
154 if (unlikely(_IOC_TYPE(cmd) != EPFS_IOCTL_MAGIC)) {
155 epfs_err("Failed to check epfs magic: %u", _IOC_TYPE(cmd));
156 return -ENOTTY;
157 }
158 if (unlikely(_IOC_NR(cmd) >= EPFS_IOCTL_MAXNR)) {
159 epfs_err("Failed to check ioctl number: %u", _IOC_NR(cmd));
160 return -ENOTTY;
161 }
162 if (unlikely(!access_ok((void __user *)arg, _IOC_SIZE(cmd)))) {
163 epfs_err("Failed to check user address space range!");
164 return -EFAULT;
165 }
166
167 switch (cmd) {
168 case IOC_SET_ORIGIN_FD:
169 return epfs_set_origin_fd(file, arg);
170 case IOC_SET_EPFS_RANGE:
171 return epfs_set_range(file, arg);
172 default:
173 epfs_info("Exit epfs unsupported ioctl, ret: %ld", rc);
174 return rc;
175 }
176 }
177
epfs_compat_ioctl(struct file * file,unsigned int cmd,unsigned long arg)178 static long epfs_compat_ioctl(struct file *file, unsigned int cmd,
179 unsigned long arg)
180 {
181 return __epfs_ioctl(file, cmd, arg);
182 }
183
epfs_unlocked_ioctl(struct file * file,unsigned int cmd,unsigned long arg)184 static long epfs_unlocked_ioctl(struct file *file, unsigned int cmd,
185 unsigned long arg)
186 {
187 return __epfs_ioctl(file, cmd, arg);
188 }
189
epfs_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)190 static ssize_t epfs_read(struct file *file, char __user *buf, size_t count,
191 loff_t *ppos)
192 {
193 struct inode *inode = file_inode(file);
194 struct epfs_inode_info *info = epfs_inode_to_private(inode);
195 struct file *origin_file;
196 struct epfs_range *range;
197 ssize_t ret = 0;
198 loff_t pos = *ppos;
199 loff_t file_size;
200 int current_range_index = 0;
201
202 mutex_lock(&info->lock);
203 range = info->range;
204 if (!range) {
205 ret = -EINVAL;
206 epfs_err("Invalid inode range!");
207 goto out_read;
208 }
209
210 origin_file = info->origin_file;
211
212 if (!origin_file) {
213 ret = -ENOENT;
214 epfs_err("origin file not exist!");
215 goto out_read;
216 }
217
218 // Reduce count when it will read over file size.
219 file_size = i_size_read(file_inode(origin_file));
220 if (IS_ENABLED(CONFIG_EPFS_DEBUG))
221 if (count > (file_size - pos))
222 epfs_debug(
223 "count will be truncated to %llu, as file_size=%llu, pos=%llu",
224 file_size - pos, file_size, pos);
225 count = count <= (file_size - pos) ? count : (file_size - pos);
226
227 // Skip ranges before pos.
228 while ((range->range[current_range_index].end <= pos) &&
229 (current_range_index < range->num))
230 current_range_index++;
231
232 while (count > 0) {
233 __u64 current_begin, current_end;
234
235 if (current_range_index >= range->num) {
236 // read directly when epfs range gone;
237 if (IS_ENABLED(CONFIG_EPFS_DEBUG))
238 epfs_debug(
239 "read from %llu with len %lu at the end.",
240 pos, count);
241 ret = vfs_read(origin_file, buf, count, &pos);
242 break;
243 }
244 current_begin = range->range[current_range_index].begin;
245 current_end = range->range[current_range_index].end;
246 if (current_begin <= pos) {
247 // Clear user memory
248 unsigned long clear_len = current_end - pos;
249
250 clear_len = clear_len < count ? clear_len : count;
251 if (IS_ENABLED(CONFIG_EPFS_DEBUG))
252 epfs_debug(
253 "clear user memory from %llu with len %lu",
254 pos, clear_len);
255 if (clear_user(buf, clear_len)) {
256 ret = EFAULT;
257 break;
258 }
259 buf += clear_len;
260 pos += clear_len;
261 count -= clear_len;
262 current_range_index++;
263 } else {
264 // Read from pos to (next)current_begin
265 unsigned long read_len = current_begin - pos;
266
267 read_len = read_len < count ? read_len : count;
268 if (IS_ENABLED(CONFIG_EPFS_DEBUG))
269 epfs_debug(
270 "read from %llu with len %lu",
271 pos, read_len);
272 ret = vfs_read(origin_file, buf, read_len, &pos);
273 if (ret < 0 || ret < read_len) {
274 // Could not read enough bytes;
275 break;
276 }
277 buf += ret;
278 count -= ret;
279 }
280 }
281
282 if (ret >= 0) {
283 ret = pos - *ppos;
284 *ppos = pos;
285 }
286 out_read:
287 mutex_unlock(&info->lock);
288 return ret;
289 }
290
291 const struct file_operations epfs_file_fops = {
292 .unlocked_ioctl = epfs_unlocked_ioctl,
293 .compat_ioctl = epfs_compat_ioctl,
294 .read = epfs_read,
295 .llseek = generic_file_llseek,
296 };
297