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