File Index
Files and directories
Sign in
FILE

vfs_file.c

File preview with syntax highlighting.

linux-6.19.14/fs/9p/vfs_file.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * This file contians vfs file ops for 9P2000.
  4 *
  5 *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
  6 *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
  7 */
  8
  9#include <linux/module.h>
 10#include <linux/errno.h>
 11#include <linux/fs.h>
 12#include <linux/filelock.h>
 13#include <linux/sched.h>
 14#include <linux/file.h>
 15#include <linux/stat.h>
 16#include <linux/string.h>
 17#include <linux/list.h>
 18#include <linux/pagemap.h>
 19#include <linux/utsname.h>
 20#include <linux/uaccess.h>
 21#include <linux/uio.h>
 22#include <linux/slab.h>
 23#include <net/9p/9p.h>
 24#include <net/9p/client.h>
 25
 26#include "v9fs.h"
 27#include "v9fs_vfs.h"
 28#include "fid.h"
 29#include "cache.h"
 30
 31static const struct vm_operations_struct v9fs_mmap_file_vm_ops;
 32
 33/**
 34 * v9fs_file_open - open a file (or directory)
 35 * @inode: inode to be opened
 36 * @file: file being opened
 37 *
 38 */
 39
 40int v9fs_file_open(struct inode *inode, struct file *file)
 41{
 42	int err;
 43	struct v9fs_session_info *v9ses;
 44	struct p9_fid *fid;
 45	int omode;
 46	int o_append;
 47
 48	p9_debug(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, file);
 49	v9ses = v9fs_inode2v9ses(inode);
 50	if (v9fs_proto_dotl(v9ses)) {
 51		omode = v9fs_open_to_dotl_flags(file->f_flags);
 52		o_append = P9_DOTL_APPEND;
 53	} else {
 54		omode = v9fs_uflags2omode(file->f_flags,
 55					v9fs_proto_dotu(v9ses));
 56		o_append = P9_OAPPEND;
 57	}
 58	fid = file->private_data;
 59	if (!fid) {
 60		fid = v9fs_fid_clone(file_dentry(file));
 61		if (IS_ERR(fid))
 62			return PTR_ERR(fid);
 63
 64		if ((v9ses->cache & CACHE_WRITEBACK) && (omode & P9_OWRITE)) {
 65			int writeback_omode = (omode & ~(P9_OWRITE | o_append)) | P9_ORDWR;
 66
 67			p9_debug(P9_DEBUG_CACHE, "write-only file with writeback enabled, try opening O_RDWR\n");
 68
 69			err = p9_client_open(fid, writeback_omode);
 70			if (err < 0) {
 71				p9_debug(P9_DEBUG_CACHE, "could not open O_RDWR, disabling caches\n");
 72				err = p9_client_open(fid, omode);
 73				fid->mode |= P9L_DIRECT;
 74			}
 75		} else {
 76			err = p9_client_open(fid, omode);
 77		}
 78		if (err < 0) {
 79			p9_fid_put(fid);
 80			return err;
 81		}
 82		if ((file->f_flags & O_APPEND) &&
 83			(!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses)))
 84			generic_file_llseek(file, 0, SEEK_END);
 85
 86		file->private_data = fid;
 87	}
 88
 89#ifdef CONFIG_9P_FSCACHE
 90	if (v9ses->cache & CACHE_FSCACHE)
 91		fscache_use_cookie(v9fs_inode_cookie(V9FS_I(inode)),
 92				   file->f_mode & FMODE_WRITE);
 93#endif
 94	v9fs_fid_add_modes(fid, v9ses->flags, v9ses->cache, file->f_flags);
 95	v9fs_open_fid_add(inode, &fid);
 96	return 0;
 97}
 98
 99/**
100 * v9fs_file_lock - lock a file (or directory)
101 * @filp: file to be locked
102 * @cmd: lock command
103 * @fl: file lock structure
104 *
105 * Bugs: this looks like a local only lock, we should extend into 9P
106 *       by using open exclusive
107 */
108
109static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl)
110{
111	struct inode *inode = file_inode(filp);
112
113	p9_debug(P9_DEBUG_VFS, "filp: %p lock: %p\n", filp, fl);
114
115	if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->c.flc_type != F_UNLCK) {
116		filemap_write_and_wait(inode->i_mapping);
117		invalidate_mapping_pages(&inode->i_data, 0, -1);
118	}
119
120	return 0;
121}
122
123static int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl)
124{
125	struct p9_flock flock;
126	struct p9_fid *fid;
127	uint8_t status = P9_LOCK_ERROR;
128	int res = 0;
129	struct v9fs_session_info *v9ses;
130
131	fid = filp->private_data;
132	BUG_ON(fid == NULL);
133
134	BUG_ON((fl->c.flc_flags & FL_POSIX) != FL_POSIX);
135
136	res = locks_lock_file_wait(filp, fl);
137	if (res < 0)
138		goto out;
139
140	/* convert posix lock to p9 tlock args */
141	memset(&flock, 0, sizeof(flock));
142	/* map the lock type */
143	switch (fl->c.flc_type) {
144	case F_RDLCK:
145		flock.type = P9_LOCK_TYPE_RDLCK;
146		break;
147	case F_WRLCK:
148		flock.type = P9_LOCK_TYPE_WRLCK;
149		break;
150	case F_UNLCK:
151		flock.type = P9_LOCK_TYPE_UNLCK;
152		break;
153	}
154	flock.start = fl->fl_start;
155	if (fl->fl_end == OFFSET_MAX)
156		flock.length = 0;
157	else
158		flock.length = fl->fl_end - fl->fl_start + 1;
159	flock.proc_id = fl->c.flc_pid;
160	flock.client_id = fid->clnt->name;
161	if (IS_SETLKW(cmd))
162		flock.flags = P9_LOCK_FLAGS_BLOCK;
163
164	v9ses = v9fs_inode2v9ses(file_inode(filp));
165
166	/*
167	 * if its a blocked request and we get P9_LOCK_BLOCKED as the status
168	 * for lock request, keep on trying
169	 */
170	for (;;) {
171		res = p9_client_lock_dotl(fid, &flock, &status);
172		if (res < 0)
173			goto out_unlock;
174
175		if (status != P9_LOCK_BLOCKED)
176			break;
177		if (status == P9_LOCK_BLOCKED && !IS_SETLKW(cmd))
178			break;
179		if (schedule_timeout_interruptible(v9ses->session_lock_timeout)
180				!= 0)
181			break;
182		/*
183		 * p9_client_lock_dotl overwrites flock.client_id with the
184		 * server message, free and reuse the client name
185		 */
186		if (flock.client_id != fid->clnt->name) {
187			kfree(flock.client_id);
188			flock.client_id = fid->clnt->name;
189		}
190	}
191
192	/* map 9p status to VFS status */
193	switch (status) {
194	case P9_LOCK_SUCCESS:
195		res = 0;
196		break;
197	case P9_LOCK_BLOCKED:
198		res = -EAGAIN;
199		break;
200	default:
201		WARN_ONCE(1, "unknown lock status code: %d\n", status);
202		fallthrough;
203	case P9_LOCK_ERROR:
204	case P9_LOCK_GRACE:
205		res = -ENOLCK;
206		break;
207	}
208
209out_unlock:
210	/*
211	 * incase server returned error for lock request, revert
212	 * it locally
213	 */
214	if (res < 0 && fl->c.flc_type != F_UNLCK) {
215		unsigned char type = fl->c.flc_type;
216
217		fl->c.flc_type = F_UNLCK;
218		/* Even if this fails we want to return the remote error */
219		locks_lock_file_wait(filp, fl);
220		fl->c.flc_type = type;
221	}
222	if (flock.client_id != fid->clnt->name)
223		kfree(flock.client_id);
224out:
225	return res;
226}
227
228static int v9fs_file_getlock(struct file *filp, struct file_lock *fl)
229{
230	struct p9_getlock glock;
231	struct p9_fid *fid;
232	int res = 0;
233
234	fid = filp->private_data;
235	BUG_ON(fid == NULL);
236
237	posix_test_lock(filp, fl);
238	/*
239	 * if we have a conflicting lock locally, no need to validate
240	 * with server
241	 */
242	if (fl->c.flc_type != F_UNLCK)
243		return res;
244
245	/* convert posix lock to p9 tgetlock args */
246	memset(&glock, 0, sizeof(glock));
247	glock.type  = P9_LOCK_TYPE_UNLCK;
248	glock.start = fl->fl_start;
249	if (fl->fl_end == OFFSET_MAX)
250		glock.length = 0;
251	else
252		glock.length = fl->fl_end - fl->fl_start + 1;
253	glock.proc_id = fl->c.flc_pid;
254	glock.client_id = fid->clnt->name;
255
256	res = p9_client_getlock_dotl(fid, &glock);
257	if (res < 0)
258		goto out;
259	/* map 9p lock type to os lock type */
260	switch (glock.type) {
261	case P9_LOCK_TYPE_RDLCK:
262		fl->c.flc_type = F_RDLCK;
263		break;
264	case P9_LOCK_TYPE_WRLCK:
265		fl->c.flc_type = F_WRLCK;
266		break;
267	case P9_LOCK_TYPE_UNLCK:
268		fl->c.flc_type = F_UNLCK;
269		break;
270	}
271	if (glock.type != P9_LOCK_TYPE_UNLCK) {
272		fl->fl_start = glock.start;
273		if (glock.length == 0)
274			fl->fl_end = OFFSET_MAX;
275		else
276			fl->fl_end = glock.start + glock.length - 1;
277		fl->c.flc_pid = -glock.proc_id;
278	}
279out:
280	if (glock.client_id != fid->clnt->name)
281		kfree(glock.client_id);
282	return res;
283}
284
285/**
286 * v9fs_file_lock_dotl - lock a file (or directory)
287 * @filp: file to be locked
288 * @cmd: lock command
289 * @fl: file lock structure
290 *
291 */
292
293static int v9fs_file_lock_dotl(struct file *filp, int cmd, struct file_lock *fl)
294{
295	struct inode *inode = file_inode(filp);
296	int ret = -ENOLCK;
297
298	p9_debug(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %pD\n",
299		 filp, cmd, fl, filp);
300
301	if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->c.flc_type != F_UNLCK) {
302		filemap_write_and_wait(inode->i_mapping);
303		invalidate_mapping_pages(&inode->i_data, 0, -1);
304	}
305
306	if (IS_SETLK(cmd) || IS_SETLKW(cmd))
307		ret = v9fs_file_do_lock(filp, cmd, fl);
308	else if (IS_GETLK(cmd))
309		ret = v9fs_file_getlock(filp, fl);
310	else
311		ret = -EINVAL;
312	return ret;
313}
314
315/**
316 * v9fs_file_flock_dotl - lock a file
317 * @filp: file to be locked
318 * @cmd: lock command
319 * @fl: file lock structure
320 *
321 */
322
323static int v9fs_file_flock_dotl(struct file *filp, int cmd,
324	struct file_lock *fl)
325{
326	struct inode *inode = file_inode(filp);
327	int ret = -ENOLCK;
328
329	p9_debug(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %pD\n",
330		 filp, cmd, fl, filp);
331
332	if (!(fl->c.flc_flags & FL_FLOCK))
333		goto out_err;
334
335	if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->c.flc_type != F_UNLCK) {
336		filemap_write_and_wait(inode->i_mapping);
337		invalidate_mapping_pages(&inode->i_data, 0, -1);
338	}
339	/* Convert flock to posix lock */
340	fl->c.flc_flags |= FL_POSIX;
341	fl->c.flc_flags ^= FL_FLOCK;
342
343	if (IS_SETLK(cmd) | IS_SETLKW(cmd))
344		ret = v9fs_file_do_lock(filp, cmd, fl);
345	else
346		ret = -EINVAL;
347out_err:
348	return ret;
349}
350
351/**
352 * v9fs_file_read_iter - read from a file
353 * @iocb: The operation parameters
354 * @to: The buffer to read into
355 *
356 */
357static ssize_t
358v9fs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
359{
360	struct p9_fid *fid = iocb->ki_filp->private_data;
361
362	p9_debug(P9_DEBUG_VFS, "fid %d count %zu offset %lld\n",
363		 fid->fid, iov_iter_count(to), iocb->ki_pos);
364
365	if (fid->mode & P9L_DIRECT)
366		return netfs_unbuffered_read_iter(iocb, to);
367
368	p9_debug(P9_DEBUG_VFS, "(cached)\n");
369	return netfs_file_read_iter(iocb, to);
370}
371
372/*
373 * v9fs_file_splice_read - splice-read from a file
374 * @in: The 9p file to read from
375 * @ppos: Where to find/update the file position
376 * @pipe: The pipe to splice into
377 * @len: The maximum amount of data to splice
378 * @flags: SPLICE_F_* flags
379 */
380static ssize_t v9fs_file_splice_read(struct file *in, loff_t *ppos,
381				     struct pipe_inode_info *pipe,
382				     size_t len, unsigned int flags)
383{
384	struct p9_fid *fid = in->private_data;
385
386	p9_debug(P9_DEBUG_VFS, "fid %d count %zu offset %lld\n",
387		 fid->fid, len, *ppos);
388
389	if (fid->mode & P9L_DIRECT)
390		return copy_splice_read(in, ppos, pipe, len, flags);
391	return filemap_splice_read(in, ppos, pipe, len, flags);
392}
393
394/**
395 * v9fs_file_write_iter - write to a file
396 * @iocb: The operation parameters
397 * @from: The data to write
398 *
399 */
400static ssize_t
401v9fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
402{
403	struct file *file = iocb->ki_filp;
404	struct p9_fid *fid = file->private_data;
405
406	p9_debug(P9_DEBUG_VFS, "fid %d\n", fid->fid);
407
408	if (fid->mode & (P9L_DIRECT | P9L_NOWRITECACHE))
409		return netfs_unbuffered_write_iter(iocb, from);
410
411	p9_debug(P9_DEBUG_CACHE, "(cached)\n");
412	return netfs_file_write_iter(iocb, from);
413}
414
415static int v9fs_file_fsync(struct file *filp, loff_t start, loff_t end,
416			   int datasync)
417{
418	struct p9_fid *fid;
419	struct inode *inode = filp->f_mapping->host;
420	struct p9_wstat wstat;
421	int retval;
422
423	retval = file_write_and_wait_range(filp, start, end);
424	if (retval)
425		return retval;
426
427	inode_lock(inode);
428	p9_debug(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync);
429
430	fid = filp->private_data;
431	v9fs_blank_wstat(&wstat);
432
433	retval = p9_client_wstat(fid, &wstat);
434	inode_unlock(inode);
435
436	return retval;
437}
438
439int v9fs_file_fsync_dotl(struct file *filp, loff_t start, loff_t end,
440			 int datasync)
441{
442	struct p9_fid *fid;
443	struct inode *inode = filp->f_mapping->host;
444	int retval;
445
446	retval = file_write_and_wait_range(filp, start, end);
447	if (retval)
448		return retval;
449
450	inode_lock(inode);
451	p9_debug(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync);
452
453	fid = filp->private_data;
454
455	retval = p9_client_fsync(fid, datasync);
456	inode_unlock(inode);
457
458	return retval;
459}
460
461static int
462v9fs_file_mmap_prepare(struct vm_area_desc *desc)
463{
464	int retval;
465	struct file *filp = desc->file;
466	struct inode *inode = file_inode(filp);
467	struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode);
468
469	p9_debug(P9_DEBUG_MMAP, "filp :%p\n", filp);
470
471	if (!(v9ses->cache & CACHE_WRITEBACK)) {
472		p9_debug(P9_DEBUG_CACHE, "(read-only mmap mode)");
473		return generic_file_readonly_mmap_prepare(desc);
474	}
475
476	retval = generic_file_mmap_prepare(desc);
477	if (!retval)
478		desc->vm_ops = &v9fs_mmap_file_vm_ops;
479
480	return retval;
481}
482
483static vm_fault_t
484v9fs_vm_page_mkwrite(struct vm_fault *vmf)
485{
486	return netfs_page_mkwrite(vmf, NULL);
487}
488
489static void v9fs_mmap_vm_close(struct vm_area_struct *vma)
490{
491	if (!(vma->vm_flags & VM_SHARED))
492		return;
493
494	p9_debug(P9_DEBUG_VFS, "9p VMA close, %p, flushing", vma);
495
496	filemap_fdatawrite_range(file_inode(vma->vm_file)->i_mapping,
497			(loff_t)vma->vm_pgoff * PAGE_SIZE,
498			(loff_t)vma->vm_pgoff * PAGE_SIZE +
499				(vma->vm_end - vma->vm_start - 1));
500}
501
502static const struct vm_operations_struct v9fs_mmap_file_vm_ops = {
503	.close = v9fs_mmap_vm_close,
504	.fault = filemap_fault,
505	.map_pages = filemap_map_pages,
506	.page_mkwrite = v9fs_vm_page_mkwrite,
507};
508
509const struct file_operations v9fs_file_operations = {
510	.llseek = generic_file_llseek,
511	.read_iter = v9fs_file_read_iter,
512	.write_iter = v9fs_file_write_iter,
513	.open = v9fs_file_open,
514	.release = v9fs_dir_release,
515	.lock = v9fs_file_lock,
516	.mmap_prepare = generic_file_readonly_mmap_prepare,
517	.splice_read = v9fs_file_splice_read,
518	.splice_write = iter_file_splice_write,
519	.fsync = v9fs_file_fsync,
520	.setlease = simple_nosetlease,
521};
522
523const struct file_operations v9fs_file_operations_dotl = {
524	.llseek = generic_file_llseek,
525	.read_iter = v9fs_file_read_iter,
526	.write_iter = v9fs_file_write_iter,
527	.open = v9fs_file_open,
528	.release = v9fs_dir_release,
529	.lock = v9fs_file_lock_dotl,
530	.flock = v9fs_file_flock_dotl,
531	.mmap_prepare = v9fs_file_mmap_prepare,
532	.splice_read = v9fs_file_splice_read,
533	.splice_write = iter_file_splice_write,
534	.fsync = v9fs_file_fsync_dotl,
535	.setlease = simple_nosetlease,
536};
~/ Read-only file preview.