• 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 	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