1/* 2 * linux/fs/9p/vfs_inode.c 3 * 4 * This file contains vfs inode ops for the 9P2000 protocol. 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/file.h> 30#include <linux/pagemap.h> 31#include <linux/stat.h> 32#include <linux/string.h> 33#include <linux/inet.h> 34#include <linux/namei.h> 35#include <linux/idr.h> 36#include <linux/sched.h> 37 38#include "debug.h" 39#include "v9fs.h" 40#include "9p.h" 41#include "v9fs_vfs.h" 42#include "fid.h" 43 44static const struct inode_operations v9fs_dir_inode_operations; 45static const struct inode_operations v9fs_dir_inode_operations_ext; 46static const struct inode_operations v9fs_file_inode_operations; 47static const struct inode_operations v9fs_symlink_inode_operations; 48 49/** 50 * unixmode2p9mode - convert unix mode bits to plan 9 51 * @v9ses: v9fs session information 52 * @mode: mode to convert 53 * 54 */ 55 56static int unixmode2p9mode(struct v9fs_session_info *v9ses, int mode) 57{ 58 int res; 59 res = mode & 0777; 60 if (S_ISDIR(mode)) 61 res |= V9FS_DMDIR; 62 if (v9ses->extended) { 63 if (S_ISLNK(mode)) 64 res |= V9FS_DMSYMLINK; 65 if (v9ses->nodev == 0) { 66 if (S_ISSOCK(mode)) 67 res |= V9FS_DMSOCKET; 68 if (S_ISFIFO(mode)) 69 res |= V9FS_DMNAMEDPIPE; 70 if (S_ISBLK(mode)) 71 res |= V9FS_DMDEVICE; 72 if (S_ISCHR(mode)) 73 res |= V9FS_DMDEVICE; 74 } 75 76 if ((mode & S_ISUID) == S_ISUID) 77 res |= V9FS_DMSETUID; 78 if ((mode & S_ISGID) == S_ISGID) 79 res |= V9FS_DMSETGID; 80 if ((mode & V9FS_DMLINK)) 81 res |= V9FS_DMLINK; 82 } 83 84 return res; 85} 86 87/** 88 * p9mode2unixmode- convert plan9 mode bits to unix mode bits 89 * @v9ses: v9fs session information 90 * @mode: mode to convert 91 * 92 */ 93 94static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode) 95{ 96 int res; 97 98 res = mode & 0777; 99 100 if ((mode & V9FS_DMDIR) == V9FS_DMDIR) 101 res |= S_IFDIR; 102 else if ((mode & V9FS_DMSYMLINK) && (v9ses->extended)) 103 res |= S_IFLNK; 104 else if ((mode & V9FS_DMSOCKET) && (v9ses->extended) 105 && (v9ses->nodev == 0)) 106 res |= S_IFSOCK; 107 else if ((mode & V9FS_DMNAMEDPIPE) && (v9ses->extended) 108 && (v9ses->nodev == 0)) 109 res |= S_IFIFO; 110 else if ((mode & V9FS_DMDEVICE) && (v9ses->extended) 111 && (v9ses->nodev == 0)) 112 res |= S_IFBLK; 113 else 114 res |= S_IFREG; 115 116 if (v9ses->extended) { 117 if ((mode & V9FS_DMSETUID) == V9FS_DMSETUID) 118 res |= S_ISUID; 119 120 if ((mode & V9FS_DMSETGID) == V9FS_DMSETGID) 121 res |= S_ISGID; 122 } 123 124 return res; 125} 126 127int v9fs_uflags2omode(int uflags) 128{ 129 int ret; 130 131 ret = 0; 132 switch (uflags&3) { 133 default: 134 case O_RDONLY: 135 ret = V9FS_OREAD; 136 break; 137 138 case O_WRONLY: 139 ret = V9FS_OWRITE; 140 break; 141 142 case O_RDWR: 143 ret = V9FS_ORDWR; 144 break; 145 } 146 147 if (uflags & O_EXCL) 148 ret |= V9FS_OEXCL; 149 150 if (uflags & O_TRUNC) 151 ret |= V9FS_OTRUNC; 152 153 if (uflags & O_APPEND) 154 ret |= V9FS_OAPPEND; 155 156 return ret; 157} 158 159/** 160 * v9fs_blank_wstat - helper function to setup a 9P stat structure 161 * @v9ses: 9P session info (for determining extended mode) 162 * @wstat: structure to initialize 163 * 164 */ 165 166static void 167v9fs_blank_wstat(struct v9fs_wstat *wstat) 168{ 169 wstat->type = ~0; 170 wstat->dev = ~0; 171 wstat->qid.type = ~0; 172 wstat->qid.version = ~0; 173 *((long long *)&wstat->qid.path) = ~0; 174 wstat->mode = ~0; 175 wstat->atime = ~0; 176 wstat->mtime = ~0; 177 wstat->length = ~0; 178 wstat->name = NULL; 179 wstat->uid = NULL; 180 wstat->gid = NULL; 181 wstat->muid = NULL; 182 wstat->n_uid = ~0; 183 wstat->n_gid = ~0; 184 wstat->n_muid = ~0; 185 wstat->extension = NULL; 186} 187 188/** 189 * v9fs_get_inode - helper function to setup an inode 190 * @sb: superblock 191 * @mode: mode to setup inode with 192 * 193 */ 194 195struct inode *v9fs_get_inode(struct super_block *sb, int mode) 196{ 197 struct inode *inode; 198 struct v9fs_session_info *v9ses = sb->s_fs_info; 199 200 dprintk(DEBUG_VFS, "super block: %p mode: %o\n", sb, mode); 201 202 inode = new_inode(sb); 203 if (inode) { 204 inode->i_mode = mode; 205 inode->i_uid = current->fsuid; 206 inode->i_gid = current->fsgid; 207 inode->i_blocks = 0; 208 inode->i_rdev = 0; 209 inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; 210 inode->i_mapping->a_ops = &v9fs_addr_operations; 211 212 switch (mode & S_IFMT) { 213 case S_IFIFO: 214 case S_IFBLK: 215 case S_IFCHR: 216 case S_IFSOCK: 217 if(!v9ses->extended) { 218 dprintk(DEBUG_ERROR, "special files without extended mode\n"); 219 return ERR_PTR(-EINVAL); 220 } 221 init_special_inode(inode, inode->i_mode, 222 inode->i_rdev); 223 break; 224 case S_IFREG: 225 inode->i_op = &v9fs_file_inode_operations; 226 inode->i_fop = &v9fs_file_operations; 227 break; 228 case S_IFLNK: 229 if(!v9ses->extended) { 230 dprintk(DEBUG_ERROR, "extended modes used w/o 9P2000.u\n"); 231 return ERR_PTR(-EINVAL); 232 } 233 inode->i_op = &v9fs_symlink_inode_operations; 234 break; 235 case S_IFDIR: 236 inc_nlink(inode); 237 if(v9ses->extended) 238 inode->i_op = &v9fs_dir_inode_operations_ext; 239 else 240 inode->i_op = &v9fs_dir_inode_operations; 241 inode->i_fop = &v9fs_dir_operations; 242 break; 243 default: 244 dprintk(DEBUG_ERROR, "BAD mode 0x%x S_IFMT 0x%x\n", 245 mode, mode & S_IFMT); 246 return ERR_PTR(-EINVAL); 247 } 248 } else { 249 eprintk(KERN_WARNING, "Problem allocating inode\n"); 250 return ERR_PTR(-ENOMEM); 251 } 252 return inode; 253} 254 255static int 256v9fs_create(struct v9fs_session_info *v9ses, u32 pfid, char *name, u32 perm, 257 u8 mode, char *extension, u32 *fidp, struct v9fs_qid *qid, u32 *iounit) 258{ 259 int fid; 260 int err; 261 struct v9fs_fcall *fcall; 262 263 fid = v9fs_get_idpool(&v9ses->fidpool); 264 if (fid < 0) { 265 eprintk(KERN_WARNING, "no free fids available\n"); 266 return -ENOSPC; 267 } 268 269 err = v9fs_t_walk(v9ses, pfid, fid, NULL, &fcall); 270 if (err < 0) { 271 PRINT_FCALL_ERROR("clone error", fcall); 272 if (fcall && fcall->id == RWALK) 273 goto clunk_fid; 274 else 275 goto put_fid; 276 } 277 kfree(fcall); 278 279 err = v9fs_t_create(v9ses, fid, name, perm, mode, extension, &fcall); 280 if (err < 0) { 281 PRINT_FCALL_ERROR("create fails", fcall); 282 goto clunk_fid; 283 } 284 285 if (iounit) 286 *iounit = fcall->params.rcreate.iounit; 287 288 if (qid) 289 *qid = fcall->params.rcreate.qid; 290 291 if (fidp) 292 *fidp = fid; 293 294 kfree(fcall); 295 return 0; 296 297clunk_fid: 298 v9fs_t_clunk(v9ses, fid); 299 fid = V9FS_NOFID; 300 301put_fid: 302 if (fid != V9FS_NOFID) 303 v9fs_put_idpool(fid, &v9ses->fidpool); 304 305 kfree(fcall); 306 return err; 307} 308 309static struct v9fs_fid* 310v9fs_clone_walk(struct v9fs_session_info *v9ses, u32 fid, struct dentry *dentry) 311{ 312 int err; 313 int nfid; 314 struct v9fs_fid *ret; 315 struct v9fs_fcall *fcall; 316 317 nfid = v9fs_get_idpool(&v9ses->fidpool); 318 if (nfid < 0) { 319 eprintk(KERN_WARNING, "no free fids available\n"); 320 return ERR_PTR(-ENOSPC); 321 } 322 323 err = v9fs_t_walk(v9ses, fid, nfid, (char *) dentry->d_name.name, 324 &fcall); 325 326 if (err < 0) { 327 if (fcall && fcall->id == RWALK) 328 goto clunk_fid; 329 330 PRINT_FCALL_ERROR("walk error", fcall); 331 v9fs_put_idpool(nfid, &v9ses->fidpool); 332 goto error; 333 } 334 335 kfree(fcall); 336 fcall = NULL; 337 ret = v9fs_fid_create(v9ses, nfid); 338 if (!ret) { 339 err = -ENOMEM; 340 goto clunk_fid; 341 } 342 343 err = v9fs_fid_insert(ret, dentry); 344 if (err < 0) { 345 v9fs_fid_destroy(ret); 346 goto clunk_fid; 347 } 348 349 return ret; 350 351clunk_fid: 352 v9fs_t_clunk(v9ses, nfid); 353 354error: 355 kfree(fcall); 356 return ERR_PTR(err); 357} 358 359static struct inode * 360v9fs_inode_from_fid(struct v9fs_session_info *v9ses, u32 fid, 361 struct super_block *sb) 362{ 363 int err, umode; 364 struct inode *ret; 365 struct v9fs_fcall *fcall; 366 367 ret = NULL; 368 err = v9fs_t_stat(v9ses, fid, &fcall); 369 if (err) { 370 PRINT_FCALL_ERROR("stat error", fcall); 371 goto error; 372 } 373 374 umode = p9mode2unixmode(v9ses, fcall->params.rstat.stat.mode); 375 ret = v9fs_get_inode(sb, umode); 376 if (IS_ERR(ret)) { 377 err = PTR_ERR(ret); 378 ret = NULL; 379 goto error; 380 } 381 382 v9fs_stat2inode(&fcall->params.rstat.stat, ret, sb); 383 kfree(fcall); 384 return ret; 385 386error: 387 kfree(fcall); 388 if (ret) 389 iput(ret); 390 391 return ERR_PTR(err); 392} 393 394/** 395 * v9fs_remove - helper function to remove files and directories 396 * @dir: directory inode that is being deleted 397 * @file: dentry that is being deleted 398 * @rmdir: removing a directory 399 * 400 */ 401 402static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir) 403{ 404 struct v9fs_fcall *fcall = NULL; 405 struct super_block *sb = NULL; 406 struct v9fs_session_info *v9ses = NULL; 407 struct v9fs_fid *v9fid = NULL; 408 struct inode *file_inode = NULL; 409 int fid = -1; 410 int result = 0; 411 412 dprintk(DEBUG_VFS, "inode: %p dentry: %p rmdir: %d\n", dir, file, 413 rmdir); 414 415 file_inode = file->d_inode; 416 sb = file_inode->i_sb; 417 v9ses = v9fs_inode2v9ses(file_inode); 418 v9fid = v9fs_fid_clone(file); 419 if(IS_ERR(v9fid)) 420 return PTR_ERR(v9fid); 421 422 fid = v9fid->fid; 423 if (fid < 0) { 424 dprintk(DEBUG_ERROR, "inode #%lu, no fid!\n", 425 file_inode->i_ino); 426 return -EBADF; 427 } 428 429 result = v9fs_t_remove(v9ses, fid, &fcall); 430 if (result < 0) { 431 PRINT_FCALL_ERROR("remove fails", fcall); 432 goto Error; 433 } 434 435 v9fs_put_idpool(fid, &v9ses->fidpool); 436 v9fs_fid_destroy(v9fid); 437 438Error: 439 kfree(fcall); 440 return result; 441} 442 443static int 444v9fs_open_created(struct inode *inode, struct file *file) 445{ 446 return 0; 447} 448 449/** 450 * v9fs_vfs_create - VFS hook to create files 451 * @inode: directory inode that is being deleted 452 * @dentry: dentry that is being deleted 453 * @mode: create permissions 454 * @nd: path information 455 * 456 */ 457 458static int 459v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode, 460 struct nameidata *nd) 461{ 462 int err; 463 u32 fid, perm, iounit; 464 int flags; 465 struct v9fs_session_info *v9ses; 466 struct v9fs_fid *dfid, *vfid, *ffid; 467 struct inode *inode; 468 struct v9fs_qid qid; 469 struct file *filp; 470 471 inode = NULL; 472 vfid = NULL; 473 v9ses = v9fs_inode2v9ses(dir); 474 dfid = v9fs_fid_clone(dentry->d_parent); 475 if(IS_ERR(dfid)) { 476 err = PTR_ERR(dfid); 477 goto error; 478 } 479 480 perm = unixmode2p9mode(v9ses, mode); 481 if (nd && nd->flags & LOOKUP_OPEN) 482 flags = nd->intent.open.flags - 1; 483 else 484 flags = O_RDWR; 485 486 err = v9fs_create(v9ses, dfid->fid, (char *) dentry->d_name.name, 487 perm, v9fs_uflags2omode(flags), NULL, &fid, &qid, &iounit); 488 489 if (err) 490 goto clunk_dfid; 491 492 vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); 493 v9fs_fid_clunk(v9ses, dfid); 494 if (IS_ERR(vfid)) { 495 err = PTR_ERR(vfid); 496 vfid = NULL; 497 goto error; 498 } 499 500 inode = v9fs_inode_from_fid(v9ses, vfid->fid, dir->i_sb); 501 if (IS_ERR(inode)) { 502 err = PTR_ERR(inode); 503 inode = NULL; 504 goto error; 505 } 506 507 if(v9ses->cache) 508 dentry->d_op = &v9fs_cached_dentry_operations; 509 else 510 dentry->d_op = &v9fs_dentry_operations; 511 d_instantiate(dentry, inode); 512 513 if (nd && nd->flags & LOOKUP_OPEN) { 514 ffid = v9fs_fid_create(v9ses, fid); 515 if (!ffid) 516 return -ENOMEM; 517 518 filp = lookup_instantiate_filp(nd, dentry, v9fs_open_created); 519 if (IS_ERR(filp)) { 520 v9fs_fid_destroy(ffid); 521 return PTR_ERR(filp); 522 } 523 524 ffid->rdir_pos = 0; 525 ffid->rdir_fcall = NULL; 526 ffid->fidopen = 1; 527 ffid->iounit = iounit; 528 ffid->filp = filp; 529 filp->private_data = ffid; 530 } 531 532 return 0; 533 534clunk_dfid: 535 v9fs_fid_clunk(v9ses, dfid); 536 537error: 538 if (vfid) 539 v9fs_fid_destroy(vfid); 540 541 return err; 542} 543 544/** 545 * v9fs_vfs_mkdir - VFS mkdir hook to create a directory 546 * @inode: inode that is being unlinked 547 * @dentry: dentry that is being unlinked 548 * @mode: mode for new directory 549 * 550 */ 551 552static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) 553{ 554 int err; 555 u32 fid, perm; 556 struct v9fs_session_info *v9ses; 557 struct v9fs_fid *dfid, *vfid; 558 struct inode *inode; 559 560 inode = NULL; 561 vfid = NULL; 562 v9ses = v9fs_inode2v9ses(dir); 563 dfid = v9fs_fid_clone(dentry->d_parent); 564 if(IS_ERR(dfid)) { 565 err = PTR_ERR(dfid); 566 goto error; 567 } 568 569 perm = unixmode2p9mode(v9ses, mode | S_IFDIR); 570 571 err = v9fs_create(v9ses, dfid->fid, (char *) dentry->d_name.name, 572 perm, V9FS_OREAD, NULL, &fid, NULL, NULL); 573 574 if (err) { 575 dprintk(DEBUG_ERROR, "create error %d\n", err); 576 goto clean_up_dfid; 577 } 578 579 vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); 580 if (IS_ERR(vfid)) { 581 err = PTR_ERR(vfid); 582 vfid = NULL; 583 goto clean_up_dfid; 584 } 585 586 v9fs_fid_clunk(v9ses, dfid); 587 inode = v9fs_inode_from_fid(v9ses, vfid->fid, dir->i_sb); 588 if (IS_ERR(inode)) { 589 err = PTR_ERR(inode); 590 inode = NULL; 591 v9fs_fid_destroy(vfid); 592 goto error; 593 } 594 595 if(v9ses->cache) 596 dentry->d_op = &v9fs_cached_dentry_operations; 597 else 598 dentry->d_op = &v9fs_dentry_operations; 599 d_instantiate(dentry, inode); 600 return 0; 601 602clean_up_dfid: 603 v9fs_fid_clunk(v9ses, dfid); 604 605error: 606 return err; 607} 608 609/** 610 * v9fs_vfs_lookup - VFS lookup hook to "walk" to a new inode 611 * @dir: inode that is being walked from 612 * @dentry: dentry that is being walked to? 613 * @nameidata: path data 614 * 615 */ 616 617static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, 618 struct nameidata *nameidata) 619{ 620 struct super_block *sb; 621 struct v9fs_session_info *v9ses; 622 struct v9fs_fid *dirfid; 623 struct v9fs_fid *fid; 624 struct inode *inode; 625 struct v9fs_fcall *fcall = NULL; 626 int dirfidnum = -1; 627 int newfid = -1; 628 int result = 0; 629 630 dprintk(DEBUG_VFS, "dir: %p dentry: (%s) %p nameidata: %p\n", 631 dir, dentry->d_name.name, dentry, nameidata); 632 633 sb = dir->i_sb; 634 v9ses = v9fs_inode2v9ses(dir); 635 dirfid = v9fs_fid_lookup(dentry->d_parent); 636 637 if(IS_ERR(dirfid)) 638 return ERR_PTR(PTR_ERR(dirfid)); 639 640 dirfidnum = dirfid->fid; 641 642 newfid = v9fs_get_idpool(&v9ses->fidpool); 643 if (newfid < 0) { 644 eprintk(KERN_WARNING, "newfid fails!\n"); 645 result = -ENOSPC; 646 goto Release_Dirfid; 647 } 648 649 result = v9fs_t_walk(v9ses, dirfidnum, newfid, 650 (char *)dentry->d_name.name, &fcall); 651 652 up(&dirfid->lock); 653 654 if (result < 0) { 655 if (fcall && fcall->id == RWALK) 656 v9fs_t_clunk(v9ses, newfid); 657 else 658 v9fs_put_idpool(newfid, &v9ses->fidpool); 659 660 if (result == -ENOENT) { 661 d_add(dentry, NULL); 662 dprintk(DEBUG_VFS, 663 "Return negative dentry %p count %d\n", 664 dentry, atomic_read(&dentry->d_count)); 665 kfree(fcall); 666 return NULL; 667 } 668 dprintk(DEBUG_ERROR, "walk error:%d\n", result); 669 goto FreeFcall; 670 } 671 kfree(fcall); 672 673 result = v9fs_t_stat(v9ses, newfid, &fcall); 674 if (result < 0) { 675 dprintk(DEBUG_ERROR, "stat error\n"); 676 goto FreeFcall; 677 } 678 679 inode = v9fs_get_inode(sb, p9mode2unixmode(v9ses, 680 fcall->params.rstat.stat.mode)); 681 682 if (IS_ERR(inode) && (PTR_ERR(inode) == -ENOSPC)) { 683 eprintk(KERN_WARNING, "inode alloc failes, returns %ld\n", 684 PTR_ERR(inode)); 685 686 result = -ENOSPC; 687 goto FreeFcall; 688 } 689 690 inode->i_ino = v9fs_qid2ino(&fcall->params.rstat.stat.qid); 691 692 fid = v9fs_fid_create(v9ses, newfid); 693 if (fid == NULL) { 694 dprintk(DEBUG_ERROR, "couldn't insert\n"); 695 result = -ENOMEM; 696 goto FreeFcall; 697 } 698 699 result = v9fs_fid_insert(fid, dentry); 700 if (result < 0) 701 goto FreeFcall; 702 703 fid->qid = fcall->params.rstat.stat.qid; 704 v9fs_stat2inode(&fcall->params.rstat.stat, inode, inode->i_sb); 705 if((fid->qid.version)&&(v9ses->cache)) 706 dentry->d_op = &v9fs_cached_dentry_operations; 707 else 708 dentry->d_op = &v9fs_dentry_operations; 709 710 d_add(dentry, inode); 711 kfree(fcall); 712 713 return NULL; 714 715Release_Dirfid: 716 up(&dirfid->lock); 717 718FreeFcall: 719 kfree(fcall); 720 721 return ERR_PTR(result); 722} 723 724/** 725 * v9fs_vfs_unlink - VFS unlink hook to delete an inode 726 * @i: inode that is being unlinked 727 * @d: dentry that is being unlinked 728 * 729 */ 730 731static int v9fs_vfs_unlink(struct inode *i, struct dentry *d) 732{ 733 return v9fs_remove(i, d, 0); 734} 735 736/** 737 * v9fs_vfs_rmdir - VFS unlink hook to delete a directory 738 * @i: inode that is being unlinked 739 * @d: dentry that is being unlinked 740 * 741 */ 742 743static int v9fs_vfs_rmdir(struct inode *i, struct dentry *d) 744{ 745 return v9fs_remove(i, d, 1); 746} 747 748/** 749 * v9fs_vfs_rename - VFS hook to rename an inode 750 * @old_dir: old dir inode 751 * @old_dentry: old dentry 752 * @new_dir: new dir inode 753 * @new_dentry: new dentry 754 * 755 */ 756 757static int 758v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, 759 struct inode *new_dir, struct dentry *new_dentry) 760{ 761 struct inode *old_inode = old_dentry->d_inode; 762 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(old_inode); 763 struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry); 764 struct v9fs_fid *olddirfid; 765 struct v9fs_fid *newdirfid; 766 struct v9fs_wstat wstat; 767 struct v9fs_fcall *fcall = NULL; 768 int fid = -1; 769 int olddirfidnum = -1; 770 int newdirfidnum = -1; 771 int retval = 0; 772 773 dprintk(DEBUG_VFS, "\n"); 774 775 if(IS_ERR(oldfid)) 776 return PTR_ERR(oldfid); 777 778 olddirfid = v9fs_fid_clone(old_dentry->d_parent); 779 if(IS_ERR(olddirfid)) { 780 retval = PTR_ERR(olddirfid); 781 goto Release_lock; 782 } 783 784 newdirfid = v9fs_fid_clone(new_dentry->d_parent); 785 if(IS_ERR(newdirfid)) { 786 retval = PTR_ERR(newdirfid); 787 goto Clunk_olddir; 788 } 789 790 /* 9P can only handle file rename in the same directory */ 791 if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) { 792 dprintk(DEBUG_ERROR, "old dir and new dir are different\n"); 793 retval = -EXDEV; 794 goto Clunk_newdir; 795 } 796 797 fid = oldfid->fid; 798 olddirfidnum = olddirfid->fid; 799 newdirfidnum = newdirfid->fid; 800 801 if (fid < 0) { 802 dprintk(DEBUG_ERROR, "no fid for old file #%lu\n", 803 old_inode->i_ino); 804 retval = -EBADF; 805 goto Clunk_newdir; 806 } 807 808 v9fs_blank_wstat(&wstat); 809 wstat.muid = v9ses->name; 810 wstat.name = (char *) new_dentry->d_name.name; 811 812 retval = v9fs_t_wstat(v9ses, fid, &wstat, &fcall); 813 814 if (retval < 0) 815 PRINT_FCALL_ERROR("wstat error", fcall); 816 817 kfree(fcall); 818 819Clunk_newdir: 820 v9fs_fid_clunk(v9ses, newdirfid); 821 822Clunk_olddir: 823 v9fs_fid_clunk(v9ses, olddirfid); 824 825Release_lock: 826 up(&oldfid->lock); 827 828 return retval; 829} 830 831/** 832 * v9fs_vfs_getattr - retrieve file metadata 833 * @mnt - mount information 834 * @dentry - file to get attributes on 835 * @stat - metadata structure to populate 836 * 837 */ 838 839static int 840v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, 841 struct kstat *stat) 842{ 843 struct v9fs_fcall *fcall = NULL; 844 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); 845 struct v9fs_fid *fid = v9fs_fid_clone(dentry); 846 int err = -EPERM; 847 848 dprintk(DEBUG_VFS, "dentry: %p\n", dentry); 849 if(IS_ERR(fid)) 850 return PTR_ERR(fid); 851 852 err = v9fs_t_stat(v9ses, fid->fid, &fcall); 853 854 if (err < 0) 855 dprintk(DEBUG_ERROR, "stat error\n"); 856 else { 857 v9fs_stat2inode(&fcall->params.rstat.stat, dentry->d_inode, 858 dentry->d_inode->i_sb); 859 generic_fillattr(dentry->d_inode, stat); 860 } 861 862 kfree(fcall); 863 v9fs_fid_clunk(v9ses, fid); 864 return err; 865} 866 867/** 868 * v9fs_vfs_setattr - set file metadata 869 * @dentry: file whose metadata to set 870 * @iattr: metadata assignment structure 871 * 872 */ 873 874static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) 875{ 876 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); 877 struct v9fs_fid *fid = v9fs_fid_clone(dentry); 878 struct v9fs_fcall *fcall = NULL; 879 struct v9fs_wstat wstat; 880 int res = -EPERM; 881 882 dprintk(DEBUG_VFS, "\n"); 883 if(IS_ERR(fid)) 884 return PTR_ERR(fid); 885 886 v9fs_blank_wstat(&wstat); 887 if (iattr->ia_valid & ATTR_MODE) 888 wstat.mode = unixmode2p9mode(v9ses, iattr->ia_mode); 889 890 if (iattr->ia_valid & ATTR_MTIME) 891 wstat.mtime = iattr->ia_mtime.tv_sec; 892 893 if (iattr->ia_valid & ATTR_ATIME) 894 wstat.atime = iattr->ia_atime.tv_sec; 895 896 if (iattr->ia_valid & ATTR_SIZE) 897 wstat.length = iattr->ia_size; 898 899 if (v9ses->extended) { 900 if (iattr->ia_valid & ATTR_UID) 901 wstat.n_uid = iattr->ia_uid; 902 903 if (iattr->ia_valid & ATTR_GID) 904 wstat.n_gid = iattr->ia_gid; 905 } 906 907 res = v9fs_t_wstat(v9ses, fid->fid, &wstat, &fcall); 908 909 if (res < 0) 910 PRINT_FCALL_ERROR("wstat error", fcall); 911 912 kfree(fcall); 913 if (res >= 0) 914 res = inode_setattr(dentry->d_inode, iattr); 915 916 v9fs_fid_clunk(v9ses, fid); 917 return res; 918} 919 920/** 921 * v9fs_stat2inode - populate an inode structure with mistat info 922 * @stat: Plan 9 metadata (mistat) structure 923 * @inode: inode to populate 924 * @sb: superblock of filesystem 925 * 926 */ 927 928void 929v9fs_stat2inode(struct v9fs_stat *stat, struct inode *inode, 930 struct super_block *sb) 931{ 932 int n; 933 char ext[32]; 934 struct v9fs_session_info *v9ses = sb->s_fs_info; 935 936 inode->i_nlink = 1; 937 938 inode->i_atime.tv_sec = stat->atime; 939 inode->i_mtime.tv_sec = stat->mtime; 940 inode->i_ctime.tv_sec = stat->mtime; 941 942 inode->i_uid = v9ses->uid; 943 inode->i_gid = v9ses->gid; 944 945 if (v9ses->extended) { 946 inode->i_uid = stat->n_uid; 947 inode->i_gid = stat->n_gid; 948 } 949 950 inode->i_mode = p9mode2unixmode(v9ses, stat->mode); 951 if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode))) { 952 char type = 0; 953 int major = -1; 954 int minor = -1; 955 956 n = stat->extension.len; 957 if (n > sizeof(ext)-1) 958 n = sizeof(ext)-1; 959 memmove(ext, stat->extension.str, n); 960 ext[n] = 0; 961 sscanf(ext, "%c %u %u", &type, &major, &minor); 962 switch (type) { 963 case 'c': 964 inode->i_mode &= ~S_IFBLK; 965 inode->i_mode |= S_IFCHR; 966 break; 967 case 'b': 968 break; 969 default: 970 dprintk(DEBUG_ERROR, "Unknown special type %c (%.*s)\n", 971 type, stat->extension.len, stat->extension.str); 972 }; 973 inode->i_rdev = MKDEV(major, minor); 974 } else 975 inode->i_rdev = 0; 976 977 inode->i_size = stat->length; 978 979 inode->i_blocks = 980 (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; 981} 982 983/** 984 * v9fs_qid2ino - convert qid into inode number 985 * @qid: qid to hash 986 * 987 * BUG: potential for inode number collisions? 988 */ 989 990ino_t v9fs_qid2ino(struct v9fs_qid *qid) 991{ 992 u64 path = qid->path + 2; 993 ino_t i = 0; 994 995 if (sizeof(ino_t) == sizeof(path)) 996 memcpy(&i, &path, sizeof(ino_t)); 997 else 998 i = (ino_t) (path ^ (path >> 32)); 999 1000 return i; 1001} 1002 1003/** 1004 * v9fs_readlink - read a symlink's location (internal version) 1005 * @dentry: dentry for symlink 1006 * @buffer: buffer to load symlink location into 1007 * @buflen: length of buffer 1008 * 1009 */ 1010 1011static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) 1012{ 1013 int retval = -EPERM; 1014 1015 struct v9fs_fcall *fcall = NULL; 1016 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); 1017 struct v9fs_fid *fid = v9fs_fid_clone(dentry); 1018 1019 if(IS_ERR(fid)) 1020 return PTR_ERR(fid); 1021 1022 if (!v9ses->extended) { 1023 retval = -EBADF; 1024 dprintk(DEBUG_ERROR, "not extended\n"); 1025 goto ClunkFid; 1026 } 1027 1028 dprintk(DEBUG_VFS, " %s\n", dentry->d_name.name); 1029 retval = v9fs_t_stat(v9ses, fid->fid, &fcall); 1030 1031 if (retval < 0) { 1032 dprintk(DEBUG_ERROR, "stat error\n"); 1033 goto FreeFcall; 1034 } 1035 1036 if (!fcall) { 1037 retval = -EIO; 1038 goto ClunkFid; 1039 } 1040 1041 if (!(fcall->params.rstat.stat.mode & V9FS_DMSYMLINK)) { 1042 retval = -EINVAL; 1043 goto FreeFcall; 1044 } 1045 1046 /* copy extension buffer into buffer */ 1047 if (fcall->params.rstat.stat.extension.len < buflen) 1048 buflen = fcall->params.rstat.stat.extension.len + 1; 1049 1050 memmove(buffer, fcall->params.rstat.stat.extension.str, buflen - 1); 1051 buffer[buflen-1] = 0; 1052 1053 dprintk(DEBUG_ERROR, "%s -> %.*s (%s)\n", dentry->d_name.name, fcall->params.rstat.stat.extension.len, 1054 fcall->params.rstat.stat.extension.str, buffer); 1055 retval = buflen; 1056 1057FreeFcall: 1058 kfree(fcall); 1059 1060ClunkFid: 1061 v9fs_fid_clunk(v9ses, fid); 1062 1063 return retval; 1064} 1065 1066/** 1067 * v9fs_vfs_readlink - read a symlink's location 1068 * @dentry: dentry for symlink 1069 * @buf: buffer to load symlink location into 1070 * @buflen: length of buffer 1071 * 1072 */ 1073 1074static int v9fs_vfs_readlink(struct dentry *dentry, char __user * buffer, 1075 int buflen) 1076{ 1077 int retval; 1078 int ret; 1079 char *link = __getname(); 1080 1081 if (unlikely(!link)) 1082 return -ENOMEM; 1083 1084 if (buflen > PATH_MAX) 1085 buflen = PATH_MAX; 1086 1087 dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); 1088 1089 retval = v9fs_readlink(dentry, link, buflen); 1090 1091 if (retval > 0) { 1092 if ((ret = copy_to_user(buffer, link, retval)) != 0) { 1093 dprintk(DEBUG_ERROR, "problem copying to user: %d\n", 1094 ret); 1095 retval = ret; 1096 } 1097 } 1098 1099 __putname(link); 1100 return retval; 1101} 1102 1103/** 1104 * v9fs_vfs_follow_link - follow a symlink path 1105 * @dentry: dentry for symlink 1106 * @nd: nameidata 1107 * 1108 */ 1109 1110static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd) 1111{ 1112 int len = 0; 1113 char *link = __getname(); 1114 1115 dprintk(DEBUG_VFS, "%s n", dentry->d_name.name); 1116 1117 if (!link) 1118 link = ERR_PTR(-ENOMEM); 1119 else { 1120 len = v9fs_readlink(dentry, link, PATH_MAX); 1121 1122 if (len < 0) { 1123 __putname(link); 1124 link = ERR_PTR(len); 1125 } else 1126 link[len] = 0; 1127 } 1128 nd_set_link(nd, link); 1129 1130 return NULL; 1131} 1132 1133/** 1134 * v9fs_vfs_put_link - release a symlink path 1135 * @dentry: dentry for symlink 1136 * @nd: nameidata 1137 * 1138 */ 1139 1140static void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p) 1141{ 1142 char *s = nd_get_link(nd); 1143 1144 dprintk(DEBUG_VFS, " %s %s\n", dentry->d_name.name, s); 1145 if (!IS_ERR(s)) 1146 __putname(s); 1147} 1148 1149static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry, 1150 int mode, const char *extension) 1151{ 1152 int err; 1153 u32 fid, perm; 1154 struct v9fs_session_info *v9ses; 1155 struct v9fs_fid *dfid, *vfid = NULL; 1156 struct inode *inode = NULL; 1157 1158 v9ses = v9fs_inode2v9ses(dir); 1159 if (!v9ses->extended) { 1160 dprintk(DEBUG_ERROR, "not extended\n"); 1161 return -EPERM; 1162 } 1163 1164 dfid = v9fs_fid_clone(dentry->d_parent); 1165 if(IS_ERR(dfid)) { 1166 err = PTR_ERR(dfid); 1167 goto error; 1168 } 1169 1170 perm = unixmode2p9mode(v9ses, mode); 1171 1172 err = v9fs_create(v9ses, dfid->fid, (char *) dentry->d_name.name, 1173 perm, V9FS_OREAD, (char *) extension, &fid, NULL, NULL); 1174 1175 if (err) 1176 goto clunk_dfid; 1177 1178 err = v9fs_t_clunk(v9ses, fid); 1179 if (err) 1180 goto clunk_dfid; 1181 1182 vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); 1183 if (IS_ERR(vfid)) { 1184 err = PTR_ERR(vfid); 1185 vfid = NULL; 1186 goto clunk_dfid; 1187 } 1188 1189 inode = v9fs_inode_from_fid(v9ses, vfid->fid, dir->i_sb); 1190 if (IS_ERR(inode)) { 1191 err = PTR_ERR(inode); 1192 inode = NULL; 1193 goto free_vfid; 1194 } 1195 1196 if(v9ses->cache) 1197 dentry->d_op = &v9fs_cached_dentry_operations; 1198 else 1199 dentry->d_op = &v9fs_dentry_operations; 1200 d_instantiate(dentry, inode); 1201 return 0; 1202 1203free_vfid: 1204 v9fs_fid_destroy(vfid); 1205 1206clunk_dfid: 1207 v9fs_fid_clunk(v9ses, dfid); 1208 1209error: 1210 return err; 1211 1212} 1213 1214/** 1215 * v9fs_vfs_symlink - helper function to create symlinks 1216 * @dir: directory inode containing symlink 1217 * @dentry: dentry for symlink 1218 * @symname: symlink data 1219 * 1220 * See 9P2000.u RFC for more information 1221 * 1222 */ 1223 1224static int 1225v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) 1226{ 1227 dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, 1228 symname); 1229 1230 return v9fs_vfs_mkspecial(dir, dentry, S_IFLNK, symname); 1231} 1232 1233/** 1234 * v9fs_vfs_link - create a hardlink 1235 * @old_dentry: dentry for file to link to 1236 * @dir: inode destination for new link 1237 * @dentry: dentry for link 1238 * 1239 */ 1240 1241 1242static int 1243v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, 1244 struct dentry *dentry) 1245{ 1246 int retval; 1247 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); 1248 struct v9fs_fid *oldfid; 1249 char *name; 1250 1251 dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, 1252 old_dentry->d_name.name); 1253 1254 oldfid = v9fs_fid_clone(old_dentry); 1255 if(IS_ERR(oldfid)) 1256 return PTR_ERR(oldfid); 1257 1258 name = __getname(); 1259 if (unlikely(!name)) { 1260 retval = -ENOMEM; 1261 goto clunk_fid; 1262 } 1263 1264 sprintf(name, "%d\n", oldfid->fid); 1265 retval = v9fs_vfs_mkspecial(dir, dentry, V9FS_DMLINK, name); 1266 __putname(name); 1267 1268clunk_fid: 1269 v9fs_fid_clunk(v9ses, oldfid); 1270 return retval; 1271} 1272 1273/** 1274 * v9fs_vfs_mknod - create a special file 1275 * @dir: inode destination for new link 1276 * @dentry: dentry for file 1277 * @mode: mode for creation 1278 * @dev_t: device associated with special file 1279 * 1280 */ 1281 1282static int 1283v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) 1284{ 1285 int retval; 1286 char *name; 1287 1288 dprintk(DEBUG_VFS, " %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino, 1289 dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev)); 1290 1291 if (!new_valid_dev(rdev)) 1292 return -EINVAL; 1293 1294 name = __getname(); 1295 if (!name) 1296 return -ENOMEM; 1297 /* build extension */ 1298 if (S_ISBLK(mode)) 1299 sprintf(name, "b %u %u", MAJOR(rdev), MINOR(rdev)); 1300 else if (S_ISCHR(mode)) 1301 sprintf(name, "c %u %u", MAJOR(rdev), MINOR(rdev)); 1302 else if (S_ISFIFO(mode)) 1303 *name = 0; 1304 else { 1305 __putname(name); 1306 return -EINVAL; 1307 } 1308 1309 retval = v9fs_vfs_mkspecial(dir, dentry, mode, name); 1310 __putname(name); 1311 1312 return retval; 1313} 1314 1315static const struct inode_operations v9fs_dir_inode_operations_ext = { 1316 .create = v9fs_vfs_create, 1317 .lookup = v9fs_vfs_lookup, 1318 .symlink = v9fs_vfs_symlink, 1319 .link = v9fs_vfs_link, 1320 .unlink = v9fs_vfs_unlink, 1321 .mkdir = v9fs_vfs_mkdir, 1322 .rmdir = v9fs_vfs_rmdir, 1323 .mknod = v9fs_vfs_mknod, 1324 .rename = v9fs_vfs_rename, 1325 .readlink = v9fs_vfs_readlink, 1326 .getattr = v9fs_vfs_getattr, 1327 .setattr = v9fs_vfs_setattr, 1328}; 1329 1330static const struct inode_operations v9fs_dir_inode_operations = { 1331 .create = v9fs_vfs_create, 1332 .lookup = v9fs_vfs_lookup, 1333 .unlink = v9fs_vfs_unlink, 1334 .mkdir = v9fs_vfs_mkdir, 1335 .rmdir = v9fs_vfs_rmdir, 1336 .mknod = v9fs_vfs_mknod, 1337 .rename = v9fs_vfs_rename, 1338 .getattr = v9fs_vfs_getattr, 1339 .setattr = v9fs_vfs_setattr, 1340}; 1341 1342static const struct inode_operations v9fs_file_inode_operations = { 1343 .getattr = v9fs_vfs_getattr, 1344 .setattr = v9fs_vfs_setattr, 1345}; 1346 1347static const struct inode_operations v9fs_symlink_inode_operations = { 1348 .readlink = v9fs_vfs_readlink, 1349 .follow_link = v9fs_vfs_follow_link, 1350 .put_link = v9fs_vfs_put_link, 1351 .getattr = v9fs_vfs_getattr, 1352 .setattr = v9fs_vfs_setattr, 1353}; 1354