xref: /linux/fs/coda/file.c (revision 69bfec7548f4c1595bac0e3ddfc0458a5af31f4c)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * File operations for Coda.
4  * Original version: (C) 1996 Peter Braam
5  * Rewritten for Linux 2.1: (C) 1997 Carnegie Mellon University
6  *
7  * Carnegie Mellon encourages users of this code to contribute improvements
8  * to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
9  */
10 
11 #include <linux/refcount.h>
12 #include <linux/types.h>
13 #include <linux/kernel.h>
14 #include <linux/time.h>
15 #include <linux/file.h>
16 #include <linux/fs.h>
17 #include <linux/pagemap.h>
18 #include <linux/stat.h>
19 #include <linux/cred.h>
20 #include <linux/errno.h>
21 #include <linux/spinlock.h>
22 #include <linux/string.h>
23 #include <linux/slab.h>
24 #include <linux/uaccess.h>
25 #include <linux/uio.h>
26 
27 #include <linux/coda.h>
28 #include "coda_psdev.h"
29 #include "coda_linux.h"
30 #include "coda_int.h"
31 
32 struct coda_vm_ops {
33 	refcount_t refcnt;
34 	struct file *coda_file;
35 	const struct vm_operations_struct *host_vm_ops;
36 	struct vm_operations_struct vm_ops;
37 };
38 
39 static ssize_t
40 coda_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
41 {
42 	struct file *coda_file = iocb->ki_filp;
43 	struct inode *coda_inode = file_inode(coda_file);
44 	struct coda_file_info *cfi = coda_ftoc(coda_file);
45 	loff_t ki_pos = iocb->ki_pos;
46 	size_t count = iov_iter_count(to);
47 	ssize_t ret;
48 
49 	ret = venus_access_intent(coda_inode->i_sb, coda_i2f(coda_inode),
50 				  &cfi->cfi_access_intent,
51 				  count, ki_pos, CODA_ACCESS_TYPE_READ);
52 	if (ret)
53 		goto finish_read;
54 
55 	ret = vfs_iter_read(cfi->cfi_container, to, &iocb->ki_pos, 0);
56 
57 finish_read:
58 	venus_access_intent(coda_inode->i_sb, coda_i2f(coda_inode),
59 			    &cfi->cfi_access_intent,
60 			    count, ki_pos, CODA_ACCESS_TYPE_READ_FINISH);
61 	return ret;
62 }
63 
64 static ssize_t
65 coda_file_write_iter(struct kiocb *iocb, struct iov_iter *to)
66 {
67 	struct file *coda_file = iocb->ki_filp;
68 	struct inode *coda_inode = file_inode(coda_file);
69 	struct coda_file_info *cfi = coda_ftoc(coda_file);
70 	struct file *host_file = cfi->cfi_container;
71 	loff_t ki_pos = iocb->ki_pos;
72 	size_t count = iov_iter_count(to);
73 	ssize_t ret;
74 
75 	ret = venus_access_intent(coda_inode->i_sb, coda_i2f(coda_inode),
76 				  &cfi->cfi_access_intent,
77 				  count, ki_pos, CODA_ACCESS_TYPE_WRITE);
78 	if (ret)
79 		goto finish_write;
80 
81 	file_start_write(host_file);
82 	inode_lock(coda_inode);
83 	ret = vfs_iter_write(cfi->cfi_container, to, &iocb->ki_pos, 0);
84 	coda_inode->i_size = file_inode(host_file)->i_size;
85 	coda_inode->i_blocks = (coda_inode->i_size + 511) >> 9;
86 	coda_inode->i_mtime = coda_inode->i_ctime = current_time(coda_inode);
87 	inode_unlock(coda_inode);
88 	file_end_write(host_file);
89 
90 finish_write:
91 	venus_access_intent(coda_inode->i_sb, coda_i2f(coda_inode),
92 			    &cfi->cfi_access_intent,
93 			    count, ki_pos, CODA_ACCESS_TYPE_WRITE_FINISH);
94 	return ret;
95 }
96 
97 static void
98 coda_vm_open(struct vm_area_struct *vma)
99 {
100 	struct coda_vm_ops *cvm_ops =
101 		container_of(vma->vm_ops, struct coda_vm_ops, vm_ops);
102 
103 	refcount_inc(&cvm_ops->refcnt);
104 
105 	if (cvm_ops->host_vm_ops && cvm_ops->host_vm_ops->open)
106 		cvm_ops->host_vm_ops->open(vma);
107 }
108 
109 static void
110 coda_vm_close(struct vm_area_struct *vma)
111 {
112 	struct coda_vm_ops *cvm_ops =
113 		container_of(vma->vm_ops, struct coda_vm_ops, vm_ops);
114 
115 	if (cvm_ops->host_vm_ops && cvm_ops->host_vm_ops->close)
116 		cvm_ops->host_vm_ops->close(vma);
117 
118 	if (refcount_dec_and_test(&cvm_ops->refcnt)) {
119 		vma->vm_ops = cvm_ops->host_vm_ops;
120 		fput(cvm_ops->coda_file);
121 		kfree(cvm_ops);
122 	}
123 }
124 
125 static int
126 coda_file_mmap(struct file *coda_file, struct vm_area_struct *vma)
127 {
128 	struct inode *coda_inode = file_inode(coda_file);
129 	struct coda_file_info *cfi = coda_ftoc(coda_file);
130 	struct file *host_file = cfi->cfi_container;
131 	struct inode *host_inode = file_inode(host_file);
132 	struct coda_inode_info *cii;
133 	struct coda_vm_ops *cvm_ops;
134 	loff_t ppos;
135 	size_t count;
136 	int ret;
137 
138 	if (!host_file->f_op->mmap)
139 		return -ENODEV;
140 
141 	if (WARN_ON(coda_file != vma->vm_file))
142 		return -EIO;
143 
144 	count = vma->vm_end - vma->vm_start;
145 	ppos = vma->vm_pgoff * PAGE_SIZE;
146 
147 	ret = venus_access_intent(coda_inode->i_sb, coda_i2f(coda_inode),
148 				  &cfi->cfi_access_intent,
149 				  count, ppos, CODA_ACCESS_TYPE_MMAP);
150 	if (ret)
151 		return ret;
152 
153 	cvm_ops = kmalloc(sizeof(struct coda_vm_ops), GFP_KERNEL);
154 	if (!cvm_ops)
155 		return -ENOMEM;
156 
157 	cii = ITOC(coda_inode);
158 	spin_lock(&cii->c_lock);
159 	coda_file->f_mapping = host_file->f_mapping;
160 	if (coda_inode->i_mapping == &coda_inode->i_data)
161 		coda_inode->i_mapping = host_inode->i_mapping;
162 
163 	/* only allow additional mmaps as long as userspace isn't changing
164 	 * the container file on us! */
165 	else if (coda_inode->i_mapping != host_inode->i_mapping) {
166 		spin_unlock(&cii->c_lock);
167 		kfree(cvm_ops);
168 		return -EBUSY;
169 	}
170 
171 	/* keep track of how often the coda_inode/host_file has been mmapped */
172 	cii->c_mapcount++;
173 	cfi->cfi_mapcount++;
174 	spin_unlock(&cii->c_lock);
175 
176 	vma->vm_file = get_file(host_file);
177 	ret = call_mmap(vma->vm_file, vma);
178 
179 	if (ret) {
180 		/* if call_mmap fails, our caller will put host_file so we
181 		 * should drop the reference to the coda_file that we got.
182 		 */
183 		fput(coda_file);
184 		kfree(cvm_ops);
185 	} else {
186 		/* here we add redirects for the open/close vm_operations */
187 		cvm_ops->host_vm_ops = vma->vm_ops;
188 		if (vma->vm_ops)
189 			cvm_ops->vm_ops = *vma->vm_ops;
190 
191 		cvm_ops->vm_ops.open = coda_vm_open;
192 		cvm_ops->vm_ops.close = coda_vm_close;
193 		cvm_ops->coda_file = coda_file;
194 		refcount_set(&cvm_ops->refcnt, 1);
195 
196 		vma->vm_ops = &cvm_ops->vm_ops;
197 	}
198 	return ret;
199 }
200 
201 int coda_open(struct inode *coda_inode, struct file *coda_file)
202 {
203 	struct file *host_file = NULL;
204 	int error;
205 	unsigned short flags = coda_file->f_flags & (~O_EXCL);
206 	unsigned short coda_flags = coda_flags_to_cflags(flags);
207 	struct coda_file_info *cfi;
208 
209 	cfi = kmalloc(sizeof(struct coda_file_info), GFP_KERNEL);
210 	if (!cfi)
211 		return -ENOMEM;
212 
213 	error = venus_open(coda_inode->i_sb, coda_i2f(coda_inode), coda_flags,
214 			   &host_file);
215 	if (!host_file)
216 		error = -EIO;
217 
218 	if (error) {
219 		kfree(cfi);
220 		return error;
221 	}
222 
223 	host_file->f_flags |= coda_file->f_flags & (O_APPEND | O_SYNC);
224 
225 	cfi->cfi_magic = CODA_MAGIC;
226 	cfi->cfi_mapcount = 0;
227 	cfi->cfi_container = host_file;
228 	/* assume access intents are supported unless we hear otherwise */
229 	cfi->cfi_access_intent = true;
230 
231 	BUG_ON(coda_file->private_data != NULL);
232 	coda_file->private_data = cfi;
233 	return 0;
234 }
235 
236 int coda_release(struct inode *coda_inode, struct file *coda_file)
237 {
238 	unsigned short flags = (coda_file->f_flags) & (~O_EXCL);
239 	unsigned short coda_flags = coda_flags_to_cflags(flags);
240 	struct coda_file_info *cfi;
241 	struct coda_inode_info *cii;
242 	struct inode *host_inode;
243 
244 	cfi = coda_ftoc(coda_file);
245 
246 	venus_close(coda_inode->i_sb, coda_i2f(coda_inode),
247 			  coda_flags, coda_file->f_cred->fsuid);
248 
249 	host_inode = file_inode(cfi->cfi_container);
250 	cii = ITOC(coda_inode);
251 
252 	/* did we mmap this file? */
253 	spin_lock(&cii->c_lock);
254 	if (coda_inode->i_mapping == &host_inode->i_data) {
255 		cii->c_mapcount -= cfi->cfi_mapcount;
256 		if (!cii->c_mapcount)
257 			coda_inode->i_mapping = &coda_inode->i_data;
258 	}
259 	spin_unlock(&cii->c_lock);
260 
261 	fput(cfi->cfi_container);
262 	kfree(coda_file->private_data);
263 	coda_file->private_data = NULL;
264 
265 	/* VFS fput ignores the return value from file_operations->release, so
266 	 * there is no use returning an error here */
267 	return 0;
268 }
269 
270 int coda_fsync(struct file *coda_file, loff_t start, loff_t end, int datasync)
271 {
272 	struct file *host_file;
273 	struct inode *coda_inode = file_inode(coda_file);
274 	struct coda_file_info *cfi;
275 	int err;
276 
277 	if (!(S_ISREG(coda_inode->i_mode) || S_ISDIR(coda_inode->i_mode) ||
278 	      S_ISLNK(coda_inode->i_mode)))
279 		return -EINVAL;
280 
281 	err = filemap_write_and_wait_range(coda_inode->i_mapping, start, end);
282 	if (err)
283 		return err;
284 	inode_lock(coda_inode);
285 
286 	cfi = coda_ftoc(coda_file);
287 	host_file = cfi->cfi_container;
288 
289 	err = vfs_fsync(host_file, datasync);
290 	if (!err && !datasync)
291 		err = venus_fsync(coda_inode->i_sb, coda_i2f(coda_inode));
292 	inode_unlock(coda_inode);
293 
294 	return err;
295 }
296 
297 const struct file_operations coda_file_operations = {
298 	.llseek		= generic_file_llseek,
299 	.read_iter	= coda_file_read_iter,
300 	.write_iter	= coda_file_write_iter,
301 	.mmap		= coda_file_mmap,
302 	.open		= coda_open,
303 	.release	= coda_release,
304 	.fsync		= coda_fsync,
305 	.splice_read	= generic_file_splice_read,
306 };
307