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