1 /* 2 * linux/fs/9p/vfs_file.c 3 * 4 * This file contians vfs file ops for 9P2000. 5 * 6 * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> 7 * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 11 * as published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to: 20 * Free Software Foundation 21 * 51 Franklin Street, Fifth Floor 22 * Boston, MA 02111-1301 USA 23 * 24 */ 25 26 #include <linux/module.h> 27 #include <linux/errno.h> 28 #include <linux/fs.h> 29 #include <linux/sched.h> 30 #include <linux/file.h> 31 #include <linux/stat.h> 32 #include <linux/string.h> 33 #include <linux/inet.h> 34 #include <linux/list.h> 35 #include <linux/pagemap.h> 36 #include <asm/uaccess.h> 37 #include <linux/idr.h> 38 #include <net/9p/9p.h> 39 #include <net/9p/client.h> 40 41 #include "v9fs.h" 42 #include "v9fs_vfs.h" 43 #include "fid.h" 44 #include "cache.h" 45 46 static const struct file_operations v9fs_cached_file_operations; 47 48 /** 49 * v9fs_file_open - open a file (or directory) 50 * @inode: inode to be opened 51 * @file: file being opened 52 * 53 */ 54 55 int v9fs_file_open(struct inode *inode, struct file *file) 56 { 57 int err; 58 struct v9fs_session_info *v9ses; 59 struct p9_fid *fid; 60 int omode; 61 62 P9_DPRINTK(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, file); 63 v9ses = v9fs_inode2v9ses(inode); 64 if (v9fs_proto_dotl(v9ses)) 65 omode = file->f_flags; 66 else 67 omode = v9fs_uflags2omode(file->f_flags, 68 v9fs_proto_dotu(v9ses)); 69 fid = file->private_data; 70 if (!fid) { 71 fid = v9fs_fid_clone(file->f_path.dentry); 72 if (IS_ERR(fid)) 73 return PTR_ERR(fid); 74 75 err = p9_client_open(fid, omode); 76 if (err < 0) { 77 p9_client_clunk(fid); 78 return err; 79 } 80 if (file->f_flags & O_TRUNC) { 81 i_size_write(inode, 0); 82 inode->i_blocks = 0; 83 } 84 if ((file->f_flags & O_APPEND) && 85 (!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses))) 86 generic_file_llseek(file, 0, SEEK_END); 87 } 88 89 file->private_data = fid; 90 if ((fid->qid.version) && (v9ses->cache)) { 91 P9_DPRINTK(P9_DEBUG_VFS, "cached"); 92 /* enable cached file options */ 93 if(file->f_op == &v9fs_file_operations) 94 file->f_op = &v9fs_cached_file_operations; 95 96 #ifdef CONFIG_9P_FSCACHE 97 v9fs_cache_inode_set_cookie(inode, file); 98 #endif 99 } 100 101 return 0; 102 } 103 104 /** 105 * v9fs_file_lock - lock a file (or directory) 106 * @filp: file to be locked 107 * @cmd: lock command 108 * @fl: file lock structure 109 * 110 * Bugs: this looks like a local only lock, we should extend into 9P 111 * by using open exclusive 112 */ 113 114 static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl) 115 { 116 int res = 0; 117 struct inode *inode = filp->f_path.dentry->d_inode; 118 119 P9_DPRINTK(P9_DEBUG_VFS, "filp: %p lock: %p\n", filp, fl); 120 121 /* No mandatory locks */ 122 if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK) 123 return -ENOLCK; 124 125 if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) { 126 filemap_write_and_wait(inode->i_mapping); 127 invalidate_mapping_pages(&inode->i_data, 0, -1); 128 } 129 130 return res; 131 } 132 133 /** 134 * v9fs_file_readn - read from a file 135 * @filp: file pointer to read 136 * @data: data buffer to read data into 137 * @udata: user data buffer to read data into 138 * @count: size of buffer 139 * @offset: offset at which to read data 140 * 141 */ 142 143 ssize_t 144 v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count, 145 u64 offset) 146 { 147 int n, total, size; 148 struct p9_fid *fid = filp->private_data; 149 150 P9_DPRINTK(P9_DEBUG_VFS, "fid %d offset %llu count %d\n", fid->fid, 151 (long long unsigned) offset, count); 152 153 n = 0; 154 total = 0; 155 size = fid->iounit ? fid->iounit : fid->clnt->msize - P9_IOHDRSZ; 156 do { 157 n = p9_client_read(fid, data, udata, offset, count); 158 if (n <= 0) 159 break; 160 161 if (data) 162 data += n; 163 if (udata) 164 udata += n; 165 166 offset += n; 167 count -= n; 168 total += n; 169 } while (count > 0 && n == size); 170 171 if (n < 0) 172 total = n; 173 174 return total; 175 } 176 177 /** 178 * v9fs_file_read - read from a file 179 * @filp: file pointer to read 180 * @udata: user data buffer to read data into 181 * @count: size of buffer 182 * @offset: offset at which to read data 183 * 184 */ 185 186 static ssize_t 187 v9fs_file_read(struct file *filp, char __user *udata, size_t count, 188 loff_t * offset) 189 { 190 int ret; 191 struct p9_fid *fid; 192 size_t size; 193 194 P9_DPRINTK(P9_DEBUG_VFS, "count %zu offset %lld\n", count, *offset); 195 fid = filp->private_data; 196 197 size = fid->iounit ? fid->iounit : fid->clnt->msize - P9_IOHDRSZ; 198 if (count > size) 199 ret = v9fs_file_readn(filp, NULL, udata, count, *offset); 200 else 201 ret = p9_client_read(fid, NULL, udata, *offset, count); 202 203 if (ret > 0) 204 *offset += ret; 205 206 return ret; 207 } 208 209 /** 210 * v9fs_file_write - write to a file 211 * @filp: file pointer to write 212 * @data: data buffer to write data from 213 * @count: size of buffer 214 * @offset: offset at which to write data 215 * 216 */ 217 218 static ssize_t 219 v9fs_file_write(struct file *filp, const char __user * data, 220 size_t count, loff_t * offset) 221 { 222 int n, rsize, total = 0; 223 struct p9_fid *fid; 224 struct p9_client *clnt; 225 struct inode *inode = filp->f_path.dentry->d_inode; 226 loff_t origin = *offset; 227 unsigned long pg_start, pg_end; 228 229 P9_DPRINTK(P9_DEBUG_VFS, "data %p count %d offset %x\n", data, 230 (int)count, (int)*offset); 231 232 fid = filp->private_data; 233 clnt = fid->clnt; 234 235 rsize = fid->iounit ? fid->iounit : clnt->msize - P9_IOHDRSZ; 236 237 do { 238 if (count < rsize) 239 rsize = count; 240 241 n = p9_client_write(fid, NULL, data+total, origin+total, 242 rsize); 243 if (n <= 0) 244 break; 245 count -= n; 246 total += n; 247 } while (count > 0); 248 249 if (total > 0) { 250 pg_start = origin >> PAGE_CACHE_SHIFT; 251 pg_end = (origin + total - 1) >> PAGE_CACHE_SHIFT; 252 if (inode->i_mapping && inode->i_mapping->nrpages) 253 invalidate_inode_pages2_range(inode->i_mapping, 254 pg_start, pg_end); 255 *offset += total; 256 i_size_write(inode, i_size_read(inode) + total); 257 inode->i_blocks = (i_size_read(inode) + 512 - 1) >> 9; 258 } 259 260 if (n < 0) 261 return n; 262 263 return total; 264 } 265 266 static int v9fs_file_fsync(struct file *filp, int datasync) 267 { 268 struct p9_fid *fid; 269 struct p9_wstat wstat; 270 int retval; 271 272 P9_DPRINTK(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync); 273 274 fid = filp->private_data; 275 v9fs_blank_wstat(&wstat); 276 277 retval = p9_client_wstat(fid, &wstat); 278 return retval; 279 } 280 281 static const struct file_operations v9fs_cached_file_operations = { 282 .llseek = generic_file_llseek, 283 .read = do_sync_read, 284 .aio_read = generic_file_aio_read, 285 .write = v9fs_file_write, 286 .open = v9fs_file_open, 287 .release = v9fs_dir_release, 288 .lock = v9fs_file_lock, 289 .mmap = generic_file_readonly_mmap, 290 .fsync = v9fs_file_fsync, 291 }; 292 293 const struct file_operations v9fs_file_operations = { 294 .llseek = generic_file_llseek, 295 .read = v9fs_file_read, 296 .write = v9fs_file_write, 297 .open = v9fs_file_open, 298 .release = v9fs_dir_release, 299 .lock = v9fs_file_lock, 300 .mmap = generic_file_readonly_mmap, 301 .fsync = v9fs_file_fsync, 302 }; 303 304 const struct file_operations v9fs_file_operations_dotl = { 305 .llseek = generic_file_llseek, 306 .read = v9fs_file_read, 307 .write = v9fs_file_write, 308 .open = v9fs_file_open, 309 .release = v9fs_dir_release, 310 .lock = v9fs_file_lock, 311 .mmap = generic_file_readonly_mmap, 312 .fsync = v9fs_file_fsync, 313 }; 314