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