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 <linux/utsname.h> 37 #include <asm/uaccess.h> 38 #include <linux/idr.h> 39 #include <net/9p/9p.h> 40 #include <net/9p/client.h> 41 42 #include "v9fs.h" 43 #include "v9fs_vfs.h" 44 #include "fid.h" 45 #include "cache.h" 46 47 static const struct file_operations v9fs_cached_file_operations; 48 static const struct file_operations v9fs_cached_file_operations_dotl; 49 50 /** 51 * v9fs_file_open - open a file (or directory) 52 * @inode: inode to be opened 53 * @file: file being opened 54 * 55 */ 56 57 int v9fs_file_open(struct inode *inode, struct file *file) 58 { 59 int err; 60 struct v9fs_session_info *v9ses; 61 struct p9_fid *fid; 62 int omode; 63 64 P9_DPRINTK(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, file); 65 v9ses = v9fs_inode2v9ses(inode); 66 if (v9fs_proto_dotl(v9ses)) 67 omode = file->f_flags; 68 else 69 omode = v9fs_uflags2omode(file->f_flags, 70 v9fs_proto_dotu(v9ses)); 71 fid = file->private_data; 72 if (!fid) { 73 fid = v9fs_fid_clone(file->f_path.dentry); 74 if (IS_ERR(fid)) 75 return PTR_ERR(fid); 76 77 err = p9_client_open(fid, omode); 78 if (err < 0) { 79 p9_client_clunk(fid); 80 return err; 81 } 82 if (file->f_flags & O_TRUNC) { 83 i_size_write(inode, 0); 84 inode->i_blocks = 0; 85 } 86 if ((file->f_flags & O_APPEND) && 87 (!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses))) 88 generic_file_llseek(file, 0, SEEK_END); 89 } 90 91 file->private_data = fid; 92 if ((fid->qid.version) && (v9ses->cache)) { 93 P9_DPRINTK(P9_DEBUG_VFS, "cached"); 94 /* enable cached file options */ 95 if(file->f_op == &v9fs_file_operations) 96 file->f_op = &v9fs_cached_file_operations; 97 else if (file->f_op == &v9fs_file_operations_dotl) 98 file->f_op = &v9fs_cached_file_operations_dotl; 99 100 #ifdef CONFIG_9P_FSCACHE 101 v9fs_cache_inode_set_cookie(inode, file); 102 #endif 103 } 104 105 return 0; 106 } 107 108 /** 109 * v9fs_file_lock - lock a file (or directory) 110 * @filp: file to be locked 111 * @cmd: lock command 112 * @fl: file lock structure 113 * 114 * Bugs: this looks like a local only lock, we should extend into 9P 115 * by using open exclusive 116 */ 117 118 static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl) 119 { 120 int res = 0; 121 struct inode *inode = filp->f_path.dentry->d_inode; 122 123 P9_DPRINTK(P9_DEBUG_VFS, "filp: %p lock: %p\n", filp, fl); 124 125 /* No mandatory locks */ 126 if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK) 127 return -ENOLCK; 128 129 if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) { 130 filemap_write_and_wait(inode->i_mapping); 131 invalidate_mapping_pages(&inode->i_data, 0, -1); 132 } 133 134 return res; 135 } 136 137 static int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl) 138 { 139 struct p9_flock flock; 140 struct p9_fid *fid; 141 uint8_t status; 142 int res = 0; 143 unsigned char fl_type; 144 145 fid = filp->private_data; 146 BUG_ON(fid == NULL); 147 148 if ((fl->fl_flags & FL_POSIX) != FL_POSIX) 149 BUG(); 150 151 res = posix_lock_file_wait(filp, fl); 152 if (res < 0) 153 goto out; 154 155 /* convert posix lock to p9 tlock args */ 156 memset(&flock, 0, sizeof(flock)); 157 flock.type = fl->fl_type; 158 flock.start = fl->fl_start; 159 if (fl->fl_end == OFFSET_MAX) 160 flock.length = 0; 161 else 162 flock.length = fl->fl_end - fl->fl_start + 1; 163 flock.proc_id = fl->fl_pid; 164 flock.client_id = utsname()->nodename; 165 if (IS_SETLKW(cmd)) 166 flock.flags = P9_LOCK_FLAGS_BLOCK; 167 168 /* 169 * if its a blocked request and we get P9_LOCK_BLOCKED as the status 170 * for lock request, keep on trying 171 */ 172 for (;;) { 173 res = p9_client_lock_dotl(fid, &flock, &status); 174 if (res < 0) 175 break; 176 177 if (status != P9_LOCK_BLOCKED) 178 break; 179 if (status == P9_LOCK_BLOCKED && !IS_SETLKW(cmd)) 180 break; 181 schedule_timeout_interruptible(P9_LOCK_TIMEOUT); 182 } 183 184 /* map 9p status to VFS status */ 185 switch (status) { 186 case P9_LOCK_SUCCESS: 187 res = 0; 188 break; 189 case P9_LOCK_BLOCKED: 190 res = -EAGAIN; 191 break; 192 case P9_LOCK_ERROR: 193 case P9_LOCK_GRACE: 194 res = -ENOLCK; 195 break; 196 default: 197 BUG(); 198 } 199 200 /* 201 * incase server returned error for lock request, revert 202 * it locally 203 */ 204 if (res < 0 && fl->fl_type != F_UNLCK) { 205 fl_type = fl->fl_type; 206 fl->fl_type = F_UNLCK; 207 res = posix_lock_file_wait(filp, fl); 208 fl->fl_type = fl_type; 209 } 210 out: 211 return res; 212 } 213 214 static int v9fs_file_getlock(struct file *filp, struct file_lock *fl) 215 { 216 struct p9_getlock glock; 217 struct p9_fid *fid; 218 int res = 0; 219 220 fid = filp->private_data; 221 BUG_ON(fid == NULL); 222 223 posix_test_lock(filp, fl); 224 /* 225 * if we have a conflicting lock locally, no need to validate 226 * with server 227 */ 228 if (fl->fl_type != F_UNLCK) 229 return res; 230 231 /* convert posix lock to p9 tgetlock args */ 232 memset(&glock, 0, sizeof(glock)); 233 glock.type = fl->fl_type; 234 glock.start = fl->fl_start; 235 if (fl->fl_end == OFFSET_MAX) 236 glock.length = 0; 237 else 238 glock.length = fl->fl_end - fl->fl_start + 1; 239 glock.proc_id = fl->fl_pid; 240 glock.client_id = utsname()->nodename; 241 242 res = p9_client_getlock_dotl(fid, &glock); 243 if (res < 0) 244 return res; 245 if (glock.type != F_UNLCK) { 246 fl->fl_type = glock.type; 247 fl->fl_start = glock.start; 248 if (glock.length == 0) 249 fl->fl_end = OFFSET_MAX; 250 else 251 fl->fl_end = glock.start + glock.length - 1; 252 fl->fl_pid = glock.proc_id; 253 } else 254 fl->fl_type = F_UNLCK; 255 256 return res; 257 } 258 259 /** 260 * v9fs_file_lock_dotl - lock a file (or directory) 261 * @filp: file to be locked 262 * @cmd: lock command 263 * @fl: file lock structure 264 * 265 */ 266 267 static int v9fs_file_lock_dotl(struct file *filp, int cmd, struct file_lock *fl) 268 { 269 struct inode *inode = filp->f_path.dentry->d_inode; 270 int ret = -ENOLCK; 271 272 P9_DPRINTK(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %s\n", filp, 273 cmd, fl, filp->f_path.dentry->d_name.name); 274 275 /* No mandatory locks */ 276 if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK) 277 goto out_err; 278 279 if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) { 280 filemap_write_and_wait(inode->i_mapping); 281 invalidate_mapping_pages(&inode->i_data, 0, -1); 282 } 283 284 if (IS_SETLK(cmd) || IS_SETLKW(cmd)) 285 ret = v9fs_file_do_lock(filp, cmd, fl); 286 else if (IS_GETLK(cmd)) 287 ret = v9fs_file_getlock(filp, fl); 288 else 289 ret = -EINVAL; 290 out_err: 291 return ret; 292 } 293 294 /** 295 * v9fs_file_flock_dotl - lock a file 296 * @filp: file to be locked 297 * @cmd: lock command 298 * @fl: file lock structure 299 * 300 */ 301 302 static int v9fs_file_flock_dotl(struct file *filp, int cmd, 303 struct file_lock *fl) 304 { 305 struct inode *inode = filp->f_path.dentry->d_inode; 306 int ret = -ENOLCK; 307 308 P9_DPRINTK(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %s\n", filp, 309 cmd, fl, filp->f_path.dentry->d_name.name); 310 311 /* No mandatory locks */ 312 if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK) 313 goto out_err; 314 315 if (!(fl->fl_flags & FL_FLOCK)) 316 goto out_err; 317 318 if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) { 319 filemap_write_and_wait(inode->i_mapping); 320 invalidate_mapping_pages(&inode->i_data, 0, -1); 321 } 322 /* Convert flock to posix lock */ 323 fl->fl_owner = (fl_owner_t)filp; 324 fl->fl_start = 0; 325 fl->fl_end = OFFSET_MAX; 326 fl->fl_flags |= FL_POSIX; 327 fl->fl_flags ^= FL_FLOCK; 328 329 if (IS_SETLK(cmd) | IS_SETLKW(cmd)) 330 ret = v9fs_file_do_lock(filp, cmd, fl); 331 else 332 ret = -EINVAL; 333 out_err: 334 return ret; 335 } 336 337 /** 338 * v9fs_file_readn - read from a file 339 * @filp: file pointer to read 340 * @data: data buffer to read data into 341 * @udata: user data buffer to read data into 342 * @count: size of buffer 343 * @offset: offset at which to read data 344 * 345 */ 346 347 ssize_t 348 v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count, 349 u64 offset) 350 { 351 int n, total, size; 352 struct p9_fid *fid = filp->private_data; 353 354 P9_DPRINTK(P9_DEBUG_VFS, "fid %d offset %llu count %d\n", fid->fid, 355 (long long unsigned) offset, count); 356 357 n = 0; 358 total = 0; 359 size = fid->iounit ? fid->iounit : fid->clnt->msize - P9_IOHDRSZ; 360 do { 361 n = p9_client_read(fid, data, udata, offset, count); 362 if (n <= 0) 363 break; 364 365 if (data) 366 data += n; 367 if (udata) 368 udata += n; 369 370 offset += n; 371 count -= n; 372 total += n; 373 } while (count > 0 && n == size); 374 375 if (n < 0) 376 total = n; 377 378 return total; 379 } 380 381 /** 382 * v9fs_file_read - read from a file 383 * @filp: file pointer to read 384 * @udata: user data buffer to read data into 385 * @count: size of buffer 386 * @offset: offset at which to read data 387 * 388 */ 389 390 static ssize_t 391 v9fs_file_read(struct file *filp, char __user *udata, size_t count, 392 loff_t * offset) 393 { 394 int ret; 395 struct p9_fid *fid; 396 size_t size; 397 398 P9_DPRINTK(P9_DEBUG_VFS, "count %zu offset %lld\n", count, *offset); 399 fid = filp->private_data; 400 401 size = fid->iounit ? fid->iounit : fid->clnt->msize - P9_IOHDRSZ; 402 if (count > size) 403 ret = v9fs_file_readn(filp, NULL, udata, count, *offset); 404 else 405 ret = p9_client_read(fid, NULL, udata, *offset, count); 406 407 if (ret > 0) 408 *offset += ret; 409 410 return ret; 411 } 412 413 /** 414 * v9fs_file_write - write to a file 415 * @filp: file pointer to write 416 * @data: data buffer to write data from 417 * @count: size of buffer 418 * @offset: offset at which to write data 419 * 420 */ 421 422 static ssize_t 423 v9fs_file_write(struct file *filp, const char __user * data, 424 size_t count, loff_t * offset) 425 { 426 ssize_t retval; 427 size_t total = 0; 428 int n; 429 struct p9_fid *fid; 430 struct p9_client *clnt; 431 struct inode *inode = filp->f_path.dentry->d_inode; 432 loff_t origin = *offset; 433 unsigned long pg_start, pg_end; 434 435 P9_DPRINTK(P9_DEBUG_VFS, "data %p count %d offset %x\n", data, 436 (int)count, (int)*offset); 437 438 fid = filp->private_data; 439 clnt = fid->clnt; 440 441 retval = generic_write_checks(filp, &origin, &count, 0); 442 if (retval) 443 goto out; 444 445 retval = -EINVAL; 446 if ((ssize_t) count < 0) 447 goto out; 448 retval = 0; 449 if (!count) 450 goto out; 451 452 do { 453 n = p9_client_write(fid, NULL, data+total, origin+total, count); 454 if (n <= 0) 455 break; 456 count -= n; 457 total += n; 458 } while (count > 0); 459 460 if (total > 0) { 461 pg_start = origin >> PAGE_CACHE_SHIFT; 462 pg_end = (origin + total - 1) >> PAGE_CACHE_SHIFT; 463 if (inode->i_mapping && inode->i_mapping->nrpages) 464 invalidate_inode_pages2_range(inode->i_mapping, 465 pg_start, pg_end); 466 *offset += total; 467 i_size_write(inode, i_size_read(inode) + total); 468 inode->i_blocks = (i_size_read(inode) + 512 - 1) >> 9; 469 } 470 471 if (n < 0) 472 retval = n; 473 else 474 retval = total; 475 out: 476 return retval; 477 } 478 479 static int v9fs_file_fsync(struct file *filp, int datasync) 480 { 481 struct p9_fid *fid; 482 struct p9_wstat wstat; 483 int retval; 484 485 P9_DPRINTK(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync); 486 487 fid = filp->private_data; 488 v9fs_blank_wstat(&wstat); 489 490 retval = p9_client_wstat(fid, &wstat); 491 return retval; 492 } 493 494 int v9fs_file_fsync_dotl(struct file *filp, int datasync) 495 { 496 struct p9_fid *fid; 497 int retval; 498 499 P9_DPRINTK(P9_DEBUG_VFS, "v9fs_file_fsync_dotl: filp %p datasync %x\n", 500 filp, datasync); 501 502 fid = filp->private_data; 503 504 retval = p9_client_fsync(fid, datasync); 505 return retval; 506 } 507 508 static const struct file_operations v9fs_cached_file_operations = { 509 .llseek = generic_file_llseek, 510 .read = do_sync_read, 511 .aio_read = generic_file_aio_read, 512 .write = v9fs_file_write, 513 .open = v9fs_file_open, 514 .release = v9fs_dir_release, 515 .lock = v9fs_file_lock, 516 .mmap = generic_file_readonly_mmap, 517 .fsync = v9fs_file_fsync, 518 }; 519 520 static const struct file_operations v9fs_cached_file_operations_dotl = { 521 .llseek = generic_file_llseek, 522 .read = do_sync_read, 523 .aio_read = generic_file_aio_read, 524 .write = v9fs_file_write, 525 .open = v9fs_file_open, 526 .release = v9fs_dir_release, 527 .lock = v9fs_file_lock_dotl, 528 .flock = v9fs_file_flock_dotl, 529 .mmap = generic_file_readonly_mmap, 530 .fsync = v9fs_file_fsync_dotl, 531 }; 532 533 const struct file_operations v9fs_file_operations = { 534 .llseek = generic_file_llseek, 535 .read = v9fs_file_read, 536 .write = v9fs_file_write, 537 .open = v9fs_file_open, 538 .release = v9fs_dir_release, 539 .lock = v9fs_file_lock, 540 .mmap = generic_file_readonly_mmap, 541 .fsync = v9fs_file_fsync, 542 }; 543 544 const struct file_operations v9fs_file_operations_dotl = { 545 .llseek = generic_file_llseek, 546 .read = v9fs_file_read, 547 .write = v9fs_file_write, 548 .open = v9fs_file_open, 549 .release = v9fs_dir_release, 550 .lock = v9fs_file_lock_dotl, 551 .flock = v9fs_file_flock_dotl, 552 .mmap = generic_file_readonly_mmap, 553 .fsync = v9fs_file_fsync_dotl, 554 }; 555