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