1 /* 2 * Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 * 5 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 6 * Distributed under the terms of the NewOS License. 7 */ 8 9 /* Virtual File System and File System Interface Layer */ 10 11 #include "vfs.h" 12 13 #include <stdlib.h> 14 15 #include "fd.h" 16 #include "fssh_atomic.h" 17 #include "fssh_defs.h" 18 #include "fssh_dirent.h" 19 #include "fssh_errno.h" 20 #include "fssh_fcntl.h" 21 #include "fssh_fs_info.h" 22 #include "fssh_fs_volume.h" 23 #include "fssh_kernel_export.h" 24 #include "fssh_module.h" 25 #include "fssh_stat.h" 26 #include "fssh_stdio.h" 27 #include "fssh_string.h" 28 #include "fssh_unistd.h" 29 #include "hash.h" 30 #include "KPath.h" 31 #include "posix_compatibility.h" 32 #include "syscalls.h" 33 34 35 //#define TRACE_VFS 36 #ifdef TRACE_VFS 37 # define TRACE(x) fssh_dprintf x 38 # define FUNCTION(x) fssh_dprintf x 39 #else 40 # define TRACE(x) ; 41 # define FUNCTION(x) ; 42 #endif 43 44 #define ADD_DEBUGGER_COMMANDS 45 46 #define ASSERT_LOCKED_MUTEX(x) 47 #define ASSERT(x) 48 49 namespace FSShell { 50 51 52 const static uint32_t kMaxUnusedVnodes = 16; 53 // This is the maximum number of unused vnodes that the system 54 // will keep around (weak limit, if there is enough memory left, 55 // they won't get flushed even when hitting that limit). 56 // It may be chosen with respect to the available memory or enhanced 57 // by some timestamp/frequency heurism. 58 59 struct vnode { 60 struct vnode *next; 61 vm_cache_ref *cache; 62 fssh_mount_id device; 63 list_link mount_link; 64 list_link unused_link; 65 fssh_vnode_id id; 66 fssh_fs_vnode private_node; 67 struct fs_mount *mount; 68 struct vnode *covered_by; 69 int32_t ref_count; 70 uint8_t remove : 1; 71 uint8_t busy : 1; 72 uint8_t unpublished : 1; 73 struct file_descriptor *mandatory_locked_by; 74 }; 75 76 struct vnode_hash_key { 77 fssh_mount_id device; 78 fssh_vnode_id vnode; 79 }; 80 81 #define FS_CALL(vnode, op) (vnode->mount->fs->op) 82 #define FS_MOUNT_CALL(mount, op) (mount->fs->op) 83 84 /** \brief Structure to manage a mounted file system 85 86 Note: The root_vnode and covers_vnode fields (what others?) are 87 initialized in fs_mount() and not changed afterwards. That is as soon 88 as the mount is mounted and it is made sure it won't be unmounted 89 (e.g. by holding a reference to a vnode of that mount) (read) access 90 to those fields is always safe, even without additional locking. Morever 91 while mounted the mount holds a reference to the covers_vnode, and thus 92 making the access path vnode->mount->covers_vnode->mount->... safe if a 93 reference to vnode is held (note that for the root mount covers_vnode 94 is NULL, though). 95 */ 96 struct fs_mount { 97 struct fs_mount *next; 98 fssh_file_system_module_info *fs; 99 fssh_mount_id id; 100 void *cookie; 101 char *device_name; 102 char *fs_name; 103 recursive_lock rlock; // guards the vnodes list 104 struct vnode *root_vnode; 105 struct vnode *covers_vnode; 106 struct list vnodes; 107 bool unmounting; 108 bool owns_file_device; 109 }; 110 111 static mutex sFileSystemsMutex; 112 113 /** \brief Guards sMountsTable. 114 * 115 * The holder is allowed to read/write access the sMountsTable. 116 * Manipulation of the fs_mount structures themselves 117 * (and their destruction) requires different locks though. 118 */ 119 static mutex sMountMutex; 120 121 /** \brief Guards mount/unmount operations. 122 * 123 * The fs_mount() and fs_unmount() hold the lock during their whole operation. 124 * That is locking the lock ensures that no FS is mounted/unmounted. In 125 * particular this means that 126 * - sMountsTable will not be modified, 127 * - the fields immutable after initialization of the fs_mount structures in 128 * sMountsTable will not be modified, 129 * - vnode::covered_by of any vnode in sVnodeTable will not be modified. 130 * 131 * The thread trying to lock the lock must not hold sVnodeMutex or 132 * sMountMutex. 133 */ 134 static recursive_lock sMountOpLock; 135 136 /** \brief Guards the vnode::covered_by field of any vnode 137 * 138 * The holder is allowed to read access the vnode::covered_by field of any 139 * vnode. Additionally holding sMountOpLock allows for write access. 140 * 141 * The thread trying to lock the must not hold sVnodeMutex. 142 */ 143 static mutex sVnodeCoveredByMutex; 144 145 /** \brief Guards sVnodeTable. 146 * 147 * The holder is allowed to read/write access sVnodeTable and to 148 * to any unbusy vnode in that table, save 149 * to the immutable fields (device, id, private_node, mount) to which 150 * only read-only access is allowed, and to the field covered_by, which is 151 * guarded by sMountOpLock and sVnodeCoveredByMutex. 152 * 153 * The thread trying to lock the mutex must not hold sMountMutex. 154 * You must not have this mutex held when calling create_sem(), as this 155 * might call vfs_free_unused_vnodes(). 156 */ 157 static mutex sVnodeMutex; 158 159 #define VNODE_HASH_TABLE_SIZE 1024 160 static hash_table *sVnodeTable; 161 static list sUnusedVnodeList; 162 static uint32_t sUnusedVnodes = 0; 163 static struct vnode *sRoot; 164 165 #define MOUNTS_HASH_TABLE_SIZE 16 166 static hash_table *sMountsTable; 167 static fssh_mount_id sNextMountID = 1; 168 169 fssh_mode_t __fssh_gUmask = 022; 170 171 /* function declarations */ 172 173 // file descriptor operation prototypes 174 static fssh_status_t file_read(struct file_descriptor *, fssh_off_t pos, 175 void *buffer, fssh_size_t *); 176 static fssh_status_t file_write(struct file_descriptor *, fssh_off_t pos, 177 const void *buffer, fssh_size_t *); 178 static fssh_off_t file_seek(struct file_descriptor *, fssh_off_t pos, 179 int seek_type); 180 static void file_free_fd(struct file_descriptor *); 181 static fssh_status_t file_close(struct file_descriptor *); 182 static fssh_status_t dir_read(struct file_descriptor *, 183 struct fssh_dirent *buffer, fssh_size_t bufferSize, 184 uint32_t *_count); 185 static fssh_status_t dir_read(struct vnode *vnode, fssh_fs_cookie cookie, 186 struct fssh_dirent *buffer, fssh_size_t bufferSize, 187 uint32_t *_count); 188 static fssh_status_t dir_rewind(struct file_descriptor *); 189 static void dir_free_fd(struct file_descriptor *); 190 static fssh_status_t dir_close(struct file_descriptor *); 191 static fssh_status_t attr_dir_read(struct file_descriptor *, 192 struct fssh_dirent *buffer, fssh_size_t bufferSize, 193 uint32_t *_count); 194 static fssh_status_t attr_dir_rewind(struct file_descriptor *); 195 static void attr_dir_free_fd(struct file_descriptor *); 196 static fssh_status_t attr_dir_close(struct file_descriptor *); 197 static fssh_status_t attr_read(struct file_descriptor *, fssh_off_t pos, 198 void *buffer, fssh_size_t *); 199 static fssh_status_t attr_write(struct file_descriptor *, fssh_off_t pos, 200 const void *buffer, fssh_size_t *); 201 static fssh_off_t attr_seek(struct file_descriptor *, fssh_off_t pos, 202 int seek_type); 203 static void attr_free_fd(struct file_descriptor *); 204 static fssh_status_t attr_close(struct file_descriptor *); 205 static fssh_status_t attr_read_stat(struct file_descriptor *, 206 struct fssh_stat *); 207 static fssh_status_t attr_write_stat(struct file_descriptor *, 208 const struct fssh_stat *, int statMask); 209 static fssh_status_t index_dir_read(struct file_descriptor *, 210 struct fssh_dirent *buffer, fssh_size_t bufferSize, 211 uint32_t *_count); 212 static fssh_status_t index_dir_rewind(struct file_descriptor *); 213 static void index_dir_free_fd(struct file_descriptor *); 214 static fssh_status_t index_dir_close(struct file_descriptor *); 215 static fssh_status_t query_read(struct file_descriptor *, 216 struct fssh_dirent *buffer, fssh_size_t bufferSize, 217 uint32_t *_count); 218 static fssh_status_t query_rewind(struct file_descriptor *); 219 static void query_free_fd(struct file_descriptor *); 220 static fssh_status_t query_close(struct file_descriptor *); 221 222 static fssh_status_t common_ioctl(struct file_descriptor *, uint32_t, void *buf, 223 fssh_size_t len); 224 static fssh_status_t common_read_stat(struct file_descriptor *, 225 struct fssh_stat *); 226 static fssh_status_t common_write_stat(struct file_descriptor *, 227 const struct fssh_stat *, int statMask); 228 229 static fssh_status_t vnode_path_to_vnode(struct vnode *vnode, char *path, 230 bool traverseLeafLink, int count, struct vnode **_vnode, 231 fssh_vnode_id *_parentID, int *_type); 232 static fssh_status_t dir_vnode_to_path(struct vnode *vnode, char *buffer, 233 fssh_size_t bufferSize); 234 static fssh_status_t fd_and_path_to_vnode(int fd, char *path, 235 bool traverseLeafLink, struct vnode **_vnode, 236 fssh_vnode_id *_parentID, bool kernel); 237 static void inc_vnode_ref_count(struct vnode *vnode); 238 static fssh_status_t dec_vnode_ref_count(struct vnode *vnode, bool reenter); 239 static inline void put_vnode(struct vnode *vnode); 240 241 static struct fd_ops sFileOps = { 242 file_read, 243 file_write, 244 file_seek, 245 common_ioctl, 246 NULL, 247 NULL, 248 NULL, // read_dir() 249 NULL, // rewind_dir() 250 common_read_stat, 251 common_write_stat, 252 file_close, 253 file_free_fd 254 }; 255 256 static struct fd_ops sDirectoryOps = { 257 NULL, // read() 258 NULL, // write() 259 NULL, // seek() 260 common_ioctl, 261 NULL, // select() 262 NULL, // deselect() 263 dir_read, 264 dir_rewind, 265 common_read_stat, 266 common_write_stat, 267 dir_close, 268 dir_free_fd 269 }; 270 271 static struct fd_ops sAttributeDirectoryOps = { 272 NULL, // read() 273 NULL, // write() 274 NULL, // seek() 275 common_ioctl, 276 NULL, // select() 277 NULL, // deselect() 278 attr_dir_read, 279 attr_dir_rewind, 280 common_read_stat, 281 common_write_stat, 282 attr_dir_close, 283 attr_dir_free_fd 284 }; 285 286 static struct fd_ops sAttributeOps = { 287 attr_read, 288 attr_write, 289 attr_seek, 290 common_ioctl, 291 NULL, // select() 292 NULL, // deselect() 293 NULL, // read_dir() 294 NULL, // rewind_dir() 295 attr_read_stat, 296 attr_write_stat, 297 attr_close, 298 attr_free_fd 299 }; 300 301 static struct fd_ops sIndexDirectoryOps = { 302 NULL, // read() 303 NULL, // write() 304 NULL, // seek() 305 NULL, // ioctl() 306 NULL, // select() 307 NULL, // deselect() 308 index_dir_read, 309 index_dir_rewind, 310 NULL, // read_stat() 311 NULL, // write_stat() 312 index_dir_close, 313 index_dir_free_fd 314 }; 315 316 #if 0 317 static struct fd_ops sIndexOps = { 318 NULL, // read() 319 NULL, // write() 320 NULL, // seek() 321 NULL, // ioctl() 322 NULL, // select() 323 NULL, // deselect() 324 NULL, // dir_read() 325 NULL, // dir_rewind() 326 index_read_stat, // read_stat() 327 NULL, // write_stat() 328 NULL, // dir_close() 329 NULL // free_fd() 330 }; 331 #endif 332 333 static struct fd_ops sQueryOps = { 334 NULL, // read() 335 NULL, // write() 336 NULL, // seek() 337 NULL, // ioctl() 338 NULL, // select() 339 NULL, // deselect() 340 query_read, 341 query_rewind, 342 NULL, // read_stat() 343 NULL, // write_stat() 344 query_close, 345 query_free_fd 346 }; 347 348 349 // VNodePutter 350 class VNodePutter { 351 public: 352 VNodePutter(struct vnode *vnode = NULL) : fVNode(vnode) {} 353 354 ~VNodePutter() 355 { 356 Put(); 357 } 358 359 void SetTo(struct vnode *vnode) 360 { 361 Put(); 362 fVNode = vnode; 363 } 364 365 void Put() 366 { 367 if (fVNode) { 368 put_vnode(fVNode); 369 fVNode = NULL; 370 } 371 } 372 373 struct vnode *Detach() 374 { 375 struct vnode *vnode = fVNode; 376 fVNode = NULL; 377 return vnode; 378 } 379 380 private: 381 struct vnode *fVNode; 382 }; 383 384 385 static int 386 mount_compare(void *_m, const void *_key) 387 { 388 struct fs_mount *mount = (fs_mount *)_m; 389 const fssh_mount_id *id = (fssh_mount_id *)_key; 390 391 if (mount->id == *id) 392 return 0; 393 394 return -1; 395 } 396 397 398 static uint32_t 399 mount_hash(void *_m, const void *_key, uint32_t range) 400 { 401 struct fs_mount *mount = (fs_mount *)_m; 402 const fssh_mount_id *id = (fssh_mount_id *)_key; 403 404 if (mount) 405 return mount->id % range; 406 407 return (uint32_t)*id % range; 408 } 409 410 411 /** Finds the mounted device (the fs_mount structure) with the given ID. 412 * Note, you must hold the gMountMutex lock when you call this function. 413 */ 414 415 static struct fs_mount * 416 find_mount(fssh_mount_id id) 417 { 418 ASSERT_LOCKED_MUTEX(&sMountMutex); 419 420 return (fs_mount *)hash_lookup(sMountsTable, (void *)&id); 421 } 422 423 424 static fssh_status_t 425 get_mount(fssh_mount_id id, struct fs_mount **_mount) 426 { 427 struct fs_mount *mount; 428 fssh_status_t status; 429 430 mutex_lock(&sMountMutex); 431 432 mount = find_mount(id); 433 if (mount) { 434 // ToDo: the volume is locked (against removal) by locking 435 // its root node - investigate if that's a good idea 436 if (mount->root_vnode) 437 inc_vnode_ref_count(mount->root_vnode); 438 else { 439 // might have been called during a mount operation in which 440 // case the root node may still be NULL 441 mount = NULL; 442 } 443 } else 444 status = FSSH_B_BAD_VALUE; 445 446 mutex_unlock(&sMountMutex); 447 448 if (mount == NULL) 449 return FSSH_B_BUSY; 450 451 *_mount = mount; 452 return FSSH_B_OK; 453 } 454 455 456 static void 457 put_mount(struct fs_mount *mount) 458 { 459 if (mount) 460 put_vnode(mount->root_vnode); 461 } 462 463 464 static fssh_status_t 465 put_file_system(fssh_file_system_module_info *fs) 466 { 467 return fssh_put_module(fs->info.name); 468 } 469 470 471 /** Tries to open the specified file system module. 472 * Accepts a file system name of the form "bfs" or "file_systems/bfs/v1". 473 * Returns a pointer to file system module interface, or NULL if it 474 * could not open the module. 475 */ 476 477 static fssh_file_system_module_info * 478 get_file_system(const char *fsName) 479 { 480 char name[FSSH_B_FILE_NAME_LENGTH]; 481 if (fssh_strncmp(fsName, "file_systems/", fssh_strlen("file_systems/"))) { 482 // construct module name if we didn't get one 483 // (we currently support only one API) 484 fssh_snprintf(name, sizeof(name), "file_systems/%s/v1", fsName); 485 fsName = NULL; 486 } 487 488 fssh_file_system_module_info *info; 489 if (fssh_get_module(fsName ? fsName : name, (fssh_module_info **)&info) != FSSH_B_OK) 490 return NULL; 491 492 return info; 493 } 494 495 496 /** Accepts a file system name of the form "bfs" or "file_systems/bfs/v1" 497 * and returns a compatible fs_info.fsh_name name ("bfs" in both cases). 498 * The name is allocated for you, and you have to free() it when you're 499 * done with it. 500 * Returns NULL if the required memory is no available. 501 */ 502 503 static char * 504 get_file_system_name(const char *fsName) 505 { 506 const fssh_size_t length = fssh_strlen("file_systems/"); 507 508 if (fssh_strncmp(fsName, "file_systems/", length)) { 509 // the name already seems to be the module's file name 510 return fssh_strdup(fsName); 511 } 512 513 fsName += length; 514 const char *end = fssh_strchr(fsName, '/'); 515 if (end == NULL) { 516 // this doesn't seem to be a valid name, but well... 517 return fssh_strdup(fsName); 518 } 519 520 // cut off the trailing /v1 521 522 char *name = (char *)malloc(end + 1 - fsName); 523 if (name == NULL) 524 return NULL; 525 526 fssh_strlcpy(name, fsName, end + 1 - fsName); 527 return name; 528 } 529 530 531 static int 532 vnode_compare(void *_vnode, const void *_key) 533 { 534 struct vnode *vnode = (struct vnode *)_vnode; 535 const struct vnode_hash_key *key = (vnode_hash_key *)_key; 536 537 if (vnode->device == key->device && vnode->id == key->vnode) 538 return 0; 539 540 return -1; 541 } 542 543 544 static uint32_t 545 vnode_hash(void *_vnode, const void *_key, uint32_t range) 546 { 547 struct vnode *vnode = (struct vnode *)_vnode; 548 const struct vnode_hash_key *key = (vnode_hash_key *)_key; 549 550 #define VHASH(mountid, vnodeid) (((uint32_t)((vnodeid) >> 32) + (uint32_t)(vnodeid)) ^ (uint32_t)(mountid)) 551 552 if (vnode != NULL) 553 return VHASH(vnode->device, vnode->id) % range; 554 555 return VHASH(key->device, key->vnode) % range; 556 557 #undef VHASH 558 } 559 560 561 static void 562 add_vnode_to_mount_list(struct vnode *vnode, struct fs_mount *mount) 563 { 564 recursive_lock_lock(&mount->rlock); 565 566 list_add_link_to_head(&mount->vnodes, &vnode->mount_link); 567 568 recursive_lock_unlock(&mount->rlock); 569 } 570 571 572 static void 573 remove_vnode_from_mount_list(struct vnode *vnode, struct fs_mount *mount) 574 { 575 recursive_lock_lock(&mount->rlock); 576 577 list_remove_link(&vnode->mount_link); 578 vnode->mount_link.next = vnode->mount_link.prev = NULL; 579 580 recursive_lock_unlock(&mount->rlock); 581 } 582 583 584 static fssh_status_t 585 create_new_vnode(struct vnode **_vnode, fssh_mount_id mountID, fssh_vnode_id vnodeID) 586 { 587 FUNCTION(("create_new_vnode()\n")); 588 589 struct vnode *vnode = (struct vnode *)malloc(sizeof(struct vnode)); 590 if (vnode == NULL) 591 return FSSH_B_NO_MEMORY; 592 593 // initialize basic values 594 fssh_memset(vnode, 0, sizeof(struct vnode)); 595 vnode->device = mountID; 596 vnode->id = vnodeID; 597 598 // add the vnode to the mount structure 599 mutex_lock(&sMountMutex); 600 vnode->mount = find_mount(mountID); 601 if (!vnode->mount || vnode->mount->unmounting) { 602 mutex_unlock(&sMountMutex); 603 free(vnode); 604 return FSSH_B_ENTRY_NOT_FOUND; 605 } 606 607 hash_insert(sVnodeTable, vnode); 608 add_vnode_to_mount_list(vnode, vnode->mount); 609 610 mutex_unlock(&sMountMutex); 611 612 vnode->ref_count = 1; 613 *_vnode = vnode; 614 615 return FSSH_B_OK; 616 } 617 618 619 /** Frees the vnode and all resources it has acquired, and removes 620 * it from the vnode hash as well as from its mount structure. 621 * Will also make sure that any cache modifications are written back. 622 */ 623 624 static void 625 free_vnode(struct vnode *vnode, bool reenter) 626 { 627 ASSERT(vnode->ref_count == 0 && vnode->busy); 628 629 // write back any changes in this vnode's cache -- but only 630 // if the vnode won't be deleted, in which case the changes 631 // will be discarded 632 633 if (!vnode->remove && FS_CALL(vnode, fsync) != NULL) 634 FS_CALL(vnode, fsync)(vnode->mount->cookie, vnode->private_node); 635 636 if (!vnode->unpublished) { 637 if (vnode->remove) 638 FS_CALL(vnode, remove_vnode)(vnode->mount->cookie, vnode->private_node, reenter); 639 else 640 FS_CALL(vnode, put_vnode)(vnode->mount->cookie, vnode->private_node, reenter); 641 } 642 643 // The file system has removed the resources of the vnode now, so we can 644 // make it available again (and remove the busy vnode from the hash) 645 mutex_lock(&sVnodeMutex); 646 hash_remove(sVnodeTable, vnode); 647 mutex_unlock(&sVnodeMutex); 648 649 remove_vnode_from_mount_list(vnode, vnode->mount); 650 651 free(vnode); 652 } 653 654 655 /** \brief Decrements the reference counter of the given vnode and deletes it, 656 * if the counter dropped to 0. 657 * 658 * The caller must, of course, own a reference to the vnode to call this 659 * function. 660 * The caller must not hold the sVnodeMutex or the sMountMutex. 661 * 662 * \param vnode the vnode. 663 * \param reenter \c true, if this function is called (indirectly) from within 664 * a file system. 665 * \return \c FSSH_B_OK, if everything went fine, an error code otherwise. 666 */ 667 668 static fssh_status_t 669 dec_vnode_ref_count(struct vnode *vnode, bool reenter) 670 { 671 mutex_lock(&sVnodeMutex); 672 673 int32_t oldRefCount = fssh_atomic_add(&vnode->ref_count, -1); 674 675 TRACE(("dec_vnode_ref_count: vnode %p, ref now %ld\n", vnode, vnode->ref_count)); 676 677 if (oldRefCount == 1) { 678 if (vnode->busy) 679 fssh_panic("dec_vnode_ref_count: called on busy vnode %p\n", vnode); 680 681 bool freeNode = false; 682 683 // Just insert the vnode into an unused list if we don't need 684 // to delete it 685 if (vnode->remove) { 686 vnode->busy = true; 687 freeNode = true; 688 } else { 689 list_add_item(&sUnusedVnodeList, vnode); 690 if (++sUnusedVnodes > kMaxUnusedVnodes) { 691 // there are too many unused vnodes so we free the oldest one 692 // ToDo: evaluate this mechanism 693 vnode = (struct vnode *)list_remove_head_item(&sUnusedVnodeList); 694 vnode->busy = true; 695 freeNode = true; 696 sUnusedVnodes--; 697 } 698 } 699 700 mutex_unlock(&sVnodeMutex); 701 702 if (freeNode) 703 free_vnode(vnode, reenter); 704 } else 705 mutex_unlock(&sVnodeMutex); 706 707 return FSSH_B_OK; 708 } 709 710 711 /** \brief Increments the reference counter of the given vnode. 712 * 713 * The caller must either already have a reference to the vnode or hold 714 * the sVnodeMutex. 715 * 716 * \param vnode the vnode. 717 */ 718 719 static void 720 inc_vnode_ref_count(struct vnode *vnode) 721 { 722 fssh_atomic_add(&vnode->ref_count, 1); 723 TRACE(("inc_vnode_ref_count: vnode %p, ref now %ld\n", vnode, vnode->ref_count)); 724 } 725 726 727 /** \brief Looks up a vnode by mount and node ID in the sVnodeTable. 728 * 729 * The caller must hold the sVnodeMutex. 730 * 731 * \param mountID the mount ID. 732 * \param vnodeID the node ID. 733 * 734 * \return The vnode structure, if it was found in the hash table, \c NULL 735 * otherwise. 736 */ 737 738 static struct vnode * 739 lookup_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID) 740 { 741 struct vnode_hash_key key; 742 743 key.device = mountID; 744 key.vnode = vnodeID; 745 746 return (vnode *)hash_lookup(sVnodeTable, &key); 747 } 748 749 750 /** \brief Retrieves a vnode for a given mount ID, node ID pair. 751 * 752 * If the node is not yet in memory, it will be loaded. 753 * 754 * The caller must not hold the sVnodeMutex or the sMountMutex. 755 * 756 * \param mountID the mount ID. 757 * \param vnodeID the node ID. 758 * \param _vnode Pointer to a vnode* variable into which the pointer to the 759 * retrieved vnode structure shall be written. 760 * \param reenter \c true, if this function is called (indirectly) from within 761 * a file system. 762 * \return \c FSSH_B_OK, if everything when fine, an error code otherwise. 763 */ 764 765 static fssh_status_t 766 get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, struct vnode **_vnode, int reenter) 767 { 768 FUNCTION(("get_vnode: mountid %ld vnid 0x%Lx %p\n", mountID, vnodeID, _vnode)); 769 770 mutex_lock(&sVnodeMutex); 771 772 int32_t tries = 300; 773 // try for 3 secs 774 restart: 775 struct vnode *vnode = lookup_vnode(mountID, vnodeID); 776 if (vnode && vnode->busy) { 777 mutex_unlock(&sVnodeMutex); 778 if (--tries < 0) { 779 // vnode doesn't seem to become unbusy 780 fssh_panic("vnode %d:%lld is not becoming unbusy!\n", (int)mountID, vnodeID); 781 return FSSH_B_BUSY; 782 } 783 fssh_snooze(10000); // 10 ms 784 mutex_lock(&sVnodeMutex); 785 goto restart; 786 } 787 788 TRACE(("get_vnode: tried to lookup vnode, got %p\n", vnode)); 789 790 fssh_status_t status; 791 792 if (vnode) { 793 if (vnode->ref_count == 0) { 794 // this vnode has been unused before 795 list_remove_item(&sUnusedVnodeList, vnode); 796 sUnusedVnodes--; 797 } 798 inc_vnode_ref_count(vnode); 799 } else { 800 // we need to create a new vnode and read it in 801 status = create_new_vnode(&vnode, mountID, vnodeID); 802 if (status < FSSH_B_OK) 803 goto err; 804 805 vnode->busy = true; 806 mutex_unlock(&sVnodeMutex); 807 808 status = FS_CALL(vnode, get_vnode)(vnode->mount->cookie, vnodeID, &vnode->private_node, reenter); 809 if (status == FSSH_B_OK && vnode->private_node == NULL) 810 status = FSSH_B_BAD_VALUE; 811 812 mutex_lock(&sVnodeMutex); 813 814 if (status < FSSH_B_OK) 815 goto err1; 816 817 vnode->busy = false; 818 } 819 820 mutex_unlock(&sVnodeMutex); 821 822 TRACE(("get_vnode: returning %p\n", vnode)); 823 824 *_vnode = vnode; 825 return FSSH_B_OK; 826 827 err1: 828 hash_remove(sVnodeTable, vnode); 829 remove_vnode_from_mount_list(vnode, vnode->mount); 830 err: 831 mutex_unlock(&sVnodeMutex); 832 if (vnode) 833 free(vnode); 834 835 return status; 836 } 837 838 839 /** \brief Decrements the reference counter of the given vnode and deletes it, 840 * if the counter dropped to 0. 841 * 842 * The caller must, of course, own a reference to the vnode to call this 843 * function. 844 * The caller must not hold the sVnodeMutex or the sMountMutex. 845 * 846 * \param vnode the vnode. 847 */ 848 849 static inline void 850 put_vnode(struct vnode *vnode) 851 { 852 dec_vnode_ref_count(vnode, false); 853 } 854 855 856 /** Disconnects all file descriptors that are associated with the 857 * \a vnodeToDisconnect, or if this is NULL, all vnodes of the specified 858 * \a mount object. 859 * 860 * Note, after you've called this function, there might still be ongoing 861 * accesses - they won't be interrupted if they already happened before. 862 * However, any subsequent access will fail. 863 * 864 * This is not a cheap function and should be used with care and rarely. 865 * TODO: there is currently no means to stop a blocking read/write! 866 */ 867 868 void 869 disconnect_mount_or_vnode_fds(struct fs_mount *mount, 870 struct vnode *vnodeToDisconnect) 871 { 872 } 873 874 875 /** \brief Resolves a mount point vnode to the volume root vnode it is covered 876 * by. 877 * 878 * Given an arbitrary vnode, the function checks, whether the node is covered 879 * by the root of a volume. If it is the function obtains a reference to the 880 * volume root node and returns it. 881 * 882 * \param vnode The vnode in question. 883 * \return The volume root vnode the vnode cover is covered by, if it is 884 * indeed a mount point, or \c NULL otherwise. 885 */ 886 887 static struct vnode * 888 resolve_mount_point_to_volume_root(struct vnode *vnode) 889 { 890 if (!vnode) 891 return NULL; 892 893 struct vnode *volumeRoot = NULL; 894 895 mutex_lock(&sVnodeCoveredByMutex); 896 if (vnode->covered_by) { 897 volumeRoot = vnode->covered_by; 898 inc_vnode_ref_count(volumeRoot); 899 } 900 mutex_unlock(&sVnodeCoveredByMutex); 901 902 return volumeRoot; 903 } 904 905 906 /** \brief Resolves a mount point vnode to the volume root vnode it is covered 907 * by. 908 * 909 * Given an arbitrary vnode (identified by mount and node ID), the function 910 * checks, whether the node is covered by the root of a volume. If it is the 911 * function returns the mount and node ID of the volume root node. Otherwise 912 * it simply returns the supplied mount and node ID. 913 * 914 * In case of error (e.g. the supplied node could not be found) the variables 915 * for storing the resolved mount and node ID remain untouched and an error 916 * code is returned. 917 * 918 * \param mountID The mount ID of the vnode in question. 919 * \param nodeID The node ID of the vnode in question. 920 * \param resolvedMountID Pointer to storage for the resolved mount ID. 921 * \param resolvedNodeID Pointer to storage for the resolved node ID. 922 * \return 923 * - \c FSSH_B_OK, if everything went fine, 924 * - another error code, if something went wrong. 925 */ 926 927 fssh_status_t 928 resolve_mount_point_to_volume_root(fssh_mount_id mountID, fssh_vnode_id nodeID, 929 fssh_mount_id *resolvedMountID, fssh_vnode_id *resolvedNodeID) 930 { 931 // get the node 932 struct vnode *node; 933 fssh_status_t error = get_vnode(mountID, nodeID, &node, false); 934 if (error != FSSH_B_OK) 935 return error; 936 937 // resolve the node 938 struct vnode *resolvedNode = resolve_mount_point_to_volume_root(node); 939 if (resolvedNode) { 940 put_vnode(node); 941 node = resolvedNode; 942 } 943 944 // set the return values 945 *resolvedMountID = node->device; 946 *resolvedNodeID = node->id; 947 948 put_vnode(node); 949 950 return FSSH_B_OK; 951 } 952 953 954 /** \brief Resolves a volume root vnode to the underlying mount point vnode. 955 * 956 * Given an arbitrary vnode, the function checks, whether the node is the 957 * root of a volume. If it is (and if it is not "/"), the function obtains 958 * a reference to the underlying mount point node and returns it. 959 * 960 * \param vnode The vnode in question (caller must have a reference). 961 * \return The mount point vnode the vnode covers, if it is indeed a volume 962 * root and not "/", or \c NULL otherwise. 963 */ 964 965 static struct vnode * 966 resolve_volume_root_to_mount_point(struct vnode *vnode) 967 { 968 if (!vnode) 969 return NULL; 970 971 struct vnode *mountPoint = NULL; 972 973 struct fs_mount *mount = vnode->mount; 974 if (vnode == mount->root_vnode && mount->covers_vnode) { 975 mountPoint = mount->covers_vnode; 976 inc_vnode_ref_count(mountPoint); 977 } 978 979 return mountPoint; 980 } 981 982 983 /** \brief Gets the directory path and leaf name for a given path. 984 * 985 * The supplied \a path is transformed to refer to the directory part of 986 * the entry identified by the original path, and into the buffer \a filename 987 * the leaf name of the original entry is written. 988 * Neither the returned path nor the leaf name can be expected to be 989 * canonical. 990 * 991 * \param path The path to be analyzed. Must be able to store at least one 992 * additional character. 993 * \param filename The buffer into which the leaf name will be written. 994 * Must be of size FSSH_B_FILE_NAME_LENGTH at least. 995 * \return \c FSSH_B_OK, if everything went fine, \c FSSH_B_NAME_TOO_LONG, if the leaf 996 * name is longer than \c FSSH_B_FILE_NAME_LENGTH. 997 */ 998 999 static fssh_status_t 1000 get_dir_path_and_leaf(char *path, char *filename) 1001 { 1002 char *p = fssh_strrchr(path, '/'); 1003 // '/' are not allowed in file names! 1004 1005 FUNCTION(("get_dir_path_and_leaf(path = %s)\n", path)); 1006 1007 if (!p) { 1008 // this path is single segment with no '/' in it 1009 // ex. "foo" 1010 if (fssh_strlcpy(filename, path, FSSH_B_FILE_NAME_LENGTH) >= FSSH_B_FILE_NAME_LENGTH) 1011 return FSSH_B_NAME_TOO_LONG; 1012 fssh_strcpy(path, "."); 1013 } else { 1014 p++; 1015 if (*p == '\0') { 1016 // special case: the path ends in '/' 1017 fssh_strcpy(filename, "."); 1018 } else { 1019 // normal leaf: replace the leaf portion of the path with a '.' 1020 if (fssh_strlcpy(filename, p, FSSH_B_FILE_NAME_LENGTH) 1021 >= FSSH_B_FILE_NAME_LENGTH) { 1022 return FSSH_B_NAME_TOO_LONG; 1023 } 1024 } 1025 p[0] = '.'; 1026 p[1] = '\0'; 1027 } 1028 return FSSH_B_OK; 1029 } 1030 1031 1032 static fssh_status_t 1033 entry_ref_to_vnode(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, struct vnode **_vnode) 1034 { 1035 char clonedName[FSSH_B_FILE_NAME_LENGTH + 1]; 1036 if (fssh_strlcpy(clonedName, name, FSSH_B_FILE_NAME_LENGTH) >= FSSH_B_FILE_NAME_LENGTH) 1037 return FSSH_B_NAME_TOO_LONG; 1038 1039 // get the directory vnode and let vnode_path_to_vnode() do the rest 1040 struct vnode *directory; 1041 1042 fssh_status_t status = get_vnode(mountID, directoryID, &directory, false); 1043 if (status < 0) 1044 return status; 1045 1046 return vnode_path_to_vnode(directory, clonedName, false, 0, _vnode, NULL, NULL); 1047 } 1048 1049 1050 /** Returns the vnode for the relative path starting at the specified \a vnode. 1051 * \a path must not be NULL. 1052 * If it returns successfully, \a path contains the name of the last path 1053 * component. 1054 * Note, this reduces the ref_count of the starting \a vnode, no matter if 1055 * it is successful or not! 1056 */ 1057 1058 static fssh_status_t 1059 vnode_path_to_vnode(struct vnode *vnode, char *path, bool traverseLeafLink, 1060 int count, struct vnode **_vnode, fssh_vnode_id *_parentID, int *_type) 1061 { 1062 fssh_status_t status = 0; 1063 fssh_vnode_id lastParentID = vnode->id; 1064 int type = 0; 1065 1066 FUNCTION(("vnode_path_to_vnode(vnode = %p, path = %s)\n", vnode, path)); 1067 1068 if (path == NULL) { 1069 put_vnode(vnode); 1070 return FSSH_B_BAD_VALUE; 1071 } 1072 1073 while (true) { 1074 struct vnode *nextVnode; 1075 fssh_vnode_id vnodeID; 1076 char *nextPath; 1077 1078 TRACE(("vnode_path_to_vnode: top of loop. p = %p, p = '%s'\n", path, path)); 1079 1080 // done? 1081 if (path[0] == '\0') 1082 break; 1083 1084 // walk to find the next path component ("path" will point to a single 1085 // path component), and filter out multiple slashes 1086 for (nextPath = path + 1; *nextPath != '\0' && *nextPath != '/'; nextPath++); 1087 1088 if (*nextPath == '/') { 1089 *nextPath = '\0'; 1090 do 1091 nextPath++; 1092 while (*nextPath == '/'); 1093 } 1094 1095 // See if the '..' is at the root of a mount and move to the covered 1096 // vnode so we pass the '..' path to the underlying filesystem 1097 if (!fssh_strcmp("..", path) 1098 && vnode->mount->root_vnode == vnode 1099 && vnode->mount->covers_vnode) { 1100 nextVnode = vnode->mount->covers_vnode; 1101 inc_vnode_ref_count(nextVnode); 1102 put_vnode(vnode); 1103 vnode = nextVnode; 1104 } 1105 1106 // Check if we have the right to search the current directory vnode. 1107 // If a file system doesn't have the access() function, we assume that 1108 // searching a directory is always allowed 1109 if (FS_CALL(vnode, access)) 1110 status = FS_CALL(vnode, access)(vnode->mount->cookie, vnode->private_node, FSSH_X_OK); 1111 1112 // Tell the filesystem to get the vnode of this path component (if we got the 1113 // permission from the call above) 1114 if (status >= FSSH_B_OK) 1115 status = FS_CALL(vnode, lookup)(vnode->mount->cookie, vnode->private_node, path, &vnodeID, &type); 1116 1117 if (status < FSSH_B_OK) { 1118 put_vnode(vnode); 1119 return status; 1120 } 1121 1122 // Lookup the vnode, the call to fs_lookup should have caused a get_vnode to be called 1123 // from inside the filesystem, thus the vnode would have to be in the list and it's 1124 // ref count incremented at this point 1125 mutex_lock(&sVnodeMutex); 1126 nextVnode = lookup_vnode(vnode->device, vnodeID); 1127 mutex_unlock(&sVnodeMutex); 1128 1129 if (!nextVnode) { 1130 // pretty screwed up here - the file system found the vnode, but the hash 1131 // lookup failed, so our internal structures are messed up 1132 fssh_panic("vnode_path_to_vnode: could not lookup vnode (mountid 0x%x vnid 0x%llx)\n", 1133 (int)vnode->device, vnodeID); 1134 put_vnode(vnode); 1135 return FSSH_B_ENTRY_NOT_FOUND; 1136 } 1137 1138 // If the new node is a symbolic link, resolve it (if we've been told to do it) 1139 if (FSSH_S_ISLNK(type) && !(!traverseLeafLink && nextPath[0] == '\0')) { 1140 fssh_size_t bufferSize; 1141 char *buffer; 1142 1143 TRACE(("traverse link\n")); 1144 1145 // it's not exactly nice style using goto in this way, but hey, it works :-/ 1146 if (count + 1 > FSSH_B_MAX_SYMLINKS) { 1147 status = FSSH_B_LINK_LIMIT; 1148 goto resolve_link_error; 1149 } 1150 1151 buffer = (char *)malloc(bufferSize = FSSH_B_PATH_NAME_LENGTH); 1152 if (buffer == NULL) { 1153 status = FSSH_B_NO_MEMORY; 1154 goto resolve_link_error; 1155 } 1156 1157 if (FS_CALL(nextVnode, read_symlink) != NULL) { 1158 status = FS_CALL(nextVnode, read_symlink)( 1159 nextVnode->mount->cookie, nextVnode->private_node, buffer, 1160 &bufferSize); 1161 } else 1162 status = FSSH_B_BAD_VALUE; 1163 1164 if (status < FSSH_B_OK) { 1165 free(buffer); 1166 1167 resolve_link_error: 1168 put_vnode(vnode); 1169 put_vnode(nextVnode); 1170 1171 return status; 1172 } 1173 put_vnode(nextVnode); 1174 1175 // Check if we start from the root directory or the current 1176 // directory ("vnode" still points to that one). 1177 // Cut off all leading slashes if it's the root directory 1178 path = buffer; 1179 if (path[0] == '/') { 1180 // we don't need the old directory anymore 1181 put_vnode(vnode); 1182 1183 while (*++path == '/') 1184 ; 1185 vnode = sRoot; 1186 inc_vnode_ref_count(vnode); 1187 } 1188 inc_vnode_ref_count(vnode); 1189 // balance the next recursion - we will decrement the ref_count 1190 // of the vnode, no matter if we succeeded or not 1191 1192 status = vnode_path_to_vnode(vnode, path, traverseLeafLink, count + 1, 1193 &nextVnode, &lastParentID, _type); 1194 1195 free(buffer); 1196 1197 if (status < FSSH_B_OK) { 1198 put_vnode(vnode); 1199 return status; 1200 } 1201 } else 1202 lastParentID = vnode->id; 1203 1204 // decrease the ref count on the old dir we just looked up into 1205 put_vnode(vnode); 1206 1207 path = nextPath; 1208 vnode = nextVnode; 1209 1210 // see if we hit a mount point 1211 struct vnode *mountPoint = resolve_mount_point_to_volume_root(vnode); 1212 if (mountPoint) { 1213 put_vnode(vnode); 1214 vnode = mountPoint; 1215 } 1216 } 1217 1218 *_vnode = vnode; 1219 if (_type) 1220 *_type = type; 1221 if (_parentID) 1222 *_parentID = lastParentID; 1223 1224 return FSSH_B_OK; 1225 } 1226 1227 1228 static fssh_status_t 1229 path_to_vnode(char *path, bool traverseLink, struct vnode **_vnode, 1230 fssh_vnode_id *_parentID, bool kernel) 1231 { 1232 struct vnode *start = NULL; 1233 1234 FUNCTION(("path_to_vnode(path = \"%s\")\n", path)); 1235 1236 if (!path) 1237 return FSSH_B_BAD_VALUE; 1238 1239 // figure out if we need to start at root or at cwd 1240 if (*path == '/') { 1241 if (sRoot == NULL) { 1242 // we're a bit early, aren't we? 1243 return FSSH_B_ERROR; 1244 } 1245 1246 while (*++path == '/') 1247 ; 1248 start = sRoot; 1249 inc_vnode_ref_count(start); 1250 } else { 1251 struct io_context *context = get_current_io_context(kernel); 1252 1253 mutex_lock(&context->io_mutex); 1254 start = context->cwd; 1255 if (start != NULL) 1256 inc_vnode_ref_count(start); 1257 mutex_unlock(&context->io_mutex); 1258 1259 if (start == NULL) 1260 return FSSH_B_ERROR; 1261 } 1262 1263 return vnode_path_to_vnode(start, path, traverseLink, 0, _vnode, _parentID, NULL); 1264 } 1265 1266 1267 /** Returns the vnode in the next to last segment of the path, and returns 1268 * the last portion in filename. 1269 * The path buffer must be able to store at least one additional character. 1270 */ 1271 1272 static fssh_status_t 1273 path_to_dir_vnode(char *path, struct vnode **_vnode, char *filename, bool kernel) 1274 { 1275 fssh_status_t status = get_dir_path_and_leaf(path, filename); 1276 if (status != FSSH_B_OK) 1277 return status; 1278 1279 return path_to_vnode(path, true, _vnode, NULL, kernel); 1280 } 1281 1282 1283 /** \brief Retrieves the directory vnode and the leaf name of an entry referred 1284 * to by a FD + path pair. 1285 * 1286 * \a path must be given in either case. \a fd might be omitted, in which 1287 * case \a path is either an absolute path or one relative to the current 1288 * directory. If both a supplied and \a path is relative it is reckoned off 1289 * of the directory referred to by \a fd. If \a path is absolute \a fd is 1290 * ignored. 1291 * 1292 * The caller has the responsibility to call put_vnode() on the returned 1293 * directory vnode. 1294 * 1295 * \param fd The FD. May be < 0. 1296 * \param path The absolute or relative path. Must not be \c NULL. The buffer 1297 * is modified by this function. It must have at least room for a 1298 * string one character longer than the path it contains. 1299 * \param _vnode A pointer to a variable the directory vnode shall be written 1300 * into. 1301 * \param filename A buffer of size FSSH_B_FILE_NAME_LENGTH or larger into which 1302 * the leaf name of the specified entry will be written. 1303 * \param kernel \c true, if invoked from inside the kernel, \c false if 1304 * invoked from userland. 1305 * \return \c FSSH_B_OK, if everything went fine, another error code otherwise. 1306 */ 1307 1308 static fssh_status_t 1309 fd_and_path_to_dir_vnode(int fd, char *path, struct vnode **_vnode, 1310 char *filename, bool kernel) 1311 { 1312 if (!path) 1313 return FSSH_B_BAD_VALUE; 1314 if (fd < 0) 1315 return path_to_dir_vnode(path, _vnode, filename, kernel); 1316 1317 fssh_status_t status = get_dir_path_and_leaf(path, filename); 1318 if (status != FSSH_B_OK) 1319 return status; 1320 1321 return fd_and_path_to_vnode(fd, path, true, _vnode, NULL, kernel); 1322 } 1323 1324 1325 /** Returns a vnode's name in the d_name field of a supplied dirent buffer. 1326 */ 1327 1328 static fssh_status_t 1329 get_vnode_name(struct vnode *vnode, struct vnode *parent, 1330 struct fssh_dirent *buffer, fssh_size_t bufferSize) 1331 { 1332 if (bufferSize < sizeof(struct fssh_dirent)) 1333 return FSSH_B_BAD_VALUE; 1334 1335 // See if vnode is the root of a mount and move to the covered 1336 // vnode so we get the underlying file system 1337 VNodePutter vnodePutter; 1338 if (vnode->mount->root_vnode == vnode && vnode->mount->covers_vnode != NULL) { 1339 vnode = vnode->mount->covers_vnode; 1340 inc_vnode_ref_count(vnode); 1341 vnodePutter.SetTo(vnode); 1342 } 1343 1344 if (FS_CALL(vnode, get_vnode_name)) { 1345 // The FS supports getting the name of a vnode. 1346 return FS_CALL(vnode, get_vnode_name)(vnode->mount->cookie, 1347 vnode->private_node, buffer->d_name, 1348 (char*)buffer + bufferSize - buffer->d_name); 1349 } 1350 1351 // The FS doesn't support getting the name of a vnode. So we search the 1352 // parent directory for the vnode, if the caller let us. 1353 1354 if (parent == NULL) 1355 return FSSH_EOPNOTSUPP; 1356 1357 fssh_fs_cookie cookie; 1358 1359 fssh_status_t status = FS_CALL(parent, open_dir)(parent->mount->cookie, 1360 parent->private_node, &cookie); 1361 if (status >= FSSH_B_OK) { 1362 while (true) { 1363 uint32_t num = 1; 1364 status = dir_read(parent, cookie, buffer, bufferSize, &num); 1365 if (status < FSSH_B_OK) 1366 break; 1367 if (num == 0) { 1368 status = FSSH_B_ENTRY_NOT_FOUND; 1369 break; 1370 } 1371 1372 if (vnode->id == buffer->d_ino) { 1373 // found correct entry! 1374 break; 1375 } 1376 } 1377 1378 FS_CALL(vnode, close_dir)(vnode->mount->cookie, vnode->private_node, 1379 cookie); 1380 FS_CALL(vnode, free_dir_cookie)(vnode->mount->cookie, 1381 vnode->private_node, cookie); 1382 } 1383 return status; 1384 } 1385 1386 1387 static fssh_status_t 1388 get_vnode_name(struct vnode *vnode, struct vnode *parent, char *name, 1389 fssh_size_t nameSize) 1390 { 1391 char buffer[sizeof(struct fssh_dirent) + FSSH_B_FILE_NAME_LENGTH]; 1392 struct fssh_dirent *dirent = (struct fssh_dirent *)buffer; 1393 1394 fssh_status_t status = get_vnode_name(vnode, parent, buffer, sizeof(buffer)); 1395 if (status != FSSH_B_OK) 1396 return status; 1397 1398 if (fssh_strlcpy(name, dirent->d_name, nameSize) >= nameSize) 1399 return FSSH_B_BUFFER_OVERFLOW; 1400 1401 return FSSH_B_OK; 1402 } 1403 1404 1405 /** Gets the full path to a given directory vnode. 1406 * It uses the fs_get_vnode_name() call to get the name of a vnode; if a 1407 * file system doesn't support this call, it will fall back to iterating 1408 * through the parent directory to get the name of the child. 1409 * 1410 * To protect against circular loops, it supports a maximum tree depth 1411 * of 256 levels. 1412 * 1413 * Note that the path may not be correct the time this function returns! 1414 * It doesn't use any locking to prevent returning the correct path, as 1415 * paths aren't safe anyway: the path to a file can change at any time. 1416 * 1417 * It might be a good idea, though, to check if the returned path exists 1418 * in the calling function (it's not done here because of efficiency) 1419 */ 1420 1421 static fssh_status_t 1422 dir_vnode_to_path(struct vnode *vnode, char *buffer, fssh_size_t bufferSize) 1423 { 1424 FUNCTION(("dir_vnode_to_path(%p, %p, %lu)\n", vnode, buffer, bufferSize)); 1425 1426 if (vnode == NULL || buffer == NULL) 1427 return FSSH_B_BAD_VALUE; 1428 1429 /* this implementation is currently bound to FSSH_B_PATH_NAME_LENGTH */ 1430 KPath pathBuffer; 1431 if (pathBuffer.InitCheck() != FSSH_B_OK) 1432 return FSSH_B_NO_MEMORY; 1433 1434 char *path = pathBuffer.LockBuffer(); 1435 int32_t insert = pathBuffer.BufferSize(); 1436 int32_t maxLevel = 256; 1437 int32_t length; 1438 fssh_status_t status; 1439 1440 // we don't use get_vnode() here because this call is more 1441 // efficient and does all we need from get_vnode() 1442 inc_vnode_ref_count(vnode); 1443 1444 // resolve a volume root to its mount point 1445 struct vnode *mountPoint = resolve_volume_root_to_mount_point(vnode); 1446 if (mountPoint) { 1447 put_vnode(vnode); 1448 vnode = mountPoint; 1449 } 1450 1451 path[--insert] = '\0'; 1452 1453 while (true) { 1454 // the name buffer is also used for fs_read_dir() 1455 char nameBuffer[sizeof(struct fssh_dirent) + FSSH_B_FILE_NAME_LENGTH]; 1456 char *name = &((struct fssh_dirent *)nameBuffer)->d_name[0]; 1457 struct vnode *parentVnode; 1458 fssh_vnode_id parentID; 1459 int type; 1460 1461 // lookup the parent vnode 1462 status = FS_CALL(vnode, lookup)(vnode->mount->cookie, vnode->private_node, "..", 1463 &parentID, &type); 1464 if (status < FSSH_B_OK) 1465 goto out; 1466 1467 mutex_lock(&sVnodeMutex); 1468 parentVnode = lookup_vnode(vnode->device, parentID); 1469 mutex_unlock(&sVnodeMutex); 1470 1471 if (parentVnode == NULL) { 1472 fssh_panic("dir_vnode_to_path: could not lookup vnode (mountid 0x%x vnid 0x%llx)\n", 1473 (int)vnode->device, parentID); 1474 status = FSSH_B_ENTRY_NOT_FOUND; 1475 goto out; 1476 } 1477 1478 // get the node's name 1479 status = get_vnode_name(vnode, parentVnode, 1480 (struct fssh_dirent*)nameBuffer, sizeof(nameBuffer)); 1481 1482 // resolve a volume root to its mount point 1483 mountPoint = resolve_volume_root_to_mount_point(parentVnode); 1484 if (mountPoint) { 1485 put_vnode(parentVnode); 1486 parentVnode = mountPoint; 1487 parentID = parentVnode->id; 1488 } 1489 1490 bool hitRoot = (parentVnode == vnode); 1491 1492 // release the current vnode, we only need its parent from now on 1493 put_vnode(vnode); 1494 vnode = parentVnode; 1495 1496 if (status < FSSH_B_OK) 1497 goto out; 1498 1499 if (hitRoot) { 1500 // we have reached "/", which means we have constructed the full 1501 // path 1502 break; 1503 } 1504 1505 // ToDo: add an explicit check for loops in about 10 levels to do 1506 // real loop detection 1507 1508 // don't go deeper as 'maxLevel' to prevent circular loops 1509 if (maxLevel-- < 0) { 1510 status = FSSH_ELOOP; 1511 goto out; 1512 } 1513 1514 // add the name in front of the current path 1515 name[FSSH_B_FILE_NAME_LENGTH - 1] = '\0'; 1516 length = fssh_strlen(name); 1517 insert -= length; 1518 if (insert <= 0) { 1519 status = FSSH_ENOBUFS; 1520 goto out; 1521 } 1522 fssh_memcpy(path + insert, name, length); 1523 path[--insert] = '/'; 1524 } 1525 1526 // the root dir will result in an empty path: fix it 1527 if (path[insert] == '\0') 1528 path[--insert] = '/'; 1529 1530 TRACE((" path is: %s\n", path + insert)); 1531 1532 // copy the path to the output buffer 1533 length = pathBuffer.BufferSize() - insert; 1534 if (length <= (int)bufferSize) 1535 fssh_memcpy(buffer, path + insert, length); 1536 else 1537 status = FSSH_ENOBUFS; 1538 1539 out: 1540 put_vnode(vnode); 1541 return status; 1542 } 1543 1544 1545 /** Checks the length of every path component, and adds a '.' 1546 * if the path ends in a slash. 1547 * The given path buffer must be able to store at least one 1548 * additional character. 1549 */ 1550 1551 static fssh_status_t 1552 check_path(char *to) 1553 { 1554 int32_t length = 0; 1555 1556 // check length of every path component 1557 1558 while (*to) { 1559 char *begin; 1560 if (*to == '/') 1561 to++, length++; 1562 1563 begin = to; 1564 while (*to != '/' && *to) 1565 to++, length++; 1566 1567 if (to - begin > FSSH_B_FILE_NAME_LENGTH) 1568 return FSSH_B_NAME_TOO_LONG; 1569 } 1570 1571 if (length == 0) 1572 return FSSH_B_ENTRY_NOT_FOUND; 1573 1574 // complete path if there is a slash at the end 1575 1576 if (*(to - 1) == '/') { 1577 if (length > FSSH_B_PATH_NAME_LENGTH - 2) 1578 return FSSH_B_NAME_TOO_LONG; 1579 1580 to[0] = '.'; 1581 to[1] = '\0'; 1582 } 1583 1584 return FSSH_B_OK; 1585 } 1586 1587 1588 static struct file_descriptor * 1589 get_fd_and_vnode(int fd, struct vnode **_vnode, bool kernel) 1590 { 1591 struct file_descriptor *descriptor = get_fd(get_current_io_context(kernel), fd); 1592 if (descriptor == NULL) 1593 return NULL; 1594 1595 if (fd_vnode(descriptor) == NULL) { 1596 put_fd(descriptor); 1597 return NULL; 1598 } 1599 1600 // ToDo: when we can close a file descriptor at any point, investigate 1601 // if this is still valid to do (accessing the vnode without ref_count 1602 // or locking) 1603 *_vnode = descriptor->u.vnode; 1604 return descriptor; 1605 } 1606 1607 1608 static struct vnode * 1609 get_vnode_from_fd(int fd, bool kernel) 1610 { 1611 struct file_descriptor *descriptor; 1612 struct vnode *vnode; 1613 1614 descriptor = get_fd(get_current_io_context(kernel), fd); 1615 if (descriptor == NULL) 1616 return NULL; 1617 1618 vnode = fd_vnode(descriptor); 1619 if (vnode != NULL) 1620 inc_vnode_ref_count(vnode); 1621 1622 put_fd(descriptor); 1623 return vnode; 1624 } 1625 1626 1627 /** Gets the vnode from an FD + path combination. If \a fd is lower than zero, 1628 * only the path will be considered. In this case, the \a path must not be 1629 * NULL. 1630 * If \a fd is a valid file descriptor, \a path may be NULL for directories, 1631 * and should be NULL for files. 1632 */ 1633 1634 static fssh_status_t 1635 fd_and_path_to_vnode(int fd, char *path, bool traverseLeafLink, 1636 struct vnode **_vnode, fssh_vnode_id *_parentID, bool kernel) 1637 { 1638 if (fd < 0 && !path) 1639 return FSSH_B_BAD_VALUE; 1640 1641 if (fd < 0 || (path != NULL && path[0] == '/')) { 1642 // no FD or absolute path 1643 return path_to_vnode(path, traverseLeafLink, _vnode, _parentID, kernel); 1644 } 1645 1646 // FD only, or FD + relative path 1647 struct vnode *vnode = get_vnode_from_fd(fd, kernel); 1648 if (!vnode) 1649 return FSSH_B_FILE_ERROR; 1650 1651 if (path != NULL) { 1652 return vnode_path_to_vnode(vnode, path, traverseLeafLink, 0, 1653 _vnode, _parentID, NULL); 1654 } 1655 1656 // there is no relative path to take into account 1657 1658 *_vnode = vnode; 1659 if (_parentID) 1660 *_parentID = -1; 1661 1662 return FSSH_B_OK; 1663 } 1664 1665 1666 static int 1667 get_new_fd(int type, struct fs_mount *mount, struct vnode *vnode, 1668 fssh_fs_cookie cookie, int openMode, bool kernel) 1669 { 1670 struct file_descriptor *descriptor; 1671 int fd; 1672 1673 // if the vnode is locked, we don't allow creating a new file descriptor for it 1674 if (vnode && vnode->mandatory_locked_by != NULL) 1675 return FSSH_B_BUSY; 1676 1677 descriptor = alloc_fd(); 1678 if (!descriptor) 1679 return FSSH_B_NO_MEMORY; 1680 1681 if (vnode) 1682 descriptor->u.vnode = vnode; 1683 else 1684 descriptor->u.mount = mount; 1685 descriptor->cookie = cookie; 1686 1687 switch (type) { 1688 // vnode types 1689 case FDTYPE_FILE: 1690 descriptor->ops = &sFileOps; 1691 break; 1692 case FDTYPE_DIR: 1693 descriptor->ops = &sDirectoryOps; 1694 break; 1695 case FDTYPE_ATTR: 1696 descriptor->ops = &sAttributeOps; 1697 break; 1698 case FDTYPE_ATTR_DIR: 1699 descriptor->ops = &sAttributeDirectoryOps; 1700 break; 1701 1702 // mount types 1703 case FDTYPE_INDEX_DIR: 1704 descriptor->ops = &sIndexDirectoryOps; 1705 break; 1706 case FDTYPE_QUERY: 1707 descriptor->ops = &sQueryOps; 1708 break; 1709 1710 default: 1711 fssh_panic("get_new_fd() called with unknown type %d\n", type); 1712 break; 1713 } 1714 descriptor->type = type; 1715 descriptor->open_mode = openMode; 1716 1717 fd = new_fd(get_current_io_context(kernel), descriptor); 1718 if (fd < 0) { 1719 free(descriptor); 1720 return FSSH_B_NO_MORE_FDS; 1721 } 1722 1723 return fd; 1724 } 1725 1726 1727 // #pragma mark - public VFS API 1728 1729 1730 extern "C" fssh_status_t 1731 fssh_new_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, 1732 fssh_fs_vnode privateNode) 1733 { 1734 FUNCTION(("new_vnode(mountID = %ld, vnodeID = %Ld, node = %p)\n", 1735 mountID, vnodeID, privateNode)); 1736 1737 if (privateNode == NULL) 1738 return FSSH_B_BAD_VALUE; 1739 1740 mutex_lock(&sVnodeMutex); 1741 1742 // file system integrity check: 1743 // test if the vnode already exists and bail out if this is the case! 1744 1745 // ToDo: the R5 implementation obviously checks for a different cookie 1746 // and doesn't panic if they are equal 1747 1748 struct vnode *vnode = lookup_vnode(mountID, vnodeID); 1749 if (vnode != NULL) 1750 fssh_panic("vnode %d:%lld already exists (node = %p, vnode->node = %p)!", (int)mountID, vnodeID, privateNode, vnode->private_node); 1751 1752 fssh_status_t status = create_new_vnode(&vnode, mountID, vnodeID); 1753 if (status == FSSH_B_OK) { 1754 vnode->private_node = privateNode; 1755 vnode->busy = true; 1756 vnode->unpublished = true; 1757 } 1758 1759 TRACE(("returns: %s\n", strerror(status))); 1760 1761 mutex_unlock(&sVnodeMutex); 1762 return status; 1763 } 1764 1765 1766 extern "C" fssh_status_t 1767 fssh_publish_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, 1768 fssh_fs_vnode privateNode) 1769 { 1770 FUNCTION(("publish_vnode()\n")); 1771 1772 mutex_lock(&sVnodeMutex); 1773 1774 struct vnode *vnode = lookup_vnode(mountID, vnodeID); 1775 fssh_status_t status = FSSH_B_OK; 1776 1777 if (vnode != NULL && vnode->busy && vnode->unpublished 1778 && vnode->private_node == privateNode) { 1779 vnode->busy = false; 1780 vnode->unpublished = false; 1781 } else if (vnode == NULL && privateNode != NULL) { 1782 status = create_new_vnode(&vnode, mountID, vnodeID); 1783 if (status == FSSH_B_OK) 1784 vnode->private_node = privateNode; 1785 } else 1786 status = FSSH_B_BAD_VALUE; 1787 1788 TRACE(("returns: %s\n", strerror(status))); 1789 1790 mutex_unlock(&sVnodeMutex); 1791 return status; 1792 } 1793 1794 1795 extern "C" fssh_status_t 1796 fssh_get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, 1797 fssh_fs_vnode *_fsNode) 1798 { 1799 struct vnode *vnode; 1800 1801 fssh_status_t status = get_vnode(mountID, vnodeID, &vnode, true); 1802 if (status < FSSH_B_OK) 1803 return status; 1804 1805 *_fsNode = vnode->private_node; 1806 return FSSH_B_OK; 1807 } 1808 1809 1810 extern "C" fssh_status_t 1811 fssh_put_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID) 1812 { 1813 struct vnode *vnode; 1814 1815 mutex_lock(&sVnodeMutex); 1816 vnode = lookup_vnode(mountID, vnodeID); 1817 mutex_unlock(&sVnodeMutex); 1818 1819 if (vnode) 1820 dec_vnode_ref_count(vnode, true); 1821 1822 return FSSH_B_OK; 1823 } 1824 1825 1826 extern "C" fssh_status_t 1827 fssh_remove_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID) 1828 { 1829 struct vnode *vnode; 1830 bool remove = false; 1831 1832 mutex_lock(&sVnodeMutex); 1833 1834 vnode = lookup_vnode(mountID, vnodeID); 1835 if (vnode != NULL) { 1836 if (vnode->covered_by != NULL) { 1837 // this vnode is in use 1838 mutex_unlock(&sVnodeMutex); 1839 return FSSH_B_BUSY; 1840 } 1841 1842 vnode->remove = true; 1843 if (vnode->unpublished) { 1844 // prepare the vnode for deletion 1845 vnode->busy = true; 1846 remove = true; 1847 } 1848 } 1849 1850 mutex_unlock(&sVnodeMutex); 1851 1852 if (remove) { 1853 // if the vnode hasn't been published yet, we delete it here 1854 fssh_atomic_add(&vnode->ref_count, -1); 1855 free_vnode(vnode, true); 1856 } 1857 1858 return FSSH_B_OK; 1859 } 1860 1861 1862 extern "C" fssh_status_t 1863 fssh_unremove_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID) 1864 { 1865 struct vnode *vnode; 1866 1867 mutex_lock(&sVnodeMutex); 1868 1869 vnode = lookup_vnode(mountID, vnodeID); 1870 if (vnode) 1871 vnode->remove = false; 1872 1873 mutex_unlock(&sVnodeMutex); 1874 return FSSH_B_OK; 1875 } 1876 1877 1878 extern "C" fssh_status_t 1879 fssh_get_vnode_removed(fssh_mount_id mountID, fssh_vnode_id vnodeID, 1880 bool* removed) 1881 { 1882 mutex_lock(&sVnodeMutex); 1883 1884 fssh_status_t result; 1885 1886 if (struct vnode* vnode = lookup_vnode(mountID, vnodeID)) { 1887 if (removed) 1888 *removed = vnode->remove; 1889 result = FSSH_B_OK; 1890 } else 1891 result = FSSH_B_BAD_VALUE; 1892 1893 mutex_unlock(&sVnodeMutex); 1894 return result; 1895 } 1896 1897 1898 // #pragma mark - private VFS API 1899 // Functions the VFS exports for other parts of the kernel 1900 1901 1902 /** Acquires another reference to the vnode that has to be released 1903 * by calling vfs_put_vnode(). 1904 */ 1905 1906 void 1907 vfs_acquire_vnode(void *_vnode) 1908 { 1909 inc_vnode_ref_count((struct vnode *)_vnode); 1910 } 1911 1912 1913 /** This is currently called from file_cache_create() only. 1914 * It's probably a temporary solution as long as devfs requires that 1915 * fs_read_pages()/fs_write_pages() are called with the standard 1916 * open cookie and not with a device cookie. 1917 * If that's done differently, remove this call; it has no other 1918 * purpose. 1919 */ 1920 1921 fssh_status_t 1922 vfs_get_cookie_from_fd(int fd, void **_cookie) 1923 { 1924 struct file_descriptor *descriptor; 1925 1926 descriptor = get_fd(get_current_io_context(true), fd); 1927 if (descriptor == NULL) 1928 return FSSH_B_FILE_ERROR; 1929 1930 *_cookie = descriptor->cookie; 1931 return FSSH_B_OK; 1932 } 1933 1934 1935 int 1936 vfs_get_vnode_from_fd(int fd, bool kernel, void **vnode) 1937 { 1938 *vnode = get_vnode_from_fd(fd, kernel); 1939 1940 if (*vnode == NULL) 1941 return FSSH_B_FILE_ERROR; 1942 1943 return FSSH_B_NO_ERROR; 1944 } 1945 1946 1947 fssh_status_t 1948 vfs_get_vnode_from_path(const char *path, bool kernel, void **_vnode) 1949 { 1950 TRACE(("vfs_get_vnode_from_path: entry. path = '%s', kernel %d\n", path, kernel)); 1951 1952 KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); 1953 if (pathBuffer.InitCheck() != FSSH_B_OK) 1954 return FSSH_B_NO_MEMORY; 1955 1956 char *buffer = pathBuffer.LockBuffer(); 1957 fssh_strlcpy(buffer, path, pathBuffer.BufferSize()); 1958 1959 struct vnode *vnode; 1960 fssh_status_t status = path_to_vnode(buffer, true, &vnode, NULL, kernel); 1961 if (status < FSSH_B_OK) 1962 return status; 1963 1964 *_vnode = vnode; 1965 return FSSH_B_OK; 1966 } 1967 1968 1969 fssh_status_t 1970 vfs_get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, void **_vnode) 1971 { 1972 struct vnode *vnode; 1973 1974 fssh_status_t status = get_vnode(mountID, vnodeID, &vnode, false); 1975 if (status < FSSH_B_OK) 1976 return status; 1977 1978 *_vnode = vnode; 1979 return FSSH_B_OK; 1980 } 1981 1982 1983 fssh_status_t 1984 vfs_entry_ref_to_vnode(fssh_mount_id mountID, fssh_vnode_id directoryID, 1985 const char *name, void **_vnode) 1986 { 1987 return entry_ref_to_vnode(mountID, directoryID, name, (struct vnode **)_vnode); 1988 } 1989 1990 1991 void 1992 vfssh_fs_vnode_to_node_ref(void *_vnode, fssh_mount_id *_mountID, fssh_vnode_id *_vnodeID) 1993 { 1994 struct vnode *vnode = (struct vnode *)_vnode; 1995 1996 *_mountID = vnode->device; 1997 *_vnodeID = vnode->id; 1998 } 1999 2000 2001 /** Looks up a vnode with the given mount and vnode ID. 2002 * Must only be used with "in-use" vnodes as it doesn't grab a reference 2003 * to the node. 2004 * It's currently only be used by file_cache_create(). 2005 */ 2006 2007 fssh_status_t 2008 vfs_lookup_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, void **_vnode) 2009 { 2010 mutex_lock(&sVnodeMutex); 2011 struct vnode *vnode = lookup_vnode(mountID, vnodeID); 2012 mutex_unlock(&sVnodeMutex); 2013 2014 if (vnode == NULL) 2015 return FSSH_B_ERROR; 2016 2017 *_vnode = vnode; 2018 return FSSH_B_OK; 2019 } 2020 2021 2022 fssh_status_t 2023 vfs_get_fs_node_from_path(fssh_mount_id mountID, const char *path, bool kernel, void **_node) 2024 { 2025 TRACE(("vfs_get_fs_node_from_path(mountID = %ld, path = \"%s\", kernel %d)\n", 2026 mountID, path, kernel)); 2027 2028 KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); 2029 if (pathBuffer.InitCheck() != FSSH_B_OK) 2030 return FSSH_B_NO_MEMORY; 2031 2032 fs_mount *mount; 2033 fssh_status_t status = get_mount(mountID, &mount); 2034 if (status < FSSH_B_OK) 2035 return status; 2036 2037 char *buffer = pathBuffer.LockBuffer(); 2038 fssh_strlcpy(buffer, path, pathBuffer.BufferSize()); 2039 2040 struct vnode *vnode = mount->root_vnode; 2041 2042 if (buffer[0] == '/') 2043 status = path_to_vnode(buffer, true, &vnode, NULL, true); 2044 else { 2045 inc_vnode_ref_count(vnode); 2046 // vnode_path_to_vnode() releases a reference to the starting vnode 2047 status = vnode_path_to_vnode(vnode, buffer, true, 0, &vnode, NULL, NULL); 2048 } 2049 2050 put_mount(mount); 2051 2052 if (status < FSSH_B_OK) 2053 return status; 2054 2055 if (vnode->device != mountID) { 2056 // wrong mount ID - must not gain access on foreign file system nodes 2057 put_vnode(vnode); 2058 return FSSH_B_BAD_VALUE; 2059 } 2060 2061 *_node = vnode->private_node; 2062 return FSSH_B_OK; 2063 } 2064 2065 2066 /** Finds the full path to the file that contains the module \a moduleName, 2067 * puts it into \a pathBuffer, and returns FSSH_B_OK for success. 2068 * If \a pathBuffer was too small, it returns \c FSSH_B_BUFFER_OVERFLOW, 2069 * \c FSSH_B_ENTRY_NOT_FOUNT if no file could be found. 2070 * \a pathBuffer is clobbered in any case and must not be relied on if this 2071 * functions returns unsuccessfully. 2072 */ 2073 2074 fssh_status_t 2075 vfs_get_module_path(const char *basePath, const char *moduleName, char *pathBuffer, 2076 fssh_size_t bufferSize) 2077 { 2078 struct vnode *dir, *file; 2079 fssh_status_t status; 2080 fssh_size_t length; 2081 char *path; 2082 2083 if (bufferSize == 0 || fssh_strlcpy(pathBuffer, basePath, bufferSize) >= bufferSize) 2084 return FSSH_B_BUFFER_OVERFLOW; 2085 2086 status = path_to_vnode(pathBuffer, true, &dir, NULL, true); 2087 if (status < FSSH_B_OK) 2088 return status; 2089 2090 // the path buffer had been clobbered by the above call 2091 length = fssh_strlcpy(pathBuffer, basePath, bufferSize); 2092 if (pathBuffer[length - 1] != '/') 2093 pathBuffer[length++] = '/'; 2094 2095 path = pathBuffer + length; 2096 bufferSize -= length; 2097 2098 while (moduleName) { 2099 int type; 2100 2101 char *nextPath = fssh_strchr(moduleName, '/'); 2102 if (nextPath == NULL) 2103 length = fssh_strlen(moduleName); 2104 else { 2105 length = nextPath - moduleName; 2106 nextPath++; 2107 } 2108 2109 if (length + 1 >= bufferSize) { 2110 status = FSSH_B_BUFFER_OVERFLOW; 2111 goto err; 2112 } 2113 2114 fssh_memcpy(path, moduleName, length); 2115 path[length] = '\0'; 2116 moduleName = nextPath; 2117 2118 status = vnode_path_to_vnode(dir, path, true, 0, &file, NULL, &type); 2119 if (status < FSSH_B_OK) { 2120 // vnode_path_to_vnode() has already released the reference to dir 2121 return status; 2122 } 2123 2124 if (FSSH_S_ISDIR(type)) { 2125 // goto the next directory 2126 path[length] = '/'; 2127 path[length + 1] = '\0'; 2128 path += length + 1; 2129 bufferSize -= length + 1; 2130 2131 dir = file; 2132 } else if (FSSH_S_ISREG(type)) { 2133 // it's a file so it should be what we've searched for 2134 put_vnode(file); 2135 2136 return FSSH_B_OK; 2137 } else { 2138 TRACE(("vfs_get_module_path(): something is strange here: %d...\n", type)); 2139 status = FSSH_B_ERROR; 2140 dir = file; 2141 goto err; 2142 } 2143 } 2144 2145 // if we got here, the moduleName just pointed to a directory, not to 2146 // a real module - what should we do in this case? 2147 status = FSSH_B_ENTRY_NOT_FOUND; 2148 2149 err: 2150 put_vnode(dir); 2151 return status; 2152 } 2153 2154 2155 /** \brief Normalizes a given path. 2156 * 2157 * The path must refer to an existing or non-existing entry in an existing 2158 * directory, that is chopping off the leaf component the remaining path must 2159 * refer to an existing directory. 2160 * 2161 * The returned will be canonical in that it will be absolute, will not 2162 * contain any "." or ".." components or duplicate occurrences of '/'s, 2163 * and none of the directory components will by symbolic links. 2164 * 2165 * Any two paths referring to the same entry, will result in the same 2166 * normalized path (well, that is pretty much the definition of `normalized', 2167 * isn't it :-). 2168 * 2169 * \param path The path to be normalized. 2170 * \param buffer The buffer into which the normalized path will be written. 2171 * \param bufferSize The size of \a buffer. 2172 * \param kernel \c true, if the IO context of the kernel shall be used, 2173 * otherwise that of the team this thread belongs to. Only relevant, 2174 * if the path is relative (to get the CWD). 2175 * \return \c FSSH_B_OK if everything went fine, another error code otherwise. 2176 */ 2177 2178 fssh_status_t 2179 vfs_normalize_path(const char *path, char *buffer, fssh_size_t bufferSize, 2180 bool kernel) 2181 { 2182 if (!path || !buffer || bufferSize < 1) 2183 return FSSH_B_BAD_VALUE; 2184 2185 TRACE(("vfs_normalize_path(`%s')\n", path)); 2186 2187 // copy the supplied path to the stack, so it can be modified 2188 KPath mutablePathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); 2189 if (mutablePathBuffer.InitCheck() != FSSH_B_OK) 2190 return FSSH_B_NO_MEMORY; 2191 2192 char *mutablePath = mutablePathBuffer.LockBuffer(); 2193 if (fssh_strlcpy(mutablePath, path, FSSH_B_PATH_NAME_LENGTH) >= FSSH_B_PATH_NAME_LENGTH) 2194 return FSSH_B_NAME_TOO_LONG; 2195 2196 // get the dir vnode and the leaf name 2197 struct vnode *dirNode; 2198 char leaf[FSSH_B_FILE_NAME_LENGTH]; 2199 fssh_status_t error = path_to_dir_vnode(mutablePath, &dirNode, leaf, kernel); 2200 if (error != FSSH_B_OK) { 2201 TRACE(("vfs_normalize_path(): failed to get dir vnode: %s\n", strerror(error))); 2202 return error; 2203 } 2204 2205 // if the leaf is "." or "..", we directly get the correct directory 2206 // vnode and ignore the leaf later 2207 bool isDir = (fssh_strcmp(leaf, ".") == 0 || fssh_strcmp(leaf, "..") == 0); 2208 if (isDir) 2209 error = vnode_path_to_vnode(dirNode, leaf, false, 0, &dirNode, NULL, NULL); 2210 if (error != FSSH_B_OK) { 2211 TRACE(("vfs_normalize_path(): failed to get dir vnode for \".\" or \"..\": %s\n", 2212 strerror(error))); 2213 return error; 2214 } 2215 2216 // get the directory path 2217 error = dir_vnode_to_path(dirNode, buffer, bufferSize); 2218 put_vnode(dirNode); 2219 if (error < FSSH_B_OK) { 2220 TRACE(("vfs_normalize_path(): failed to get dir path: %s\n", strerror(error))); 2221 return error; 2222 } 2223 2224 // append the leaf name 2225 if (!isDir) { 2226 // insert a directory separator only if this is not the file system root 2227 if ((fssh_strcmp(buffer, "/") != 0 2228 && fssh_strlcat(buffer, "/", bufferSize) >= bufferSize) 2229 || fssh_strlcat(buffer, leaf, bufferSize) >= bufferSize) { 2230 return FSSH_B_NAME_TOO_LONG; 2231 } 2232 } 2233 2234 TRACE(("vfs_normalize_path() -> `%s'\n", buffer)); 2235 return FSSH_B_OK; 2236 } 2237 2238 2239 void 2240 vfs_put_vnode(void *_vnode) 2241 { 2242 put_vnode((struct vnode *)_vnode); 2243 } 2244 2245 2246 fssh_status_t 2247 vfs_get_cwd(fssh_mount_id *_mountID, fssh_vnode_id *_vnodeID) 2248 { 2249 // Get current working directory from io context 2250 struct io_context *context = get_current_io_context(false); 2251 fssh_status_t status = FSSH_B_OK; 2252 2253 mutex_lock(&context->io_mutex); 2254 2255 if (context->cwd != NULL) { 2256 *_mountID = context->cwd->device; 2257 *_vnodeID = context->cwd->id; 2258 } else 2259 status = FSSH_B_ERROR; 2260 2261 mutex_unlock(&context->io_mutex); 2262 return status; 2263 } 2264 2265 2266 fssh_status_t 2267 vfs_get_file_map(void *_vnode, fssh_off_t offset, fssh_size_t size, 2268 fssh_file_io_vec *vecs, fssh_size_t *_count) 2269 { 2270 struct vnode *vnode = (struct vnode *)_vnode; 2271 2272 FUNCTION(("vfs_get_file_map: vnode %p, vecs %p, offset %lld, size = %u\n", vnode, vecs, offset, (unsigned)size)); 2273 2274 return FS_CALL(vnode, get_file_map)(vnode->mount->cookie, vnode->private_node, offset, size, vecs, _count); 2275 } 2276 2277 2278 fssh_status_t 2279 vfs_stat_vnode(void *_vnode, struct fssh_stat *stat) 2280 { 2281 struct vnode *vnode = (struct vnode *)_vnode; 2282 2283 fssh_status_t status = FS_CALL(vnode, read_stat)(vnode->mount->cookie, 2284 vnode->private_node, stat); 2285 2286 // fill in the st_dev and st_ino fields 2287 if (status == FSSH_B_OK) { 2288 stat->fssh_st_dev = vnode->device; 2289 stat->fssh_st_ino = vnode->id; 2290 } 2291 2292 return status; 2293 } 2294 2295 2296 fssh_status_t 2297 vfs_get_vnode_name(void *_vnode, char *name, fssh_size_t nameSize) 2298 { 2299 return get_vnode_name((struct vnode *)_vnode, NULL, name, nameSize); 2300 } 2301 2302 2303 /** If the given descriptor locked its vnode, that lock will be released. 2304 */ 2305 2306 void 2307 vfs_unlock_vnode_if_locked(struct file_descriptor *descriptor) 2308 { 2309 struct vnode *vnode = fd_vnode(descriptor); 2310 2311 if (vnode != NULL && vnode->mandatory_locked_by == descriptor) 2312 vnode->mandatory_locked_by = NULL; 2313 } 2314 2315 2316 /** Closes all file descriptors of the specified I/O context that 2317 * don't have the FSSH_O_CLOEXEC flag set. 2318 */ 2319 2320 void 2321 vfs_exec_io_context(void *_context) 2322 { 2323 struct io_context *context = (struct io_context *)_context; 2324 uint32_t i; 2325 2326 for (i = 0; i < context->table_size; i++) { 2327 mutex_lock(&context->io_mutex); 2328 2329 struct file_descriptor *descriptor = context->fds[i]; 2330 bool remove = false; 2331 2332 if (descriptor != NULL && fd_close_on_exec(context, i)) { 2333 context->fds[i] = NULL; 2334 context->num_used_fds--; 2335 2336 remove = true; 2337 } 2338 2339 mutex_unlock(&context->io_mutex); 2340 2341 if (remove) { 2342 close_fd(descriptor); 2343 put_fd(descriptor); 2344 } 2345 } 2346 } 2347 2348 2349 /** Sets up a new io_control structure, and inherits the properties 2350 * of the parent io_control if it is given. 2351 */ 2352 2353 void * 2354 vfs_new_io_context(void *_parentContext) 2355 { 2356 fssh_size_t tableSize; 2357 struct io_context *context; 2358 struct io_context *parentContext; 2359 2360 context = (io_context *)malloc(sizeof(struct io_context)); 2361 if (context == NULL) 2362 return NULL; 2363 2364 fssh_memset(context, 0, sizeof(struct io_context)); 2365 2366 parentContext = (struct io_context *)_parentContext; 2367 if (parentContext) 2368 tableSize = parentContext->table_size; 2369 else 2370 tableSize = DEFAULT_FD_TABLE_SIZE; 2371 2372 // allocate space for FDs and their close-on-exec flag 2373 context->fds = (file_descriptor **)malloc(sizeof(struct file_descriptor *) * tableSize 2374 + (tableSize + 7) / 8); 2375 if (context->fds == NULL) { 2376 free(context); 2377 return NULL; 2378 } 2379 2380 fssh_memset(context->fds, 0, sizeof(struct file_descriptor *) * tableSize 2381 + (tableSize + 7) / 8); 2382 context->fds_close_on_exec = (uint8_t *)(context->fds + tableSize); 2383 2384 if (mutex_init(&context->io_mutex, "I/O context") < 0) { 2385 free(context->fds); 2386 free(context); 2387 return NULL; 2388 } 2389 2390 // Copy all parent files which don't have the FSSH_O_CLOEXEC flag set 2391 2392 if (parentContext) { 2393 fssh_size_t i; 2394 2395 mutex_lock(&parentContext->io_mutex); 2396 2397 context->cwd = parentContext->cwd; 2398 if (context->cwd) 2399 inc_vnode_ref_count(context->cwd); 2400 2401 for (i = 0; i < tableSize; i++) { 2402 struct file_descriptor *descriptor = parentContext->fds[i]; 2403 2404 if (descriptor != NULL && !fd_close_on_exec(parentContext, i)) { 2405 context->fds[i] = descriptor; 2406 context->num_used_fds++; 2407 fssh_atomic_add(&descriptor->ref_count, 1); 2408 fssh_atomic_add(&descriptor->open_count, 1); 2409 } 2410 } 2411 2412 mutex_unlock(&parentContext->io_mutex); 2413 } else { 2414 context->cwd = sRoot; 2415 2416 if (context->cwd) 2417 inc_vnode_ref_count(context->cwd); 2418 } 2419 2420 context->table_size = tableSize; 2421 2422 return context; 2423 } 2424 2425 2426 fssh_status_t 2427 vfs_free_io_context(void *_ioContext) 2428 { 2429 struct io_context *context = (struct io_context *)_ioContext; 2430 uint32_t i; 2431 2432 if (context->cwd) 2433 dec_vnode_ref_count(context->cwd, false); 2434 2435 mutex_lock(&context->io_mutex); 2436 2437 for (i = 0; i < context->table_size; i++) { 2438 if (struct file_descriptor *descriptor = context->fds[i]) { 2439 close_fd(descriptor); 2440 put_fd(descriptor); 2441 } 2442 } 2443 2444 mutex_destroy(&context->io_mutex); 2445 2446 free(context->fds); 2447 free(context); 2448 2449 return FSSH_B_OK; 2450 } 2451 2452 2453 fssh_status_t 2454 vfs_init(kernel_args *args) 2455 { 2456 sVnodeTable = hash_init(VNODE_HASH_TABLE_SIZE, fssh_offsetof(struct vnode, next), 2457 &vnode_compare, &vnode_hash); 2458 if (sVnodeTable == NULL) 2459 fssh_panic("vfs_init: error creating vnode hash table\n"); 2460 2461 list_init_etc(&sUnusedVnodeList, fssh_offsetof(struct vnode, unused_link)); 2462 2463 sMountsTable = hash_init(MOUNTS_HASH_TABLE_SIZE, fssh_offsetof(struct fs_mount, next), 2464 &mount_compare, &mount_hash); 2465 if (sMountsTable == NULL) 2466 fssh_panic("vfs_init: error creating mounts hash table\n"); 2467 2468 sRoot = NULL; 2469 2470 if (mutex_init(&sFileSystemsMutex, "vfs_lock") < 0) 2471 fssh_panic("vfs_init: error allocating file systems lock\n"); 2472 2473 if (recursive_lock_init(&sMountOpLock, "vfs_mount_op_lock") < 0) 2474 fssh_panic("vfs_init: error allocating mount op lock\n"); 2475 2476 if (mutex_init(&sMountMutex, "vfs_mount_lock") < 0) 2477 fssh_panic("vfs_init: error allocating mount lock\n"); 2478 2479 if (mutex_init(&sVnodeCoveredByMutex, "vfs_vnode_covered_by_lock") < 0) 2480 fssh_panic("vfs_init: error allocating vnode::covered_by lock\n"); 2481 2482 if (mutex_init(&sVnodeMutex, "vfs_vnode_lock") < 0) 2483 fssh_panic("vfs_init: error allocating vnode lock\n"); 2484 2485 if (block_cache_init() != FSSH_B_OK) 2486 return FSSH_B_ERROR; 2487 2488 return file_cache_init(); 2489 } 2490 2491 2492 // #pragma mark - 2493 // The filetype-dependent implementations (fd_ops + open/create/rename/remove, ...) 2494 2495 2496 /** Calls fs_open() on the given vnode and returns a new 2497 * file descriptor for it 2498 */ 2499 2500 static int 2501 create_vnode(struct vnode *directory, const char *name, int openMode, int perms, bool kernel) 2502 { 2503 struct vnode *vnode; 2504 fssh_fs_cookie cookie; 2505 fssh_vnode_id newID; 2506 int status; 2507 2508 if (FS_CALL(directory, create) == NULL) 2509 return FSSH_EROFS; 2510 2511 status = FS_CALL(directory, create)(directory->mount->cookie, directory->private_node, name, openMode, perms, &cookie, &newID); 2512 if (status < FSSH_B_OK) 2513 return status; 2514 2515 mutex_lock(&sVnodeMutex); 2516 vnode = lookup_vnode(directory->device, newID); 2517 mutex_unlock(&sVnodeMutex); 2518 2519 if (vnode == NULL) { 2520 fssh_dprintf("vfs: fs_create() returned success but there is no vnode!"); 2521 return FSSH_EINVAL; 2522 } 2523 2524 if ((status = get_new_fd(FDTYPE_FILE, NULL, vnode, cookie, openMode, kernel)) >= 0) 2525 return status; 2526 2527 // something went wrong, clean up 2528 2529 FS_CALL(vnode, close)(vnode->mount->cookie, vnode->private_node, cookie); 2530 FS_CALL(vnode, free_cookie)(vnode->mount->cookie, vnode->private_node, cookie); 2531 put_vnode(vnode); 2532 2533 FS_CALL(directory, unlink)(directory->mount->cookie, directory->private_node, name); 2534 2535 return status; 2536 } 2537 2538 2539 /** Calls fs_open() on the given vnode and returns a new 2540 * file descriptor for it 2541 */ 2542 2543 static int 2544 open_vnode(struct vnode *vnode, int openMode, bool kernel) 2545 { 2546 fssh_fs_cookie cookie; 2547 int status; 2548 2549 status = FS_CALL(vnode, open)(vnode->mount->cookie, vnode->private_node, openMode, &cookie); 2550 if (status < 0) 2551 return status; 2552 2553 status = get_new_fd(FDTYPE_FILE, NULL, vnode, cookie, openMode, kernel); 2554 if (status < 0) { 2555 FS_CALL(vnode, close)(vnode->mount->cookie, vnode->private_node, cookie); 2556 FS_CALL(vnode, free_cookie)(vnode->mount->cookie, vnode->private_node, cookie); 2557 } 2558 return status; 2559 } 2560 2561 2562 /** Calls fs open_dir() on the given vnode and returns a new 2563 * file descriptor for it 2564 */ 2565 2566 static int 2567 open_dir_vnode(struct vnode *vnode, bool kernel) 2568 { 2569 fssh_fs_cookie cookie; 2570 int status; 2571 2572 status = FS_CALL(vnode, open_dir)(vnode->mount->cookie, vnode->private_node, &cookie); 2573 if (status < FSSH_B_OK) 2574 return status; 2575 2576 // file is opened, create a fd 2577 status = get_new_fd(FDTYPE_DIR, NULL, vnode, cookie, 0, kernel); 2578 if (status >= 0) 2579 return status; 2580 2581 FS_CALL(vnode, close_dir)(vnode->mount->cookie, vnode->private_node, cookie); 2582 FS_CALL(vnode, free_dir_cookie)(vnode->mount->cookie, vnode->private_node, cookie); 2583 2584 return status; 2585 } 2586 2587 2588 /** Calls fs open_attr_dir() on the given vnode and returns a new 2589 * file descriptor for it. 2590 * Used by attr_dir_open(), and attr_dir_open_fd(). 2591 */ 2592 2593 static int 2594 open_attr_dir_vnode(struct vnode *vnode, bool kernel) 2595 { 2596 fssh_fs_cookie cookie; 2597 int status; 2598 2599 if (FS_CALL(vnode, open_attr_dir) == NULL) 2600 return FSSH_EOPNOTSUPP; 2601 2602 status = FS_CALL(vnode, open_attr_dir)(vnode->mount->cookie, vnode->private_node, &cookie); 2603 if (status < 0) 2604 return status; 2605 2606 // file is opened, create a fd 2607 status = get_new_fd(FDTYPE_ATTR_DIR, NULL, vnode, cookie, 0, kernel); 2608 if (status >= 0) 2609 return status; 2610 2611 FS_CALL(vnode, close_attr_dir)(vnode->mount->cookie, vnode->private_node, cookie); 2612 FS_CALL(vnode, free_attr_dir_cookie)(vnode->mount->cookie, vnode->private_node, cookie); 2613 2614 return status; 2615 } 2616 2617 2618 static int 2619 file_create_entry_ref(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, int openMode, int perms, bool kernel) 2620 { 2621 struct vnode *directory; 2622 int status; 2623 2624 FUNCTION(("file_create_entry_ref: name = '%s', omode %x, perms %d, kernel %d\n", name, openMode, perms, kernel)); 2625 2626 // get directory to put the new file in 2627 status = get_vnode(mountID, directoryID, &directory, false); 2628 if (status < FSSH_B_OK) 2629 return status; 2630 2631 status = create_vnode(directory, name, openMode, perms, kernel); 2632 put_vnode(directory); 2633 2634 return status; 2635 } 2636 2637 2638 static int 2639 file_create(int fd, char *path, int openMode, int perms, bool kernel) 2640 { 2641 char name[FSSH_B_FILE_NAME_LENGTH]; 2642 struct vnode *directory; 2643 int status; 2644 2645 FUNCTION(("file_create: path '%s', omode %x, perms %d, kernel %d\n", path, openMode, perms, kernel)); 2646 2647 // get directory to put the new file in 2648 status = fd_and_path_to_dir_vnode(fd, path, &directory, name, kernel); 2649 if (status < 0) 2650 return status; 2651 2652 status = create_vnode(directory, name, openMode, perms, kernel); 2653 2654 put_vnode(directory); 2655 return status; 2656 } 2657 2658 2659 static int 2660 file_open_entry_ref(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, int openMode, bool kernel) 2661 { 2662 struct vnode *vnode; 2663 int status; 2664 2665 if (name == NULL || *name == '\0') 2666 return FSSH_B_BAD_VALUE; 2667 2668 FUNCTION(("file_open_entry_ref(ref = (%ld, %Ld, %s), openMode = %d)\n", 2669 mountID, directoryID, name, openMode)); 2670 2671 // get the vnode matching the entry_ref 2672 status = entry_ref_to_vnode(mountID, directoryID, name, &vnode); 2673 if (status < FSSH_B_OK) 2674 return status; 2675 2676 status = open_vnode(vnode, openMode, kernel); 2677 if (status < FSSH_B_OK) 2678 put_vnode(vnode); 2679 2680 return status; 2681 } 2682 2683 2684 static int 2685 file_open(int fd, char *path, int openMode, bool kernel) 2686 { 2687 int status = FSSH_B_OK; 2688 bool traverse = ((openMode & FSSH_O_NOTRAVERSE) == 0); 2689 2690 FUNCTION(("file_open: fd: %d, entry path = '%s', omode %d, kernel %d\n", 2691 fd, path, openMode, kernel)); 2692 2693 // get the vnode matching the vnode + path combination 2694 struct vnode *vnode = NULL; 2695 fssh_vnode_id parentID; 2696 status = fd_and_path_to_vnode(fd, path, traverse, &vnode, &parentID, kernel); 2697 if (status != FSSH_B_OK) 2698 return status; 2699 2700 // open the vnode 2701 status = open_vnode(vnode, openMode, kernel); 2702 // put only on error -- otherwise our reference was transferred to the FD 2703 if (status < FSSH_B_OK) 2704 put_vnode(vnode); 2705 2706 return status; 2707 } 2708 2709 2710 static fssh_status_t 2711 file_close(struct file_descriptor *descriptor) 2712 { 2713 struct vnode *vnode = descriptor->u.vnode; 2714 fssh_status_t status = FSSH_B_OK; 2715 2716 FUNCTION(("file_close(descriptor = %p)\n", descriptor)); 2717 2718 if (FS_CALL(vnode, close)) 2719 status = FS_CALL(vnode, close)(vnode->mount->cookie, vnode->private_node, descriptor->cookie); 2720 2721 return status; 2722 } 2723 2724 2725 static void 2726 file_free_fd(struct file_descriptor *descriptor) 2727 { 2728 struct vnode *vnode = descriptor->u.vnode; 2729 2730 if (vnode != NULL) { 2731 FS_CALL(vnode, free_cookie)(vnode->mount->cookie, vnode->private_node, descriptor->cookie); 2732 put_vnode(vnode); 2733 } 2734 } 2735 2736 2737 static fssh_status_t 2738 file_read(struct file_descriptor *descriptor, fssh_off_t pos, void *buffer, fssh_size_t *length) 2739 { 2740 struct vnode *vnode = descriptor->u.vnode; 2741 2742 FUNCTION(("file_read: buf %p, pos %Ld, len %p = %ld\n", buffer, pos, length, *length)); 2743 return FS_CALL(vnode, read)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, pos, buffer, length); 2744 } 2745 2746 2747 static fssh_status_t 2748 file_write(struct file_descriptor *descriptor, fssh_off_t pos, const void *buffer, fssh_size_t *length) 2749 { 2750 struct vnode *vnode = descriptor->u.vnode; 2751 2752 FUNCTION(("file_write: buf %p, pos %Ld, len %p\n", buffer, pos, length)); 2753 return FS_CALL(vnode, write)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, pos, buffer, length); 2754 } 2755 2756 2757 static fssh_off_t 2758 file_seek(struct file_descriptor *descriptor, fssh_off_t pos, int seekType) 2759 { 2760 fssh_off_t offset; 2761 2762 FUNCTION(("file_seek(pos = %Ld, seekType = %d)\n", pos, seekType)); 2763 // ToDo: seek should fail for pipes and FIFOs... 2764 2765 switch (seekType) { 2766 case FSSH_SEEK_SET: 2767 offset = 0; 2768 break; 2769 case FSSH_SEEK_CUR: 2770 offset = descriptor->pos; 2771 break; 2772 case FSSH_SEEK_END: 2773 { 2774 struct vnode *vnode = descriptor->u.vnode; 2775 struct fssh_stat stat; 2776 fssh_status_t status; 2777 2778 if (FS_CALL(vnode, read_stat) == NULL) 2779 return FSSH_EOPNOTSUPP; 2780 2781 status = FS_CALL(vnode, read_stat)(vnode->mount->cookie, vnode->private_node, &stat); 2782 if (status < FSSH_B_OK) 2783 return status; 2784 2785 offset = stat.fssh_st_size; 2786 break; 2787 } 2788 default: 2789 return FSSH_B_BAD_VALUE; 2790 } 2791 2792 // assumes fssh_off_t is 64 bits wide 2793 if (offset > 0 && LLONG_MAX - offset < pos) 2794 return FSSH_EOVERFLOW; 2795 2796 pos += offset; 2797 if (pos < 0) 2798 return FSSH_B_BAD_VALUE; 2799 2800 return descriptor->pos = pos; 2801 } 2802 2803 2804 static fssh_status_t 2805 dir_create_entry_ref(fssh_mount_id mountID, fssh_vnode_id parentID, const char *name, int perms, bool kernel) 2806 { 2807 struct vnode *vnode; 2808 fssh_vnode_id newID; 2809 fssh_status_t status; 2810 2811 if (name == NULL || *name == '\0') 2812 return FSSH_B_BAD_VALUE; 2813 2814 FUNCTION(("dir_create_entry_ref(dev = %ld, ino = %Ld, name = '%s', perms = %d)\n", mountID, parentID, name, perms)); 2815 2816 status = get_vnode(mountID, parentID, &vnode, kernel); 2817 if (status < FSSH_B_OK) 2818 return status; 2819 2820 if (FS_CALL(vnode, create_dir)) 2821 status = FS_CALL(vnode, create_dir)(vnode->mount->cookie, vnode->private_node, name, perms, &newID); 2822 else 2823 status = FSSH_EROFS; 2824 2825 put_vnode(vnode); 2826 return status; 2827 } 2828 2829 2830 static fssh_status_t 2831 dir_create(int fd, char *path, int perms, bool kernel) 2832 { 2833 char filename[FSSH_B_FILE_NAME_LENGTH]; 2834 struct vnode *vnode; 2835 fssh_vnode_id newID; 2836 fssh_status_t status; 2837 2838 FUNCTION(("dir_create: path '%s', perms %d, kernel %d\n", path, perms, kernel)); 2839 2840 status = fd_and_path_to_dir_vnode(fd, path, &vnode, filename, kernel); 2841 if (status < 0) 2842 return status; 2843 2844 if (FS_CALL(vnode, create_dir)) 2845 status = FS_CALL(vnode, create_dir)(vnode->mount->cookie, vnode->private_node, filename, perms, &newID); 2846 else 2847 status = FSSH_EROFS; 2848 2849 put_vnode(vnode); 2850 return status; 2851 } 2852 2853 2854 static int 2855 dir_open_entry_ref(fssh_mount_id mountID, fssh_vnode_id parentID, const char *name, bool kernel) 2856 { 2857 struct vnode *vnode; 2858 int status; 2859 2860 FUNCTION(("dir_open_entry_ref()\n")); 2861 2862 if (name && *name == '\0') 2863 return FSSH_B_BAD_VALUE; 2864 2865 // get the vnode matching the entry_ref/node_ref 2866 if (name) 2867 status = entry_ref_to_vnode(mountID, parentID, name, &vnode); 2868 else 2869 status = get_vnode(mountID, parentID, &vnode, false); 2870 if (status < FSSH_B_OK) 2871 return status; 2872 2873 status = open_dir_vnode(vnode, kernel); 2874 if (status < FSSH_B_OK) 2875 put_vnode(vnode); 2876 2877 return status; 2878 } 2879 2880 2881 static int 2882 dir_open(int fd, char *path, bool kernel) 2883 { 2884 int status = FSSH_B_OK; 2885 2886 FUNCTION(("dir_open: fd: %d, entry path = '%s', kernel %d\n", fd, path, kernel)); 2887 2888 // get the vnode matching the vnode + path combination 2889 struct vnode *vnode = NULL; 2890 fssh_vnode_id parentID; 2891 status = fd_and_path_to_vnode(fd, path, true, &vnode, &parentID, kernel); 2892 if (status != FSSH_B_OK) 2893 return status; 2894 2895 // open the dir 2896 status = open_dir_vnode(vnode, kernel); 2897 if (status < FSSH_B_OK) 2898 put_vnode(vnode); 2899 2900 return status; 2901 } 2902 2903 2904 static fssh_status_t 2905 dir_close(struct file_descriptor *descriptor) 2906 { 2907 struct vnode *vnode = descriptor->u.vnode; 2908 2909 FUNCTION(("dir_close(descriptor = %p)\n", descriptor)); 2910 2911 if (FS_CALL(vnode, close_dir)) 2912 return FS_CALL(vnode, close_dir)(vnode->mount->cookie, vnode->private_node, descriptor->cookie); 2913 2914 return FSSH_B_OK; 2915 } 2916 2917 2918 static void 2919 dir_free_fd(struct file_descriptor *descriptor) 2920 { 2921 struct vnode *vnode = descriptor->u.vnode; 2922 2923 if (vnode != NULL) { 2924 FS_CALL(vnode, free_dir_cookie)(vnode->mount->cookie, vnode->private_node, descriptor->cookie); 2925 put_vnode(vnode); 2926 } 2927 } 2928 2929 2930 static fssh_status_t 2931 dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer, 2932 fssh_size_t bufferSize, uint32_t *_count) 2933 { 2934 return dir_read(descriptor->u.vnode, descriptor->cookie, buffer, bufferSize, _count); 2935 } 2936 2937 2938 static void 2939 fix_dirent(struct vnode *parent, struct fssh_dirent *entry) 2940 { 2941 // set d_pdev and d_pino 2942 entry->d_pdev = parent->device; 2943 entry->d_pino = parent->id; 2944 2945 // If this is the ".." entry and the directory is the root of a FS, 2946 // we need to replace d_dev and d_ino with the actual values. 2947 if (fssh_strcmp(entry->d_name, "..") == 0 2948 && parent->mount->root_vnode == parent 2949 && parent->mount->covers_vnode) { 2950 inc_vnode_ref_count(parent); 2951 // vnode_path_to_vnode() puts the node 2952 2953 struct vnode *vnode; 2954 fssh_status_t status = vnode_path_to_vnode(parent, "..", false, 0, &vnode, 2955 NULL, NULL); 2956 2957 if (status == FSSH_B_OK) { 2958 entry->d_dev = vnode->device; 2959 entry->d_ino = vnode->id; 2960 } 2961 } else { 2962 // resolve mount points 2963 struct vnode *vnode = NULL; 2964 fssh_status_t status = get_vnode(entry->d_dev, entry->d_ino, &vnode, false); 2965 if (status != FSSH_B_OK) 2966 return; 2967 2968 mutex_lock(&sVnodeCoveredByMutex); 2969 if (vnode->covered_by) { 2970 entry->d_dev = vnode->covered_by->device; 2971 entry->d_ino = vnode->covered_by->id; 2972 } 2973 mutex_unlock(&sVnodeCoveredByMutex); 2974 2975 put_vnode(vnode); 2976 } 2977 } 2978 2979 2980 static fssh_status_t 2981 dir_read(struct vnode *vnode, fssh_fs_cookie cookie, struct fssh_dirent *buffer, 2982 fssh_size_t bufferSize, uint32_t *_count) 2983 { 2984 if (!FS_CALL(vnode, read_dir)) 2985 return FSSH_EOPNOTSUPP; 2986 2987 fssh_status_t error = FS_CALL(vnode, read_dir)(vnode->mount->cookie,vnode->private_node,cookie,buffer,bufferSize,_count); 2988 if (error != FSSH_B_OK) 2989 return error; 2990 2991 // we need to adjust the read dirents 2992 if (*_count > 0) { 2993 // XXX: Currently reading only one dirent is supported. Make this a loop! 2994 fix_dirent(vnode, buffer); 2995 } 2996 2997 return error; 2998 } 2999 3000 3001 static fssh_status_t 3002 dir_rewind(struct file_descriptor *descriptor) 3003 { 3004 struct vnode *vnode = descriptor->u.vnode; 3005 3006 if (FS_CALL(vnode, rewind_dir)) 3007 return FS_CALL(vnode, rewind_dir)(vnode->mount->cookie,vnode->private_node,descriptor->cookie); 3008 3009 return FSSH_EOPNOTSUPP; 3010 } 3011 3012 3013 static fssh_status_t 3014 dir_remove(int fd, char *path, bool kernel) 3015 { 3016 char name[FSSH_B_FILE_NAME_LENGTH]; 3017 struct vnode *directory; 3018 fssh_status_t status; 3019 3020 if (path != NULL) { 3021 // we need to make sure our path name doesn't stop with "/", ".", or ".." 3022 char *lastSlash = fssh_strrchr(path, '/'); 3023 if (lastSlash != NULL) { 3024 char *leaf = lastSlash + 1; 3025 if (!fssh_strcmp(leaf, "..")) 3026 return FSSH_B_NOT_ALLOWED; 3027 3028 // omit multiple slashes 3029 while (lastSlash > path && lastSlash[-1] == '/') { 3030 lastSlash--; 3031 } 3032 3033 if (!leaf[0] 3034 || !fssh_strcmp(leaf, ".")) { 3035 // "name/" -> "name", or "name/." -> "name" 3036 lastSlash[0] = '\0'; 3037 } 3038 } else if (!fssh_strcmp(path, "..")) 3039 return FSSH_B_NOT_ALLOWED; 3040 } 3041 3042 status = fd_and_path_to_dir_vnode(fd, path, &directory, name, kernel); 3043 if (status < FSSH_B_OK) 3044 return status; 3045 3046 if (FS_CALL(directory, remove_dir)) { 3047 status = FS_CALL(directory, remove_dir)(directory->mount->cookie, 3048 directory->private_node, name); 3049 } else 3050 status = FSSH_EROFS; 3051 3052 put_vnode(directory); 3053 return status; 3054 } 3055 3056 3057 static fssh_status_t 3058 common_ioctl(struct file_descriptor *descriptor, uint32_t op, void *buffer, 3059 fssh_size_t length) 3060 { 3061 struct vnode *vnode = descriptor->u.vnode; 3062 3063 if (FS_CALL(vnode, ioctl)) { 3064 return FS_CALL(vnode, ioctl)(vnode->mount->cookie, vnode->private_node, 3065 descriptor->cookie, op, buffer, length); 3066 } 3067 3068 return FSSH_EOPNOTSUPP; 3069 } 3070 3071 3072 static fssh_status_t 3073 common_fcntl(int fd, int op, uint32_t argument, bool kernel) 3074 { 3075 struct file_descriptor *descriptor; 3076 struct vnode *vnode; 3077 fssh_status_t status; 3078 3079 FUNCTION(("common_fcntl(fd = %d, op = %d, argument = %lx, %s)\n", 3080 fd, op, argument, kernel ? "kernel" : "user")); 3081 3082 descriptor = get_fd_and_vnode(fd, &vnode, kernel); 3083 if (descriptor == NULL) 3084 return FSSH_B_FILE_ERROR; 3085 3086 switch (op) { 3087 case FSSH_F_SETFD: 3088 { 3089 struct io_context *context = get_current_io_context(kernel); 3090 // Set file descriptor flags 3091 3092 // FSSH_O_CLOEXEC is the only flag available at this time 3093 mutex_lock(&context->io_mutex); 3094 fd_set_close_on_exec(context, fd, argument == FSSH_FD_CLOEXEC); 3095 mutex_unlock(&context->io_mutex); 3096 3097 status = FSSH_B_OK; 3098 break; 3099 } 3100 3101 case FSSH_F_GETFD: 3102 { 3103 struct io_context *context = get_current_io_context(kernel); 3104 3105 // Get file descriptor flags 3106 mutex_lock(&context->io_mutex); 3107 status = fd_close_on_exec(context, fd) ? FSSH_FD_CLOEXEC : 0; 3108 mutex_unlock(&context->io_mutex); 3109 break; 3110 } 3111 3112 case FSSH_F_SETFL: 3113 // Set file descriptor open mode 3114 if (FS_CALL(vnode, set_flags)) { 3115 // we only accept changes to FSSH_O_APPEND and FSSH_O_NONBLOCK 3116 argument &= FSSH_O_APPEND | FSSH_O_NONBLOCK; 3117 3118 status = FS_CALL(vnode, set_flags)(vnode->mount->cookie, 3119 vnode->private_node, descriptor->cookie, (int)argument); 3120 if (status == FSSH_B_OK) { 3121 // update this descriptor's open_mode field 3122 descriptor->open_mode = (descriptor->open_mode & ~(FSSH_O_APPEND | FSSH_O_NONBLOCK)) 3123 | argument; 3124 } 3125 } else 3126 status = FSSH_EOPNOTSUPP; 3127 break; 3128 3129 case FSSH_F_GETFL: 3130 // Get file descriptor open mode 3131 status = descriptor->open_mode; 3132 break; 3133 3134 case FSSH_F_DUPFD: 3135 { 3136 struct io_context *context = get_current_io_context(kernel); 3137 3138 status = new_fd_etc(context, descriptor, (int)argument); 3139 if (status >= 0) { 3140 mutex_lock(&context->io_mutex); 3141 fd_set_close_on_exec(context, fd, false); 3142 mutex_unlock(&context->io_mutex); 3143 3144 fssh_atomic_add(&descriptor->ref_count, 1); 3145 } 3146 break; 3147 } 3148 3149 case FSSH_F_GETLK: 3150 case FSSH_F_SETLK: 3151 case FSSH_F_SETLKW: 3152 status = FSSH_B_BAD_VALUE; 3153 break; 3154 3155 // ToDo: add support for more ops? 3156 3157 default: 3158 status = FSSH_B_BAD_VALUE; 3159 } 3160 3161 put_fd(descriptor); 3162 return status; 3163 } 3164 3165 3166 static fssh_status_t 3167 common_sync(int fd, bool kernel) 3168 { 3169 struct file_descriptor *descriptor; 3170 struct vnode *vnode; 3171 fssh_status_t status; 3172 3173 FUNCTION(("common_fsync: entry. fd %d kernel %d\n", fd, kernel)); 3174 3175 descriptor = get_fd_and_vnode(fd, &vnode, kernel); 3176 if (descriptor == NULL) 3177 return FSSH_B_FILE_ERROR; 3178 3179 if (FS_CALL(vnode, fsync) != NULL) 3180 status = FS_CALL(vnode, fsync)(vnode->mount->cookie, vnode->private_node); 3181 else 3182 status = FSSH_EOPNOTSUPP; 3183 3184 put_fd(descriptor); 3185 return status; 3186 } 3187 3188 3189 static fssh_status_t 3190 common_lock_node(int fd, bool kernel) 3191 { 3192 struct file_descriptor *descriptor; 3193 struct vnode *vnode; 3194 3195 descriptor = get_fd_and_vnode(fd, &vnode, kernel); 3196 if (descriptor == NULL) 3197 return FSSH_B_FILE_ERROR; 3198 3199 fssh_status_t status = FSSH_B_OK; 3200 3201 // We need to set the locking atomically - someone 3202 // else might set one at the same time 3203 #ifdef __x86_64__ 3204 if (fssh_atomic_test_and_set64((vint64_t *)&vnode->mandatory_locked_by, 3205 (fssh_addr_t)descriptor, 0) != 0) 3206 #else 3207 if (fssh_atomic_test_and_set((vint32_t *)&vnode->mandatory_locked_by, 3208 (fssh_addr_t)descriptor, 0) != 0) 3209 #endif 3210 status = FSSH_B_BUSY; 3211 3212 put_fd(descriptor); 3213 return status; 3214 } 3215 3216 3217 static fssh_status_t 3218 common_unlock_node(int fd, bool kernel) 3219 { 3220 struct file_descriptor *descriptor; 3221 struct vnode *vnode; 3222 3223 descriptor = get_fd_and_vnode(fd, &vnode, kernel); 3224 if (descriptor == NULL) 3225 return FSSH_B_FILE_ERROR; 3226 3227 fssh_status_t status = FSSH_B_OK; 3228 3229 // We need to set the locking atomically - someone 3230 // else might set one at the same time 3231 #ifdef __x86_64__ 3232 if (fssh_atomic_test_and_set64((vint64_t *)&vnode->mandatory_locked_by, 3233 0, (fssh_addr_t)descriptor) != (int64_t)descriptor) 3234 #else 3235 if (fssh_atomic_test_and_set((vint32_t *)&vnode->mandatory_locked_by, 3236 0, (fssh_addr_t)descriptor) != (int32_t)descriptor) 3237 #endif 3238 status = FSSH_B_BAD_VALUE; 3239 3240 put_fd(descriptor); 3241 return status; 3242 } 3243 3244 3245 static fssh_status_t 3246 common_read_link(int fd, char *path, char *buffer, fssh_size_t *_bufferSize, 3247 bool kernel) 3248 { 3249 struct vnode *vnode; 3250 fssh_status_t status; 3251 3252 status = fd_and_path_to_vnode(fd, path, false, &vnode, NULL, kernel); 3253 if (status < FSSH_B_OK) 3254 return status; 3255 3256 if (FS_CALL(vnode, read_symlink) != NULL) { 3257 status = FS_CALL(vnode, read_symlink)(vnode->mount->cookie, 3258 vnode->private_node, buffer, _bufferSize); 3259 } else 3260 status = FSSH_B_BAD_VALUE; 3261 3262 put_vnode(vnode); 3263 return status; 3264 } 3265 3266 3267 static fssh_status_t 3268 common_create_symlink(int fd, char *path, const char *toPath, int mode, 3269 bool kernel) 3270 { 3271 // path validity checks have to be in the calling function! 3272 char name[FSSH_B_FILE_NAME_LENGTH]; 3273 struct vnode *vnode; 3274 fssh_status_t status; 3275 3276 FUNCTION(("common_create_symlink(fd = %d, path = %s, toPath = %s, mode = %d, kernel = %d)\n", fd, path, toPath, mode, kernel)); 3277 3278 status = fd_and_path_to_dir_vnode(fd, path, &vnode, name, kernel); 3279 if (status < FSSH_B_OK) 3280 return status; 3281 3282 if (FS_CALL(vnode, create_symlink) != NULL) 3283 status = FS_CALL(vnode, create_symlink)(vnode->mount->cookie, vnode->private_node, name, toPath, mode); 3284 else 3285 status = FSSH_EROFS; 3286 3287 put_vnode(vnode); 3288 3289 return status; 3290 } 3291 3292 3293 static fssh_status_t 3294 common_create_link(char *path, char *toPath, bool kernel) 3295 { 3296 // path validity checks have to be in the calling function! 3297 char name[FSSH_B_FILE_NAME_LENGTH]; 3298 struct vnode *directory, *vnode; 3299 fssh_status_t status; 3300 3301 FUNCTION(("common_create_link(path = %s, toPath = %s, kernel = %d)\n", path, toPath, kernel)); 3302 3303 status = path_to_dir_vnode(path, &directory, name, kernel); 3304 if (status < FSSH_B_OK) 3305 return status; 3306 3307 status = path_to_vnode(toPath, true, &vnode, NULL, kernel); 3308 if (status < FSSH_B_OK) 3309 goto err; 3310 3311 if (directory->mount != vnode->mount) { 3312 status = FSSH_B_CROSS_DEVICE_LINK; 3313 goto err1; 3314 } 3315 3316 if (FS_CALL(vnode, link) != NULL) 3317 status = FS_CALL(vnode, link)(directory->mount->cookie, directory->private_node, name, vnode->private_node); 3318 else 3319 status = FSSH_EROFS; 3320 3321 err1: 3322 put_vnode(vnode); 3323 err: 3324 put_vnode(directory); 3325 3326 return status; 3327 } 3328 3329 3330 static fssh_status_t 3331 common_unlink(int fd, char *path, bool kernel) 3332 { 3333 char filename[FSSH_B_FILE_NAME_LENGTH]; 3334 struct vnode *vnode; 3335 fssh_status_t status; 3336 3337 FUNCTION(("common_unlink: fd: %d, path '%s', kernel %d\n", fd, path, kernel)); 3338 3339 status = fd_and_path_to_dir_vnode(fd, path, &vnode, filename, kernel); 3340 if (status < 0) 3341 return status; 3342 3343 if (FS_CALL(vnode, unlink) != NULL) 3344 status = FS_CALL(vnode, unlink)(vnode->mount->cookie, vnode->private_node, filename); 3345 else 3346 status = FSSH_EROFS; 3347 3348 put_vnode(vnode); 3349 3350 return status; 3351 } 3352 3353 3354 static fssh_status_t 3355 common_access(char *path, int mode, bool kernel) 3356 { 3357 struct vnode *vnode; 3358 fssh_status_t status; 3359 3360 status = path_to_vnode(path, true, &vnode, NULL, kernel); 3361 if (status < FSSH_B_OK) 3362 return status; 3363 3364 if (FS_CALL(vnode, access) != NULL) 3365 status = FS_CALL(vnode, access)(vnode->mount->cookie, vnode->private_node, mode); 3366 else 3367 status = FSSH_B_OK; 3368 3369 put_vnode(vnode); 3370 3371 return status; 3372 } 3373 3374 3375 static fssh_status_t 3376 common_rename(int fd, char *path, int newFD, char *newPath, bool kernel) 3377 { 3378 struct vnode *fromVnode, *toVnode; 3379 char fromName[FSSH_B_FILE_NAME_LENGTH]; 3380 char toName[FSSH_B_FILE_NAME_LENGTH]; 3381 fssh_status_t status; 3382 3383 FUNCTION(("common_rename(fd = %d, path = %s, newFD = %d, newPath = %s, kernel = %d)\n", fd, path, newFD, newPath, kernel)); 3384 3385 status = fd_and_path_to_dir_vnode(fd, path, &fromVnode, fromName, kernel); 3386 if (status < 0) 3387 return status; 3388 3389 status = fd_and_path_to_dir_vnode(newFD, newPath, &toVnode, toName, kernel); 3390 if (status < 0) 3391 goto err; 3392 3393 if (fromVnode->device != toVnode->device) { 3394 status = FSSH_B_CROSS_DEVICE_LINK; 3395 goto err1; 3396 } 3397 3398 if (FS_CALL(fromVnode, rename) != NULL) 3399 status = FS_CALL(fromVnode, rename)(fromVnode->mount->cookie, fromVnode->private_node, fromName, toVnode->private_node, toName); 3400 else 3401 status = FSSH_EROFS; 3402 3403 err1: 3404 put_vnode(toVnode); 3405 err: 3406 put_vnode(fromVnode); 3407 3408 return status; 3409 } 3410 3411 3412 static fssh_status_t 3413 common_read_stat(struct file_descriptor *descriptor, struct fssh_stat *stat) 3414 { 3415 struct vnode *vnode = descriptor->u.vnode; 3416 3417 FUNCTION(("common_read_stat: stat %p\n", stat)); 3418 3419 fssh_status_t status = FS_CALL(vnode, read_stat)(vnode->mount->cookie, 3420 vnode->private_node, stat); 3421 3422 // fill in the st_dev and st_ino fields 3423 if (status == FSSH_B_OK) { 3424 stat->fssh_st_dev = vnode->device; 3425 stat->fssh_st_ino = vnode->id; 3426 } 3427 3428 return status; 3429 } 3430 3431 3432 static fssh_status_t 3433 common_write_stat(struct file_descriptor *descriptor, 3434 const struct fssh_stat *stat, int statMask) 3435 { 3436 struct vnode *vnode = descriptor->u.vnode; 3437 3438 FUNCTION(("common_write_stat(vnode = %p, stat = %p, statMask = %d)\n", vnode, stat, statMask)); 3439 if (!FS_CALL(vnode, write_stat)) 3440 return FSSH_EROFS; 3441 3442 return FS_CALL(vnode, write_stat)(vnode->mount->cookie, vnode->private_node, stat, statMask); 3443 } 3444 3445 3446 static fssh_status_t 3447 common_path_read_stat(int fd, char *path, bool traverseLeafLink, 3448 struct fssh_stat *stat, bool kernel) 3449 { 3450 struct vnode *vnode; 3451 fssh_status_t status; 3452 3453 FUNCTION(("common_path_read_stat: fd: %d, path '%s', stat %p,\n", fd, path, stat)); 3454 3455 status = fd_and_path_to_vnode(fd, path, traverseLeafLink, &vnode, NULL, kernel); 3456 if (status < 0) 3457 return status; 3458 3459 status = FS_CALL(vnode, read_stat)(vnode->mount->cookie, vnode->private_node, stat); 3460 3461 // fill in the st_dev and st_ino fields 3462 if (status == FSSH_B_OK) { 3463 stat->fssh_st_dev = vnode->device; 3464 stat->fssh_st_ino = vnode->id; 3465 } 3466 3467 put_vnode(vnode); 3468 return status; 3469 } 3470 3471 3472 static fssh_status_t 3473 common_path_write_stat(int fd, char *path, bool traverseLeafLink, 3474 const struct fssh_stat *stat, int statMask, bool kernel) 3475 { 3476 struct vnode *vnode; 3477 fssh_status_t status; 3478 3479 FUNCTION(("common_write_stat: fd: %d, path '%s', stat %p, stat_mask %d, kernel %d\n", fd, path, stat, statMask, kernel)); 3480 3481 status = fd_and_path_to_vnode(fd, path, traverseLeafLink, &vnode, NULL, kernel); 3482 if (status < 0) 3483 return status; 3484 3485 if (FS_CALL(vnode, write_stat)) 3486 status = FS_CALL(vnode, write_stat)(vnode->mount->cookie, vnode->private_node, stat, statMask); 3487 else 3488 status = FSSH_EROFS; 3489 3490 put_vnode(vnode); 3491 3492 return status; 3493 } 3494 3495 3496 static int 3497 attr_dir_open(int fd, char *path, bool kernel) 3498 { 3499 struct vnode *vnode; 3500 int status; 3501 3502 FUNCTION(("attr_dir_open(fd = %d, path = '%s', kernel = %d)\n", fd, path, kernel)); 3503 3504 status = fd_and_path_to_vnode(fd, path, true, &vnode, NULL, kernel); 3505 if (status < FSSH_B_OK) 3506 return status; 3507 3508 status = open_attr_dir_vnode(vnode, kernel); 3509 if (status < 0) 3510 put_vnode(vnode); 3511 3512 return status; 3513 } 3514 3515 3516 static fssh_status_t 3517 attr_dir_close(struct file_descriptor *descriptor) 3518 { 3519 struct vnode *vnode = descriptor->u.vnode; 3520 3521 FUNCTION(("attr_dir_close(descriptor = %p)\n", descriptor)); 3522 3523 if (FS_CALL(vnode, close_attr_dir)) 3524 return FS_CALL(vnode, close_attr_dir)(vnode->mount->cookie, vnode->private_node, descriptor->cookie); 3525 3526 return FSSH_B_OK; 3527 } 3528 3529 3530 static void 3531 attr_dir_free_fd(struct file_descriptor *descriptor) 3532 { 3533 struct vnode *vnode = descriptor->u.vnode; 3534 3535 if (vnode != NULL) { 3536 FS_CALL(vnode, free_attr_dir_cookie)(vnode->mount->cookie, vnode->private_node, descriptor->cookie); 3537 put_vnode(vnode); 3538 } 3539 } 3540 3541 3542 static fssh_status_t 3543 attr_dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer, 3544 fssh_size_t bufferSize, uint32_t *_count) 3545 { 3546 struct vnode *vnode = descriptor->u.vnode; 3547 3548 FUNCTION(("attr_dir_read(descriptor = %p)\n", descriptor)); 3549 3550 if (FS_CALL(vnode, read_attr_dir)) 3551 return FS_CALL(vnode, read_attr_dir)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, buffer, bufferSize, _count); 3552 3553 return FSSH_EOPNOTSUPP; 3554 } 3555 3556 3557 static fssh_status_t 3558 attr_dir_rewind(struct file_descriptor *descriptor) 3559 { 3560 struct vnode *vnode = descriptor->u.vnode; 3561 3562 FUNCTION(("attr_dir_rewind(descriptor = %p)\n", descriptor)); 3563 3564 if (FS_CALL(vnode, rewind_attr_dir)) 3565 return FS_CALL(vnode, rewind_attr_dir)(vnode->mount->cookie, vnode->private_node, descriptor->cookie); 3566 3567 return FSSH_EOPNOTSUPP; 3568 } 3569 3570 3571 static int 3572 attr_create(int fd, const char *name, uint32_t type, int openMode, bool kernel) 3573 { 3574 struct vnode *vnode; 3575 fssh_fs_cookie cookie; 3576 int status; 3577 3578 if (name == NULL || *name == '\0') 3579 return FSSH_B_BAD_VALUE; 3580 3581 vnode = get_vnode_from_fd(fd, kernel); 3582 if (vnode == NULL) 3583 return FSSH_B_FILE_ERROR; 3584 3585 if (FS_CALL(vnode, create_attr) == NULL) { 3586 status = FSSH_EROFS; 3587 goto err; 3588 } 3589 3590 status = FS_CALL(vnode, create_attr)(vnode->mount->cookie, vnode->private_node, name, type, openMode, &cookie); 3591 if (status < FSSH_B_OK) 3592 goto err; 3593 3594 if ((status = get_new_fd(FDTYPE_ATTR, NULL, vnode, cookie, openMode, kernel)) >= 0) 3595 return status; 3596 3597 FS_CALL(vnode, close_attr)(vnode->mount->cookie, vnode->private_node, cookie); 3598 FS_CALL(vnode, free_attr_cookie)(vnode->mount->cookie, vnode->private_node, cookie); 3599 3600 FS_CALL(vnode, remove_attr)(vnode->mount->cookie, vnode->private_node, name); 3601 3602 err: 3603 put_vnode(vnode); 3604 3605 return status; 3606 } 3607 3608 3609 static int 3610 attr_open(int fd, const char *name, int openMode, bool kernel) 3611 { 3612 struct vnode *vnode; 3613 fssh_fs_cookie cookie; 3614 int status; 3615 3616 if (name == NULL || *name == '\0') 3617 return FSSH_B_BAD_VALUE; 3618 3619 vnode = get_vnode_from_fd(fd, kernel); 3620 if (vnode == NULL) 3621 return FSSH_B_FILE_ERROR; 3622 3623 if (FS_CALL(vnode, open_attr) == NULL) { 3624 status = FSSH_EOPNOTSUPP; 3625 goto err; 3626 } 3627 3628 status = FS_CALL(vnode, open_attr)(vnode->mount->cookie, vnode->private_node, name, openMode, &cookie); 3629 if (status < FSSH_B_OK) 3630 goto err; 3631 3632 // now we only need a file descriptor for this attribute and we're done 3633 if ((status = get_new_fd(FDTYPE_ATTR, NULL, vnode, cookie, openMode, kernel)) >= 0) 3634 return status; 3635 3636 FS_CALL(vnode, close_attr)(vnode->mount->cookie, vnode->private_node, cookie); 3637 FS_CALL(vnode, free_attr_cookie)(vnode->mount->cookie, vnode->private_node, cookie); 3638 3639 err: 3640 put_vnode(vnode); 3641 3642 return status; 3643 } 3644 3645 3646 static fssh_status_t 3647 attr_close(struct file_descriptor *descriptor) 3648 { 3649 struct vnode *vnode = descriptor->u.vnode; 3650 3651 FUNCTION(("attr_close(descriptor = %p)\n", descriptor)); 3652 3653 if (FS_CALL(vnode, close_attr)) 3654 return FS_CALL(vnode, close_attr)(vnode->mount->cookie, vnode->private_node, descriptor->cookie); 3655 3656 return FSSH_B_OK; 3657 } 3658 3659 3660 static void 3661 attr_free_fd(struct file_descriptor *descriptor) 3662 { 3663 struct vnode *vnode = descriptor->u.vnode; 3664 3665 if (vnode != NULL) { 3666 FS_CALL(vnode, free_attr_cookie)(vnode->mount->cookie, vnode->private_node, descriptor->cookie); 3667 put_vnode(vnode); 3668 } 3669 } 3670 3671 3672 static fssh_status_t 3673 attr_read(struct file_descriptor *descriptor, fssh_off_t pos, void *buffer, fssh_size_t *length) 3674 { 3675 struct vnode *vnode = descriptor->u.vnode; 3676 3677 FUNCTION(("attr_read: buf %p, pos %Ld, len %p = %ld\n", buffer, pos, length, *length)); 3678 if (!FS_CALL(vnode, read_attr)) 3679 return FSSH_EOPNOTSUPP; 3680 3681 return FS_CALL(vnode, read_attr)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, pos, buffer, length); 3682 } 3683 3684 3685 static fssh_status_t 3686 attr_write(struct file_descriptor *descriptor, fssh_off_t pos, const void *buffer, fssh_size_t *length) 3687 { 3688 struct vnode *vnode = descriptor->u.vnode; 3689 3690 FUNCTION(("attr_write: buf %p, pos %Ld, len %p\n", buffer, pos, length)); 3691 if (!FS_CALL(vnode, write_attr)) 3692 return FSSH_EOPNOTSUPP; 3693 3694 return FS_CALL(vnode, write_attr)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, pos, buffer, length); 3695 } 3696 3697 3698 static fssh_off_t 3699 attr_seek(struct file_descriptor *descriptor, fssh_off_t pos, int seekType) 3700 { 3701 fssh_off_t offset; 3702 3703 switch (seekType) { 3704 case FSSH_SEEK_SET: 3705 offset = 0; 3706 break; 3707 case FSSH_SEEK_CUR: 3708 offset = descriptor->pos; 3709 break; 3710 case FSSH_SEEK_END: 3711 { 3712 struct vnode *vnode = descriptor->u.vnode; 3713 struct fssh_stat stat; 3714 fssh_status_t status; 3715 3716 if (FS_CALL(vnode, read_stat) == NULL) 3717 return FSSH_EOPNOTSUPP; 3718 3719 status = FS_CALL(vnode, read_attr_stat)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, &stat); 3720 if (status < FSSH_B_OK) 3721 return status; 3722 3723 offset = stat.fssh_st_size; 3724 break; 3725 } 3726 default: 3727 return FSSH_B_BAD_VALUE; 3728 } 3729 3730 // assumes fssh_off_t is 64 bits wide 3731 if (offset > 0 && LLONG_MAX - offset < pos) 3732 return FSSH_EOVERFLOW; 3733 3734 pos += offset; 3735 if (pos < 0) 3736 return FSSH_B_BAD_VALUE; 3737 3738 return descriptor->pos = pos; 3739 } 3740 3741 3742 static fssh_status_t 3743 attr_read_stat(struct file_descriptor *descriptor, struct fssh_stat *stat) 3744 { 3745 struct vnode *vnode = descriptor->u.vnode; 3746 3747 FUNCTION(("attr_read_stat: stat 0x%p\n", stat)); 3748 3749 if (!FS_CALL(vnode, read_attr_stat)) 3750 return FSSH_EOPNOTSUPP; 3751 3752 return FS_CALL(vnode, read_attr_stat)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, stat); 3753 } 3754 3755 3756 static fssh_status_t 3757 attr_write_stat(struct file_descriptor *descriptor, 3758 const struct fssh_stat *stat, int statMask) 3759 { 3760 struct vnode *vnode = descriptor->u.vnode; 3761 3762 FUNCTION(("attr_write_stat: stat = %p, statMask %d\n", stat, statMask)); 3763 3764 if (!FS_CALL(vnode, write_attr_stat)) 3765 return FSSH_EROFS; 3766 3767 return FS_CALL(vnode, write_attr_stat)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, stat, statMask); 3768 } 3769 3770 3771 static fssh_status_t 3772 attr_remove(int fd, const char *name, bool kernel) 3773 { 3774 struct file_descriptor *descriptor; 3775 struct vnode *vnode; 3776 fssh_status_t status; 3777 3778 if (name == NULL || *name == '\0') 3779 return FSSH_B_BAD_VALUE; 3780 3781 FUNCTION(("attr_remove: fd = %d, name = \"%s\", kernel %d\n", fd, name, kernel)); 3782 3783 descriptor = get_fd_and_vnode(fd, &vnode, kernel); 3784 if (descriptor == NULL) 3785 return FSSH_B_FILE_ERROR; 3786 3787 if (FS_CALL(vnode, remove_attr)) 3788 status = FS_CALL(vnode, remove_attr)(vnode->mount->cookie, vnode->private_node, name); 3789 else 3790 status = FSSH_EROFS; 3791 3792 put_fd(descriptor); 3793 3794 return status; 3795 } 3796 3797 3798 static fssh_status_t 3799 attr_rename(int fromfd, const char *fromName, int tofd, const char *toName, bool kernel) 3800 { 3801 struct file_descriptor *fromDescriptor, *toDescriptor; 3802 struct vnode *fromVnode, *toVnode; 3803 fssh_status_t status; 3804 3805 if (fromName == NULL || *fromName == '\0' || toName == NULL || *toName == '\0') 3806 return FSSH_B_BAD_VALUE; 3807 3808 FUNCTION(("attr_rename: from fd = %d, from name = \"%s\", to fd = %d, to name = \"%s\", kernel %d\n", fromfd, fromName, tofd, toName, kernel)); 3809 3810 fromDescriptor = get_fd_and_vnode(fromfd, &fromVnode, kernel); 3811 if (fromDescriptor == NULL) 3812 return FSSH_B_FILE_ERROR; 3813 3814 toDescriptor = get_fd_and_vnode(tofd, &toVnode, kernel); 3815 if (toDescriptor == NULL) { 3816 status = FSSH_B_FILE_ERROR; 3817 goto err; 3818 } 3819 3820 // are the files on the same volume? 3821 if (fromVnode->device != toVnode->device) { 3822 status = FSSH_B_CROSS_DEVICE_LINK; 3823 goto err1; 3824 } 3825 3826 if (FS_CALL(fromVnode, rename_attr)) 3827 status = FS_CALL(fromVnode, rename_attr)(fromVnode->mount->cookie, fromVnode->private_node, fromName, toVnode->private_node, toName); 3828 else 3829 status = FSSH_EROFS; 3830 3831 err1: 3832 put_fd(toDescriptor); 3833 err: 3834 put_fd(fromDescriptor); 3835 3836 return status; 3837 } 3838 3839 3840 static fssh_status_t 3841 index_dir_open(fssh_mount_id mountID, bool kernel) 3842 { 3843 struct fs_mount *mount; 3844 fssh_fs_cookie cookie; 3845 3846 FUNCTION(("index_dir_open(mountID = %ld, kernel = %d)\n", mountID, kernel)); 3847 3848 fssh_status_t status = get_mount(mountID, &mount); 3849 if (status < FSSH_B_OK) 3850 return status; 3851 3852 if (FS_MOUNT_CALL(mount, open_index_dir) == NULL) { 3853 status = FSSH_EOPNOTSUPP; 3854 goto out; 3855 } 3856 3857 status = FS_MOUNT_CALL(mount, open_index_dir)(mount->cookie, &cookie); 3858 if (status < FSSH_B_OK) 3859 goto out; 3860 3861 // get fd for the index directory 3862 status = get_new_fd(FDTYPE_INDEX_DIR, mount, NULL, cookie, 0, kernel); 3863 if (status >= 0) 3864 goto out; 3865 3866 // something went wrong 3867 FS_MOUNT_CALL(mount, close_index_dir)(mount->cookie, cookie); 3868 FS_MOUNT_CALL(mount, free_index_dir_cookie)(mount->cookie, cookie); 3869 3870 out: 3871 put_mount(mount); 3872 return status; 3873 } 3874 3875 3876 static fssh_status_t 3877 index_dir_close(struct file_descriptor *descriptor) 3878 { 3879 struct fs_mount *mount = descriptor->u.mount; 3880 3881 FUNCTION(("index_dir_close(descriptor = %p)\n", descriptor)); 3882 3883 if (FS_MOUNT_CALL(mount, close_index_dir)) 3884 return FS_MOUNT_CALL(mount, close_index_dir)(mount->cookie, descriptor->cookie); 3885 3886 return FSSH_B_OK; 3887 } 3888 3889 3890 static void 3891 index_dir_free_fd(struct file_descriptor *descriptor) 3892 { 3893 struct fs_mount *mount = descriptor->u.mount; 3894 3895 if (mount != NULL) { 3896 FS_MOUNT_CALL(mount, free_index_dir_cookie)(mount->cookie, descriptor->cookie); 3897 // ToDo: find a replacement ref_count object - perhaps the root dir? 3898 //put_vnode(vnode); 3899 } 3900 } 3901 3902 3903 static fssh_status_t 3904 index_dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer, 3905 fssh_size_t bufferSize, uint32_t *_count) 3906 { 3907 struct fs_mount *mount = descriptor->u.mount; 3908 3909 if (FS_MOUNT_CALL(mount, read_index_dir)) 3910 return FS_MOUNT_CALL(mount, read_index_dir)(mount->cookie, descriptor->cookie, buffer, bufferSize, _count); 3911 3912 return FSSH_EOPNOTSUPP; 3913 } 3914 3915 3916 static fssh_status_t 3917 index_dir_rewind(struct file_descriptor *descriptor) 3918 { 3919 struct fs_mount *mount = descriptor->u.mount; 3920 3921 if (FS_MOUNT_CALL(mount, rewind_index_dir)) 3922 return FS_MOUNT_CALL(mount, rewind_index_dir)(mount->cookie, descriptor->cookie); 3923 3924 return FSSH_EOPNOTSUPP; 3925 } 3926 3927 3928 static fssh_status_t 3929 index_create(fssh_mount_id mountID, const char *name, uint32_t type, uint32_t flags, bool kernel) 3930 { 3931 FUNCTION(("index_create(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel)); 3932 3933 struct fs_mount *mount; 3934 fssh_status_t status = get_mount(mountID, &mount); 3935 if (status < FSSH_B_OK) 3936 return status; 3937 3938 if (FS_MOUNT_CALL(mount, create_index) == NULL) { 3939 status = FSSH_EROFS; 3940 goto out; 3941 } 3942 3943 status = FS_MOUNT_CALL(mount, create_index)(mount->cookie, name, type, flags); 3944 3945 out: 3946 put_mount(mount); 3947 return status; 3948 } 3949 3950 3951 static fssh_status_t 3952 index_name_read_stat(fssh_mount_id mountID, const char *name, 3953 struct fssh_stat *stat, bool kernel) 3954 { 3955 FUNCTION(("index_remove(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel)); 3956 3957 struct fs_mount *mount; 3958 fssh_status_t status = get_mount(mountID, &mount); 3959 if (status < FSSH_B_OK) 3960 return status; 3961 3962 if (FS_MOUNT_CALL(mount, read_index_stat) == NULL) { 3963 status = FSSH_EOPNOTSUPP; 3964 goto out; 3965 } 3966 3967 status = FS_MOUNT_CALL(mount, read_index_stat)(mount->cookie, name, stat); 3968 3969 out: 3970 put_mount(mount); 3971 return status; 3972 } 3973 3974 3975 static fssh_status_t 3976 index_remove(fssh_mount_id mountID, const char *name, bool kernel) 3977 { 3978 FUNCTION(("index_remove(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel)); 3979 3980 struct fs_mount *mount; 3981 fssh_status_t status = get_mount(mountID, &mount); 3982 if (status < FSSH_B_OK) 3983 return status; 3984 3985 if (FS_MOUNT_CALL(mount, remove_index) == NULL) { 3986 status = FSSH_EROFS; 3987 goto out; 3988 } 3989 3990 status = FS_MOUNT_CALL(mount, remove_index)(mount->cookie, name); 3991 3992 out: 3993 put_mount(mount); 3994 return status; 3995 } 3996 3997 3998 static fssh_status_t 3999 query_close(struct file_descriptor *descriptor) 4000 { 4001 struct fs_mount *mount = descriptor->u.mount; 4002 4003 FUNCTION(("query_close(descriptor = %p)\n", descriptor)); 4004 4005 if (FS_MOUNT_CALL(mount, close_query)) 4006 return FS_MOUNT_CALL(mount, close_query)(mount->cookie, descriptor->cookie); 4007 4008 return FSSH_B_OK; 4009 } 4010 4011 4012 static void 4013 query_free_fd(struct file_descriptor *descriptor) 4014 { 4015 struct fs_mount *mount = descriptor->u.mount; 4016 4017 if (mount != NULL) { 4018 FS_MOUNT_CALL(mount, free_query_cookie)(mount->cookie, descriptor->cookie); 4019 // ToDo: find a replacement ref_count object - perhaps the root dir? 4020 //put_vnode(vnode); 4021 } 4022 } 4023 4024 4025 static fssh_status_t 4026 query_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer, 4027 fssh_size_t bufferSize, uint32_t *_count) 4028 { 4029 struct fs_mount *mount = descriptor->u.mount; 4030 4031 if (FS_MOUNT_CALL(mount, read_query)) 4032 return FS_MOUNT_CALL(mount, read_query)(mount->cookie, descriptor->cookie, buffer, bufferSize, _count); 4033 4034 return FSSH_EOPNOTSUPP; 4035 } 4036 4037 4038 static fssh_status_t 4039 query_rewind(struct file_descriptor *descriptor) 4040 { 4041 struct fs_mount *mount = descriptor->u.mount; 4042 4043 if (FS_MOUNT_CALL(mount, rewind_query)) 4044 return FS_MOUNT_CALL(mount, rewind_query)(mount->cookie, descriptor->cookie); 4045 4046 return FSSH_EOPNOTSUPP; 4047 } 4048 4049 4050 // #pragma mark - 4051 // General File System functions 4052 4053 4054 static fssh_dev_t 4055 fs_mount(char *path, const char *device, const char *fsName, uint32_t flags, 4056 const char *args, bool kernel) 4057 { 4058 struct fs_mount *mount; 4059 fssh_status_t status = 0; 4060 4061 FUNCTION(("fs_mount: entry. path = '%s', fs_name = '%s'\n", path, fsName)); 4062 4063 // The path is always safe, we just have to make sure that fsName is 4064 // almost valid - we can't make any assumptions about args, though. 4065 // A NULL fsName is OK, if a device was given and the FS is not virtual. 4066 // We'll get it from the DDM later. 4067 if (fsName == NULL) { 4068 if (!device || flags & FSSH_B_MOUNT_VIRTUAL_DEVICE) 4069 return FSSH_B_BAD_VALUE; 4070 } else if (fsName[0] == '\0') 4071 return FSSH_B_BAD_VALUE; 4072 4073 RecursiveLocker mountOpLocker(sMountOpLock); 4074 4075 // If the file system is not a "virtual" one, the device argument should 4076 // point to a real file/device (if given at all). 4077 // get the partition 4078 KPath normalizedDevice; 4079 4080 if (!(flags & FSSH_B_MOUNT_VIRTUAL_DEVICE) && device) { 4081 // normalize the device path 4082 // status = normalizedDevice.SetTo(device, true); 4083 // NOTE: normalizing works only in our namespace. 4084 status = normalizedDevice.SetTo(device, false); 4085 if (status != FSSH_B_OK) 4086 return status; 4087 4088 device = normalizedDevice.Path(); 4089 // correct path to file device 4090 } 4091 4092 mount = (struct fs_mount *)malloc(sizeof(struct fs_mount)); 4093 if (mount == NULL) 4094 return FSSH_B_NO_MEMORY; 4095 4096 list_init_etc(&mount->vnodes, fssh_offsetof(struct vnode, mount_link)); 4097 4098 mount->fs_name = get_file_system_name(fsName); 4099 if (mount->fs_name == NULL) { 4100 status = FSSH_B_NO_MEMORY; 4101 goto err1; 4102 } 4103 4104 mount->device_name = fssh_strdup(device); 4105 // "device" can be NULL 4106 4107 mount->fs = get_file_system(fsName); 4108 if (mount->fs == NULL) { 4109 status = FSSH_ENODEV; 4110 goto err3; 4111 } 4112 4113 status = recursive_lock_init(&mount->rlock, "mount rlock"); 4114 if (status < FSSH_B_OK) 4115 goto err4; 4116 4117 // initialize structure 4118 mount->id = sNextMountID++; 4119 mount->root_vnode = NULL; 4120 mount->covers_vnode = NULL; 4121 mount->cookie = NULL; 4122 mount->unmounting = false; 4123 mount->owns_file_device = false; 4124 4125 // insert mount struct into list before we call FS's mount() function 4126 // so that vnodes can be created for this mount 4127 mutex_lock(&sMountMutex); 4128 hash_insert(sMountsTable, mount); 4129 mutex_unlock(&sMountMutex); 4130 4131 fssh_vnode_id rootID; 4132 4133 if (!sRoot) { 4134 // we haven't mounted anything yet 4135 if (fssh_strcmp(path, "/") != 0) { 4136 status = FSSH_B_ERROR; 4137 goto err5; 4138 } 4139 4140 status = FS_MOUNT_CALL(mount, mount)(mount->id, device, flags, args, &mount->cookie, &rootID); 4141 if (status < 0) { 4142 // ToDo: why should we hide the error code from the file system here? 4143 //status = ERR_VFS_GENERAL; 4144 goto err5; 4145 } 4146 } else { 4147 struct vnode *coveredVnode; 4148 status = path_to_vnode(path, true, &coveredVnode, NULL, kernel); 4149 if (status < FSSH_B_OK) 4150 goto err5; 4151 4152 // make sure covered_vnode is a DIR 4153 struct fssh_stat coveredNodeStat; 4154 status = FS_CALL(coveredVnode, read_stat)(coveredVnode->mount->cookie, 4155 coveredVnode->private_node, &coveredNodeStat); 4156 if (status < FSSH_B_OK) 4157 goto err5; 4158 4159 if (!FSSH_S_ISDIR(coveredNodeStat.fssh_st_mode)) { 4160 status = FSSH_B_NOT_A_DIRECTORY; 4161 goto err5; 4162 } 4163 4164 if (coveredVnode->mount->root_vnode == coveredVnode) { 4165 // this is already a mount point 4166 status = FSSH_B_BUSY; 4167 goto err5; 4168 } 4169 4170 mount->covers_vnode = coveredVnode; 4171 4172 // mount it 4173 status = FS_MOUNT_CALL(mount, mount)(mount->id, device, flags, args, &mount->cookie, &rootID); 4174 if (status < FSSH_B_OK) 4175 goto err6; 4176 } 4177 4178 // the root node is supposed to be owned by the file system - it must 4179 // exist at this point 4180 mount->root_vnode = lookup_vnode(mount->id, rootID); 4181 if (mount->root_vnode == NULL || mount->root_vnode->ref_count != 1) { 4182 fssh_panic("fs_mount: file system does not own its root node!\n"); 4183 status = FSSH_B_ERROR; 4184 goto err7; 4185 } 4186 4187 // No race here, since fs_mount() is the only function changing 4188 // covers_vnode (and holds sMountOpLock at that time). 4189 mutex_lock(&sVnodeCoveredByMutex); 4190 if (mount->covers_vnode) 4191 mount->covers_vnode->covered_by = mount->root_vnode; 4192 mutex_unlock(&sVnodeCoveredByMutex); 4193 4194 if (!sRoot) 4195 sRoot = mount->root_vnode; 4196 4197 return mount->id; 4198 4199 err7: 4200 FS_MOUNT_CALL(mount, unmount)(mount->cookie); 4201 err6: 4202 if (mount->covers_vnode) 4203 put_vnode(mount->covers_vnode); 4204 err5: 4205 mutex_lock(&sMountMutex); 4206 hash_remove(sMountsTable, mount); 4207 mutex_unlock(&sMountMutex); 4208 4209 recursive_lock_destroy(&mount->rlock); 4210 err4: 4211 put_file_system(mount->fs); 4212 free(mount->device_name); 4213 err3: 4214 free(mount->fs_name); 4215 err1: 4216 free(mount); 4217 4218 return status; 4219 } 4220 4221 4222 static fssh_status_t 4223 fs_unmount(char *path, uint32_t flags, bool kernel) 4224 { 4225 struct fs_mount *mount; 4226 struct vnode *vnode; 4227 fssh_status_t err; 4228 4229 FUNCTION(("vfs_unmount: entry. path = '%s', kernel %d\n", path, kernel)); 4230 4231 err = path_to_vnode(path, true, &vnode, NULL, kernel); 4232 if (err < 0) 4233 return FSSH_B_ENTRY_NOT_FOUND; 4234 4235 RecursiveLocker mountOpLocker(sMountOpLock); 4236 4237 mount = find_mount(vnode->device); 4238 if (!mount) 4239 fssh_panic("vfs_unmount: find_mount() failed on root vnode @%p of mount\n", vnode); 4240 4241 if (mount->root_vnode != vnode) { 4242 // not mountpoint 4243 put_vnode(vnode); 4244 return FSSH_B_BAD_VALUE; 4245 } 4246 4247 // grab the vnode master mutex to keep someone from creating 4248 // a vnode while we're figuring out if we can continue 4249 mutex_lock(&sVnodeMutex); 4250 4251 bool disconnectedDescriptors = false; 4252 4253 while (true) { 4254 bool busy = false; 4255 4256 // cycle through the list of vnodes associated with this mount and 4257 // make sure all of them are not busy or have refs on them 4258 vnode = NULL; 4259 while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) { 4260 // The root vnode ref_count needs to be 2 here: one for the file 4261 // system, one from the path_to_vnode() call above 4262 if (vnode->busy 4263 || ((vnode->ref_count != 0 && mount->root_vnode != vnode) 4264 || (vnode->ref_count != 2 && mount->root_vnode == vnode))) { 4265 // there are still vnodes in use on this mount, so we cannot 4266 // unmount yet 4267 busy = true; 4268 break; 4269 } 4270 } 4271 4272 if (!busy) 4273 break; 4274 4275 if ((flags & FSSH_B_FORCE_UNMOUNT) == 0) { 4276 mutex_unlock(&sVnodeMutex); 4277 put_vnode(mount->root_vnode); 4278 4279 return FSSH_B_BUSY; 4280 } 4281 4282 if (disconnectedDescriptors) { 4283 // wait a bit until the last access is finished, and then try again 4284 mutex_unlock(&sVnodeMutex); 4285 fssh_snooze(100000); 4286 // TODO: if there is some kind of bug that prevents the ref counts 4287 // from getting back to zero, this will fall into an endless loop... 4288 mutex_lock(&sVnodeMutex); 4289 continue; 4290 } 4291 4292 // the file system is still busy - but we're forced to unmount it, 4293 // so let's disconnect all open file descriptors 4294 4295 mount->unmounting = true; 4296 // prevent new vnodes from being created 4297 4298 mutex_unlock(&sVnodeMutex); 4299 4300 disconnect_mount_or_vnode_fds(mount, NULL); 4301 disconnectedDescriptors = true; 4302 4303 mutex_lock(&sVnodeMutex); 4304 } 4305 4306 // we can safely continue, mark all of the vnodes busy and this mount 4307 // structure in unmounting state 4308 mount->unmounting = true; 4309 4310 while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) { 4311 vnode->busy = true; 4312 4313 if (vnode->ref_count == 0) { 4314 // this vnode has been unused before 4315 list_remove_item(&sUnusedVnodeList, vnode); 4316 sUnusedVnodes--; 4317 } 4318 } 4319 4320 // The ref_count of the root node is 2 at this point, see above why this is 4321 mount->root_vnode->ref_count -= 2; 4322 4323 mutex_unlock(&sVnodeMutex); 4324 4325 mutex_lock(&sVnodeCoveredByMutex); 4326 mount->covers_vnode->covered_by = NULL; 4327 mutex_unlock(&sVnodeCoveredByMutex); 4328 put_vnode(mount->covers_vnode); 4329 4330 // Free all vnodes associated with this mount. 4331 // They will be removed from the mount list by free_vnode(), so 4332 // we don't have to do this. 4333 while ((vnode = (struct vnode *)list_get_first_item(&mount->vnodes)) != NULL) { 4334 free_vnode(vnode, false); 4335 } 4336 4337 // remove the mount structure from the hash table 4338 mutex_lock(&sMountMutex); 4339 hash_remove(sMountsTable, mount); 4340 mutex_unlock(&sMountMutex); 4341 4342 mountOpLocker.Unlock(); 4343 4344 FS_MOUNT_CALL(mount, unmount)(mount->cookie); 4345 4346 // release the file system 4347 put_file_system(mount->fs); 4348 4349 free(mount->device_name); 4350 free(mount->fs_name); 4351 free(mount); 4352 4353 return FSSH_B_OK; 4354 } 4355 4356 4357 static fssh_status_t 4358 fs_sync(fssh_dev_t device) 4359 { 4360 struct fs_mount *mount; 4361 fssh_status_t status = get_mount(device, &mount); 4362 if (status < FSSH_B_OK) 4363 return status; 4364 4365 mutex_lock(&sMountMutex); 4366 4367 if (FS_MOUNT_CALL(mount, sync)) 4368 status = FS_MOUNT_CALL(mount, sync)(mount->cookie); 4369 4370 mutex_unlock(&sMountMutex); 4371 4372 struct vnode *previousVnode = NULL; 4373 while (true) { 4374 // synchronize access to vnode list 4375 recursive_lock_lock(&mount->rlock); 4376 4377 struct vnode *vnode = (struct vnode *)list_get_next_item(&mount->vnodes, 4378 previousVnode); 4379 4380 fssh_vnode_id id = -1; 4381 if (vnode != NULL) 4382 id = vnode->id; 4383 4384 recursive_lock_unlock(&mount->rlock); 4385 4386 if (vnode == NULL) 4387 break; 4388 4389 // acquire a reference to the vnode 4390 4391 if (get_vnode(mount->id, id, &vnode, true) == FSSH_B_OK) { 4392 if (previousVnode != NULL) 4393 put_vnode(previousVnode); 4394 4395 if (FS_CALL(vnode, fsync) != NULL) 4396 FS_CALL(vnode, fsync)(vnode->mount->cookie, vnode->private_node); 4397 4398 // the next vnode might change until we lock the vnode list again, 4399 // but this vnode won't go away since we keep a reference to it. 4400 previousVnode = vnode; 4401 } else { 4402 fssh_dprintf("syncing of mount %d stopped due to vnode %lld.\n", (int)mount->id, id); 4403 break; 4404 } 4405 } 4406 4407 if (previousVnode != NULL) 4408 put_vnode(previousVnode); 4409 4410 put_mount(mount); 4411 return status; 4412 } 4413 4414 4415 static fssh_status_t 4416 fs_read_info(fssh_dev_t device, struct fssh_fs_info *info) 4417 { 4418 struct fs_mount *mount; 4419 fssh_status_t status = get_mount(device, &mount); 4420 if (status < FSSH_B_OK) 4421 return status; 4422 4423 fssh_memset(info, 0, sizeof(struct fssh_fs_info)); 4424 4425 if (FS_MOUNT_CALL(mount, read_fs_info)) 4426 status = FS_MOUNT_CALL(mount, read_fs_info)(mount->cookie, info); 4427 4428 // fill in info the file system doesn't (have to) know about 4429 if (status == FSSH_B_OK) { 4430 info->dev = mount->id; 4431 info->root = mount->root_vnode->id; 4432 fssh_strlcpy(info->fsh_name, mount->fs_name, sizeof(info->fsh_name)); 4433 if (mount->device_name != NULL) { 4434 fssh_strlcpy(info->device_name, mount->device_name, 4435 sizeof(info->device_name)); 4436 } 4437 } 4438 4439 // if the call is not supported by the file system, there are still 4440 // the parts that we filled out ourselves 4441 4442 put_mount(mount); 4443 return status; 4444 } 4445 4446 4447 static fssh_status_t 4448 fs_write_info(fssh_dev_t device, const struct fssh_fs_info *info, int mask) 4449 { 4450 struct fs_mount *mount; 4451 fssh_status_t status = get_mount(device, &mount); 4452 if (status < FSSH_B_OK) 4453 return status; 4454 4455 if (FS_MOUNT_CALL(mount, write_fs_info)) 4456 status = FS_MOUNT_CALL(mount, write_fs_info)(mount->cookie, info, mask); 4457 else 4458 status = FSSH_EROFS; 4459 4460 put_mount(mount); 4461 return status; 4462 } 4463 4464 4465 static fssh_dev_t 4466 fs_next_device(int32_t *_cookie) 4467 { 4468 struct fs_mount *mount = NULL; 4469 fssh_dev_t device = *_cookie; 4470 4471 mutex_lock(&sMountMutex); 4472 4473 // Since device IDs are assigned sequentially, this algorithm 4474 // does work good enough. It makes sure that the device list 4475 // returned is sorted, and that no device is skipped when an 4476 // already visited device got unmounted. 4477 4478 while (device < sNextMountID) { 4479 mount = find_mount(device++); 4480 if (mount != NULL && mount->cookie != NULL) 4481 break; 4482 } 4483 4484 *_cookie = device; 4485 4486 if (mount != NULL) 4487 device = mount->id; 4488 else 4489 device = FSSH_B_BAD_VALUE; 4490 4491 mutex_unlock(&sMountMutex); 4492 4493 return device; 4494 } 4495 4496 4497 static fssh_status_t 4498 get_cwd(char *buffer, fssh_size_t size, bool kernel) 4499 { 4500 // Get current working directory from io context 4501 struct io_context *context = get_current_io_context(kernel); 4502 fssh_status_t status; 4503 4504 FUNCTION(("vfs_get_cwd: buf %p, size %ld\n", buffer, size)); 4505 4506 mutex_lock(&context->io_mutex); 4507 4508 if (context->cwd) 4509 status = dir_vnode_to_path(context->cwd, buffer, size); 4510 else 4511 status = FSSH_B_ERROR; 4512 4513 mutex_unlock(&context->io_mutex); 4514 return status; 4515 } 4516 4517 4518 static fssh_status_t 4519 set_cwd(int fd, char *path, bool kernel) 4520 { 4521 struct io_context *context; 4522 struct vnode *vnode = NULL; 4523 struct vnode *oldDirectory; 4524 struct fssh_stat stat; 4525 fssh_status_t status; 4526 4527 FUNCTION(("set_cwd: path = \'%s\'\n", path)); 4528 4529 // Get vnode for passed path, and bail if it failed 4530 status = fd_and_path_to_vnode(fd, path, true, &vnode, NULL, kernel); 4531 if (status < 0) 4532 return status; 4533 4534 status = FS_CALL(vnode, read_stat)(vnode->mount->cookie, vnode->private_node, &stat); 4535 if (status < 0) 4536 goto err; 4537 4538 if (!FSSH_S_ISDIR(stat.fssh_st_mode)) { 4539 // nope, can't cwd to here 4540 status = FSSH_B_NOT_A_DIRECTORY; 4541 goto err; 4542 } 4543 4544 // Get current io context and lock 4545 context = get_current_io_context(kernel); 4546 mutex_lock(&context->io_mutex); 4547 4548 // save the old current working directory first 4549 oldDirectory = context->cwd; 4550 context->cwd = vnode; 4551 4552 mutex_unlock(&context->io_mutex); 4553 4554 if (oldDirectory) 4555 put_vnode(oldDirectory); 4556 4557 return FSSH_B_NO_ERROR; 4558 4559 err: 4560 put_vnode(vnode); 4561 return status; 4562 } 4563 4564 4565 // #pragma mark - 4566 // Calls from within the kernel 4567 4568 4569 fssh_dev_t 4570 _kern_mount(const char *path, const char *device, const char *fsName, 4571 uint32_t flags, const char *args, fssh_size_t argsLength) 4572 { 4573 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 4574 if (pathBuffer.InitCheck() != FSSH_B_OK) 4575 return FSSH_B_NO_MEMORY; 4576 4577 return fs_mount(pathBuffer.LockBuffer(), device, fsName, flags, args, true); 4578 } 4579 4580 4581 fssh_status_t 4582 _kern_unmount(const char *path, uint32_t flags) 4583 { 4584 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 4585 if (pathBuffer.InitCheck() != FSSH_B_OK) 4586 return FSSH_B_NO_MEMORY; 4587 4588 return fs_unmount(pathBuffer.LockBuffer(), flags, true); 4589 } 4590 4591 4592 fssh_status_t 4593 _kern_read_fs_info(fssh_dev_t device, struct fssh_fs_info *info) 4594 { 4595 if (info == NULL) 4596 return FSSH_B_BAD_VALUE; 4597 4598 return fs_read_info(device, info); 4599 } 4600 4601 4602 fssh_status_t 4603 _kern_write_fs_info(fssh_dev_t device, const struct fssh_fs_info *info, int mask) 4604 { 4605 if (info == NULL) 4606 return FSSH_B_BAD_VALUE; 4607 4608 return fs_write_info(device, info, mask); 4609 } 4610 4611 4612 fssh_status_t 4613 _kern_sync(void) 4614 { 4615 // Note: _kern_sync() is also called from _user_sync() 4616 int32_t cookie = 0; 4617 fssh_dev_t device; 4618 while ((device = fs_next_device(&cookie)) >= 0) { 4619 fssh_status_t status = fs_sync(device); 4620 if (status != FSSH_B_OK && status != FSSH_B_BAD_VALUE) 4621 fssh_dprintf("sync: device %d couldn't sync: %s\n", (int)device, fssh_strerror(status)); 4622 } 4623 4624 return FSSH_B_OK; 4625 } 4626 4627 4628 fssh_dev_t 4629 _kern_next_device(int32_t *_cookie) 4630 { 4631 return fs_next_device(_cookie); 4632 } 4633 4634 4635 int 4636 _kern_open_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name, int openMode, int perms) 4637 { 4638 if (openMode & FSSH_O_CREAT) 4639 return file_create_entry_ref(device, inode, name, openMode, perms, true); 4640 4641 return file_open_entry_ref(device, inode, name, openMode, true); 4642 } 4643 4644 4645 /** \brief Opens a node specified by a FD + path pair. 4646 * 4647 * At least one of \a fd and \a path must be specified. 4648 * If only \a fd is given, the function opens the node identified by this 4649 * FD. If only a path is given, this path is opened. If both are given and 4650 * the path is absolute, \a fd is ignored; a relative path is reckoned off 4651 * of the directory (!) identified by \a fd. 4652 * 4653 * \param fd The FD. May be < 0. 4654 * \param path The absolute or relative path. May be \c NULL. 4655 * \param openMode The open mode. 4656 * \return A FD referring to the newly opened node, or an error code, 4657 * if an error occurs. 4658 */ 4659 4660 int 4661 _kern_open(int fd, const char *path, int openMode, int perms) 4662 { 4663 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 4664 if (pathBuffer.InitCheck() != FSSH_B_OK) 4665 return FSSH_B_NO_MEMORY; 4666 4667 if (openMode & FSSH_O_CREAT) 4668 return file_create(fd, pathBuffer.LockBuffer(), openMode, perms, true); 4669 4670 return file_open(fd, pathBuffer.LockBuffer(), openMode, true); 4671 } 4672 4673 4674 /** \brief Opens a directory specified by entry_ref or node_ref. 4675 * 4676 * The supplied name may be \c NULL, in which case directory identified 4677 * by \a device and \a inode will be opened. Otherwise \a device and 4678 * \a inode identify the parent directory of the directory to be opened 4679 * and \a name its entry name. 4680 * 4681 * \param device If \a name is specified the ID of the device the parent 4682 * directory of the directory to be opened resides on, otherwise 4683 * the device of the directory itself. 4684 * \param inode If \a name is specified the node ID of the parent 4685 * directory of the directory to be opened, otherwise node ID of the 4686 * directory itself. 4687 * \param name The entry name of the directory to be opened. If \c NULL, 4688 * the \a device + \a inode pair identify the node to be opened. 4689 * \return The FD of the newly opened directory or an error code, if 4690 * something went wrong. 4691 */ 4692 4693 int 4694 _kern_open_dir_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name) 4695 { 4696 return dir_open_entry_ref(device, inode, name, true); 4697 } 4698 4699 4700 /** \brief Opens a directory specified by a FD + path pair. 4701 * 4702 * At least one of \a fd and \a path must be specified. 4703 * If only \a fd is given, the function opens the directory identified by this 4704 * FD. If only a path is given, this path is opened. If both are given and 4705 * the path is absolute, \a fd is ignored; a relative path is reckoned off 4706 * of the directory (!) identified by \a fd. 4707 * 4708 * \param fd The FD. May be < 0. 4709 * \param path The absolute or relative path. May be \c NULL. 4710 * \return A FD referring to the newly opened directory, or an error code, 4711 * if an error occurs. 4712 */ 4713 4714 int 4715 _kern_open_dir(int fd, const char *path) 4716 { 4717 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 4718 if (pathBuffer.InitCheck() != FSSH_B_OK) 4719 return FSSH_B_NO_MEMORY; 4720 4721 return dir_open(fd, pathBuffer.LockBuffer(), true); 4722 } 4723 4724 4725 fssh_status_t 4726 _kern_fcntl(int fd, int op, uint32_t argument) 4727 { 4728 return common_fcntl(fd, op, argument, true); 4729 } 4730 4731 4732 fssh_status_t 4733 _kern_fsync(int fd) 4734 { 4735 return common_sync(fd, true); 4736 } 4737 4738 4739 fssh_status_t 4740 _kern_lock_node(int fd) 4741 { 4742 return common_lock_node(fd, true); 4743 } 4744 4745 4746 fssh_status_t 4747 _kern_unlock_node(int fd) 4748 { 4749 return common_unlock_node(fd, true); 4750 } 4751 4752 4753 fssh_status_t 4754 _kern_create_dir_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name, int perms) 4755 { 4756 return dir_create_entry_ref(device, inode, name, perms, true); 4757 } 4758 4759 4760 /** \brief Creates a directory specified by a FD + path pair. 4761 * 4762 * \a path must always be specified (it contains the name of the new directory 4763 * at least). If only a path is given, this path identifies the location at 4764 * which the directory shall be created. If both \a fd and \a path are given and 4765 * the path is absolute, \a fd is ignored; a relative path is reckoned off 4766 * of the directory (!) identified by \a fd. 4767 * 4768 * \param fd The FD. May be < 0. 4769 * \param path The absolute or relative path. Must not be \c NULL. 4770 * \param perms The access permissions the new directory shall have. 4771 * \return \c FSSH_B_OK, if the directory has been created successfully, another 4772 * error code otherwise. 4773 */ 4774 4775 fssh_status_t 4776 _kern_create_dir(int fd, const char *path, int perms) 4777 { 4778 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 4779 if (pathBuffer.InitCheck() != FSSH_B_OK) 4780 return FSSH_B_NO_MEMORY; 4781 4782 return dir_create(fd, pathBuffer.LockBuffer(), perms, true); 4783 } 4784 4785 4786 fssh_status_t 4787 _kern_remove_dir(int fd, const char *path) 4788 { 4789 if (path) { 4790 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 4791 if (pathBuffer.InitCheck() != FSSH_B_OK) 4792 return FSSH_B_NO_MEMORY; 4793 4794 return dir_remove(fd, pathBuffer.LockBuffer(), true); 4795 } 4796 4797 return dir_remove(fd, NULL, true); 4798 } 4799 4800 4801 /** \brief Reads the contents of a symlink referred to by a FD + path pair. 4802 * 4803 * At least one of \a fd and \a path must be specified. 4804 * If only \a fd is given, the function the symlink to be read is the node 4805 * identified by this FD. If only a path is given, this path identifies the 4806 * symlink to be read. If both are given and the path is absolute, \a fd is 4807 * ignored; a relative path is reckoned off of the directory (!) identified 4808 * by \a fd. 4809 * If this function fails with FSSH_B_BUFFER_OVERFLOW, the \a _bufferSize pointer 4810 * will still be updated to reflect the required buffer size. 4811 * 4812 * \param fd The FD. May be < 0. 4813 * \param path The absolute or relative path. May be \c NULL. 4814 * \param buffer The buffer into which the contents of the symlink shall be 4815 * written. 4816 * \param _bufferSize A pointer to the size of the supplied buffer. 4817 * \return The length of the link on success or an appropriate error code 4818 */ 4819 4820 fssh_status_t 4821 _kern_read_link(int fd, const char *path, char *buffer, fssh_size_t *_bufferSize) 4822 { 4823 if (path) { 4824 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 4825 if (pathBuffer.InitCheck() != FSSH_B_OK) 4826 return FSSH_B_NO_MEMORY; 4827 4828 return common_read_link(fd, pathBuffer.LockBuffer(), 4829 buffer, _bufferSize, true); 4830 } 4831 4832 return common_read_link(fd, NULL, buffer, _bufferSize, true); 4833 } 4834 4835 4836 /** \brief Creates a symlink specified by a FD + path pair. 4837 * 4838 * \a path must always be specified (it contains the name of the new symlink 4839 * at least). If only a path is given, this path identifies the location at 4840 * which the symlink shall be created. If both \a fd and \a path are given and 4841 * the path is absolute, \a fd is ignored; a relative path is reckoned off 4842 * of the directory (!) identified by \a fd. 4843 * 4844 * \param fd The FD. May be < 0. 4845 * \param toPath The absolute or relative path. Must not be \c NULL. 4846 * \param mode The access permissions the new symlink shall have. 4847 * \return \c FSSH_B_OK, if the symlink has been created successfully, another 4848 * error code otherwise. 4849 */ 4850 4851 fssh_status_t 4852 _kern_create_symlink(int fd, const char *path, const char *toPath, int mode) 4853 { 4854 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 4855 KPath toPathBuffer(toPath, false, FSSH_B_PATH_NAME_LENGTH + 1); 4856 if (pathBuffer.InitCheck() != FSSH_B_OK || toPathBuffer.InitCheck() != FSSH_B_OK) 4857 return FSSH_B_NO_MEMORY; 4858 4859 char *toBuffer = toPathBuffer.LockBuffer(); 4860 4861 fssh_status_t status = check_path(toBuffer); 4862 if (status < FSSH_B_OK) 4863 return status; 4864 4865 return common_create_symlink(fd, pathBuffer.LockBuffer(), 4866 toBuffer, mode, true); 4867 } 4868 4869 4870 fssh_status_t 4871 _kern_create_link(const char *path, const char *toPath) 4872 { 4873 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 4874 KPath toPathBuffer(toPath, false, FSSH_B_PATH_NAME_LENGTH + 1); 4875 if (pathBuffer.InitCheck() != FSSH_B_OK || toPathBuffer.InitCheck() != FSSH_B_OK) 4876 return FSSH_B_NO_MEMORY; 4877 4878 return common_create_link(pathBuffer.LockBuffer(), 4879 toPathBuffer.LockBuffer(), true); 4880 } 4881 4882 4883 /** \brief Removes an entry specified by a FD + path pair from its directory. 4884 * 4885 * \a path must always be specified (it contains at least the name of the entry 4886 * to be deleted). If only a path is given, this path identifies the entry 4887 * directly. If both \a fd and \a path are given and the path is absolute, 4888 * \a fd is ignored; a relative path is reckoned off of the directory (!) 4889 * identified by \a fd. 4890 * 4891 * \param fd The FD. May be < 0. 4892 * \param path The absolute or relative path. Must not be \c NULL. 4893 * \return \c FSSH_B_OK, if the entry has been removed successfully, another 4894 * error code otherwise. 4895 */ 4896 4897 fssh_status_t 4898 _kern_unlink(int fd, const char *path) 4899 { 4900 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 4901 if (pathBuffer.InitCheck() != FSSH_B_OK) 4902 return FSSH_B_NO_MEMORY; 4903 4904 return common_unlink(fd, pathBuffer.LockBuffer(), true); 4905 } 4906 4907 4908 /** \brief Moves an entry specified by a FD + path pair to a an entry specified 4909 * by another FD + path pair. 4910 * 4911 * \a oldPath and \a newPath must always be specified (they contain at least 4912 * the name of the entry). If only a path is given, this path identifies the 4913 * entry directly. If both a FD and a path are given and the path is absolute, 4914 * the FD is ignored; a relative path is reckoned off of the directory (!) 4915 * identified by the respective FD. 4916 * 4917 * \param oldFD The FD of the old location. May be < 0. 4918 * \param oldPath The absolute or relative path of the old location. Must not 4919 * be \c NULL. 4920 * \param newFD The FD of the new location. May be < 0. 4921 * \param newPath The absolute or relative path of the new location. Must not 4922 * be \c NULL. 4923 * \return \c FSSH_B_OK, if the entry has been moved successfully, another 4924 * error code otherwise. 4925 */ 4926 4927 fssh_status_t 4928 _kern_rename(int oldFD, const char *oldPath, int newFD, const char *newPath) 4929 { 4930 KPath oldPathBuffer(oldPath, false, FSSH_B_PATH_NAME_LENGTH + 1); 4931 KPath newPathBuffer(newPath, false, FSSH_B_PATH_NAME_LENGTH + 1); 4932 if (oldPathBuffer.InitCheck() != FSSH_B_OK || newPathBuffer.InitCheck() != FSSH_B_OK) 4933 return FSSH_B_NO_MEMORY; 4934 4935 return common_rename(oldFD, oldPathBuffer.LockBuffer(), 4936 newFD, newPathBuffer.LockBuffer(), true); 4937 } 4938 4939 4940 fssh_status_t 4941 _kern_access(const char *path, int mode) 4942 { 4943 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 4944 if (pathBuffer.InitCheck() != FSSH_B_OK) 4945 return FSSH_B_NO_MEMORY; 4946 4947 return common_access(pathBuffer.LockBuffer(), mode, true); 4948 } 4949 4950 4951 /** \brief Reads stat data of an entity specified by a FD + path pair. 4952 * 4953 * If only \a fd is given, the stat operation associated with the type 4954 * of the FD (node, attr, attr dir etc.) is performed. If only \a path is 4955 * given, this path identifies the entry for whose node to retrieve the 4956 * stat data. If both \a fd and \a path are given and the path is absolute, 4957 * \a fd is ignored; a relative path is reckoned off of the directory (!) 4958 * identified by \a fd and specifies the entry whose stat data shall be 4959 * retrieved. 4960 * 4961 * \param fd The FD. May be < 0. 4962 * \param path The absolute or relative path. Must not be \c NULL. 4963 * \param traverseLeafLink If \a path is given, \c true specifies that the 4964 * function shall not stick to symlinks, but traverse them. 4965 * \param stat The buffer the stat data shall be written into. 4966 * \param statSize The size of the supplied stat buffer. 4967 * \return \c FSSH_B_OK, if the the stat data have been read successfully, another 4968 * error code otherwise. 4969 */ 4970 4971 fssh_status_t 4972 _kern_read_stat(int fd, const char *path, bool traverseLeafLink, 4973 fssh_struct_stat *stat, fssh_size_t statSize) 4974 { 4975 fssh_struct_stat completeStat; 4976 fssh_struct_stat *originalStat = NULL; 4977 fssh_status_t status; 4978 4979 if (statSize > sizeof(fssh_struct_stat)) 4980 return FSSH_B_BAD_VALUE; 4981 4982 // this supports different stat extensions 4983 if (statSize < sizeof(fssh_struct_stat)) { 4984 originalStat = stat; 4985 stat = &completeStat; 4986 } 4987 4988 if (path) { 4989 // path given: get the stat of the node referred to by (fd, path) 4990 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 4991 if (pathBuffer.InitCheck() != FSSH_B_OK) 4992 return FSSH_B_NO_MEMORY; 4993 4994 status = common_path_read_stat(fd, pathBuffer.LockBuffer(), 4995 traverseLeafLink, stat, true); 4996 } else { 4997 // no path given: get the FD and use the FD operation 4998 struct file_descriptor *descriptor 4999 = get_fd(get_current_io_context(true), fd); 5000 if (descriptor == NULL) 5001 return FSSH_B_FILE_ERROR; 5002 5003 if (descriptor->ops->fd_read_stat) 5004 status = descriptor->ops->fd_read_stat(descriptor, stat); 5005 else 5006 status = FSSH_EOPNOTSUPP; 5007 5008 put_fd(descriptor); 5009 } 5010 5011 if (status == FSSH_B_OK && originalStat != NULL) 5012 fssh_memcpy(originalStat, stat, statSize); 5013 5014 return status; 5015 } 5016 5017 5018 /** \brief Writes stat data of an entity specified by a FD + path pair. 5019 * 5020 * If only \a fd is given, the stat operation associated with the type 5021 * of the FD (node, attr, attr dir etc.) is performed. If only \a path is 5022 * given, this path identifies the entry for whose node to write the 5023 * stat data. If both \a fd and \a path are given and the path is absolute, 5024 * \a fd is ignored; a relative path is reckoned off of the directory (!) 5025 * identified by \a fd and specifies the entry whose stat data shall be 5026 * written. 5027 * 5028 * \param fd The FD. May be < 0. 5029 * \param path The absolute or relative path. Must not be \c NULL. 5030 * \param traverseLeafLink If \a path is given, \c true specifies that the 5031 * function shall not stick to symlinks, but traverse them. 5032 * \param stat The buffer containing the stat data to be written. 5033 * \param statSize The size of the supplied stat buffer. 5034 * \param statMask A mask specifying which parts of the stat data shall be 5035 * written. 5036 * \return \c FSSH_B_OK, if the the stat data have been written successfully, 5037 * another error code otherwise. 5038 */ 5039 5040 fssh_status_t 5041 _kern_write_stat(int fd, const char *path, bool traverseLeafLink, 5042 const fssh_struct_stat *stat, fssh_size_t statSize, int statMask) 5043 { 5044 fssh_struct_stat completeStat; 5045 5046 if (statSize > sizeof(fssh_struct_stat)) 5047 return FSSH_B_BAD_VALUE; 5048 5049 // this supports different stat extensions 5050 if (statSize < sizeof(fssh_struct_stat)) { 5051 fssh_memset((uint8_t *)&completeStat + statSize, 0, sizeof(fssh_struct_stat) - statSize); 5052 fssh_memcpy(&completeStat, stat, statSize); 5053 stat = &completeStat; 5054 } 5055 5056 fssh_status_t status; 5057 5058 if (path) { 5059 // path given: write the stat of the node referred to by (fd, path) 5060 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5061 if (pathBuffer.InitCheck() != FSSH_B_OK) 5062 return FSSH_B_NO_MEMORY; 5063 5064 status = common_path_write_stat(fd, pathBuffer.LockBuffer(), 5065 traverseLeafLink, stat, statMask, true); 5066 } else { 5067 // no path given: get the FD and use the FD operation 5068 struct file_descriptor *descriptor 5069 = get_fd(get_current_io_context(true), fd); 5070 if (descriptor == NULL) 5071 return FSSH_B_FILE_ERROR; 5072 5073 if (descriptor->ops->fd_write_stat) 5074 status = descriptor->ops->fd_write_stat(descriptor, stat, statMask); 5075 else 5076 status = FSSH_EOPNOTSUPP; 5077 5078 put_fd(descriptor); 5079 } 5080 5081 return status; 5082 } 5083 5084 5085 int 5086 _kern_open_attr_dir(int fd, const char *path) 5087 { 5088 KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); 5089 if (pathBuffer.InitCheck() != FSSH_B_OK) 5090 return FSSH_B_NO_MEMORY; 5091 5092 if (path != NULL) 5093 pathBuffer.SetTo(path); 5094 5095 return attr_dir_open(fd, path ? pathBuffer.LockBuffer() : NULL, true); 5096 } 5097 5098 5099 int 5100 _kern_create_attr(int fd, const char *name, uint32_t type, int openMode) 5101 { 5102 return attr_create(fd, name, type, openMode, true); 5103 } 5104 5105 5106 int 5107 _kern_open_attr(int fd, const char *name, int openMode) 5108 { 5109 return attr_open(fd, name, openMode, true); 5110 } 5111 5112 5113 fssh_status_t 5114 _kern_remove_attr(int fd, const char *name) 5115 { 5116 return attr_remove(fd, name, true); 5117 } 5118 5119 5120 fssh_status_t 5121 _kern_rename_attr(int fromFile, const char *fromName, int toFile, const char *toName) 5122 { 5123 return attr_rename(fromFile, fromName, toFile, toName, true); 5124 } 5125 5126 5127 int 5128 _kern_open_index_dir(fssh_dev_t device) 5129 { 5130 return index_dir_open(device, true); 5131 } 5132 5133 5134 fssh_status_t 5135 _kern_create_index(fssh_dev_t device, const char *name, uint32_t type, uint32_t flags) 5136 { 5137 return index_create(device, name, type, flags, true); 5138 } 5139 5140 5141 fssh_status_t 5142 _kern_read_index_stat(fssh_dev_t device, const char *name, fssh_struct_stat *stat) 5143 { 5144 return index_name_read_stat(device, name, stat, true); 5145 } 5146 5147 5148 fssh_status_t 5149 _kern_remove_index(fssh_dev_t device, const char *name) 5150 { 5151 return index_remove(device, name, true); 5152 } 5153 5154 5155 fssh_status_t 5156 _kern_getcwd(char *buffer, fssh_size_t size) 5157 { 5158 TRACE(("_kern_getcwd: buf %p, %ld\n", buffer, size)); 5159 5160 // Call vfs to get current working directory 5161 return get_cwd(buffer, size, true); 5162 } 5163 5164 5165 fssh_status_t 5166 _kern_setcwd(int fd, const char *path) 5167 { 5168 KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); 5169 if (pathBuffer.InitCheck() != FSSH_B_OK) 5170 return FSSH_B_NO_MEMORY; 5171 5172 if (path != NULL) 5173 pathBuffer.SetTo(path); 5174 5175 return set_cwd(fd, path != NULL ? pathBuffer.LockBuffer() : NULL, true); 5176 } 5177 5178 5179 fssh_status_t 5180 _kern_initialize_volume(const char* fsName, const char *partition, 5181 const char *name, const char *parameters) 5182 { 5183 if (!fsName || ! partition) 5184 return FSSH_B_BAD_VALUE; 5185 5186 // The partition argument should point to a real file/device. 5187 5188 // open partition 5189 int fd = fssh_open(partition, FSSH_O_RDWR); 5190 if (fd < 0) 5191 return fssh_errno; 5192 5193 // get the file system module 5194 fssh_file_system_module_info* fsModule = get_file_system(fsName); 5195 if (fsModule == NULL) { 5196 fssh_close(fd); 5197 return FSSH_ENODEV; 5198 } 5199 5200 // initialize 5201 fssh_status_t status; 5202 if (fsModule->initialize) { 5203 status = (*fsModule->initialize)(fd, -1, name, parameters, 0, -1); 5204 // We've got no partition or job IDs -- the FS will hopefully 5205 // ignore that. 5206 // TODO: Get the actual size! 5207 } else 5208 status = FSSH_B_NOT_SUPPORTED; 5209 5210 // put the file system module, close partition 5211 put_file_system(fsModule); 5212 fssh_close(fd); 5213 5214 return status; 5215 } 5216 5217 } // namespace FSShell 5218