162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file contians vfs file ops for 9P2000. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> 662306a36Sopenharmony_ci * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/fs.h> 1262306a36Sopenharmony_ci#include <linux/filelock.h> 1362306a36Sopenharmony_ci#include <linux/sched.h> 1462306a36Sopenharmony_ci#include <linux/file.h> 1562306a36Sopenharmony_ci#include <linux/stat.h> 1662306a36Sopenharmony_ci#include <linux/string.h> 1762306a36Sopenharmony_ci#include <linux/list.h> 1862306a36Sopenharmony_ci#include <linux/pagemap.h> 1962306a36Sopenharmony_ci#include <linux/utsname.h> 2062306a36Sopenharmony_ci#include <linux/uaccess.h> 2162306a36Sopenharmony_ci#include <linux/uio.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <net/9p/9p.h> 2462306a36Sopenharmony_ci#include <net/9p/client.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "v9fs.h" 2762306a36Sopenharmony_ci#include "v9fs_vfs.h" 2862306a36Sopenharmony_ci#include "fid.h" 2962306a36Sopenharmony_ci#include "cache.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic const struct vm_operations_struct v9fs_mmap_file_vm_ops; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/** 3462306a36Sopenharmony_ci * v9fs_file_open - open a file (or directory) 3562306a36Sopenharmony_ci * @inode: inode to be opened 3662306a36Sopenharmony_ci * @file: file being opened 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ciint v9fs_file_open(struct inode *inode, struct file *file) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci int err; 4362306a36Sopenharmony_ci struct v9fs_session_info *v9ses; 4462306a36Sopenharmony_ci struct p9_fid *fid; 4562306a36Sopenharmony_ci int omode; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, file); 4862306a36Sopenharmony_ci v9ses = v9fs_inode2v9ses(inode); 4962306a36Sopenharmony_ci if (v9fs_proto_dotl(v9ses)) 5062306a36Sopenharmony_ci omode = v9fs_open_to_dotl_flags(file->f_flags); 5162306a36Sopenharmony_ci else 5262306a36Sopenharmony_ci omode = v9fs_uflags2omode(file->f_flags, 5362306a36Sopenharmony_ci v9fs_proto_dotu(v9ses)); 5462306a36Sopenharmony_ci fid = file->private_data; 5562306a36Sopenharmony_ci if (!fid) { 5662306a36Sopenharmony_ci fid = v9fs_fid_clone(file_dentry(file)); 5762306a36Sopenharmony_ci if (IS_ERR(fid)) 5862306a36Sopenharmony_ci return PTR_ERR(fid); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if ((v9ses->cache & CACHE_WRITEBACK) && (omode & P9_OWRITE)) { 6162306a36Sopenharmony_ci int writeback_omode = (omode & ~P9_OWRITE) | P9_ORDWR; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci p9_debug(P9_DEBUG_CACHE, "write-only file with writeback enabled, try opening O_RDWR\n"); 6462306a36Sopenharmony_ci err = p9_client_open(fid, writeback_omode); 6562306a36Sopenharmony_ci if (err < 0) { 6662306a36Sopenharmony_ci p9_debug(P9_DEBUG_CACHE, "could not open O_RDWR, disabling caches\n"); 6762306a36Sopenharmony_ci err = p9_client_open(fid, omode); 6862306a36Sopenharmony_ci fid->mode |= P9L_DIRECT; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci } else { 7162306a36Sopenharmony_ci err = p9_client_open(fid, omode); 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci if (err < 0) { 7462306a36Sopenharmony_ci p9_fid_put(fid); 7562306a36Sopenharmony_ci return err; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci if ((file->f_flags & O_APPEND) && 7862306a36Sopenharmony_ci (!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses))) 7962306a36Sopenharmony_ci generic_file_llseek(file, 0, SEEK_END); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci file->private_data = fid; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#ifdef CONFIG_9P_FSCACHE 8562306a36Sopenharmony_ci if (v9ses->cache & CACHE_FSCACHE) 8662306a36Sopenharmony_ci fscache_use_cookie(v9fs_inode_cookie(V9FS_I(inode)), 8762306a36Sopenharmony_ci file->f_mode & FMODE_WRITE); 8862306a36Sopenharmony_ci#endif 8962306a36Sopenharmony_ci v9fs_fid_add_modes(fid, v9ses->flags, v9ses->cache, file->f_flags); 9062306a36Sopenharmony_ci v9fs_open_fid_add(inode, &fid); 9162306a36Sopenharmony_ci return 0; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/** 9562306a36Sopenharmony_ci * v9fs_file_lock - lock a file (or directory) 9662306a36Sopenharmony_ci * @filp: file to be locked 9762306a36Sopenharmony_ci * @cmd: lock command 9862306a36Sopenharmony_ci * @fl: file lock structure 9962306a36Sopenharmony_ci * 10062306a36Sopenharmony_ci * Bugs: this looks like a local only lock, we should extend into 9P 10162306a36Sopenharmony_ci * by using open exclusive 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "filp: %p lock: %p\n", filp, fl); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) { 11162306a36Sopenharmony_ci filemap_write_and_wait(inode->i_mapping); 11262306a36Sopenharmony_ci invalidate_mapping_pages(&inode->i_data, 0, -1); 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct p9_flock flock; 12162306a36Sopenharmony_ci struct p9_fid *fid; 12262306a36Sopenharmony_ci uint8_t status = P9_LOCK_ERROR; 12362306a36Sopenharmony_ci int res = 0; 12462306a36Sopenharmony_ci unsigned char fl_type; 12562306a36Sopenharmony_ci struct v9fs_session_info *v9ses; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci fid = filp->private_data; 12862306a36Sopenharmony_ci BUG_ON(fid == NULL); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci BUG_ON((fl->fl_flags & FL_POSIX) != FL_POSIX); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci res = locks_lock_file_wait(filp, fl); 13362306a36Sopenharmony_ci if (res < 0) 13462306a36Sopenharmony_ci goto out; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* convert posix lock to p9 tlock args */ 13762306a36Sopenharmony_ci memset(&flock, 0, sizeof(flock)); 13862306a36Sopenharmony_ci /* map the lock type */ 13962306a36Sopenharmony_ci switch (fl->fl_type) { 14062306a36Sopenharmony_ci case F_RDLCK: 14162306a36Sopenharmony_ci flock.type = P9_LOCK_TYPE_RDLCK; 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci case F_WRLCK: 14462306a36Sopenharmony_ci flock.type = P9_LOCK_TYPE_WRLCK; 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci case F_UNLCK: 14762306a36Sopenharmony_ci flock.type = P9_LOCK_TYPE_UNLCK; 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci flock.start = fl->fl_start; 15162306a36Sopenharmony_ci if (fl->fl_end == OFFSET_MAX) 15262306a36Sopenharmony_ci flock.length = 0; 15362306a36Sopenharmony_ci else 15462306a36Sopenharmony_ci flock.length = fl->fl_end - fl->fl_start + 1; 15562306a36Sopenharmony_ci flock.proc_id = fl->fl_pid; 15662306a36Sopenharmony_ci flock.client_id = fid->clnt->name; 15762306a36Sopenharmony_ci if (IS_SETLKW(cmd)) 15862306a36Sopenharmony_ci flock.flags = P9_LOCK_FLAGS_BLOCK; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci v9ses = v9fs_inode2v9ses(file_inode(filp)); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* 16362306a36Sopenharmony_ci * if its a blocked request and we get P9_LOCK_BLOCKED as the status 16462306a36Sopenharmony_ci * for lock request, keep on trying 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci for (;;) { 16762306a36Sopenharmony_ci res = p9_client_lock_dotl(fid, &flock, &status); 16862306a36Sopenharmony_ci if (res < 0) 16962306a36Sopenharmony_ci goto out_unlock; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (status != P9_LOCK_BLOCKED) 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci if (status == P9_LOCK_BLOCKED && !IS_SETLKW(cmd)) 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci if (schedule_timeout_interruptible(v9ses->session_lock_timeout) 17662306a36Sopenharmony_ci != 0) 17762306a36Sopenharmony_ci break; 17862306a36Sopenharmony_ci /* 17962306a36Sopenharmony_ci * p9_client_lock_dotl overwrites flock.client_id with the 18062306a36Sopenharmony_ci * server message, free and reuse the client name 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci if (flock.client_id != fid->clnt->name) { 18362306a36Sopenharmony_ci kfree(flock.client_id); 18462306a36Sopenharmony_ci flock.client_id = fid->clnt->name; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* map 9p status to VFS status */ 18962306a36Sopenharmony_ci switch (status) { 19062306a36Sopenharmony_ci case P9_LOCK_SUCCESS: 19162306a36Sopenharmony_ci res = 0; 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci case P9_LOCK_BLOCKED: 19462306a36Sopenharmony_ci res = -EAGAIN; 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci default: 19762306a36Sopenharmony_ci WARN_ONCE(1, "unknown lock status code: %d\n", status); 19862306a36Sopenharmony_ci fallthrough; 19962306a36Sopenharmony_ci case P9_LOCK_ERROR: 20062306a36Sopenharmony_ci case P9_LOCK_GRACE: 20162306a36Sopenharmony_ci res = -ENOLCK; 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ciout_unlock: 20662306a36Sopenharmony_ci /* 20762306a36Sopenharmony_ci * incase server returned error for lock request, revert 20862306a36Sopenharmony_ci * it locally 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci if (res < 0 && fl->fl_type != F_UNLCK) { 21162306a36Sopenharmony_ci fl_type = fl->fl_type; 21262306a36Sopenharmony_ci fl->fl_type = F_UNLCK; 21362306a36Sopenharmony_ci /* Even if this fails we want to return the remote error */ 21462306a36Sopenharmony_ci locks_lock_file_wait(filp, fl); 21562306a36Sopenharmony_ci fl->fl_type = fl_type; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci if (flock.client_id != fid->clnt->name) 21862306a36Sopenharmony_ci kfree(flock.client_id); 21962306a36Sopenharmony_ciout: 22062306a36Sopenharmony_ci return res; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic int v9fs_file_getlock(struct file *filp, struct file_lock *fl) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct p9_getlock glock; 22662306a36Sopenharmony_ci struct p9_fid *fid; 22762306a36Sopenharmony_ci int res = 0; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci fid = filp->private_data; 23062306a36Sopenharmony_ci BUG_ON(fid == NULL); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci posix_test_lock(filp, fl); 23362306a36Sopenharmony_ci /* 23462306a36Sopenharmony_ci * if we have a conflicting lock locally, no need to validate 23562306a36Sopenharmony_ci * with server 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_ci if (fl->fl_type != F_UNLCK) 23862306a36Sopenharmony_ci return res; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* convert posix lock to p9 tgetlock args */ 24162306a36Sopenharmony_ci memset(&glock, 0, sizeof(glock)); 24262306a36Sopenharmony_ci glock.type = P9_LOCK_TYPE_UNLCK; 24362306a36Sopenharmony_ci glock.start = fl->fl_start; 24462306a36Sopenharmony_ci if (fl->fl_end == OFFSET_MAX) 24562306a36Sopenharmony_ci glock.length = 0; 24662306a36Sopenharmony_ci else 24762306a36Sopenharmony_ci glock.length = fl->fl_end - fl->fl_start + 1; 24862306a36Sopenharmony_ci glock.proc_id = fl->fl_pid; 24962306a36Sopenharmony_ci glock.client_id = fid->clnt->name; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci res = p9_client_getlock_dotl(fid, &glock); 25262306a36Sopenharmony_ci if (res < 0) 25362306a36Sopenharmony_ci goto out; 25462306a36Sopenharmony_ci /* map 9p lock type to os lock type */ 25562306a36Sopenharmony_ci switch (glock.type) { 25662306a36Sopenharmony_ci case P9_LOCK_TYPE_RDLCK: 25762306a36Sopenharmony_ci fl->fl_type = F_RDLCK; 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci case P9_LOCK_TYPE_WRLCK: 26062306a36Sopenharmony_ci fl->fl_type = F_WRLCK; 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci case P9_LOCK_TYPE_UNLCK: 26362306a36Sopenharmony_ci fl->fl_type = F_UNLCK; 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci if (glock.type != P9_LOCK_TYPE_UNLCK) { 26762306a36Sopenharmony_ci fl->fl_start = glock.start; 26862306a36Sopenharmony_ci if (glock.length == 0) 26962306a36Sopenharmony_ci fl->fl_end = OFFSET_MAX; 27062306a36Sopenharmony_ci else 27162306a36Sopenharmony_ci fl->fl_end = glock.start + glock.length - 1; 27262306a36Sopenharmony_ci fl->fl_pid = -glock.proc_id; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ciout: 27562306a36Sopenharmony_ci if (glock.client_id != fid->clnt->name) 27662306a36Sopenharmony_ci kfree(glock.client_id); 27762306a36Sopenharmony_ci return res; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci/** 28162306a36Sopenharmony_ci * v9fs_file_lock_dotl - lock a file (or directory) 28262306a36Sopenharmony_ci * @filp: file to be locked 28362306a36Sopenharmony_ci * @cmd: lock command 28462306a36Sopenharmony_ci * @fl: file lock structure 28562306a36Sopenharmony_ci * 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic int v9fs_file_lock_dotl(struct file *filp, int cmd, struct file_lock *fl) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 29162306a36Sopenharmony_ci int ret = -ENOLCK; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %pD\n", 29462306a36Sopenharmony_ci filp, cmd, fl, filp); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) { 29762306a36Sopenharmony_ci filemap_write_and_wait(inode->i_mapping); 29862306a36Sopenharmony_ci invalidate_mapping_pages(&inode->i_data, 0, -1); 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (IS_SETLK(cmd) || IS_SETLKW(cmd)) 30262306a36Sopenharmony_ci ret = v9fs_file_do_lock(filp, cmd, fl); 30362306a36Sopenharmony_ci else if (IS_GETLK(cmd)) 30462306a36Sopenharmony_ci ret = v9fs_file_getlock(filp, fl); 30562306a36Sopenharmony_ci else 30662306a36Sopenharmony_ci ret = -EINVAL; 30762306a36Sopenharmony_ci return ret; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci/** 31162306a36Sopenharmony_ci * v9fs_file_flock_dotl - lock a file 31262306a36Sopenharmony_ci * @filp: file to be locked 31362306a36Sopenharmony_ci * @cmd: lock command 31462306a36Sopenharmony_ci * @fl: file lock structure 31562306a36Sopenharmony_ci * 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int v9fs_file_flock_dotl(struct file *filp, int cmd, 31962306a36Sopenharmony_ci struct file_lock *fl) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 32262306a36Sopenharmony_ci int ret = -ENOLCK; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %pD\n", 32562306a36Sopenharmony_ci filp, cmd, fl, filp); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (!(fl->fl_flags & FL_FLOCK)) 32862306a36Sopenharmony_ci goto out_err; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) { 33162306a36Sopenharmony_ci filemap_write_and_wait(inode->i_mapping); 33262306a36Sopenharmony_ci invalidate_mapping_pages(&inode->i_data, 0, -1); 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci /* Convert flock to posix lock */ 33562306a36Sopenharmony_ci fl->fl_flags |= FL_POSIX; 33662306a36Sopenharmony_ci fl->fl_flags ^= FL_FLOCK; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (IS_SETLK(cmd) | IS_SETLKW(cmd)) 33962306a36Sopenharmony_ci ret = v9fs_file_do_lock(filp, cmd, fl); 34062306a36Sopenharmony_ci else 34162306a36Sopenharmony_ci ret = -EINVAL; 34262306a36Sopenharmony_ciout_err: 34362306a36Sopenharmony_ci return ret; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci/** 34762306a36Sopenharmony_ci * v9fs_file_read_iter - read from a file 34862306a36Sopenharmony_ci * @iocb: The operation parameters 34962306a36Sopenharmony_ci * @to: The buffer to read into 35062306a36Sopenharmony_ci * 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_cistatic ssize_t 35362306a36Sopenharmony_civ9fs_file_read_iter(struct kiocb *iocb, struct iov_iter *to) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct p9_fid *fid = iocb->ki_filp->private_data; 35662306a36Sopenharmony_ci int ret, err = 0; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "fid %d count %zu offset %lld\n", 35962306a36Sopenharmony_ci fid->fid, iov_iter_count(to), iocb->ki_pos); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (!(fid->mode & P9L_DIRECT)) { 36262306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "(cached)\n"); 36362306a36Sopenharmony_ci return generic_file_read_iter(iocb, to); 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (iocb->ki_filp->f_flags & O_NONBLOCK) 36762306a36Sopenharmony_ci ret = p9_client_read_once(fid, iocb->ki_pos, to, &err); 36862306a36Sopenharmony_ci else 36962306a36Sopenharmony_ci ret = p9_client_read(fid, iocb->ki_pos, to, &err); 37062306a36Sopenharmony_ci if (!ret) 37162306a36Sopenharmony_ci return err; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci iocb->ki_pos += ret; 37462306a36Sopenharmony_ci return ret; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci/* 37862306a36Sopenharmony_ci * v9fs_file_splice_read - splice-read from a file 37962306a36Sopenharmony_ci * @in: The 9p file to read from 38062306a36Sopenharmony_ci * @ppos: Where to find/update the file position 38162306a36Sopenharmony_ci * @pipe: The pipe to splice into 38262306a36Sopenharmony_ci * @len: The maximum amount of data to splice 38362306a36Sopenharmony_ci * @flags: SPLICE_F_* flags 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_cistatic ssize_t v9fs_file_splice_read(struct file *in, loff_t *ppos, 38662306a36Sopenharmony_ci struct pipe_inode_info *pipe, 38762306a36Sopenharmony_ci size_t len, unsigned int flags) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct p9_fid *fid = in->private_data; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "fid %d count %zu offset %lld\n", 39262306a36Sopenharmony_ci fid->fid, len, *ppos); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (fid->mode & P9L_DIRECT) 39562306a36Sopenharmony_ci return copy_splice_read(in, ppos, pipe, len, flags); 39662306a36Sopenharmony_ci return filemap_splice_read(in, ppos, pipe, len, flags); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci/** 40062306a36Sopenharmony_ci * v9fs_file_write_iter - write to a file 40162306a36Sopenharmony_ci * @iocb: The operation parameters 40262306a36Sopenharmony_ci * @from: The data to write 40362306a36Sopenharmony_ci * 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_cistatic ssize_t 40662306a36Sopenharmony_civ9fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci struct file *file = iocb->ki_filp; 40962306a36Sopenharmony_ci struct p9_fid *fid = file->private_data; 41062306a36Sopenharmony_ci ssize_t retval; 41162306a36Sopenharmony_ci loff_t origin; 41262306a36Sopenharmony_ci int err = 0; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "fid %d\n", fid->fid); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (!(fid->mode & (P9L_DIRECT | P9L_NOWRITECACHE))) { 41762306a36Sopenharmony_ci p9_debug(P9_DEBUG_CACHE, "(cached)\n"); 41862306a36Sopenharmony_ci return generic_file_write_iter(iocb, from); 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci retval = generic_write_checks(iocb, from); 42262306a36Sopenharmony_ci if (retval <= 0) 42362306a36Sopenharmony_ci return retval; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci origin = iocb->ki_pos; 42662306a36Sopenharmony_ci retval = p9_client_write(file->private_data, iocb->ki_pos, from, &err); 42762306a36Sopenharmony_ci if (retval > 0) { 42862306a36Sopenharmony_ci struct inode *inode = file_inode(file); 42962306a36Sopenharmony_ci loff_t i_size; 43062306a36Sopenharmony_ci unsigned long pg_start, pg_end; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci pg_start = origin >> PAGE_SHIFT; 43362306a36Sopenharmony_ci pg_end = (origin + retval - 1) >> PAGE_SHIFT; 43462306a36Sopenharmony_ci if (inode->i_mapping && inode->i_mapping->nrpages) 43562306a36Sopenharmony_ci invalidate_inode_pages2_range(inode->i_mapping, 43662306a36Sopenharmony_ci pg_start, pg_end); 43762306a36Sopenharmony_ci iocb->ki_pos += retval; 43862306a36Sopenharmony_ci i_size = i_size_read(inode); 43962306a36Sopenharmony_ci if (iocb->ki_pos > i_size) { 44062306a36Sopenharmony_ci inode_add_bytes(inode, iocb->ki_pos - i_size); 44162306a36Sopenharmony_ci /* 44262306a36Sopenharmony_ci * Need to serialize against i_size_write() in 44362306a36Sopenharmony_ci * v9fs_stat2inode() 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_ci v9fs_i_size_write(inode, iocb->ki_pos); 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci return retval; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci return err; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic int v9fs_file_fsync(struct file *filp, loff_t start, loff_t end, 45362306a36Sopenharmony_ci int datasync) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci struct p9_fid *fid; 45662306a36Sopenharmony_ci struct inode *inode = filp->f_mapping->host; 45762306a36Sopenharmony_ci struct p9_wstat wstat; 45862306a36Sopenharmony_ci int retval; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci retval = file_write_and_wait_range(filp, start, end); 46162306a36Sopenharmony_ci if (retval) 46262306a36Sopenharmony_ci return retval; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci inode_lock(inode); 46562306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci fid = filp->private_data; 46862306a36Sopenharmony_ci v9fs_blank_wstat(&wstat); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci retval = p9_client_wstat(fid, &wstat); 47162306a36Sopenharmony_ci inode_unlock(inode); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return retval; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ciint v9fs_file_fsync_dotl(struct file *filp, loff_t start, loff_t end, 47762306a36Sopenharmony_ci int datasync) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct p9_fid *fid; 48062306a36Sopenharmony_ci struct inode *inode = filp->f_mapping->host; 48162306a36Sopenharmony_ci int retval; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci retval = file_write_and_wait_range(filp, start, end); 48462306a36Sopenharmony_ci if (retval) 48562306a36Sopenharmony_ci return retval; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci inode_lock(inode); 48862306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci fid = filp->private_data; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci retval = p9_client_fsync(fid, datasync); 49362306a36Sopenharmony_ci inode_unlock(inode); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci return retval; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic int 49962306a36Sopenharmony_civ9fs_file_mmap(struct file *filp, struct vm_area_struct *vma) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci int retval; 50262306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 50362306a36Sopenharmony_ci struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci p9_debug(P9_DEBUG_MMAP, "filp :%p\n", filp); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (!(v9ses->cache & CACHE_WRITEBACK)) { 50862306a36Sopenharmony_ci p9_debug(P9_DEBUG_CACHE, "(read-only mmap mode)"); 50962306a36Sopenharmony_ci return generic_file_readonly_mmap(filp, vma); 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci retval = generic_file_mmap(filp, vma); 51362306a36Sopenharmony_ci if (!retval) 51462306a36Sopenharmony_ci vma->vm_ops = &v9fs_mmap_file_vm_ops; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci return retval; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic vm_fault_t 52062306a36Sopenharmony_civ9fs_vm_page_mkwrite(struct vm_fault *vmf) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct folio *folio = page_folio(vmf->page); 52362306a36Sopenharmony_ci struct file *filp = vmf->vma->vm_file; 52462306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "folio %p fid %lx\n", 52862306a36Sopenharmony_ci folio, (unsigned long)filp->private_data); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* Wait for the page to be written to the cache before we allow it to 53162306a36Sopenharmony_ci * be modified. We then assume the entire page will need writing back. 53262306a36Sopenharmony_ci */ 53362306a36Sopenharmony_ci#ifdef CONFIG_9P_FSCACHE 53462306a36Sopenharmony_ci if (folio_test_fscache(folio) && 53562306a36Sopenharmony_ci folio_wait_fscache_killable(folio) < 0) 53662306a36Sopenharmony_ci return VM_FAULT_NOPAGE; 53762306a36Sopenharmony_ci#endif 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* Update file times before taking page lock */ 54062306a36Sopenharmony_ci file_update_time(filp); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (folio_lock_killable(folio) < 0) 54362306a36Sopenharmony_ci return VM_FAULT_RETRY; 54462306a36Sopenharmony_ci if (folio_mapping(folio) != inode->i_mapping) 54562306a36Sopenharmony_ci goto out_unlock; 54662306a36Sopenharmony_ci folio_wait_stable(folio); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci return VM_FAULT_LOCKED; 54962306a36Sopenharmony_ciout_unlock: 55062306a36Sopenharmony_ci folio_unlock(folio); 55162306a36Sopenharmony_ci return VM_FAULT_NOPAGE; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic void v9fs_mmap_vm_close(struct vm_area_struct *vma) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct inode *inode; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci struct writeback_control wbc = { 55962306a36Sopenharmony_ci .nr_to_write = LONG_MAX, 56062306a36Sopenharmony_ci .sync_mode = WB_SYNC_ALL, 56162306a36Sopenharmony_ci .range_start = (loff_t)vma->vm_pgoff * PAGE_SIZE, 56262306a36Sopenharmony_ci /* absolute end, byte at end included */ 56362306a36Sopenharmony_ci .range_end = (loff_t)vma->vm_pgoff * PAGE_SIZE + 56462306a36Sopenharmony_ci (vma->vm_end - vma->vm_start - 1), 56562306a36Sopenharmony_ci }; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (!(vma->vm_flags & VM_SHARED)) 56862306a36Sopenharmony_ci return; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "9p VMA close, %p, flushing", vma); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci inode = file_inode(vma->vm_file); 57362306a36Sopenharmony_ci filemap_fdatawrite_wbc(inode->i_mapping, &wbc); 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic const struct vm_operations_struct v9fs_mmap_file_vm_ops = { 57762306a36Sopenharmony_ci .close = v9fs_mmap_vm_close, 57862306a36Sopenharmony_ci .fault = filemap_fault, 57962306a36Sopenharmony_ci .map_pages = filemap_map_pages, 58062306a36Sopenharmony_ci .page_mkwrite = v9fs_vm_page_mkwrite, 58162306a36Sopenharmony_ci}; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ciconst struct file_operations v9fs_file_operations = { 58462306a36Sopenharmony_ci .llseek = generic_file_llseek, 58562306a36Sopenharmony_ci .read_iter = v9fs_file_read_iter, 58662306a36Sopenharmony_ci .write_iter = v9fs_file_write_iter, 58762306a36Sopenharmony_ci .open = v9fs_file_open, 58862306a36Sopenharmony_ci .release = v9fs_dir_release, 58962306a36Sopenharmony_ci .lock = v9fs_file_lock, 59062306a36Sopenharmony_ci .mmap = generic_file_readonly_mmap, 59162306a36Sopenharmony_ci .splice_read = v9fs_file_splice_read, 59262306a36Sopenharmony_ci .splice_write = iter_file_splice_write, 59362306a36Sopenharmony_ci .fsync = v9fs_file_fsync, 59462306a36Sopenharmony_ci}; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ciconst struct file_operations v9fs_file_operations_dotl = { 59762306a36Sopenharmony_ci .llseek = generic_file_llseek, 59862306a36Sopenharmony_ci .read_iter = v9fs_file_read_iter, 59962306a36Sopenharmony_ci .write_iter = v9fs_file_write_iter, 60062306a36Sopenharmony_ci .open = v9fs_file_open, 60162306a36Sopenharmony_ci .release = v9fs_dir_release, 60262306a36Sopenharmony_ci .lock = v9fs_file_lock_dotl, 60362306a36Sopenharmony_ci .flock = v9fs_file_flock_dotl, 60462306a36Sopenharmony_ci .mmap = v9fs_file_mmap, 60562306a36Sopenharmony_ci .splice_read = v9fs_file_splice_read, 60662306a36Sopenharmony_ci .splice_write = iter_file_splice_write, 60762306a36Sopenharmony_ci .fsync = v9fs_file_fsync_dotl, 60862306a36Sopenharmony_ci}; 609