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