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