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