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 #include <net/9p/9p.h> 38 #include <net/9p/client.h> 39 40 #include "v9fs.h" 41 #include "v9fs_vfs.h" 42 #include "fid.h" 43 44 static const struct inode_operations v9fs_dir_inode_operations; 45 static const struct inode_operations v9fs_dir_inode_operations_ext; 46 static const struct inode_operations v9fs_file_inode_operations; 47 static 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 56 static int unixmode2p9mode(struct v9fs_session_info *v9ses, int mode) 57 { 58 int res; 59 res = mode & 0777; 60 if (S_ISDIR(mode)) 61 res |= P9_DMDIR; 62 if (v9fs_extended(v9ses)) { 63 if (S_ISLNK(mode)) 64 res |= P9_DMSYMLINK; 65 if (v9ses->nodev == 0) { 66 if (S_ISSOCK(mode)) 67 res |= P9_DMSOCKET; 68 if (S_ISFIFO(mode)) 69 res |= P9_DMNAMEDPIPE; 70 if (S_ISBLK(mode)) 71 res |= P9_DMDEVICE; 72 if (S_ISCHR(mode)) 73 res |= P9_DMDEVICE; 74 } 75 76 if ((mode & S_ISUID) == S_ISUID) 77 res |= P9_DMSETUID; 78 if ((mode & S_ISGID) == S_ISGID) 79 res |= P9_DMSETGID; 80 if ((mode & P9_DMLINK)) 81 res |= P9_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 94 static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode) 95 { 96 int res; 97 98 res = mode & 0777; 99 100 if ((mode & P9_DMDIR) == P9_DMDIR) 101 res |= S_IFDIR; 102 else if ((mode & P9_DMSYMLINK) && (v9fs_extended(v9ses))) 103 res |= S_IFLNK; 104 else if ((mode & P9_DMSOCKET) && (v9fs_extended(v9ses)) 105 && (v9ses->nodev == 0)) 106 res |= S_IFSOCK; 107 else if ((mode & P9_DMNAMEDPIPE) && (v9fs_extended(v9ses)) 108 && (v9ses->nodev == 0)) 109 res |= S_IFIFO; 110 else if ((mode & P9_DMDEVICE) && (v9fs_extended(v9ses)) 111 && (v9ses->nodev == 0)) 112 res |= S_IFBLK; 113 else 114 res |= S_IFREG; 115 116 if (v9fs_extended(v9ses)) { 117 if ((mode & P9_DMSETUID) == P9_DMSETUID) 118 res |= S_ISUID; 119 120 if ((mode & P9_DMSETGID) == P9_DMSETGID) 121 res |= S_ISGID; 122 } 123 124 return res; 125 } 126 127 int v9fs_uflags2omode(int uflags) 128 { 129 int ret; 130 131 ret = 0; 132 switch (uflags&3) { 133 default: 134 case O_RDONLY: 135 ret = P9_OREAD; 136 break; 137 138 case O_WRONLY: 139 ret = P9_OWRITE; 140 break; 141 142 case O_RDWR: 143 ret = P9_ORDWR; 144 break; 145 } 146 147 if (uflags & O_EXCL) 148 ret |= P9_OEXCL; 149 150 if (uflags & O_TRUNC) 151 ret |= P9_OTRUNC; 152 153 if (uflags & O_APPEND) 154 ret |= P9_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 166 static void 167 v9fs_blank_wstat(struct p9_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 195 struct 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 P9_DPRINTK(P9_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 (!v9fs_extended(v9ses)) { 218 P9_DPRINTK(P9_DEBUG_ERROR, 219 "special files without extended mode\n"); 220 return ERR_PTR(-EINVAL); 221 } 222 init_special_inode(inode, inode->i_mode, 223 inode->i_rdev); 224 break; 225 case S_IFREG: 226 inode->i_op = &v9fs_file_inode_operations; 227 inode->i_fop = &v9fs_file_operations; 228 break; 229 case S_IFLNK: 230 if (!v9fs_extended(v9ses)) { 231 P9_DPRINTK(P9_DEBUG_ERROR, 232 "extended modes used w/o 9P2000.u\n"); 233 return ERR_PTR(-EINVAL); 234 } 235 inode->i_op = &v9fs_symlink_inode_operations; 236 break; 237 case S_IFDIR: 238 inc_nlink(inode); 239 if (v9fs_extended(v9ses)) 240 inode->i_op = &v9fs_dir_inode_operations_ext; 241 else 242 inode->i_op = &v9fs_dir_inode_operations; 243 inode->i_fop = &v9fs_dir_operations; 244 break; 245 default: 246 P9_DPRINTK(P9_DEBUG_ERROR, 247 "BAD mode 0x%x S_IFMT 0x%x\n", 248 mode, mode & S_IFMT); 249 return ERR_PTR(-EINVAL); 250 } 251 } else { 252 P9_EPRINTK(KERN_WARNING, "Problem allocating inode\n"); 253 return ERR_PTR(-ENOMEM); 254 } 255 return inode; 256 } 257 258 /* 259 static struct v9fs_fid* 260 v9fs_clone_walk(struct v9fs_session_info *v9ses, u32 fid, struct dentry *dentry) 261 { 262 int err; 263 int nfid; 264 struct v9fs_fid *ret; 265 struct v9fs_fcall *fcall; 266 267 nfid = v9fs_get_idpool(&v9ses->fidpool); 268 if (nfid < 0) { 269 eprintk(KERN_WARNING, "no free fids available\n"); 270 return ERR_PTR(-ENOSPC); 271 } 272 273 err = v9fs_t_walk(v9ses, fid, nfid, (char *) dentry->d_name.name, 274 &fcall); 275 276 if (err < 0) { 277 if (fcall && fcall->id == RWALK) 278 goto clunk_fid; 279 280 PRINT_FCALL_ERROR("walk error", fcall); 281 v9fs_put_idpool(nfid, &v9ses->fidpool); 282 goto error; 283 } 284 285 kfree(fcall); 286 fcall = NULL; 287 ret = v9fs_fid_create(v9ses, nfid); 288 if (!ret) { 289 err = -ENOMEM; 290 goto clunk_fid; 291 } 292 293 err = v9fs_fid_insert(ret, dentry); 294 if (err < 0) { 295 v9fs_fid_destroy(ret); 296 goto clunk_fid; 297 } 298 299 return ret; 300 301 clunk_fid: 302 v9fs_t_clunk(v9ses, nfid); 303 304 error: 305 kfree(fcall); 306 return ERR_PTR(err); 307 } 308 */ 309 310 static struct inode * 311 v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid, 312 struct super_block *sb) 313 { 314 int err, umode; 315 struct inode *ret; 316 struct p9_stat *st; 317 318 ret = NULL; 319 st = p9_client_stat(fid); 320 if (IS_ERR(st)) { 321 err = PTR_ERR(st); 322 st = NULL; 323 goto error; 324 } 325 326 umode = p9mode2unixmode(v9ses, st->mode); 327 ret = v9fs_get_inode(sb, umode); 328 if (IS_ERR(ret)) { 329 err = PTR_ERR(ret); 330 ret = NULL; 331 goto error; 332 } 333 334 v9fs_stat2inode(st, ret, sb); 335 ret->i_ino = v9fs_qid2ino(&st->qid); 336 kfree(st); 337 return ret; 338 339 error: 340 kfree(st); 341 if (ret) 342 iput(ret); 343 344 return ERR_PTR(err); 345 } 346 347 /** 348 * v9fs_remove - helper function to remove files and directories 349 * @dir: directory inode that is being deleted 350 * @file: dentry that is being deleted 351 * @rmdir: removing a directory 352 * 353 */ 354 355 static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir) 356 { 357 struct inode *file_inode; 358 struct v9fs_session_info *v9ses; 359 struct p9_fid *v9fid; 360 361 P9_DPRINTK(P9_DEBUG_VFS, "inode: %p dentry: %p rmdir: %d\n", dir, file, 362 rmdir); 363 364 file_inode = file->d_inode; 365 v9ses = v9fs_inode2v9ses(file_inode); 366 v9fid = v9fs_fid_clone(file); 367 if (IS_ERR(v9fid)) 368 return PTR_ERR(v9fid); 369 370 return p9_client_remove(v9fid); 371 } 372 373 static int 374 v9fs_open_created(struct inode *inode, struct file *file) 375 { 376 return 0; 377 } 378 379 380 /** 381 * v9fs_create - Create a file 382 * @dentry: dentry that is being created 383 * @perm: create permissions 384 * @mode: open mode 385 * 386 */ 387 static struct p9_fid * 388 v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir, 389 struct dentry *dentry, char *extension, u32 perm, u8 mode) 390 { 391 int err; 392 char *name; 393 struct p9_fid *dfid, *ofid, *fid; 394 struct inode *inode; 395 396 err = 0; 397 ofid = NULL; 398 fid = NULL; 399 name = (char *) dentry->d_name.name; 400 dfid = v9fs_fid_clone(dentry->d_parent); 401 if (IS_ERR(dfid)) { 402 err = PTR_ERR(dfid); 403 dfid = NULL; 404 goto error; 405 } 406 407 /* clone a fid to use for creation */ 408 ofid = p9_client_walk(dfid, 0, NULL, 1); 409 if (IS_ERR(ofid)) { 410 err = PTR_ERR(ofid); 411 ofid = NULL; 412 goto error; 413 } 414 415 err = p9_client_fcreate(ofid, name, perm, mode, extension); 416 if (err < 0) 417 goto error; 418 419 /* now walk from the parent so we can get unopened fid */ 420 fid = p9_client_walk(dfid, 1, &name, 0); 421 if (IS_ERR(fid)) { 422 err = PTR_ERR(fid); 423 fid = NULL; 424 goto error; 425 } else 426 dfid = NULL; 427 428 /* instantiate inode and assign the unopened fid to the dentry */ 429 inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb); 430 if (IS_ERR(inode)) { 431 err = PTR_ERR(inode); 432 goto error; 433 } 434 435 if (v9ses->cache) 436 dentry->d_op = &v9fs_cached_dentry_operations; 437 else 438 dentry->d_op = &v9fs_dentry_operations; 439 440 d_instantiate(dentry, inode); 441 v9fs_fid_add(dentry, fid); 442 return ofid; 443 444 error: 445 if (dfid) 446 p9_client_clunk(dfid); 447 448 if (ofid) 449 p9_client_clunk(ofid); 450 451 if (fid) 452 p9_client_clunk(fid); 453 454 return ERR_PTR(err); 455 } 456 457 /** 458 * v9fs_vfs_create - VFS hook to create files 459 * @inode: directory inode that is being created 460 * @dentry: dentry that is being deleted 461 * @mode: create permissions 462 * @nd: path information 463 * 464 */ 465 466 static int 467 v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode, 468 struct nameidata *nd) 469 { 470 int err; 471 u32 perm; 472 int flags; 473 struct v9fs_session_info *v9ses; 474 struct p9_fid *fid; 475 struct file *filp; 476 477 err = 0; 478 fid = NULL; 479 v9ses = v9fs_inode2v9ses(dir); 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 fid = v9fs_create(v9ses, dir, dentry, NULL, perm, 487 v9fs_uflags2omode(flags)); 488 if (IS_ERR(fid)) { 489 err = PTR_ERR(fid); 490 fid = NULL; 491 goto error; 492 } 493 494 /* if we are opening a file, assign the open fid to the file */ 495 if (nd && nd->flags & LOOKUP_OPEN) { 496 filp = lookup_instantiate_filp(nd, dentry, v9fs_open_created); 497 if (IS_ERR(filp)) { 498 err = PTR_ERR(filp); 499 goto error; 500 } 501 502 filp->private_data = fid; 503 } else 504 p9_client_clunk(fid); 505 506 return 0; 507 508 error: 509 if (fid) 510 p9_client_clunk(fid); 511 512 return err; 513 } 514 515 /** 516 * v9fs_vfs_mkdir - VFS mkdir hook to create a directory 517 * @inode: inode that is being unlinked 518 * @dentry: dentry that is being unlinked 519 * @mode: mode for new directory 520 * 521 */ 522 523 static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) 524 { 525 int err; 526 u32 perm; 527 struct v9fs_session_info *v9ses; 528 struct p9_fid *fid; 529 530 P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name); 531 err = 0; 532 v9ses = v9fs_inode2v9ses(dir); 533 perm = unixmode2p9mode(v9ses, mode | S_IFDIR); 534 fid = v9fs_create(v9ses, dir, dentry, NULL, perm, P9_OREAD); 535 if (IS_ERR(fid)) { 536 err = PTR_ERR(fid); 537 fid = NULL; 538 } 539 540 if (fid) 541 p9_client_clunk(fid); 542 543 return err; 544 } 545 546 /** 547 * v9fs_vfs_lookup - VFS lookup hook to "walk" to a new inode 548 * @dir: inode that is being walked from 549 * @dentry: dentry that is being walked to? 550 * @nameidata: path data 551 * 552 */ 553 554 static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, 555 struct nameidata *nameidata) 556 { 557 struct super_block *sb; 558 struct v9fs_session_info *v9ses; 559 struct p9_fid *dfid, *fid; 560 struct inode *inode; 561 char *name; 562 int result = 0; 563 564 P9_DPRINTK(P9_DEBUG_VFS, "dir: %p dentry: (%s) %p nameidata: %p\n", 565 dir, dentry->d_name.name, dentry, nameidata); 566 567 sb = dir->i_sb; 568 v9ses = v9fs_inode2v9ses(dir); 569 dfid = v9fs_fid_lookup(dentry->d_parent); 570 if (IS_ERR(dfid)) 571 return ERR_PTR(PTR_ERR(dfid)); 572 573 name = (char *) dentry->d_name.name; 574 fid = p9_client_walk(dfid, 1, &name, 1); 575 if (IS_ERR(fid)) { 576 result = PTR_ERR(fid); 577 if (result == -ENOENT) { 578 d_add(dentry, NULL); 579 return NULL; 580 } 581 582 return ERR_PTR(result); 583 } 584 585 inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb); 586 if (IS_ERR(inode)) { 587 result = PTR_ERR(inode); 588 inode = NULL; 589 goto error; 590 } 591 592 result = v9fs_fid_add(dentry, fid); 593 if (result < 0) 594 goto error; 595 596 if ((fid->qid.version) && (v9ses->cache)) 597 dentry->d_op = &v9fs_cached_dentry_operations; 598 else 599 dentry->d_op = &v9fs_dentry_operations; 600 601 d_add(dentry, inode); 602 return NULL; 603 604 error: 605 if (fid) 606 p9_client_clunk(fid); 607 608 return ERR_PTR(result); 609 } 610 611 /** 612 * v9fs_vfs_unlink - VFS unlink hook to delete an inode 613 * @i: inode that is being unlinked 614 * @d: dentry that is being unlinked 615 * 616 */ 617 618 static int v9fs_vfs_unlink(struct inode *i, struct dentry *d) 619 { 620 return v9fs_remove(i, d, 0); 621 } 622 623 /** 624 * v9fs_vfs_rmdir - VFS unlink hook to delete a directory 625 * @i: inode that is being unlinked 626 * @d: dentry that is being unlinked 627 * 628 */ 629 630 static int v9fs_vfs_rmdir(struct inode *i, struct dentry *d) 631 { 632 return v9fs_remove(i, d, 1); 633 } 634 635 /** 636 * v9fs_vfs_rename - VFS hook to rename an inode 637 * @old_dir: old dir inode 638 * @old_dentry: old dentry 639 * @new_dir: new dir inode 640 * @new_dentry: new dentry 641 * 642 */ 643 644 static int 645 v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, 646 struct inode *new_dir, struct dentry *new_dentry) 647 { 648 struct inode *old_inode; 649 struct v9fs_session_info *v9ses; 650 struct p9_fid *oldfid; 651 struct p9_fid *olddirfid; 652 struct p9_fid *newdirfid; 653 struct p9_wstat wstat; 654 int retval; 655 656 P9_DPRINTK(P9_DEBUG_VFS, "\n"); 657 retval = 0; 658 old_inode = old_dentry->d_inode; 659 v9ses = v9fs_inode2v9ses(old_inode); 660 oldfid = v9fs_fid_lookup(old_dentry); 661 if (IS_ERR(oldfid)) 662 return PTR_ERR(oldfid); 663 664 olddirfid = v9fs_fid_clone(old_dentry->d_parent); 665 if (IS_ERR(olddirfid)) { 666 retval = PTR_ERR(olddirfid); 667 goto done; 668 } 669 670 newdirfid = v9fs_fid_clone(new_dentry->d_parent); 671 if (IS_ERR(newdirfid)) { 672 retval = PTR_ERR(newdirfid); 673 goto clunk_olddir; 674 } 675 676 /* 9P can only handle file rename in the same directory */ 677 if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) { 678 P9_DPRINTK(P9_DEBUG_ERROR, 679 "old dir and new dir are different\n"); 680 retval = -EXDEV; 681 goto clunk_newdir; 682 } 683 684 v9fs_blank_wstat(&wstat); 685 wstat.muid = v9ses->uname; 686 wstat.name = (char *) new_dentry->d_name.name; 687 retval = p9_client_wstat(oldfid, &wstat); 688 689 clunk_newdir: 690 p9_client_clunk(newdirfid); 691 692 clunk_olddir: 693 p9_client_clunk(olddirfid); 694 695 done: 696 return retval; 697 } 698 699 /** 700 * v9fs_vfs_getattr - retrieve file metadata 701 * @mnt - mount information 702 * @dentry - file to get attributes on 703 * @stat - metadata structure to populate 704 * 705 */ 706 707 static int 708 v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, 709 struct kstat *stat) 710 { 711 int err; 712 struct v9fs_session_info *v9ses; 713 struct p9_fid *fid; 714 struct p9_stat *st; 715 716 P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry); 717 err = -EPERM; 718 v9ses = v9fs_inode2v9ses(dentry->d_inode); 719 if (v9ses->cache == CACHE_LOOSE) 720 return simple_getattr(mnt, dentry, stat); 721 722 fid = v9fs_fid_lookup(dentry); 723 if (IS_ERR(fid)) 724 return PTR_ERR(fid); 725 726 st = p9_client_stat(fid); 727 if (IS_ERR(st)) 728 return PTR_ERR(st); 729 730 v9fs_stat2inode(st, dentry->d_inode, dentry->d_inode->i_sb); 731 generic_fillattr(dentry->d_inode, stat); 732 733 kfree(st); 734 return 0; 735 } 736 737 /** 738 * v9fs_vfs_setattr - set file metadata 739 * @dentry: file whose metadata to set 740 * @iattr: metadata assignment structure 741 * 742 */ 743 744 static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) 745 { 746 int retval; 747 struct v9fs_session_info *v9ses; 748 struct p9_fid *fid; 749 struct p9_wstat wstat; 750 751 P9_DPRINTK(P9_DEBUG_VFS, "\n"); 752 retval = -EPERM; 753 v9ses = v9fs_inode2v9ses(dentry->d_inode); 754 fid = v9fs_fid_lookup(dentry); 755 if(IS_ERR(fid)) 756 return PTR_ERR(fid); 757 758 v9fs_blank_wstat(&wstat); 759 if (iattr->ia_valid & ATTR_MODE) 760 wstat.mode = unixmode2p9mode(v9ses, iattr->ia_mode); 761 762 if (iattr->ia_valid & ATTR_MTIME) 763 wstat.mtime = iattr->ia_mtime.tv_sec; 764 765 if (iattr->ia_valid & ATTR_ATIME) 766 wstat.atime = iattr->ia_atime.tv_sec; 767 768 if (iattr->ia_valid & ATTR_SIZE) 769 wstat.length = iattr->ia_size; 770 771 if (v9fs_extended(v9ses)) { 772 if (iattr->ia_valid & ATTR_UID) 773 wstat.n_uid = iattr->ia_uid; 774 775 if (iattr->ia_valid & ATTR_GID) 776 wstat.n_gid = iattr->ia_gid; 777 } 778 779 retval = p9_client_wstat(fid, &wstat); 780 if (retval >= 0) 781 retval = inode_setattr(dentry->d_inode, iattr); 782 783 return retval; 784 } 785 786 /** 787 * v9fs_stat2inode - populate an inode structure with mistat info 788 * @stat: Plan 9 metadata (mistat) structure 789 * @inode: inode to populate 790 * @sb: superblock of filesystem 791 * 792 */ 793 794 void 795 v9fs_stat2inode(struct p9_stat *stat, struct inode *inode, 796 struct super_block *sb) 797 { 798 int n; 799 char ext[32]; 800 struct v9fs_session_info *v9ses = sb->s_fs_info; 801 802 inode->i_nlink = 1; 803 804 inode->i_atime.tv_sec = stat->atime; 805 inode->i_mtime.tv_sec = stat->mtime; 806 inode->i_ctime.tv_sec = stat->mtime; 807 808 inode->i_uid = v9ses->dfltuid; 809 inode->i_gid = v9ses->dfltgid; 810 811 if (v9fs_extended(v9ses)) { 812 inode->i_uid = stat->n_uid; 813 inode->i_gid = stat->n_gid; 814 } 815 816 inode->i_mode = p9mode2unixmode(v9ses, stat->mode); 817 if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode))) { 818 char type = 0; 819 int major = -1; 820 int minor = -1; 821 822 n = stat->extension.len; 823 if (n > sizeof(ext)-1) 824 n = sizeof(ext)-1; 825 memmove(ext, stat->extension.str, n); 826 ext[n] = 0; 827 sscanf(ext, "%c %u %u", &type, &major, &minor); 828 switch (type) { 829 case 'c': 830 inode->i_mode &= ~S_IFBLK; 831 inode->i_mode |= S_IFCHR; 832 break; 833 case 'b': 834 break; 835 default: 836 P9_DPRINTK(P9_DEBUG_ERROR, 837 "Unknown special type %c (%.*s)\n", type, 838 stat->extension.len, stat->extension.str); 839 }; 840 inode->i_rdev = MKDEV(major, minor); 841 } else 842 inode->i_rdev = 0; 843 844 inode->i_size = stat->length; 845 846 /* not real number of blocks, but 512 byte ones ... */ 847 inode->i_blocks = (inode->i_size + 512 - 1) >> 9; 848 } 849 850 /** 851 * v9fs_qid2ino - convert qid into inode number 852 * @qid: qid to hash 853 * 854 * BUG: potential for inode number collisions? 855 */ 856 857 ino_t v9fs_qid2ino(struct p9_qid *qid) 858 { 859 u64 path = qid->path + 2; 860 ino_t i = 0; 861 862 if (sizeof(ino_t) == sizeof(path)) 863 memcpy(&i, &path, sizeof(ino_t)); 864 else 865 i = (ino_t) (path ^ (path >> 32)); 866 867 return i; 868 } 869 870 /** 871 * v9fs_readlink - read a symlink's location (internal version) 872 * @dentry: dentry for symlink 873 * @buffer: buffer to load symlink location into 874 * @buflen: length of buffer 875 * 876 */ 877 878 static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) 879 { 880 int retval; 881 882 struct v9fs_session_info *v9ses; 883 struct p9_fid *fid; 884 struct p9_stat *st; 885 886 P9_DPRINTK(P9_DEBUG_VFS, " %s\n", dentry->d_name.name); 887 retval = -EPERM; 888 v9ses = v9fs_inode2v9ses(dentry->d_inode); 889 fid = v9fs_fid_lookup(dentry); 890 if (IS_ERR(fid)) 891 return PTR_ERR(fid); 892 893 if (!v9fs_extended(v9ses)) 894 return -EBADF; 895 896 st = p9_client_stat(fid); 897 if (IS_ERR(st)) 898 return PTR_ERR(st); 899 900 if (!(st->mode & P9_DMSYMLINK)) { 901 retval = -EINVAL; 902 goto done; 903 } 904 905 /* copy extension buffer into buffer */ 906 if (st->extension.len < buflen) 907 buflen = st->extension.len + 1; 908 909 memmove(buffer, st->extension.str, buflen - 1); 910 buffer[buflen-1] = 0; 911 912 P9_DPRINTK(P9_DEBUG_VFS, 913 "%s -> %.*s (%s)\n", dentry->d_name.name, st->extension.len, 914 st->extension.str, buffer); 915 916 retval = buflen; 917 918 done: 919 kfree(st); 920 return retval; 921 } 922 923 /** 924 * v9fs_vfs_readlink - read a symlink's location 925 * @dentry: dentry for symlink 926 * @buf: buffer to load symlink location into 927 * @buflen: length of buffer 928 * 929 */ 930 931 static int v9fs_vfs_readlink(struct dentry *dentry, char __user * buffer, 932 int buflen) 933 { 934 int retval; 935 int ret; 936 char *link = __getname(); 937 938 if (unlikely(!link)) 939 return -ENOMEM; 940 941 if (buflen > PATH_MAX) 942 buflen = PATH_MAX; 943 944 P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); 945 946 retval = v9fs_readlink(dentry, link, buflen); 947 948 if (retval > 0) { 949 if ((ret = copy_to_user(buffer, link, retval)) != 0) { 950 P9_DPRINTK(P9_DEBUG_ERROR, 951 "problem copying to user: %d\n", ret); 952 retval = ret; 953 } 954 } 955 956 __putname(link); 957 return retval; 958 } 959 960 /** 961 * v9fs_vfs_follow_link - follow a symlink path 962 * @dentry: dentry for symlink 963 * @nd: nameidata 964 * 965 */ 966 967 static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd) 968 { 969 int len = 0; 970 char *link = __getname(); 971 972 P9_DPRINTK(P9_DEBUG_VFS, "%s n", dentry->d_name.name); 973 974 if (!link) 975 link = ERR_PTR(-ENOMEM); 976 else { 977 len = v9fs_readlink(dentry, link, PATH_MAX); 978 979 if (len < 0) { 980 __putname(link); 981 link = ERR_PTR(len); 982 } else 983 link[len] = 0; 984 } 985 nd_set_link(nd, link); 986 987 return NULL; 988 } 989 990 /** 991 * v9fs_vfs_put_link - release a symlink path 992 * @dentry: dentry for symlink 993 * @nd: nameidata 994 * 995 */ 996 997 static void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p) 998 { 999 char *s = nd_get_link(nd); 1000 1001 P9_DPRINTK(P9_DEBUG_VFS, " %s %s\n", dentry->d_name.name, s); 1002 if (!IS_ERR(s)) 1003 __putname(s); 1004 } 1005 1006 static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry, 1007 int mode, const char *extension) 1008 { 1009 u32 perm; 1010 struct v9fs_session_info *v9ses; 1011 struct p9_fid *fid; 1012 1013 v9ses = v9fs_inode2v9ses(dir); 1014 if (!v9fs_extended(v9ses)) { 1015 P9_DPRINTK(P9_DEBUG_ERROR, "not extended\n"); 1016 return -EPERM; 1017 } 1018 1019 perm = unixmode2p9mode(v9ses, mode); 1020 fid = v9fs_create(v9ses, dir, dentry, (char *) extension, perm, 1021 P9_OREAD); 1022 if (IS_ERR(fid)) 1023 return PTR_ERR(fid); 1024 1025 p9_client_clunk(fid); 1026 return 0; 1027 } 1028 1029 /** 1030 * v9fs_vfs_symlink - helper function to create symlinks 1031 * @dir: directory inode containing symlink 1032 * @dentry: dentry for symlink 1033 * @symname: symlink data 1034 * 1035 * See 9P2000.u RFC for more information 1036 * 1037 */ 1038 1039 static int 1040 v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) 1041 { 1042 P9_DPRINTK(P9_DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, 1043 dentry->d_name.name, symname); 1044 1045 return v9fs_vfs_mkspecial(dir, dentry, S_IFLNK, symname); 1046 } 1047 1048 /** 1049 * v9fs_vfs_link - create a hardlink 1050 * @old_dentry: dentry for file to link to 1051 * @dir: inode destination for new link 1052 * @dentry: dentry for link 1053 * 1054 */ 1055 1056 /* XXX - lots of code dup'd from symlink and creates, 1057 * figure out a better reuse strategy 1058 */ 1059 1060 static int 1061 v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, 1062 struct dentry *dentry) 1063 { 1064 int retval; 1065 struct p9_fid *oldfid; 1066 char *name; 1067 1068 P9_DPRINTK(P9_DEBUG_VFS, 1069 " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, 1070 old_dentry->d_name.name); 1071 1072 oldfid = v9fs_fid_clone(old_dentry); 1073 if (IS_ERR(oldfid)) 1074 return PTR_ERR(oldfid); 1075 1076 name = __getname(); 1077 if (unlikely(!name)) { 1078 retval = -ENOMEM; 1079 goto clunk_fid; 1080 } 1081 1082 sprintf(name, "%d\n", oldfid->fid); 1083 retval = v9fs_vfs_mkspecial(dir, dentry, P9_DMLINK, name); 1084 __putname(name); 1085 1086 clunk_fid: 1087 p9_client_clunk(oldfid); 1088 return retval; 1089 } 1090 1091 /** 1092 * v9fs_vfs_mknod - create a special file 1093 * @dir: inode destination for new link 1094 * @dentry: dentry for file 1095 * @mode: mode for creation 1096 * @dev_t: device associated with special file 1097 * 1098 */ 1099 1100 static int 1101 v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) 1102 { 1103 int retval; 1104 char *name; 1105 1106 P9_DPRINTK(P9_DEBUG_VFS, 1107 " %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino, 1108 dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev)); 1109 1110 if (!new_valid_dev(rdev)) 1111 return -EINVAL; 1112 1113 name = __getname(); 1114 if (!name) 1115 return -ENOMEM; 1116 /* build extension */ 1117 if (S_ISBLK(mode)) 1118 sprintf(name, "b %u %u", MAJOR(rdev), MINOR(rdev)); 1119 else if (S_ISCHR(mode)) 1120 sprintf(name, "c %u %u", MAJOR(rdev), MINOR(rdev)); 1121 else if (S_ISFIFO(mode)) 1122 *name = 0; 1123 else { 1124 __putname(name); 1125 return -EINVAL; 1126 } 1127 1128 retval = v9fs_vfs_mkspecial(dir, dentry, mode, name); 1129 __putname(name); 1130 1131 return retval; 1132 } 1133 1134 static const struct inode_operations v9fs_dir_inode_operations_ext = { 1135 .create = v9fs_vfs_create, 1136 .lookup = v9fs_vfs_lookup, 1137 .symlink = v9fs_vfs_symlink, 1138 .link = v9fs_vfs_link, 1139 .unlink = v9fs_vfs_unlink, 1140 .mkdir = v9fs_vfs_mkdir, 1141 .rmdir = v9fs_vfs_rmdir, 1142 .mknod = v9fs_vfs_mknod, 1143 .rename = v9fs_vfs_rename, 1144 .readlink = v9fs_vfs_readlink, 1145 .getattr = v9fs_vfs_getattr, 1146 .setattr = v9fs_vfs_setattr, 1147 }; 1148 1149 static const struct inode_operations v9fs_dir_inode_operations = { 1150 .create = v9fs_vfs_create, 1151 .lookup = v9fs_vfs_lookup, 1152 .unlink = v9fs_vfs_unlink, 1153 .mkdir = v9fs_vfs_mkdir, 1154 .rmdir = v9fs_vfs_rmdir, 1155 .mknod = v9fs_vfs_mknod, 1156 .rename = v9fs_vfs_rename, 1157 .getattr = v9fs_vfs_getattr, 1158 .setattr = v9fs_vfs_setattr, 1159 }; 1160 1161 static const struct inode_operations v9fs_file_inode_operations = { 1162 .getattr = v9fs_vfs_getattr, 1163 .setattr = v9fs_vfs_setattr, 1164 }; 1165 1166 static const struct inode_operations v9fs_symlink_inode_operations = { 1167 .readlink = v9fs_vfs_readlink, 1168 .follow_link = v9fs_vfs_follow_link, 1169 .put_link = v9fs_vfs_put_link, 1170 .getattr = v9fs_vfs_getattr, 1171 .setattr = v9fs_vfs_setattr, 1172 }; 1173