• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 	__u64 num = 0;
93 	struct epfs_range *range;
94 	struct epfs_range header;
95 
96 	mutex_lock(&info->lock);
97 	if (!info->origin_file) {
98 		epfs_err("origin file not exist!");
99 		ret = -EBADF;
100 		goto out_set_range;
101 	}
102 	origin_inode = info->origin_file->f_inode;
103 	if (!in_group_p(origin_inode->i_gid)) {
104 		epfs_err("Only group member can set range: %u",
105 			 i_gid_read(origin_inode));
106 		ret = -EACCES;
107 		goto out_set_range;
108 	}
109 
110 	if (copy_from_user(&header, (struct epfs_range *)arg,
111 			   sizeof(header))) {
112 		ret = -EFAULT;
113 		epfs_err("get header failed!");
114 		goto out_set_range;
115 	}
116 	num = header.num;
117 
118 	if (num > EPFS_MAX_RANGES || num <= 0) {
119 		ret = -EINVAL;
120 		epfs_err("illegal num: %llu", num);
121 		goto out_set_range;
122 	}
123 
124 	range = kzalloc(sizeof(header) + sizeof(header.range[0]) * num,
125 			GFP_KERNEL);
126 	if (!range) {
127 		ret = -ENOMEM;
128 		goto out_set_range;
129 	}
130 
131 	if (copy_from_user(range, (struct epfs_range *)arg,
132 		sizeof(header) + sizeof(header.range[0]) * num)) {
133 		ret = -EFAULT;
134 		epfs_err("Failed to get range! num: %llu", num);
135 		kfree(range);
136 		goto out_set_range;
137 	}
138 	range->num = num;
139 
140 	ret = check_range(range);
141 	if (ret) {
142 		kfree(range);
143 		goto out_set_range;
144 	}
145 
146 	info->range = range;
147 out_set_range:
148 	mutex_unlock(&info->lock);
149 	return ret;
150 }
151 
__epfs_ioctl(struct file * file,unsigned int cmd,unsigned long arg)152 static long __epfs_ioctl(struct file *file, unsigned int cmd,
153 			 unsigned long arg)
154 {
155 	long rc = -ENOTTY;
156 
157 	if (unlikely(_IOC_TYPE(cmd) != EPFS_IOCTL_MAGIC)) {
158 		epfs_err("Failed to check epfs magic: %u", _IOC_TYPE(cmd));
159 		return -ENOTTY;
160 	}
161 	if (unlikely(_IOC_NR(cmd) >= EPFS_IOCTL_MAXNR)) {
162 		epfs_err("Failed to check ioctl number: %u", _IOC_NR(cmd));
163 		return -ENOTTY;
164 	}
165 	if (unlikely(!access_ok((void __user *)arg, _IOC_SIZE(cmd)))) {
166 		epfs_err("Failed to check user address space range!");
167 		return -EFAULT;
168 	}
169 
170 	switch (cmd) {
171 	case IOC_SET_ORIGIN_FD:
172 		return epfs_set_origin_fd(file, arg);
173 	case IOC_SET_EPFS_RANGE:
174 		return epfs_set_range(file, arg);
175 	default:
176 		epfs_info("Exit epfs unsupported ioctl, ret: %ld", rc);
177 		return rc;
178 	}
179 }
180 
epfs_compat_ioctl(struct file * file,unsigned int cmd,unsigned long arg)181 static long epfs_compat_ioctl(struct file *file, unsigned int cmd,
182 				   unsigned long arg)
183 {
184 	return __epfs_ioctl(file, cmd, arg);
185 }
186 
epfs_unlocked_ioctl(struct file * file,unsigned int cmd,unsigned long arg)187 static long epfs_unlocked_ioctl(struct file *file, unsigned int cmd,
188 				     unsigned long arg)
189 {
190 	return __epfs_ioctl(file, cmd, arg);
191 }
192 
epfs_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)193 static ssize_t epfs_read(struct file *file, char __user *buf, size_t count,
194 			      loff_t *ppos)
195 {
196 	struct inode *inode = file_inode(file);
197 	struct epfs_inode_info *info = epfs_inode_to_private(inode);
198 	struct file *origin_file;
199 	struct epfs_range *range;
200 	ssize_t ret = 0;
201 	loff_t pos = *ppos;
202 	loff_t file_size;
203 	int current_range_index = 0;
204 
205 	mutex_lock(&info->lock);
206 	range = info->range;
207 	if (!range) {
208 		ret = -EINVAL;
209 		epfs_err("Invalid inode range!");
210 		goto out_read;
211 	}
212 
213 	origin_file = info->origin_file;
214 
215 	if (!origin_file) {
216 		ret = -ENOENT;
217 		epfs_err("origin file not exist!");
218 		goto out_read;
219 	}
220 
221 	// Reduce count when it will read over file size.
222 	file_size = i_size_read(file_inode(origin_file));
223 	if (IS_ENABLED(CONFIG_EPFS_DEBUG))
224 		if (count > (file_size - pos))
225 			epfs_debug(
226 				"count will be truncated to %llu, as file_size=%llu, pos=%llu",
227 				file_size - pos, file_size, pos);
228 	count = count <= (file_size - pos) ? count : (file_size - pos);
229 
230 	// Skip ranges before pos.
231 	while ((range->range[current_range_index].end <= pos) &&
232 	       (current_range_index < range->num))
233 		current_range_index++;
234 
235 	while (count > 0) {
236 		__u64 current_begin, current_end;
237 
238 		if (current_range_index >= range->num) {
239 			// read directly when epfs range gone;
240 			if (IS_ENABLED(CONFIG_EPFS_DEBUG))
241 				epfs_debug(
242 					"read from %llu with len %zu at the end.",
243 					pos, count);
244 			ret = vfs_read(origin_file, buf, count, &pos);
245 			break;
246 		}
247 		current_begin = range->range[current_range_index].begin;
248 		current_end = range->range[current_range_index].end;
249 		if (current_begin <= pos) {
250 			// Clear user memory
251 			unsigned long clear_len = current_end - pos;
252 
253 			clear_len = clear_len < count ? clear_len : count;
254 			if (IS_ENABLED(CONFIG_EPFS_DEBUG))
255 				epfs_debug(
256 					"clear user memory from %llu with len %lu",
257 					pos, clear_len);
258 			if (clear_user(buf, clear_len)) {
259 				ret = EFAULT;
260 				break;
261 			}
262 			buf += clear_len;
263 			pos += clear_len;
264 			count -= clear_len;
265 			current_range_index++;
266 		} else {
267 			// Read from pos to (next)current_begin
268 			unsigned long read_len = current_begin - pos;
269 
270 			read_len = read_len < count ? read_len : count;
271 			if (IS_ENABLED(CONFIG_EPFS_DEBUG))
272 				epfs_debug(
273 					"read from %llu with len %lu",
274 					pos, read_len);
275 			ret = vfs_read(origin_file, buf, read_len, &pos);
276 			if (ret < 0 || ret < read_len) {
277 				// Could not read enough bytes;
278 				break;
279 			}
280 			buf += ret;
281 			count -= ret;
282 		}
283 	}
284 
285 	if (ret >= 0) {
286 		ret = pos - *ppos;
287 		*ppos = pos;
288 	}
289 out_read:
290 	mutex_unlock(&info->lock);
291 	return ret;
292 }
293 
294 const struct file_operations epfs_file_fops = {
295 	.unlocked_ioctl = epfs_unlocked_ioctl,
296 	.compat_ioctl = epfs_compat_ioctl,
297 	.read = epfs_read,
298 	.llseek = generic_file_llseek,
299 };
300