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