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 fcall = NULL; 431 file_dentry->d_op = &v9fs_dentry_operations; 432 d_instantiate(file_dentry, file_inode); 433 434 if (perm & V9FS_DMDIR) { 435 if (!v9fs_t_clunk(v9ses, newfid, &fcall)) 436 v9fs_put_idpool(newfid, &v9ses->fidpool); 437 else 438 dprintk(DEBUG_ERROR, "clunk for mkdir failed: %s\n", 439 FCALL_ERROR(fcall)); 440 kfree(fcall); 441 fid->fidopen = 0; 442 fid->fidcreate = 0; 443 d_drop(file_dentry); 444 } 445 446 return 0; 447 448 CleanUpFid: 449 kfree(fcall); 450 451 if (newfid >= 0) { 452 if (!v9fs_t_clunk(v9ses, newfid, &fcall)) 453 v9fs_put_idpool(newfid, &v9ses->fidpool); 454 else 455 dprintk(DEBUG_ERROR, "clunk failed: %s\n", 456 FCALL_ERROR(fcall)); 457 458 kfree(fcall); 459 } 460 if (wfidno >= 0) { 461 if (!v9fs_t_clunk(v9ses, wfidno, &fcall)) 462 v9fs_put_idpool(wfidno, &v9ses->fidpool); 463 else 464 dprintk(DEBUG_ERROR, "clunk failed: %s\n", 465 FCALL_ERROR(fcall)); 466 467 kfree(fcall); 468 } 469 return result; 470 } 471 472 /** 473 * v9fs_remove - helper function to remove files and directories 474 * @dir: directory inode that is being deleted 475 * @file: dentry that is being deleted 476 * @rmdir: removing a directory 477 * 478 */ 479 480 static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir) 481 { 482 struct v9fs_fcall *fcall = NULL; 483 struct super_block *sb = NULL; 484 struct v9fs_session_info *v9ses = NULL; 485 struct v9fs_fid *v9fid = NULL; 486 struct inode *file_inode = NULL; 487 int fid = -1; 488 int result = 0; 489 490 dprintk(DEBUG_VFS, "inode: %p dentry: %p rmdir: %d\n", dir, file, 491 rmdir); 492 493 file_inode = file->d_inode; 494 sb = file_inode->i_sb; 495 v9ses = v9fs_inode2v9ses(file_inode); 496 v9fid = v9fs_fid_lookup(file); 497 498 if (!v9fid) { 499 dprintk(DEBUG_ERROR, 500 "no v9fs_fid\n"); 501 return -EBADF; 502 } 503 504 fid = v9fid->fid; 505 if (fid < 0) { 506 dprintk(DEBUG_ERROR, "inode #%lu, no fid!\n", 507 file_inode->i_ino); 508 return -EBADF; 509 } 510 511 result = v9fs_t_remove(v9ses, fid, &fcall); 512 if (result < 0) 513 dprintk(DEBUG_ERROR, "remove of file fails: %s(%d)\n", 514 FCALL_ERROR(fcall), result); 515 else { 516 v9fs_put_idpool(fid, &v9ses->fidpool); 517 v9fs_fid_destroy(v9fid); 518 } 519 520 kfree(fcall); 521 return result; 522 } 523 524 /** 525 * v9fs_vfs_create - VFS hook to create files 526 * @inode: directory inode that is being deleted 527 * @dentry: dentry that is being deleted 528 * @perm: create permissions 529 * @nd: path information 530 * 531 */ 532 533 static int 534 v9fs_vfs_create(struct inode *inode, struct dentry *dentry, int perm, 535 struct nameidata *nd) 536 { 537 return v9fs_create(inode, dentry, perm, O_RDWR); 538 } 539 540 /** 541 * v9fs_vfs_mkdir - VFS mkdir hook to create a directory 542 * @inode: inode that is being unlinked 543 * @dentry: dentry that is being unlinked 544 * @mode: mode for new directory 545 * 546 */ 547 548 static int v9fs_vfs_mkdir(struct inode *inode, struct dentry *dentry, int mode) 549 { 550 return v9fs_create(inode, dentry, mode | S_IFDIR, O_RDONLY); 551 } 552 553 /** 554 * v9fs_vfs_lookup - VFS lookup hook to "walk" to a new inode 555 * @dir: inode that is being walked from 556 * @dentry: dentry that is being walked to? 557 * @nameidata: path data 558 * 559 */ 560 561 static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, 562 struct nameidata *nameidata) 563 { 564 struct super_block *sb; 565 struct v9fs_session_info *v9ses; 566 struct v9fs_fid *dirfid; 567 struct v9fs_fid *fid; 568 struct inode *inode; 569 struct v9fs_fcall *fcall = NULL; 570 struct stat newstat; 571 int dirfidnum = -1; 572 int newfid = -1; 573 int result = 0; 574 575 dprintk(DEBUG_VFS, "dir: %p dentry: (%s) %p nameidata: %p\n", 576 dir, dentry->d_iname, dentry, nameidata); 577 578 sb = dir->i_sb; 579 v9ses = v9fs_inode2v9ses(dir); 580 dirfid = v9fs_fid_lookup(dentry->d_parent); 581 582 if (!dirfid) { 583 dprintk(DEBUG_ERROR, "no dirfid\n"); 584 return ERR_PTR(-EINVAL); 585 } 586 587 dirfidnum = dirfid->fid; 588 589 if (dirfidnum < 0) { 590 dprintk(DEBUG_ERROR, "no dirfid for inode %p, #%lu\n", 591 dir, dir->i_ino); 592 return ERR_PTR(-EBADF); 593 } 594 595 newfid = v9fs_get_idpool(&v9ses->fidpool); 596 if (newfid < 0) { 597 eprintk(KERN_WARNING, "newfid fails!\n"); 598 return ERR_PTR(-ENOSPC); 599 } 600 601 result = 602 v9fs_t_walk(v9ses, dirfidnum, newfid, (char *)dentry->d_name.name, 603 NULL); 604 if (result < 0) { 605 v9fs_put_idpool(newfid, &v9ses->fidpool); 606 if (result == -ENOENT) { 607 d_add(dentry, NULL); 608 dprintk(DEBUG_VFS, 609 "Return negative dentry %p count %d\n", 610 dentry, atomic_read(&dentry->d_count)); 611 return NULL; 612 } 613 dprintk(DEBUG_ERROR, "walk error:%d\n", result); 614 goto FreeFcall; 615 } 616 617 result = v9fs_t_stat(v9ses, newfid, &fcall); 618 if (result < 0) { 619 dprintk(DEBUG_ERROR, "stat error\n"); 620 goto FreeFcall; 621 } 622 623 v9fs_mistat2unix(fcall->params.rstat.stat, &newstat, sb); 624 inode = v9fs_get_inode(sb, newstat.st_mode); 625 626 if (IS_ERR(inode) && (PTR_ERR(inode) == -ENOSPC)) { 627 eprintk(KERN_WARNING, "inode alloc failes, returns %ld\n", 628 PTR_ERR(inode)); 629 630 result = -ENOSPC; 631 goto FreeFcall; 632 } 633 634 inode->i_ino = v9fs_qid2ino(&fcall->params.rstat.stat->qid); 635 636 fid = v9fs_fid_create(dentry, v9ses, newfid, 0); 637 if (fid == NULL) { 638 dprintk(DEBUG_ERROR, "couldn't insert\n"); 639 result = -ENOMEM; 640 goto FreeFcall; 641 } 642 643 fid->qid = fcall->params.rstat.stat->qid; 644 645 dentry->d_op = &v9fs_dentry_operations; 646 v9fs_mistat2inode(fcall->params.rstat.stat, inode, inode->i_sb); 647 648 d_add(dentry, inode); 649 kfree(fcall); 650 651 return NULL; 652 653 FreeFcall: 654 kfree(fcall); 655 return ERR_PTR(result); 656 } 657 658 /** 659 * v9fs_vfs_unlink - VFS unlink hook to delete an inode 660 * @i: inode that is being unlinked 661 * @d: dentry that is being unlinked 662 * 663 */ 664 665 static int v9fs_vfs_unlink(struct inode *i, struct dentry *d) 666 { 667 return v9fs_remove(i, d, 0); 668 } 669 670 /** 671 * v9fs_vfs_rmdir - VFS unlink hook to delete a directory 672 * @i: inode that is being unlinked 673 * @d: dentry that is being unlinked 674 * 675 */ 676 677 static int v9fs_vfs_rmdir(struct inode *i, struct dentry *d) 678 { 679 return v9fs_remove(i, d, 1); 680 } 681 682 /** 683 * v9fs_vfs_rename - VFS hook to rename an inode 684 * @old_dir: old dir inode 685 * @old_dentry: old dentry 686 * @new_dir: new dir inode 687 * @new_dentry: new dentry 688 * 689 */ 690 691 static int 692 v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, 693 struct inode *new_dir, struct dentry *new_dentry) 694 { 695 struct inode *old_inode = old_dentry->d_inode; 696 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(old_inode); 697 struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry); 698 struct v9fs_fid *olddirfid = 699 v9fs_fid_lookup(old_dentry->d_parent); 700 struct v9fs_fid *newdirfid = 701 v9fs_fid_lookup(new_dentry->d_parent); 702 struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); 703 struct v9fs_fcall *fcall = NULL; 704 int fid = -1; 705 int olddirfidnum = -1; 706 int newdirfidnum = -1; 707 int retval = 0; 708 709 dprintk(DEBUG_VFS, "\n"); 710 711 if (!mistat) 712 return -ENOMEM; 713 714 if ((!oldfid) || (!olddirfid) || (!newdirfid)) { 715 dprintk(DEBUG_ERROR, "problem with arguments\n"); 716 return -EBADF; 717 } 718 719 /* 9P can only handle file rename in the same directory */ 720 if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) { 721 dprintk(DEBUG_ERROR, "old dir and new dir are different\n"); 722 retval = -EPERM; 723 goto FreeFcallnBail; 724 } 725 726 fid = oldfid->fid; 727 olddirfidnum = olddirfid->fid; 728 newdirfidnum = newdirfid->fid; 729 730 if (fid < 0) { 731 dprintk(DEBUG_ERROR, "no fid for old file #%lu\n", 732 old_inode->i_ino); 733 retval = -EBADF; 734 goto FreeFcallnBail; 735 } 736 737 v9fs_blank_mistat(v9ses, mistat); 738 739 strcpy(mistat->data + 1, v9ses->name); 740 mistat->name = mistat->data + 1 + strlen(v9ses->name); 741 742 if (new_dentry->d_name.len > 743 (v9ses->maxdata - strlen(v9ses->name) - sizeof(struct v9fs_stat))) { 744 dprintk(DEBUG_ERROR, "new name too long\n"); 745 goto FreeFcallnBail; 746 } 747 748 strcpy(mistat->name, new_dentry->d_name.name); 749 retval = v9fs_t_wstat(v9ses, fid, mistat, &fcall); 750 751 FreeFcallnBail: 752 kfree(mistat); 753 754 if (retval < 0) 755 dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n", 756 FCALL_ERROR(fcall)); 757 758 kfree(fcall); 759 return retval; 760 } 761 762 /** 763 * v9fs_vfs_getattr - retreive file metadata 764 * @mnt - mount information 765 * @dentry - file to get attributes on 766 * @stat - metadata structure to populate 767 * 768 */ 769 770 static int 771 v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, 772 struct kstat *stat) 773 { 774 struct v9fs_fcall *fcall = NULL; 775 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); 776 struct v9fs_fid *fid = v9fs_fid_lookup(dentry); 777 int err = -EPERM; 778 779 dprintk(DEBUG_VFS, "dentry: %p\n", dentry); 780 if (!fid) { 781 dprintk(DEBUG_ERROR, 782 "couldn't find fid associated with dentry\n"); 783 return -EBADF; 784 } 785 786 err = v9fs_t_stat(v9ses, fid->fid, &fcall); 787 788 if (err < 0) 789 dprintk(DEBUG_ERROR, "stat error\n"); 790 else { 791 v9fs_mistat2inode(fcall->params.rstat.stat, dentry->d_inode, 792 dentry->d_inode->i_sb); 793 generic_fillattr(dentry->d_inode, stat); 794 } 795 796 kfree(fcall); 797 return err; 798 } 799 800 /** 801 * v9fs_vfs_setattr - set file metadata 802 * @dentry: file whose metadata to set 803 * @iattr: metadata assignment structure 804 * 805 */ 806 807 static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) 808 { 809 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); 810 struct v9fs_fid *fid = v9fs_fid_lookup(dentry); 811 struct v9fs_fcall *fcall = NULL; 812 struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); 813 int res = -EPERM; 814 815 dprintk(DEBUG_VFS, "\n"); 816 817 if (!mistat) 818 return -ENOMEM; 819 820 if (!fid) { 821 dprintk(DEBUG_ERROR, 822 "Couldn't find fid associated with dentry\n"); 823 return -EBADF; 824 } 825 826 v9fs_blank_mistat(v9ses, mistat); 827 if (iattr->ia_valid & ATTR_MODE) 828 mistat->mode = unixmode2p9mode(v9ses, iattr->ia_mode); 829 830 if (iattr->ia_valid & ATTR_MTIME) 831 mistat->mtime = iattr->ia_mtime.tv_sec; 832 833 if (iattr->ia_valid & ATTR_ATIME) 834 mistat->atime = iattr->ia_atime.tv_sec; 835 836 if (iattr->ia_valid & ATTR_SIZE) 837 mistat->length = iattr->ia_size; 838 839 if (v9ses->extended) { 840 char *ptr = mistat->data+1; 841 842 if (iattr->ia_valid & ATTR_UID) { 843 mistat->uid = ptr; 844 ptr += 1+sprintf(ptr, "%08x", iattr->ia_uid); 845 mistat->n_uid = iattr->ia_uid; 846 } 847 848 if (iattr->ia_valid & ATTR_GID) { 849 mistat->gid = ptr; 850 ptr += 1+sprintf(ptr, "%08x", iattr->ia_gid); 851 mistat->n_gid = iattr->ia_gid; 852 } 853 } 854 855 res = v9fs_t_wstat(v9ses, fid->fid, mistat, &fcall); 856 857 if (res < 0) 858 dprintk(DEBUG_ERROR, "wstat error: %s\n", FCALL_ERROR(fcall)); 859 860 kfree(mistat); 861 kfree(fcall); 862 863 if (res >= 0) 864 res = inode_setattr(dentry->d_inode, iattr); 865 866 return res; 867 } 868 869 /** 870 * v9fs_mistat2inode - populate an inode structure with mistat info 871 * @mistat: Plan 9 metadata (mistat) structure 872 * @inode: inode to populate 873 * @sb: superblock of filesystem 874 * 875 */ 876 877 void 878 v9fs_mistat2inode(struct v9fs_stat *mistat, struct inode *inode, 879 struct super_block *sb) 880 { 881 struct v9fs_session_info *v9ses = sb->s_fs_info; 882 883 inode->i_nlink = 1; 884 885 inode->i_atime.tv_sec = mistat->atime; 886 inode->i_mtime.tv_sec = mistat->mtime; 887 inode->i_ctime.tv_sec = mistat->mtime; 888 889 inode->i_uid = -1; 890 inode->i_gid = -1; 891 892 if (v9ses->extended) { 893 /* TODO: string to uid mapping via user-space daemon */ 894 inode->i_uid = mistat->n_uid; 895 inode->i_gid = mistat->n_gid; 896 897 if (mistat->n_uid == -1) 898 sscanf(mistat->uid, "%x", &inode->i_uid); 899 900 if (mistat->n_gid == -1) 901 sscanf(mistat->gid, "%x", &inode->i_gid); 902 } 903 904 if (inode->i_uid == -1) 905 inode->i_uid = v9ses->uid; 906 if (inode->i_gid == -1) 907 inode->i_gid = v9ses->gid; 908 909 inode->i_mode = p9mode2unixmode(v9ses, mistat->mode); 910 if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode))) { 911 char type = 0; 912 int major = -1; 913 int minor = -1; 914 sscanf(mistat->extension, "%c %u %u", &type, &major, &minor); 915 switch (type) { 916 case 'c': 917 inode->i_mode &= ~S_IFBLK; 918 inode->i_mode |= S_IFCHR; 919 break; 920 case 'b': 921 break; 922 default: 923 dprintk(DEBUG_ERROR, "Unknown special type %c (%s)\n", 924 type, mistat->extension); 925 }; 926 inode->i_rdev = MKDEV(major, minor); 927 } else 928 inode->i_rdev = 0; 929 930 inode->i_size = mistat->length; 931 932 inode->i_blksize = sb->s_blocksize; 933 inode->i_blocks = 934 (inode->i_size + inode->i_blksize - 1) >> sb->s_blocksize_bits; 935 } 936 937 /** 938 * v9fs_qid2ino - convert qid into inode number 939 * @qid: qid to hash 940 * 941 * BUG: potential for inode number collisions? 942 */ 943 944 ino_t v9fs_qid2ino(struct v9fs_qid *qid) 945 { 946 u64 path = qid->path + 2; 947 ino_t i = 0; 948 949 if (sizeof(ino_t) == sizeof(path)) 950 memcpy(&i, &path, sizeof(ino_t)); 951 else 952 i = (ino_t) (path ^ (path >> 32)); 953 954 return i; 955 } 956 957 /** 958 * v9fs_vfs_symlink - helper function to create symlinks 959 * @dir: directory inode containing symlink 960 * @dentry: dentry for symlink 961 * @symname: symlink data 962 * 963 * See 9P2000.u RFC for more information 964 * 965 */ 966 967 static int 968 v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) 969 { 970 int retval = -EPERM; 971 struct v9fs_fid *newfid; 972 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); 973 struct v9fs_fcall *fcall = NULL; 974 struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); 975 976 dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, 977 symname); 978 979 if (!mistat) 980 return -ENOMEM; 981 982 if (!v9ses->extended) { 983 dprintk(DEBUG_ERROR, "not extended\n"); 984 goto FreeFcall; 985 } 986 987 /* issue a create */ 988 retval = v9fs_create(dir, dentry, S_IFLNK, 0); 989 if (retval != 0) 990 goto FreeFcall; 991 992 newfid = v9fs_fid_lookup(dentry); 993 994 /* issue a twstat */ 995 v9fs_blank_mistat(v9ses, mistat); 996 strcpy(mistat->data + 1, symname); 997 mistat->extension = mistat->data + 1; 998 retval = v9fs_t_wstat(v9ses, newfid->fid, mistat, &fcall); 999 if (retval < 0) { 1000 dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n", 1001 FCALL_ERROR(fcall)); 1002 goto FreeFcall; 1003 } 1004 1005 kfree(fcall); 1006 1007 if (v9fs_t_clunk(v9ses, newfid->fid, &fcall)) { 1008 dprintk(DEBUG_ERROR, "clunk for symlink failed: %s\n", 1009 FCALL_ERROR(fcall)); 1010 goto FreeFcall; 1011 } 1012 1013 d_drop(dentry); /* FID - will this also clunk? */ 1014 1015 FreeFcall: 1016 kfree(mistat); 1017 kfree(fcall); 1018 1019 return retval; 1020 } 1021 1022 /** 1023 * v9fs_readlink - read a symlink's location (internal version) 1024 * @dentry: dentry for symlink 1025 * @buffer: buffer to load symlink location into 1026 * @buflen: length of buffer 1027 * 1028 */ 1029 1030 static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) 1031 { 1032 int retval = -EPERM; 1033 1034 struct v9fs_fcall *fcall = NULL; 1035 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); 1036 struct v9fs_fid *fid = v9fs_fid_lookup(dentry); 1037 1038 if (!fid) { 1039 dprintk(DEBUG_ERROR, "could not resolve fid from dentry\n"); 1040 retval = -EBADF; 1041 goto FreeFcall; 1042 } 1043 1044 if (!v9ses->extended) { 1045 retval = -EBADF; 1046 dprintk(DEBUG_ERROR, "not extended\n"); 1047 goto FreeFcall; 1048 } 1049 1050 dprintk(DEBUG_VFS, " %s\n", dentry->d_name.name); 1051 retval = v9fs_t_stat(v9ses, fid->fid, &fcall); 1052 1053 if (retval < 0) { 1054 dprintk(DEBUG_ERROR, "stat error\n"); 1055 goto FreeFcall; 1056 } 1057 1058 if (!fcall) 1059 return -EIO; 1060 1061 if (!(fcall->params.rstat.stat->mode & V9FS_DMSYMLINK)) { 1062 retval = -EINVAL; 1063 goto FreeFcall; 1064 } 1065 1066 /* copy extension buffer into buffer */ 1067 if (strlen(fcall->params.rstat.stat->extension) < buflen) 1068 buflen = strlen(fcall->params.rstat.stat->extension); 1069 1070 memcpy(buffer, fcall->params.rstat.stat->extension, buflen + 1); 1071 1072 retval = buflen; 1073 1074 FreeFcall: 1075 kfree(fcall); 1076 1077 return retval; 1078 } 1079 1080 /** 1081 * v9fs_vfs_readlink - read a symlink's location 1082 * @dentry: dentry for symlink 1083 * @buf: buffer to load symlink location into 1084 * @buflen: length of buffer 1085 * 1086 */ 1087 1088 static int v9fs_vfs_readlink(struct dentry *dentry, char __user * buffer, 1089 int buflen) 1090 { 1091 int retval; 1092 int ret; 1093 char *link = __getname(); 1094 1095 if (buflen > PATH_MAX) 1096 buflen = PATH_MAX; 1097 1098 dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); 1099 1100 retval = v9fs_readlink(dentry, link, buflen); 1101 1102 if (retval > 0) { 1103 if ((ret = copy_to_user(buffer, link, retval)) != 0) { 1104 dprintk(DEBUG_ERROR, "problem copying to user: %d\n", 1105 ret); 1106 retval = ret; 1107 } 1108 } 1109 1110 __putname(link); 1111 return retval; 1112 } 1113 1114 /** 1115 * v9fs_vfs_follow_link - follow a symlink path 1116 * @dentry: dentry for symlink 1117 * @nd: nameidata 1118 * 1119 */ 1120 1121 static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd) 1122 { 1123 int len = 0; 1124 char *link = __getname(); 1125 1126 dprintk(DEBUG_VFS, "%s n", dentry->d_name.name); 1127 1128 if (!link) 1129 link = ERR_PTR(-ENOMEM); 1130 else { 1131 len = v9fs_readlink(dentry, link, strlen(link)); 1132 1133 if (len < 0) { 1134 __putname(link); 1135 link = ERR_PTR(len); 1136 } else 1137 link[len] = 0; 1138 } 1139 nd_set_link(nd, link); 1140 1141 return NULL; 1142 } 1143 1144 /** 1145 * v9fs_vfs_put_link - release a symlink path 1146 * @dentry: dentry for symlink 1147 * @nd: nameidata 1148 * 1149 */ 1150 1151 static void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p) 1152 { 1153 char *s = nd_get_link(nd); 1154 1155 dprintk(DEBUG_VFS, " %s %s\n", dentry->d_name.name, s); 1156 if (!IS_ERR(s)) 1157 __putname(s); 1158 } 1159 1160 /** 1161 * v9fs_vfs_link - create a hardlink 1162 * @old_dentry: dentry for file to link to 1163 * @dir: inode destination for new link 1164 * @dentry: dentry for link 1165 * 1166 */ 1167 1168 /* XXX - lots of code dup'd from symlink and creates, 1169 * figure out a better reuse strategy 1170 */ 1171 1172 static int 1173 v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, 1174 struct dentry *dentry) 1175 { 1176 int retval = -EPERM; 1177 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); 1178 struct v9fs_fcall *fcall = NULL; 1179 struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); 1180 struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry); 1181 struct v9fs_fid *newfid = NULL; 1182 char *symname = __getname(); 1183 1184 dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, 1185 old_dentry->d_name.name); 1186 1187 if (!v9ses->extended) { 1188 dprintk(DEBUG_ERROR, "not extended\n"); 1189 goto FreeMem; 1190 } 1191 1192 /* get fid of old_dentry */ 1193 sprintf(symname, "hardlink(%d)\n", oldfid->fid); 1194 1195 /* issue a create */ 1196 retval = v9fs_create(dir, dentry, V9FS_DMLINK, 0); 1197 if (retval != 0) 1198 goto FreeMem; 1199 1200 newfid = v9fs_fid_lookup(dentry); 1201 if (!newfid) { 1202 dprintk(DEBUG_ERROR, "couldn't resolve fid from dentry\n"); 1203 goto FreeMem; 1204 } 1205 1206 /* issue a twstat */ 1207 v9fs_blank_mistat(v9ses, mistat); 1208 strcpy(mistat->data + 1, symname); 1209 mistat->extension = mistat->data + 1; 1210 retval = v9fs_t_wstat(v9ses, newfid->fid, mistat, &fcall); 1211 if (retval < 0) { 1212 dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n", 1213 FCALL_ERROR(fcall)); 1214 goto FreeMem; 1215 } 1216 1217 kfree(fcall); 1218 1219 if (v9fs_t_clunk(v9ses, newfid->fid, &fcall)) { 1220 dprintk(DEBUG_ERROR, "clunk for symlink failed: %s\n", 1221 FCALL_ERROR(fcall)); 1222 goto FreeMem; 1223 } 1224 1225 d_drop(dentry); /* FID - will this also clunk? */ 1226 1227 kfree(fcall); 1228 fcall = NULL; 1229 1230 FreeMem: 1231 kfree(mistat); 1232 kfree(fcall); 1233 __putname(symname); 1234 return retval; 1235 } 1236 1237 /** 1238 * v9fs_vfs_mknod - create a special file 1239 * @dir: inode destination for new link 1240 * @dentry: dentry for file 1241 * @mode: mode for creation 1242 * @dev_t: device associated with special file 1243 * 1244 */ 1245 1246 static int 1247 v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) 1248 { 1249 int retval = -EPERM; 1250 struct v9fs_fid *newfid; 1251 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); 1252 struct v9fs_fcall *fcall = NULL; 1253 struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); 1254 char *symname = __getname(); 1255 1256 dprintk(DEBUG_VFS, " %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino, 1257 dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev)); 1258 1259 if (!mistat) 1260 return -ENOMEM; 1261 1262 if (!new_valid_dev(rdev)) { 1263 retval = -EINVAL; 1264 goto FreeMem; 1265 } 1266 1267 if (!v9ses->extended) { 1268 dprintk(DEBUG_ERROR, "not extended\n"); 1269 goto FreeMem; 1270 } 1271 1272 /* issue a create */ 1273 retval = v9fs_create(dir, dentry, mode, 0); 1274 1275 if (retval != 0) 1276 goto FreeMem; 1277 1278 newfid = v9fs_fid_lookup(dentry); 1279 if (!newfid) { 1280 dprintk(DEBUG_ERROR, "coudn't resove fid from dentry\n"); 1281 retval = -EINVAL; 1282 goto FreeMem; 1283 } 1284 1285 /* build extension */ 1286 if (S_ISBLK(mode)) 1287 sprintf(symname, "b %u %u", MAJOR(rdev), MINOR(rdev)); 1288 else if (S_ISCHR(mode)) 1289 sprintf(symname, "c %u %u", MAJOR(rdev), MINOR(rdev)); 1290 else if (S_ISFIFO(mode)) 1291 ; /* DO NOTHING */ 1292 else { 1293 retval = -EINVAL; 1294 goto FreeMem; 1295 } 1296 1297 if (!S_ISFIFO(mode)) { 1298 /* issue a twstat */ 1299 v9fs_blank_mistat(v9ses, mistat); 1300 strcpy(mistat->data + 1, symname); 1301 mistat->extension = mistat->data + 1; 1302 retval = v9fs_t_wstat(v9ses, newfid->fid, mistat, &fcall); 1303 if (retval < 0) { 1304 dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n", 1305 FCALL_ERROR(fcall)); 1306 goto FreeMem; 1307 } 1308 } 1309 1310 /* need to update dcache so we show up */ 1311 kfree(fcall); 1312 1313 if (v9fs_t_clunk(v9ses, newfid->fid, &fcall)) { 1314 dprintk(DEBUG_ERROR, "clunk for symlink failed: %s\n", 1315 FCALL_ERROR(fcall)); 1316 goto FreeMem; 1317 } 1318 1319 d_drop(dentry); /* FID - will this also clunk? */ 1320 1321 FreeMem: 1322 kfree(mistat); 1323 kfree(fcall); 1324 __putname(symname); 1325 1326 return retval; 1327 } 1328 1329 static struct inode_operations v9fs_dir_inode_operations_ext = { 1330 .create = v9fs_vfs_create, 1331 .lookup = v9fs_vfs_lookup, 1332 .symlink = v9fs_vfs_symlink, 1333 .link = v9fs_vfs_link, 1334 .unlink = v9fs_vfs_unlink, 1335 .mkdir = v9fs_vfs_mkdir, 1336 .rmdir = v9fs_vfs_rmdir, 1337 .mknod = v9fs_vfs_mknod, 1338 .rename = v9fs_vfs_rename, 1339 .readlink = v9fs_vfs_readlink, 1340 .getattr = v9fs_vfs_getattr, 1341 .setattr = v9fs_vfs_setattr, 1342 }; 1343 1344 static struct inode_operations v9fs_dir_inode_operations = { 1345 .create = v9fs_vfs_create, 1346 .lookup = v9fs_vfs_lookup, 1347 .unlink = v9fs_vfs_unlink, 1348 .mkdir = v9fs_vfs_mkdir, 1349 .rmdir = v9fs_vfs_rmdir, 1350 .mknod = v9fs_vfs_mknod, 1351 .rename = v9fs_vfs_rename, 1352 .getattr = v9fs_vfs_getattr, 1353 .setattr = v9fs_vfs_setattr, 1354 }; 1355 1356 static struct inode_operations v9fs_file_inode_operations = { 1357 .getattr = v9fs_vfs_getattr, 1358 .setattr = v9fs_vfs_setattr, 1359 }; 1360 1361 static struct inode_operations v9fs_symlink_inode_operations = { 1362 .readlink = v9fs_vfs_readlink, 1363 .follow_link = v9fs_vfs_follow_link, 1364 .put_link = v9fs_vfs_put_link, 1365 .getattr = v9fs_vfs_getattr, 1366 .setattr = v9fs_vfs_setattr, 1367 }; 1368