xref: /kernel/linux/linux-5.10/fs/sharefs/file.c (revision 8c2ecf20)
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * fs/sharefs/file.c
4 *
5 * Copyright (c) 1998-2022 Erez Zadok
6 * Copyright (c) 2009	   Shrikar Archak
7 * Copyright (c) 2003-2022 Stony Brook University
8 * Copyright (c) 2003-2022 The Research Foundation of SUNY
9 * Copyright (c) 2023 Huawei Device Co., Ltd.
10 */
11
12#include "sharefs.h"
13
14static int sharefs_readdir(struct file *file, struct dir_context *ctx)
15{
16	int err;
17	struct file *lower_file = NULL;
18	struct dentry *dentry = file->f_path.dentry;
19
20	lower_file = sharefs_lower_file(file);
21	err = iterate_dir(lower_file, ctx);
22	file->f_pos = lower_file->f_pos;
23	if (err >= 0)		/* copy the atime */
24		fsstack_copy_attr_atime(d_inode(dentry),
25					file_inode(lower_file));
26	return err;
27}
28
29static int sharefs_open(struct inode *inode, struct file *file)
30{
31	int err = 0;
32	struct file *lower_file = NULL;
33	struct path lower_path;
34
35	/* don't open unhashed/deleted files */
36	if (d_unhashed(file->f_path.dentry)) {
37		err = -ENOENT;
38		goto out_err;
39	}
40
41	file->private_data =
42		kzalloc(sizeof(struct sharefs_file_info), GFP_KERNEL);
43	if (!SHAREFS_F(file)) {
44		err = -ENOMEM;
45		goto out_err;
46	}
47
48	/* open lower object and link sharefs's file struct to lower's */
49	sharefs_get_lower_path(file->f_path.dentry, &lower_path);
50	lower_file = dentry_open(&lower_path, file->f_flags, current_cred());
51	path_put(&lower_path);
52	if (IS_ERR(lower_file)) {
53		err = PTR_ERR(lower_file);
54		lower_file = sharefs_lower_file(file);
55		if (lower_file) {
56			sharefs_set_lower_file(file, NULL);
57			fput(lower_file); /* fput calls dput for lower_dentry */
58		}
59	} else {
60		sharefs_set_lower_file(file, lower_file);
61	}
62
63	if (err) {
64		kfree(SHAREFS_F(file));
65	} else {
66		kuid_t uid = inode->i_uid;
67		kgid_t gid = inode->i_gid;
68		mode_t mode = inode->i_mode;
69		fsstack_copy_attr_all(inode, sharefs_lower_inode(inode));
70		inode->i_uid = uid;
71		inode->i_gid = gid;
72		inode->i_mode = mode;
73	}
74out_err:
75	return err;
76}
77
78static int sharefs_flush(struct file *file, fl_owner_t id)
79{
80	int err = 0;
81	struct file *lower_file = NULL;
82
83	lower_file = sharefs_lower_file(file);
84	if (lower_file && lower_file->f_op && lower_file->f_op->flush) {
85		filemap_write_and_wait(file->f_mapping);
86		err = lower_file->f_op->flush(lower_file, id);
87	}
88
89	return err;
90}
91
92/* release all lower object references & free the file info structure */
93static int sharefs_file_release(struct inode *inode, struct file *file)
94{
95	struct file *lower_file;
96
97	lower_file = sharefs_lower_file(file);
98	if (lower_file) {
99		sharefs_set_lower_file(file, NULL);
100		fput(lower_file);
101	}
102
103	kfree(SHAREFS_F(file));
104	return 0;
105}
106
107static int sharefs_fsync(struct file *file, loff_t start, loff_t end,
108			int datasync)
109{
110	int err;
111	struct file *lower_file;
112	struct path lower_path;
113	struct dentry *dentry = file->f_path.dentry;
114
115	err = __generic_file_fsync(file, start, end, datasync);
116	if (err)
117		goto out;
118	lower_file = sharefs_lower_file(file);
119	sharefs_get_lower_path(dentry, &lower_path);
120	err = vfs_fsync_range(lower_file, start, end, datasync);
121	sharefs_put_lower_path(dentry, &lower_path);
122out:
123	return err;
124}
125
126static int sharefs_fasync(int fd, struct file *file, int flag)
127{
128	int err = 0;
129	struct file *lower_file = NULL;
130
131	lower_file = sharefs_lower_file(file);
132	if (lower_file->f_op && lower_file->f_op->fasync)
133		err = lower_file->f_op->fasync(fd, lower_file, flag);
134
135	return err;
136}
137
138/*
139 * Sharefs cannot use generic_file_llseek as ->llseek, because it would
140 * only set the offset of the upper file.  So we have to implement our
141 * own method to set both the upper and lower file offsets
142 * consistently.
143 */
144static loff_t sharefs_file_llseek(struct file *file, loff_t offset, int whence)
145{
146	loff_t err;
147	struct file *lower_file;
148
149	lower_file = sharefs_lower_file(file);
150	lower_file->f_pos = file->f_pos;
151	err = generic_file_llseek(lower_file, offset, whence);
152	file->f_pos = lower_file->f_pos;
153
154	return err;
155}
156
157/*
158 * Sharefs read_iter, redirect modified iocb to lower read_iter
159 */
160ssize_t sharefs_read_iter(struct kiocb *iocb, struct iov_iter *iter)
161{
162	int err;
163	struct file *file = iocb->ki_filp;
164	struct file *lower_file;
165
166	lower_file = sharefs_lower_file(file);
167	if (!lower_file->f_op->read_iter) {
168		err = -EINVAL;
169		goto out;
170	}
171
172	/* prevent lower_file from being released */
173	get_file(lower_file);
174	iocb->ki_filp = lower_file;
175	err = lower_file->f_op->read_iter(iocb, iter);
176	iocb->ki_filp = file;
177	fput(lower_file);
178
179	/* update upper inode atime as needed */
180	if (err >= 0 || err == -EIOCBQUEUED)
181		fsstack_copy_attr_atime(d_inode(file->f_path.dentry),
182					file_inode(lower_file));
183out:
184	return err;
185}
186
187/*
188 * Sharefs write_iter, redirect modified iocb to lower write_iter
189 */
190ssize_t sharefs_write_iter(struct kiocb *iocb, struct iov_iter *iter)
191{
192	int err;
193	struct file *file = iocb->ki_filp;
194	struct file *lower_file;
195
196	lower_file = sharefs_lower_file(file);
197	if (!lower_file->f_op->write_iter) {
198		err = -EINVAL;
199		goto out;
200	}
201
202	get_file(lower_file); /* prevent lower_file from being released */
203	iocb->ki_filp = lower_file;
204	err = lower_file->f_op->write_iter(iocb, iter);
205	iocb->ki_filp = file;
206	fput(lower_file);
207	/* update upper inode times/sizes as needed */
208	if (err >= 0 || err == -EIOCBQUEUED) {
209		fsstack_copy_inode_size(d_inode(file->f_path.dentry),
210					file_inode(lower_file));
211		fsstack_copy_attr_times(d_inode(file->f_path.dentry),
212					file_inode(lower_file));
213	}
214out:
215	return err;
216}
217
218int sharefs_file_mmap(struct file *file, struct vm_area_struct *vma)
219{
220	int err = 0;
221	struct file *lower_file;
222
223	lower_file = sharefs_lower_file(file);
224	if (!lower_file)
225		return -EINVAL;
226
227	if (!lower_file->f_op->mmap)
228		return -ENODEV;
229
230	if (WARN_ON(file != vma->vm_file))
231		return -EIO;
232
233	vma->vm_file = get_file(lower_file);
234	err = call_mmap(vma->vm_file, vma);
235	if (err)
236		fput(lower_file);
237	else
238		fput(file);
239
240	file_accessed(file);
241
242	return err;
243}
244
245const struct file_operations sharefs_main_fops = {
246	.llseek		= sharefs_file_llseek,
247	.open		= sharefs_open,
248	.flush		= sharefs_flush,
249	.release	= sharefs_file_release,
250	.fsync		= sharefs_fsync,
251	.fasync		= sharefs_fasync,
252	.read_iter	= sharefs_read_iter,
253	.write_iter	= sharefs_write_iter,
254	.mmap		= sharefs_file_mmap,
255	.splice_read = generic_file_splice_read,
256	.splice_write = iter_file_splice_write,
257};
258
259/* trimmed directory options */
260const struct file_operations sharefs_dir_fops = {
261	.llseek		= sharefs_file_llseek,
262	.read		= generic_read_dir,
263	.iterate	= sharefs_readdir,
264	.open		= sharefs_open,
265	.release	= sharefs_file_release,
266	.flush		= sharefs_flush,
267	.fsync		= sharefs_fsync,
268	.fasync		= sharefs_fasync,
269};
270