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