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_add_missing(fssh_dev_t mountID, fssh_ino_t dirID, 2282 const char* name) 2283 { 2284 // We don't implement an entry cache in the FS shell. 2285 return FSSH_B_OK; 2286 } 2287 2288 2289 extern "C" fssh_status_t 2290 fssh_entry_cache_remove(fssh_dev_t mountID, fssh_ino_t dirID, const char* name) 2291 { 2292 // We don't implement an entry cache in the FS shell. 2293 return FSSH_B_ENTRY_NOT_FOUND; 2294 } 2295 2296 2297 // #pragma mark - private VFS API 2298 // Functions the VFS exports for other parts of the kernel 2299 2300 2301 /** Acquires another reference to the vnode that has to be released 2302 * by calling vfs_put_vnode(). 2303 */ 2304 2305 void 2306 vfs_acquire_vnode(void *_vnode) 2307 { 2308 inc_vnode_ref_count((struct vnode *)_vnode); 2309 } 2310 2311 2312 /** This is currently called from file_cache_create() only. 2313 * It's probably a temporary solution as long as devfs requires that 2314 * fs_read_pages()/fs_write_pages() are called with the standard 2315 * open cookie and not with a device cookie. 2316 * If that's done differently, remove this call; it has no other 2317 * purpose. 2318 */ 2319 2320 fssh_status_t 2321 vfs_get_cookie_from_fd(int fd, void **_cookie) 2322 { 2323 struct file_descriptor *descriptor; 2324 2325 descriptor = get_fd(get_current_io_context(true), fd); 2326 if (descriptor == NULL) 2327 return FSSH_B_FILE_ERROR; 2328 2329 *_cookie = descriptor->cookie; 2330 return FSSH_B_OK; 2331 } 2332 2333 2334 int 2335 vfs_get_vnode_from_fd(int fd, bool kernel, void **vnode) 2336 { 2337 *vnode = get_vnode_from_fd(fd, kernel); 2338 2339 if (*vnode == NULL) 2340 return FSSH_B_FILE_ERROR; 2341 2342 return FSSH_B_NO_ERROR; 2343 } 2344 2345 2346 fssh_status_t 2347 vfs_get_vnode_from_path(const char *path, bool kernel, void **_vnode) 2348 { 2349 TRACE(("vfs_get_vnode_from_path: entry. path = '%s', kernel %d\n", path, kernel)); 2350 2351 KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); 2352 if (pathBuffer.InitCheck() != FSSH_B_OK) 2353 return FSSH_B_NO_MEMORY; 2354 2355 char *buffer = pathBuffer.LockBuffer(); 2356 fssh_strlcpy(buffer, path, pathBuffer.BufferSize()); 2357 2358 struct vnode *vnode; 2359 fssh_status_t status = path_to_vnode(buffer, true, &vnode, NULL, kernel); 2360 if (status < FSSH_B_OK) 2361 return status; 2362 2363 *_vnode = vnode; 2364 return FSSH_B_OK; 2365 } 2366 2367 2368 fssh_status_t 2369 vfs_get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, void **_vnode) 2370 { 2371 struct vnode *vnode; 2372 2373 fssh_status_t status = get_vnode(mountID, vnodeID, &vnode, false); 2374 if (status < FSSH_B_OK) 2375 return status; 2376 2377 *_vnode = vnode; 2378 return FSSH_B_OK; 2379 } 2380 2381 2382 fssh_status_t 2383 vfs_read_pages(void *_vnode, void *cookie, fssh_off_t pos, 2384 const fssh_iovec *vecs, fssh_size_t count, fssh_size_t *_numBytes) 2385 { 2386 struct vnode *vnode = (struct vnode *)_vnode; 2387 2388 return FS_CALL(vnode, read_pages, 2389 cookie, pos, vecs, count, _numBytes); 2390 } 2391 2392 2393 fssh_status_t 2394 vfs_write_pages(void *_vnode, void *cookie, fssh_off_t pos, 2395 const fssh_iovec *vecs, fssh_size_t count, fssh_size_t *_numBytes) 2396 { 2397 struct vnode *vnode = (struct vnode *)_vnode; 2398 2399 return FS_CALL(vnode, write_pages, 2400 cookie, pos, vecs, count, _numBytes); 2401 } 2402 2403 2404 fssh_status_t 2405 vfs_entry_ref_to_vnode(fssh_mount_id mountID, fssh_vnode_id directoryID, 2406 const char *name, void **_vnode) 2407 { 2408 return entry_ref_to_vnode(mountID, directoryID, name, 2409 (struct vnode **)_vnode); 2410 } 2411 2412 2413 void 2414 vfs_fs_vnode_to_node_ref(void *_vnode, fssh_mount_id *_mountID, 2415 fssh_vnode_id *_vnodeID) 2416 { 2417 struct vnode *vnode = (struct vnode *)_vnode; 2418 2419 *_mountID = vnode->device; 2420 *_vnodeID = vnode->id; 2421 } 2422 2423 2424 /** Looks up a vnode with the given mount and vnode ID. 2425 * Must only be used with "in-use" vnodes as it doesn't grab a reference 2426 * to the node. 2427 * It's currently only be used by file_cache_create(). 2428 */ 2429 2430 fssh_status_t 2431 vfs_lookup_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, 2432 struct vnode **_vnode) 2433 { 2434 fssh_mutex_lock(&sVnodeMutex); 2435 struct vnode *vnode = lookup_vnode(mountID, vnodeID); 2436 fssh_mutex_unlock(&sVnodeMutex); 2437 2438 if (vnode == NULL) 2439 return FSSH_B_ERROR; 2440 2441 *_vnode = vnode; 2442 return FSSH_B_OK; 2443 } 2444 2445 2446 fssh_status_t 2447 vfs_get_fs_node_from_path(fssh_fs_volume *volume, const char *path, 2448 bool kernel, void **_node) 2449 { 2450 TRACE(("vfs_get_fs_node_from_path(volume = %p (%ld), path = \"%s\", " 2451 "kernel %d)\n", volume, volume->id, path, kernel)); 2452 2453 KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); 2454 if (pathBuffer.InitCheck() != FSSH_B_OK) 2455 return FSSH_B_NO_MEMORY; 2456 2457 fs_mount *mount; 2458 fssh_status_t status = get_mount(volume->id, &mount); 2459 if (status < FSSH_B_OK) 2460 return status; 2461 2462 char *buffer = pathBuffer.LockBuffer(); 2463 fssh_strlcpy(buffer, path, pathBuffer.BufferSize()); 2464 2465 struct vnode *vnode = mount->root_vnode; 2466 2467 if (buffer[0] == '/') 2468 status = path_to_vnode(buffer, true, &vnode, NULL, true); 2469 else { 2470 inc_vnode_ref_count(vnode); 2471 // vnode_path_to_vnode() releases a reference to the starting vnode 2472 status = vnode_path_to_vnode(vnode, buffer, true, 0, &vnode, NULL); 2473 } 2474 2475 put_mount(mount); 2476 2477 if (status < FSSH_B_OK) 2478 return status; 2479 2480 if (vnode->device != volume->id) { 2481 // wrong mount ID - must not gain access on foreign file system nodes 2482 put_vnode(vnode); 2483 return FSSH_B_BAD_VALUE; 2484 } 2485 2486 // Use get_vnode() to resolve the cookie for the right layer. 2487 status = ::fssh_get_vnode(volume, vnode->id, _node); 2488 put_vnode(vnode); 2489 2490 return FSSH_B_OK; 2491 } 2492 2493 2494 /** Finds the full path to the file that contains the module \a moduleName, 2495 * puts it into \a pathBuffer, and returns FSSH_B_OK for success. 2496 * If \a pathBuffer was too small, it returns \c FSSH_B_BUFFER_OVERFLOW, 2497 * \c FSSH_B_ENTRY_NOT_FOUNT if no file could be found. 2498 * \a pathBuffer is clobbered in any case and must not be relied on if this 2499 * functions returns unsuccessfully. 2500 */ 2501 2502 fssh_status_t 2503 vfs_get_module_path(const char *basePath, const char *moduleName, char *pathBuffer, 2504 fssh_size_t bufferSize) 2505 { 2506 struct vnode *dir, *file; 2507 fssh_status_t status; 2508 fssh_size_t length; 2509 char *path; 2510 2511 if (bufferSize == 0 || fssh_strlcpy(pathBuffer, basePath, bufferSize) >= bufferSize) 2512 return FSSH_B_BUFFER_OVERFLOW; 2513 2514 status = path_to_vnode(pathBuffer, true, &dir, NULL, true); 2515 if (status < FSSH_B_OK) 2516 return status; 2517 2518 // the path buffer had been clobbered by the above call 2519 length = fssh_strlcpy(pathBuffer, basePath, bufferSize); 2520 if (pathBuffer[length - 1] != '/') 2521 pathBuffer[length++] = '/'; 2522 2523 path = pathBuffer + length; 2524 bufferSize -= length; 2525 2526 while (moduleName) { 2527 char *nextPath = fssh_strchr(moduleName, '/'); 2528 if (nextPath == NULL) 2529 length = fssh_strlen(moduleName); 2530 else { 2531 length = nextPath - moduleName; 2532 nextPath++; 2533 } 2534 2535 if (length + 1 >= bufferSize) { 2536 status = FSSH_B_BUFFER_OVERFLOW; 2537 goto err; 2538 } 2539 2540 fssh_memcpy(path, moduleName, length); 2541 path[length] = '\0'; 2542 moduleName = nextPath; 2543 2544 status = vnode_path_to_vnode(dir, path, true, 0, &file, NULL); 2545 if (status < FSSH_B_OK) { 2546 // vnode_path_to_vnode() has already released the reference to dir 2547 return status; 2548 } 2549 2550 if (FSSH_S_ISDIR(file->type)) { 2551 // goto the next directory 2552 path[length] = '/'; 2553 path[length + 1] = '\0'; 2554 path += length + 1; 2555 bufferSize -= length + 1; 2556 2557 dir = file; 2558 } else if (FSSH_S_ISREG(file->type)) { 2559 // it's a file so it should be what we've searched for 2560 put_vnode(file); 2561 2562 return FSSH_B_OK; 2563 } else { 2564 TRACE(("vfs_get_module_path(): something is strange here: %d...\n", file->type)); 2565 status = FSSH_B_ERROR; 2566 dir = file; 2567 goto err; 2568 } 2569 } 2570 2571 // if we got here, the moduleName just pointed to a directory, not to 2572 // a real module - what should we do in this case? 2573 status = FSSH_B_ENTRY_NOT_FOUND; 2574 2575 err: 2576 put_vnode(dir); 2577 return status; 2578 } 2579 2580 2581 /** \brief Normalizes a given path. 2582 * 2583 * The path must refer to an existing or non-existing entry in an existing 2584 * directory, that is chopping off the leaf component the remaining path must 2585 * refer to an existing directory. 2586 * 2587 * The returned will be canonical in that it will be absolute, will not 2588 * contain any "." or ".." components or duplicate occurrences of '/'s, 2589 * and none of the directory components will by symbolic links. 2590 * 2591 * Any two paths referring to the same entry, will result in the same 2592 * normalized path (well, that is pretty much the definition of `normalized', 2593 * isn't it :-). 2594 * 2595 * \param path The path to be normalized. 2596 * \param buffer The buffer into which the normalized path will be written. 2597 * \param bufferSize The size of \a buffer. 2598 * \param kernel \c true, if the IO context of the kernel shall be used, 2599 * otherwise that of the team this thread belongs to. Only relevant, 2600 * if the path is relative (to get the CWD). 2601 * \return \c FSSH_B_OK if everything went fine, another error code otherwise. 2602 */ 2603 2604 fssh_status_t 2605 vfs_normalize_path(const char *path, char *buffer, fssh_size_t bufferSize, 2606 bool kernel) 2607 { 2608 if (!path || !buffer || bufferSize < 1) 2609 return FSSH_B_BAD_VALUE; 2610 2611 TRACE(("vfs_normalize_path(`%s')\n", path)); 2612 2613 // copy the supplied path to the stack, so it can be modified 2614 KPath mutablePathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); 2615 if (mutablePathBuffer.InitCheck() != FSSH_B_OK) 2616 return FSSH_B_NO_MEMORY; 2617 2618 char *mutablePath = mutablePathBuffer.LockBuffer(); 2619 if (fssh_strlcpy(mutablePath, path, FSSH_B_PATH_NAME_LENGTH) >= FSSH_B_PATH_NAME_LENGTH) 2620 return FSSH_B_NAME_TOO_LONG; 2621 2622 // get the dir vnode and the leaf name 2623 struct vnode *dirNode; 2624 char leaf[FSSH_B_FILE_NAME_LENGTH]; 2625 fssh_status_t error = path_to_dir_vnode(mutablePath, &dirNode, leaf, kernel); 2626 if (error != FSSH_B_OK) { 2627 TRACE(("vfs_normalize_path(): failed to get dir vnode: %s\n", strerror(error))); 2628 return error; 2629 } 2630 2631 // if the leaf is "." or "..", we directly get the correct directory 2632 // vnode and ignore the leaf later 2633 bool isDir = (fssh_strcmp(leaf, ".") == 0 || fssh_strcmp(leaf, "..") == 0); 2634 if (isDir) 2635 error = vnode_path_to_vnode(dirNode, leaf, false, 0, &dirNode, NULL); 2636 if (error != FSSH_B_OK) { 2637 TRACE(("vfs_normalize_path(): failed to get dir vnode for \".\" or \"..\": %s\n", 2638 strerror(error))); 2639 return error; 2640 } 2641 2642 // get the directory path 2643 error = dir_vnode_to_path(dirNode, buffer, bufferSize); 2644 put_vnode(dirNode); 2645 if (error < FSSH_B_OK) { 2646 TRACE(("vfs_normalize_path(): failed to get dir path: %s\n", strerror(error))); 2647 return error; 2648 } 2649 2650 // append the leaf name 2651 if (!isDir) { 2652 // insert a directory separator only if this is not the file system root 2653 if ((fssh_strcmp(buffer, "/") != 0 2654 && fssh_strlcat(buffer, "/", bufferSize) >= bufferSize) 2655 || fssh_strlcat(buffer, leaf, bufferSize) >= bufferSize) { 2656 return FSSH_B_NAME_TOO_LONG; 2657 } 2658 } 2659 2660 TRACE(("vfs_normalize_path() -> `%s'\n", buffer)); 2661 return FSSH_B_OK; 2662 } 2663 2664 2665 void 2666 vfs_put_vnode(void *_vnode) 2667 { 2668 put_vnode((struct vnode *)_vnode); 2669 } 2670 2671 2672 fssh_status_t 2673 vfs_get_cwd(fssh_mount_id *_mountID, fssh_vnode_id *_vnodeID) 2674 { 2675 // Get current working directory from io context 2676 struct io_context *context = get_current_io_context(false); 2677 fssh_status_t status = FSSH_B_OK; 2678 2679 fssh_mutex_lock(&context->io_mutex); 2680 2681 if (context->cwd != NULL) { 2682 *_mountID = context->cwd->device; 2683 *_vnodeID = context->cwd->id; 2684 } else 2685 status = FSSH_B_ERROR; 2686 2687 fssh_mutex_unlock(&context->io_mutex); 2688 return status; 2689 } 2690 2691 2692 fssh_status_t 2693 vfs_get_file_map(void *_vnode, fssh_off_t offset, fssh_size_t size, 2694 fssh_file_io_vec *vecs, fssh_size_t *_count) 2695 { 2696 struct vnode *vnode = (struct vnode *)_vnode; 2697 2698 FUNCTION(("vfs_get_file_map: vnode %p, vecs %p, offset %lld, size = %u\n", vnode, vecs, offset, (unsigned)size)); 2699 2700 return FS_CALL(vnode, get_file_map, offset, size, vecs, _count); 2701 } 2702 2703 2704 fssh_status_t 2705 vfs_stat_vnode(void *_vnode, struct fssh_stat *stat) 2706 { 2707 struct vnode *vnode = (struct vnode *)_vnode; 2708 2709 fssh_status_t status = FS_CALL(vnode, read_stat, stat); 2710 2711 // fill in the st_dev and st_ino fields 2712 if (status == FSSH_B_OK) { 2713 stat->fssh_st_dev = vnode->device; 2714 stat->fssh_st_ino = vnode->id; 2715 } 2716 2717 return status; 2718 } 2719 2720 2721 fssh_status_t 2722 vfs_get_vnode_name(void *_vnode, char *name, fssh_size_t nameSize) 2723 { 2724 return get_vnode_name((struct vnode *)_vnode, NULL, name, nameSize); 2725 } 2726 2727 2728 fssh_status_t 2729 vfs_entry_ref_to_path(fssh_dev_t device, fssh_ino_t inode, const char *leaf, 2730 bool kernel, char *path, fssh_size_t pathLength) 2731 { 2732 struct vnode *vnode; 2733 fssh_status_t status; 2734 2735 // filter invalid leaf names 2736 if (leaf != NULL && (leaf[0] == '\0' || fssh_strchr(leaf, '/'))) 2737 return FSSH_B_BAD_VALUE; 2738 2739 // get the vnode matching the dir's node_ref 2740 if (leaf && (fssh_strcmp(leaf, ".") == 0 || fssh_strcmp(leaf, "..") == 0)) { 2741 // special cases "." and "..": we can directly get the vnode of the 2742 // referenced directory 2743 status = entry_ref_to_vnode(device, inode, leaf, &vnode); 2744 leaf = NULL; 2745 } else 2746 status = get_vnode(device, inode, &vnode, false); 2747 if (status < FSSH_B_OK) 2748 return status; 2749 2750 // get the directory path 2751 status = dir_vnode_to_path(vnode, path, pathLength); 2752 put_vnode(vnode); 2753 // we don't need the vnode anymore 2754 if (status < FSSH_B_OK) 2755 return status; 2756 2757 // append the leaf name 2758 if (leaf) { 2759 // insert a directory separator if this is not the file system root 2760 if ((fssh_strcmp(path, "/") && fssh_strlcat(path, "/", pathLength) 2761 >= pathLength) 2762 || fssh_strlcat(path, leaf, pathLength) >= pathLength) { 2763 return FSSH_B_NAME_TOO_LONG; 2764 } 2765 } 2766 2767 return FSSH_B_OK; 2768 } 2769 2770 2771 /** If the given descriptor locked its vnode, that lock will be released. 2772 */ 2773 2774 void 2775 vfs_unlock_vnode_if_locked(struct file_descriptor *descriptor) 2776 { 2777 struct vnode *vnode = fd_vnode(descriptor); 2778 2779 if (vnode != NULL && vnode->mandatory_locked_by == descriptor) 2780 vnode->mandatory_locked_by = NULL; 2781 } 2782 2783 2784 /** Closes all file descriptors of the specified I/O context that 2785 * don't have the FSSH_O_CLOEXEC flag set. 2786 */ 2787 2788 void 2789 vfs_exec_io_context(void *_context) 2790 { 2791 struct io_context *context = (struct io_context *)_context; 2792 uint32_t i; 2793 2794 for (i = 0; i < context->table_size; i++) { 2795 fssh_mutex_lock(&context->io_mutex); 2796 2797 struct file_descriptor *descriptor = context->fds[i]; 2798 bool remove = false; 2799 2800 if (descriptor != NULL && fd_close_on_exec(context, i)) { 2801 context->fds[i] = NULL; 2802 context->num_used_fds--; 2803 2804 remove = true; 2805 } 2806 2807 fssh_mutex_unlock(&context->io_mutex); 2808 2809 if (remove) { 2810 close_fd(descriptor); 2811 put_fd(descriptor); 2812 } 2813 } 2814 } 2815 2816 2817 /** Sets up a new io_control structure, and inherits the properties 2818 * of the parent io_control if it is given. 2819 */ 2820 2821 void * 2822 vfs_new_io_context(void *_parentContext) 2823 { 2824 fssh_size_t tableSize; 2825 struct io_context *context; 2826 struct io_context *parentContext; 2827 2828 context = (io_context *)malloc(sizeof(struct io_context)); 2829 if (context == NULL) 2830 return NULL; 2831 2832 fssh_memset(context, 0, sizeof(struct io_context)); 2833 2834 parentContext = (struct io_context *)_parentContext; 2835 if (parentContext) 2836 tableSize = parentContext->table_size; 2837 else 2838 tableSize = DEFAULT_FD_TABLE_SIZE; 2839 2840 // allocate space for FDs and their close-on-exec flag 2841 context->fds = (file_descriptor **)malloc(sizeof(struct file_descriptor *) * tableSize 2842 + (tableSize + 7) / 8); 2843 if (context->fds == NULL) { 2844 free(context); 2845 return NULL; 2846 } 2847 2848 fssh_memset(context->fds, 0, sizeof(struct file_descriptor *) * tableSize 2849 + (tableSize + 7) / 8); 2850 context->fds_close_on_exec = (uint8_t *)(context->fds + tableSize); 2851 2852 fssh_mutex_init(&context->io_mutex, "I/O context"); 2853 2854 // Copy all parent files which don't have the FSSH_O_CLOEXEC flag set 2855 2856 if (parentContext) { 2857 fssh_size_t i; 2858 2859 fssh_mutex_lock(&parentContext->io_mutex); 2860 2861 context->cwd = parentContext->cwd; 2862 if (context->cwd) 2863 inc_vnode_ref_count(context->cwd); 2864 2865 for (i = 0; i < tableSize; i++) { 2866 struct file_descriptor *descriptor = parentContext->fds[i]; 2867 2868 if (descriptor != NULL && !fd_close_on_exec(parentContext, i)) { 2869 context->fds[i] = descriptor; 2870 context->num_used_fds++; 2871 fssh_atomic_add(&descriptor->ref_count, 1); 2872 fssh_atomic_add(&descriptor->open_count, 1); 2873 } 2874 } 2875 2876 fssh_mutex_unlock(&parentContext->io_mutex); 2877 } else { 2878 context->cwd = sRoot; 2879 2880 if (context->cwd) 2881 inc_vnode_ref_count(context->cwd); 2882 } 2883 2884 context->table_size = tableSize; 2885 2886 return context; 2887 } 2888 2889 2890 fssh_status_t 2891 vfs_free_io_context(void *_ioContext) 2892 { 2893 struct io_context *context = (struct io_context *)_ioContext; 2894 uint32_t i; 2895 2896 if (context->cwd) 2897 dec_vnode_ref_count(context->cwd, false); 2898 2899 fssh_mutex_lock(&context->io_mutex); 2900 2901 for (i = 0; i < context->table_size; i++) { 2902 if (struct file_descriptor *descriptor = context->fds[i]) { 2903 close_fd(descriptor); 2904 put_fd(descriptor); 2905 } 2906 } 2907 2908 fssh_mutex_destroy(&context->io_mutex); 2909 2910 free(context->fds); 2911 free(context); 2912 2913 return FSSH_B_OK; 2914 } 2915 2916 2917 fssh_status_t 2918 vfs_init(kernel_args *args) 2919 { 2920 sVnodeTable = hash_init(VNODE_HASH_TABLE_SIZE, fssh_offsetof(struct vnode, next), 2921 &vnode_compare, &vnode_hash); 2922 if (sVnodeTable == NULL) 2923 fssh_panic("vfs_init: error creating vnode hash table\n"); 2924 2925 list_init_etc(&sUnusedVnodeList, fssh_offsetof(struct vnode, unused_link)); 2926 2927 sMountsTable = hash_init(MOUNTS_HASH_TABLE_SIZE, fssh_offsetof(struct fs_mount, next), 2928 &mount_compare, &mount_hash); 2929 if (sMountsTable == NULL) 2930 fssh_panic("vfs_init: error creating mounts hash table\n"); 2931 2932 sRoot = NULL; 2933 2934 fssh_mutex_init(&sFileSystemsMutex, "vfs_lock"); 2935 fssh_recursive_lock_init(&sMountOpLock, "vfs_mount_op_lock"); 2936 fssh_mutex_init(&sMountMutex, "vfs_mount_lock"); 2937 fssh_mutex_init(&sVnodeCoveredByMutex, "vfs_vnode_covered_by_lock"); 2938 fssh_mutex_init(&sVnodeMutex, "vfs_vnode_lock"); 2939 2940 if (block_cache_init() != FSSH_B_OK) 2941 return FSSH_B_ERROR; 2942 2943 return file_cache_init(); 2944 } 2945 2946 2947 // #pragma mark - 2948 // The filetype-dependent implementations (fd_ops + open/create/rename/remove, ...) 2949 2950 2951 /** Calls fs_open() on the given vnode and returns a new 2952 * file descriptor for it 2953 */ 2954 2955 static int 2956 create_vnode(struct vnode *directory, const char *name, int openMode, int perms, bool kernel) 2957 { 2958 struct vnode *vnode; 2959 void *cookie; 2960 fssh_vnode_id newID; 2961 int status; 2962 2963 if (!HAS_FS_CALL(directory, create)) 2964 return FSSH_EROFS; 2965 2966 status = FS_CALL(directory, create, name, openMode, perms, &cookie, &newID); 2967 if (status < FSSH_B_OK) 2968 return status; 2969 2970 fssh_mutex_lock(&sVnodeMutex); 2971 vnode = lookup_vnode(directory->device, newID); 2972 fssh_mutex_unlock(&sVnodeMutex); 2973 2974 if (vnode == NULL) { 2975 fssh_dprintf("vfs: fs_create() returned success but there is no vnode!"); 2976 return FSSH_EINVAL; 2977 } 2978 2979 if ((status = get_new_fd(FDTYPE_FILE, NULL, vnode, cookie, openMode, kernel)) >= 0) 2980 return status; 2981 2982 // something went wrong, clean up 2983 2984 FS_CALL(vnode, close, cookie); 2985 FS_CALL(vnode, free_cookie, cookie); 2986 put_vnode(vnode); 2987 2988 FS_CALL(directory, unlink, name); 2989 2990 return status; 2991 } 2992 2993 2994 /** Calls fs_open() on the given vnode and returns a new 2995 * file descriptor for it 2996 */ 2997 2998 static int 2999 open_vnode(struct vnode *vnode, int openMode, bool kernel) 3000 { 3001 void *cookie; 3002 int status; 3003 3004 status = FS_CALL(vnode, open, openMode, &cookie); 3005 if (status < 0) 3006 return status; 3007 3008 status = get_new_fd(FDTYPE_FILE, NULL, vnode, cookie, openMode, kernel); 3009 if (status < 0) { 3010 FS_CALL(vnode, close, cookie); 3011 FS_CALL(vnode, free_cookie, cookie); 3012 } 3013 return status; 3014 } 3015 3016 3017 /** Calls fs open_dir() on the given vnode and returns a new 3018 * file descriptor for it 3019 */ 3020 3021 static int 3022 open_dir_vnode(struct vnode *vnode, bool kernel) 3023 { 3024 void *cookie; 3025 int status; 3026 3027 status = FS_CALL(vnode, open_dir, &cookie); 3028 if (status < FSSH_B_OK) 3029 return status; 3030 3031 // file is opened, create a fd 3032 status = get_new_fd(FDTYPE_DIR, NULL, vnode, cookie, 0, kernel); 3033 if (status >= 0) 3034 return status; 3035 3036 FS_CALL(vnode, close_dir, cookie); 3037 FS_CALL(vnode, free_dir_cookie, cookie); 3038 3039 return status; 3040 } 3041 3042 3043 /** Calls fs open_attr_dir() on the given vnode and returns a new 3044 * file descriptor for it. 3045 * Used by attr_dir_open(), and attr_dir_open_fd(). 3046 */ 3047 3048 static int 3049 open_attr_dir_vnode(struct vnode *vnode, bool kernel) 3050 { 3051 void *cookie; 3052 int status; 3053 3054 if (!HAS_FS_CALL(vnode, open_attr_dir)) 3055 return FSSH_EOPNOTSUPP; 3056 3057 status = FS_CALL(vnode, open_attr_dir, &cookie); 3058 if (status < 0) 3059 return status; 3060 3061 // file is opened, create a fd 3062 status = get_new_fd(FDTYPE_ATTR_DIR, NULL, vnode, cookie, 0, kernel); 3063 if (status >= 0) 3064 return status; 3065 3066 FS_CALL(vnode, close_attr_dir, cookie); 3067 FS_CALL(vnode, free_attr_dir_cookie, cookie); 3068 3069 return status; 3070 } 3071 3072 3073 static int 3074 file_create_entry_ref(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, int openMode, int perms, bool kernel) 3075 { 3076 struct vnode *directory; 3077 int status; 3078 3079 FUNCTION(("file_create_entry_ref: name = '%s', omode %x, perms %d, kernel %d\n", name, openMode, perms, kernel)); 3080 3081 // get directory to put the new file in 3082 status = get_vnode(mountID, directoryID, &directory, false); 3083 if (status < FSSH_B_OK) 3084 return status; 3085 3086 status = create_vnode(directory, name, openMode, perms, kernel); 3087 put_vnode(directory); 3088 3089 return status; 3090 } 3091 3092 3093 static int 3094 file_create(int fd, char *path, int openMode, int perms, bool kernel) 3095 { 3096 char name[FSSH_B_FILE_NAME_LENGTH]; 3097 struct vnode *directory; 3098 int status; 3099 3100 FUNCTION(("file_create: path '%s', omode %x, perms %d, kernel %d\n", path, openMode, perms, kernel)); 3101 3102 // get directory to put the new file in 3103 status = fd_and_path_to_dir_vnode(fd, path, &directory, name, kernel); 3104 if (status < 0) 3105 return status; 3106 3107 status = create_vnode(directory, name, openMode, perms, kernel); 3108 3109 put_vnode(directory); 3110 return status; 3111 } 3112 3113 3114 static int 3115 file_open_entry_ref(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, int openMode, bool kernel) 3116 { 3117 struct vnode *vnode; 3118 int status; 3119 3120 if (name == NULL || *name == '\0') 3121 return FSSH_B_BAD_VALUE; 3122 3123 FUNCTION(("file_open_entry_ref(ref = (%ld, %Ld, %s), openMode = %d)\n", 3124 mountID, directoryID, name, openMode)); 3125 3126 // get the vnode matching the entry_ref 3127 status = entry_ref_to_vnode(mountID, directoryID, name, &vnode); 3128 if (status < FSSH_B_OK) 3129 return status; 3130 3131 status = open_vnode(vnode, openMode, kernel); 3132 if (status < FSSH_B_OK) 3133 put_vnode(vnode); 3134 3135 return status; 3136 } 3137 3138 3139 static int 3140 file_open(int fd, char *path, int openMode, bool kernel) 3141 { 3142 int status = FSSH_B_OK; 3143 bool traverse = ((openMode & FSSH_O_NOTRAVERSE) == 0); 3144 3145 FUNCTION(("file_open: fd: %d, entry path = '%s', omode %d, kernel %d\n", 3146 fd, path, openMode, kernel)); 3147 3148 // get the vnode matching the vnode + path combination 3149 struct vnode *vnode = NULL; 3150 fssh_vnode_id parentID; 3151 status = fd_and_path_to_vnode(fd, path, traverse, &vnode, &parentID, kernel); 3152 if (status != FSSH_B_OK) 3153 return status; 3154 3155 // open the vnode 3156 status = open_vnode(vnode, openMode, kernel); 3157 // put only on error -- otherwise our reference was transferred to the FD 3158 if (status < FSSH_B_OK) 3159 put_vnode(vnode); 3160 3161 return status; 3162 } 3163 3164 3165 static fssh_status_t 3166 file_close(struct file_descriptor *descriptor) 3167 { 3168 struct vnode *vnode = descriptor->u.vnode; 3169 fssh_status_t status = FSSH_B_OK; 3170 3171 FUNCTION(("file_close(descriptor = %p)\n", descriptor)); 3172 3173 if (HAS_FS_CALL(vnode, close)) 3174 status = FS_CALL(vnode, close, descriptor->cookie); 3175 3176 return status; 3177 } 3178 3179 3180 static void 3181 file_free_fd(struct file_descriptor *descriptor) 3182 { 3183 struct vnode *vnode = descriptor->u.vnode; 3184 3185 if (vnode != NULL) { 3186 FS_CALL(vnode, free_cookie, descriptor->cookie); 3187 put_vnode(vnode); 3188 } 3189 } 3190 3191 3192 static fssh_status_t 3193 file_read(struct file_descriptor *descriptor, fssh_off_t pos, void *buffer, fssh_size_t *length) 3194 { 3195 struct vnode *vnode = descriptor->u.vnode; 3196 3197 FUNCTION(("file_read: buf %p, pos %Ld, len %p = %ld\n", buffer, pos, length, *length)); 3198 return FS_CALL(vnode, read, descriptor->cookie, pos, buffer, length); 3199 } 3200 3201 3202 static fssh_status_t 3203 file_write(struct file_descriptor *descriptor, fssh_off_t pos, const void *buffer, fssh_size_t *length) 3204 { 3205 struct vnode *vnode = descriptor->u.vnode; 3206 3207 FUNCTION(("file_write: buf %p, pos %Ld, len %p\n", buffer, pos, length)); 3208 return FS_CALL(vnode, write, descriptor->cookie, pos, buffer, length); 3209 } 3210 3211 3212 static fssh_off_t 3213 file_seek(struct file_descriptor *descriptor, fssh_off_t pos, int seekType) 3214 { 3215 fssh_off_t offset; 3216 3217 FUNCTION(("file_seek(pos = %Ld, seekType = %d)\n", pos, seekType)); 3218 // ToDo: seek should fail for pipes and FIFOs... 3219 3220 switch (seekType) { 3221 case FSSH_SEEK_SET: 3222 offset = 0; 3223 break; 3224 case FSSH_SEEK_CUR: 3225 offset = descriptor->pos; 3226 break; 3227 case FSSH_SEEK_END: 3228 { 3229 struct vnode *vnode = descriptor->u.vnode; 3230 struct fssh_stat stat; 3231 fssh_status_t status; 3232 3233 if (!HAS_FS_CALL(vnode, read_stat)) 3234 return FSSH_EOPNOTSUPP; 3235 3236 status = FS_CALL(vnode, read_stat, &stat); 3237 if (status < FSSH_B_OK) 3238 return status; 3239 3240 offset = stat.fssh_st_size; 3241 break; 3242 } 3243 default: 3244 return FSSH_B_BAD_VALUE; 3245 } 3246 3247 // assumes fssh_off_t is 64 bits wide 3248 if (offset > 0 && LLONG_MAX - offset < pos) 3249 return FSSH_EOVERFLOW; 3250 3251 pos += offset; 3252 if (pos < 0) 3253 return FSSH_B_BAD_VALUE; 3254 3255 return descriptor->pos = pos; 3256 } 3257 3258 3259 static fssh_status_t 3260 dir_create_entry_ref(fssh_mount_id mountID, fssh_vnode_id parentID, const char *name, int perms, bool kernel) 3261 { 3262 struct vnode *vnode; 3263 fssh_status_t status; 3264 3265 if (name == NULL || *name == '\0') 3266 return FSSH_B_BAD_VALUE; 3267 3268 FUNCTION(("dir_create_entry_ref(dev = %ld, ino = %Ld, name = '%s', perms = %d)\n", mountID, parentID, name, perms)); 3269 3270 status = get_vnode(mountID, parentID, &vnode, kernel); 3271 if (status < FSSH_B_OK) 3272 return status; 3273 3274 if (HAS_FS_CALL(vnode, create_dir)) 3275 status = FS_CALL(vnode, create_dir, name, perms); 3276 else 3277 status = FSSH_EROFS; 3278 3279 put_vnode(vnode); 3280 return status; 3281 } 3282 3283 3284 static fssh_status_t 3285 dir_create(int fd, char *path, int perms, bool kernel) 3286 { 3287 char filename[FSSH_B_FILE_NAME_LENGTH]; 3288 struct vnode *vnode; 3289 fssh_status_t status; 3290 3291 FUNCTION(("dir_create: path '%s', perms %d, kernel %d\n", path, perms, kernel)); 3292 3293 status = fd_and_path_to_dir_vnode(fd, path, &vnode, filename, kernel); 3294 if (status < 0) 3295 return status; 3296 3297 if (HAS_FS_CALL(vnode, create_dir)) 3298 status = FS_CALL(vnode, create_dir, filename, perms); 3299 else 3300 status = FSSH_EROFS; 3301 3302 put_vnode(vnode); 3303 return status; 3304 } 3305 3306 3307 static int 3308 dir_open_entry_ref(fssh_mount_id mountID, fssh_vnode_id parentID, const char *name, bool kernel) 3309 { 3310 struct vnode *vnode; 3311 int status; 3312 3313 FUNCTION(("dir_open_entry_ref()\n")); 3314 3315 if (name && *name == '\0') 3316 return FSSH_B_BAD_VALUE; 3317 3318 // get the vnode matching the entry_ref/node_ref 3319 if (name) 3320 status = entry_ref_to_vnode(mountID, parentID, name, &vnode); 3321 else 3322 status = get_vnode(mountID, parentID, &vnode, false); 3323 if (status < FSSH_B_OK) 3324 return status; 3325 3326 status = open_dir_vnode(vnode, kernel); 3327 if (status < FSSH_B_OK) 3328 put_vnode(vnode); 3329 3330 return status; 3331 } 3332 3333 3334 static int 3335 dir_open(int fd, char *path, bool kernel) 3336 { 3337 int status = FSSH_B_OK; 3338 3339 FUNCTION(("dir_open: fd: %d, entry path = '%s', kernel %d\n", fd, path, kernel)); 3340 3341 // get the vnode matching the vnode + path combination 3342 struct vnode *vnode = NULL; 3343 fssh_vnode_id parentID; 3344 status = fd_and_path_to_vnode(fd, path, true, &vnode, &parentID, kernel); 3345 if (status != FSSH_B_OK) 3346 return status; 3347 3348 // open the dir 3349 status = open_dir_vnode(vnode, kernel); 3350 if (status < FSSH_B_OK) 3351 put_vnode(vnode); 3352 3353 return status; 3354 } 3355 3356 3357 static fssh_status_t 3358 dir_close(struct file_descriptor *descriptor) 3359 { 3360 struct vnode *vnode = descriptor->u.vnode; 3361 3362 FUNCTION(("dir_close(descriptor = %p)\n", descriptor)); 3363 3364 if (HAS_FS_CALL(vnode, close_dir)) 3365 return FS_CALL(vnode, close_dir, descriptor->cookie); 3366 3367 return FSSH_B_OK; 3368 } 3369 3370 3371 static void 3372 dir_free_fd(struct file_descriptor *descriptor) 3373 { 3374 struct vnode *vnode = descriptor->u.vnode; 3375 3376 if (vnode != NULL) { 3377 FS_CALL(vnode, free_dir_cookie, descriptor->cookie); 3378 put_vnode(vnode); 3379 } 3380 } 3381 3382 3383 static fssh_status_t 3384 dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer, 3385 fssh_size_t bufferSize, uint32_t *_count) 3386 { 3387 return dir_read(descriptor->u.vnode, descriptor->cookie, buffer, bufferSize, _count); 3388 } 3389 3390 3391 static void 3392 fix_dirent(struct vnode *parent, struct fssh_dirent *entry) 3393 { 3394 // set d_pdev and d_pino 3395 entry->d_pdev = parent->device; 3396 entry->d_pino = parent->id; 3397 3398 // If this is the ".." entry and the directory is the root of a FS, 3399 // we need to replace d_dev and d_ino with the actual values. 3400 if (fssh_strcmp(entry->d_name, "..") == 0 3401 && parent->mount->root_vnode == parent 3402 && parent->mount->covers_vnode) { 3403 inc_vnode_ref_count(parent); 3404 // vnode_path_to_vnode() puts the node 3405 3406 // ".." is guaranteed to to be clobbered by this call 3407 struct vnode *vnode; 3408 fssh_status_t status = vnode_path_to_vnode(parent, (char*)"..", false, 3409 0, &vnode, NULL); 3410 3411 if (status == FSSH_B_OK) { 3412 entry->d_dev = vnode->device; 3413 entry->d_ino = vnode->id; 3414 } 3415 } else { 3416 // resolve mount points 3417 struct vnode *vnode = NULL; 3418 fssh_status_t status = get_vnode(entry->d_dev, entry->d_ino, &vnode, false); 3419 if (status != FSSH_B_OK) 3420 return; 3421 3422 fssh_mutex_lock(&sVnodeCoveredByMutex); 3423 if (vnode->covered_by) { 3424 entry->d_dev = vnode->covered_by->device; 3425 entry->d_ino = vnode->covered_by->id; 3426 } 3427 fssh_mutex_unlock(&sVnodeCoveredByMutex); 3428 3429 put_vnode(vnode); 3430 } 3431 } 3432 3433 3434 static fssh_status_t 3435 dir_read(struct vnode *vnode, void *cookie, struct fssh_dirent *buffer, 3436 fssh_size_t bufferSize, uint32_t *_count) 3437 { 3438 if (!HAS_FS_CALL(vnode, read_dir)) 3439 return FSSH_EOPNOTSUPP; 3440 3441 fssh_status_t error = FS_CALL(vnode, read_dir,cookie,buffer,bufferSize,_count); 3442 if (error != FSSH_B_OK) 3443 return error; 3444 3445 // we need to adjust the read dirents 3446 if (*_count > 0) { 3447 // XXX: Currently reading only one dirent is supported. Make this a loop! 3448 fix_dirent(vnode, buffer); 3449 } 3450 3451 return error; 3452 } 3453 3454 3455 static fssh_status_t 3456 dir_rewind(struct file_descriptor *descriptor) 3457 { 3458 struct vnode *vnode = descriptor->u.vnode; 3459 3460 if (HAS_FS_CALL(vnode, rewind_dir)) 3461 return FS_CALL(vnode, rewind_dir,descriptor->cookie); 3462 3463 return FSSH_EOPNOTSUPP; 3464 } 3465 3466 3467 static fssh_status_t 3468 dir_remove(int fd, char *path, bool kernel) 3469 { 3470 char name[FSSH_B_FILE_NAME_LENGTH]; 3471 struct vnode *directory; 3472 fssh_status_t status; 3473 3474 if (path != NULL) { 3475 // we need to make sure our path name doesn't stop with "/", ".", or ".." 3476 char *lastSlash = fssh_strrchr(path, '/'); 3477 if (lastSlash != NULL) { 3478 char *leaf = lastSlash + 1; 3479 if (!fssh_strcmp(leaf, "..")) 3480 return FSSH_B_NOT_ALLOWED; 3481 3482 // omit multiple slashes 3483 while (lastSlash > path && lastSlash[-1] == '/') { 3484 lastSlash--; 3485 } 3486 3487 if (!leaf[0] 3488 || !fssh_strcmp(leaf, ".")) { 3489 // "name/" -> "name", or "name/." -> "name" 3490 lastSlash[0] = '\0'; 3491 } 3492 } else if (!fssh_strcmp(path, "..")) 3493 return FSSH_B_NOT_ALLOWED; 3494 } 3495 3496 status = fd_and_path_to_dir_vnode(fd, path, &directory, name, kernel); 3497 if (status < FSSH_B_OK) 3498 return status; 3499 3500 if (HAS_FS_CALL(directory, remove_dir)) { 3501 status = FS_CALL(directory, remove_dir, name); 3502 } else 3503 status = FSSH_EROFS; 3504 3505 put_vnode(directory); 3506 return status; 3507 } 3508 3509 3510 static fssh_status_t 3511 common_ioctl(struct file_descriptor *descriptor, uint32_t op, void *buffer, 3512 fssh_size_t length) 3513 { 3514 struct vnode *vnode = descriptor->u.vnode; 3515 3516 if (HAS_FS_CALL(vnode, ioctl)) { 3517 return FS_CALL(vnode, ioctl, 3518 descriptor->cookie, op, buffer, length); 3519 } 3520 3521 return FSSH_EOPNOTSUPP; 3522 } 3523 3524 3525 static fssh_status_t 3526 common_fcntl(int fd, int op, uint32_t argument, bool kernel) 3527 { 3528 struct file_descriptor *descriptor; 3529 struct vnode *vnode; 3530 fssh_status_t status; 3531 3532 FUNCTION(("common_fcntl(fd = %d, op = %d, argument = %lx, %s)\n", 3533 fd, op, argument, kernel ? "kernel" : "user")); 3534 3535 descriptor = get_fd_and_vnode(fd, &vnode, kernel); 3536 if (descriptor == NULL) 3537 return FSSH_B_FILE_ERROR; 3538 3539 switch (op) { 3540 case FSSH_F_SETFD: 3541 { 3542 struct io_context *context = get_current_io_context(kernel); 3543 // Set file descriptor flags 3544 3545 // FSSH_O_CLOEXEC is the only flag available at this time 3546 fssh_mutex_lock(&context->io_mutex); 3547 fd_set_close_on_exec(context, fd, argument == FSSH_FD_CLOEXEC); 3548 fssh_mutex_unlock(&context->io_mutex); 3549 3550 status = FSSH_B_OK; 3551 break; 3552 } 3553 3554 case FSSH_F_GETFD: 3555 { 3556 struct io_context *context = get_current_io_context(kernel); 3557 3558 // Get file descriptor flags 3559 fssh_mutex_lock(&context->io_mutex); 3560 status = fd_close_on_exec(context, fd) ? FSSH_FD_CLOEXEC : 0; 3561 fssh_mutex_unlock(&context->io_mutex); 3562 break; 3563 } 3564 3565 case FSSH_F_SETFL: 3566 // Set file descriptor open mode 3567 if (HAS_FS_CALL(vnode, set_flags)) { 3568 // we only accept changes to FSSH_O_APPEND and FSSH_O_NONBLOCK 3569 argument &= FSSH_O_APPEND | FSSH_O_NONBLOCK; 3570 3571 status = FS_CALL(vnode, set_flags, descriptor->cookie, (int)argument); 3572 if (status == FSSH_B_OK) { 3573 // update this descriptor's open_mode field 3574 descriptor->open_mode = (descriptor->open_mode & ~(FSSH_O_APPEND | FSSH_O_NONBLOCK)) 3575 | argument; 3576 } 3577 } else 3578 status = FSSH_EOPNOTSUPP; 3579 break; 3580 3581 case FSSH_F_GETFL: 3582 // Get file descriptor open mode 3583 status = descriptor->open_mode; 3584 break; 3585 3586 case FSSH_F_DUPFD: 3587 { 3588 struct io_context *context = get_current_io_context(kernel); 3589 3590 status = new_fd_etc(context, descriptor, (int)argument); 3591 if (status >= 0) { 3592 fssh_mutex_lock(&context->io_mutex); 3593 fd_set_close_on_exec(context, fd, false); 3594 fssh_mutex_unlock(&context->io_mutex); 3595 3596 fssh_atomic_add(&descriptor->ref_count, 1); 3597 } 3598 break; 3599 } 3600 3601 case FSSH_F_GETLK: 3602 case FSSH_F_SETLK: 3603 case FSSH_F_SETLKW: 3604 status = FSSH_B_BAD_VALUE; 3605 break; 3606 3607 // ToDo: add support for more ops? 3608 3609 default: 3610 status = FSSH_B_BAD_VALUE; 3611 } 3612 3613 put_fd(descriptor); 3614 return status; 3615 } 3616 3617 3618 static fssh_status_t 3619 common_sync(int fd, bool kernel) 3620 { 3621 struct file_descriptor *descriptor; 3622 struct vnode *vnode; 3623 fssh_status_t status; 3624 3625 FUNCTION(("common_fsync: entry. fd %d kernel %d\n", fd, kernel)); 3626 3627 descriptor = get_fd_and_vnode(fd, &vnode, kernel); 3628 if (descriptor == NULL) 3629 return FSSH_B_FILE_ERROR; 3630 3631 if (HAS_FS_CALL(vnode, fsync)) 3632 status = FS_CALL_NO_PARAMS(vnode, fsync); 3633 else 3634 status = FSSH_EOPNOTSUPP; 3635 3636 put_fd(descriptor); 3637 return status; 3638 } 3639 3640 3641 static fssh_status_t 3642 common_lock_node(int fd, bool kernel) 3643 { 3644 struct file_descriptor *descriptor; 3645 struct vnode *vnode; 3646 3647 descriptor = get_fd_and_vnode(fd, &vnode, kernel); 3648 if (descriptor == NULL) 3649 return FSSH_B_FILE_ERROR; 3650 3651 fssh_status_t status = FSSH_B_OK; 3652 3653 // We need to set the locking atomically - someone 3654 // else might set one at the same time 3655 #if LONG_MAX == INT_MAX 3656 if (fssh_atomic_test_and_set64((int64_t *)&vnode->mandatory_locked_by, 3657 (fssh_addr_t)descriptor, 0) != 0) 3658 #else 3659 if (fssh_atomic_test_and_set((int32_t *)&vnode->mandatory_locked_by, 3660 (fssh_addr_t)descriptor, 0) != 0) 3661 #endif 3662 status = FSSH_B_BUSY; 3663 3664 put_fd(descriptor); 3665 return status; 3666 } 3667 3668 3669 static fssh_status_t 3670 common_unlock_node(int fd, bool kernel) 3671 { 3672 struct file_descriptor *descriptor; 3673 struct vnode *vnode; 3674 3675 descriptor = get_fd_and_vnode(fd, &vnode, kernel); 3676 if (descriptor == NULL) 3677 return FSSH_B_FILE_ERROR; 3678 3679 fssh_status_t status = FSSH_B_OK; 3680 3681 // We need to set the locking atomically - someone 3682 // else might set one at the same time 3683 #ifdef __x86_64__ 3684 if (fssh_atomic_test_and_set64((int64_t *)&vnode->mandatory_locked_by, 3685 0, (fssh_addr_t)descriptor) != (int64_t)descriptor) 3686 #else 3687 if (fssh_atomic_test_and_set((int32_t *)&vnode->mandatory_locked_by, 3688 0, (fssh_addr_t)descriptor) != (int32_t)descriptor) 3689 #endif 3690 status = FSSH_B_BAD_VALUE; 3691 3692 put_fd(descriptor); 3693 return status; 3694 } 3695 3696 3697 static fssh_status_t 3698 common_read_link(int fd, char *path, char *buffer, fssh_size_t *_bufferSize, 3699 bool kernel) 3700 { 3701 struct vnode *vnode; 3702 fssh_status_t status; 3703 3704 status = fd_and_path_to_vnode(fd, path, false, &vnode, NULL, kernel); 3705 if (status < FSSH_B_OK) 3706 return status; 3707 3708 if (HAS_FS_CALL(vnode, read_symlink)) { 3709 status = FS_CALL(vnode, read_symlink, buffer, _bufferSize); 3710 } else 3711 status = FSSH_B_BAD_VALUE; 3712 3713 put_vnode(vnode); 3714 return status; 3715 } 3716 3717 3718 static fssh_status_t 3719 common_create_symlink(int fd, char *path, const char *toPath, int mode, 3720 bool kernel) 3721 { 3722 // path validity checks have to be in the calling function! 3723 char name[FSSH_B_FILE_NAME_LENGTH]; 3724 struct vnode *vnode; 3725 fssh_status_t status; 3726 3727 FUNCTION(("common_create_symlink(fd = %d, path = %s, toPath = %s, mode = %d, kernel = %d)\n", fd, path, toPath, mode, kernel)); 3728 3729 status = fd_and_path_to_dir_vnode(fd, path, &vnode, name, kernel); 3730 if (status < FSSH_B_OK) 3731 return status; 3732 3733 if (HAS_FS_CALL(vnode, create_symlink)) 3734 status = FS_CALL(vnode, create_symlink, name, toPath, mode); 3735 else 3736 status = FSSH_EROFS; 3737 3738 put_vnode(vnode); 3739 3740 return status; 3741 } 3742 3743 3744 static fssh_status_t 3745 common_create_link(char *path, char *toPath, bool kernel) 3746 { 3747 // path validity checks have to be in the calling function! 3748 char name[FSSH_B_FILE_NAME_LENGTH]; 3749 struct vnode *directory, *vnode; 3750 fssh_status_t status; 3751 3752 FUNCTION(("common_create_link(path = %s, toPath = %s, kernel = %d)\n", path, toPath, kernel)); 3753 3754 status = path_to_dir_vnode(path, &directory, name, kernel); 3755 if (status < FSSH_B_OK) 3756 return status; 3757 3758 status = path_to_vnode(toPath, true, &vnode, NULL, kernel); 3759 if (status < FSSH_B_OK) 3760 goto err; 3761 3762 if (directory->mount != vnode->mount) { 3763 status = FSSH_B_CROSS_DEVICE_LINK; 3764 goto err1; 3765 } 3766 3767 if (HAS_FS_CALL(directory, link)) 3768 status = FS_CALL(directory, link, name, vnode); 3769 else 3770 status = FSSH_EROFS; 3771 3772 err1: 3773 put_vnode(vnode); 3774 err: 3775 put_vnode(directory); 3776 3777 return status; 3778 } 3779 3780 3781 static fssh_status_t 3782 common_unlink(int fd, char *path, bool kernel) 3783 { 3784 char filename[FSSH_B_FILE_NAME_LENGTH]; 3785 struct vnode *vnode; 3786 fssh_status_t status; 3787 3788 FUNCTION(("common_unlink: fd: %d, path '%s', kernel %d\n", fd, path, kernel)); 3789 3790 status = fd_and_path_to_dir_vnode(fd, path, &vnode, filename, kernel); 3791 if (status < 0) 3792 return status; 3793 3794 if (HAS_FS_CALL(vnode, unlink)) 3795 status = FS_CALL(vnode, unlink, filename); 3796 else 3797 status = FSSH_EROFS; 3798 3799 put_vnode(vnode); 3800 3801 return status; 3802 } 3803 3804 3805 static fssh_status_t 3806 common_access(char *path, int mode, bool kernel) 3807 { 3808 struct vnode *vnode; 3809 fssh_status_t status; 3810 3811 status = path_to_vnode(path, true, &vnode, NULL, kernel); 3812 if (status < FSSH_B_OK) 3813 return status; 3814 3815 if (HAS_FS_CALL(vnode, access)) 3816 status = FS_CALL(vnode, access, mode); 3817 else 3818 status = FSSH_B_OK; 3819 3820 put_vnode(vnode); 3821 3822 return status; 3823 } 3824 3825 3826 static fssh_status_t 3827 common_rename(int fd, char *path, int newFD, char *newPath, bool kernel) 3828 { 3829 struct vnode *fromVnode, *toVnode; 3830 char fromName[FSSH_B_FILE_NAME_LENGTH]; 3831 char toName[FSSH_B_FILE_NAME_LENGTH]; 3832 fssh_status_t status; 3833 3834 FUNCTION(("common_rename(fd = %d, path = %s, newFD = %d, newPath = %s, kernel = %d)\n", fd, path, newFD, newPath, kernel)); 3835 3836 status = fd_and_path_to_dir_vnode(fd, path, &fromVnode, fromName, kernel); 3837 if (status < 0) 3838 return status; 3839 3840 status = fd_and_path_to_dir_vnode(newFD, newPath, &toVnode, toName, kernel); 3841 if (status < 0) 3842 goto err; 3843 3844 if (fromVnode->device != toVnode->device) { 3845 status = FSSH_B_CROSS_DEVICE_LINK; 3846 goto err1; 3847 } 3848 3849 if (HAS_FS_CALL(fromVnode, rename)) 3850 status = FS_CALL(fromVnode, rename, fromName, toVnode, toName); 3851 else 3852 status = FSSH_EROFS; 3853 3854 err1: 3855 put_vnode(toVnode); 3856 err: 3857 put_vnode(fromVnode); 3858 3859 return status; 3860 } 3861 3862 3863 static fssh_status_t 3864 common_read_stat(struct file_descriptor *descriptor, struct fssh_stat *stat) 3865 { 3866 struct vnode *vnode = descriptor->u.vnode; 3867 3868 FUNCTION(("common_read_stat: stat %p\n", stat)); 3869 3870 stat->fssh_st_atim.tv_nsec = 0; 3871 stat->fssh_st_mtim.tv_nsec = 0; 3872 stat->fssh_st_ctim.tv_nsec = 0; 3873 stat->fssh_st_crtim.tv_nsec = 0; 3874 3875 fssh_status_t status = FS_CALL(vnode, read_stat, stat); 3876 3877 // fill in the st_dev and st_ino fields 3878 if (status == FSSH_B_OK) { 3879 stat->fssh_st_dev = vnode->device; 3880 stat->fssh_st_ino = vnode->id; 3881 } 3882 3883 return status; 3884 } 3885 3886 3887 static fssh_status_t 3888 common_write_stat(struct file_descriptor *descriptor, 3889 const struct fssh_stat *stat, int statMask) 3890 { 3891 struct vnode *vnode = descriptor->u.vnode; 3892 3893 FUNCTION(("common_write_stat(vnode = %p, stat = %p, statMask = %d)\n", vnode, stat, statMask)); 3894 if (!HAS_FS_CALL(vnode, write_stat)) 3895 return FSSH_EROFS; 3896 3897 return FS_CALL(vnode, write_stat, stat, statMask); 3898 } 3899 3900 3901 static fssh_status_t 3902 common_path_read_stat(int fd, char *path, bool traverseLeafLink, 3903 struct fssh_stat *stat, bool kernel) 3904 { 3905 struct vnode *vnode; 3906 fssh_status_t status; 3907 3908 FUNCTION(("common_path_read_stat: fd: %d, path '%s', stat %p,\n", fd, path, stat)); 3909 3910 status = fd_and_path_to_vnode(fd, path, traverseLeafLink, &vnode, NULL, kernel); 3911 if (status < 0) 3912 return status; 3913 3914 status = FS_CALL(vnode, read_stat, stat); 3915 3916 // fill in the st_dev and st_ino fields 3917 if (status == FSSH_B_OK) { 3918 stat->fssh_st_dev = vnode->device; 3919 stat->fssh_st_ino = vnode->id; 3920 } 3921 3922 put_vnode(vnode); 3923 return status; 3924 } 3925 3926 3927 static fssh_status_t 3928 common_path_write_stat(int fd, char *path, bool traverseLeafLink, 3929 const struct fssh_stat *stat, int statMask, bool kernel) 3930 { 3931 struct vnode *vnode; 3932 fssh_status_t status; 3933 3934 FUNCTION(("common_write_stat: fd: %d, path '%s', stat %p, stat_mask %d, kernel %d\n", fd, path, stat, statMask, kernel)); 3935 3936 status = fd_and_path_to_vnode(fd, path, traverseLeafLink, &vnode, NULL, kernel); 3937 if (status < 0) 3938 return status; 3939 3940 if (HAS_FS_CALL(vnode, write_stat)) 3941 status = FS_CALL(vnode, write_stat, stat, statMask); 3942 else 3943 status = FSSH_EROFS; 3944 3945 put_vnode(vnode); 3946 3947 return status; 3948 } 3949 3950 3951 static int 3952 attr_dir_open(int fd, char *path, bool kernel) 3953 { 3954 struct vnode *vnode; 3955 int status; 3956 3957 FUNCTION(("attr_dir_open(fd = %d, path = '%s', kernel = %d)\n", fd, path, kernel)); 3958 3959 status = fd_and_path_to_vnode(fd, path, true, &vnode, NULL, kernel); 3960 if (status < FSSH_B_OK) 3961 return status; 3962 3963 status = open_attr_dir_vnode(vnode, kernel); 3964 if (status < 0) 3965 put_vnode(vnode); 3966 3967 return status; 3968 } 3969 3970 3971 static fssh_status_t 3972 attr_dir_close(struct file_descriptor *descriptor) 3973 { 3974 struct vnode *vnode = descriptor->u.vnode; 3975 3976 FUNCTION(("attr_dir_close(descriptor = %p)\n", descriptor)); 3977 3978 if (HAS_FS_CALL(vnode, close_attr_dir)) 3979 return FS_CALL(vnode, close_attr_dir, descriptor->cookie); 3980 3981 return FSSH_B_OK; 3982 } 3983 3984 3985 static void 3986 attr_dir_free_fd(struct file_descriptor *descriptor) 3987 { 3988 struct vnode *vnode = descriptor->u.vnode; 3989 3990 if (vnode != NULL) { 3991 FS_CALL(vnode, free_attr_dir_cookie, descriptor->cookie); 3992 put_vnode(vnode); 3993 } 3994 } 3995 3996 3997 static fssh_status_t 3998 attr_dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer, 3999 fssh_size_t bufferSize, uint32_t *_count) 4000 { 4001 struct vnode *vnode = descriptor->u.vnode; 4002 4003 FUNCTION(("attr_dir_read(descriptor = %p)\n", descriptor)); 4004 4005 if (HAS_FS_CALL(vnode, read_attr_dir)) 4006 return FS_CALL(vnode, read_attr_dir, descriptor->cookie, buffer, bufferSize, _count); 4007 4008 return FSSH_EOPNOTSUPP; 4009 } 4010 4011 4012 static fssh_status_t 4013 attr_dir_rewind(struct file_descriptor *descriptor) 4014 { 4015 struct vnode *vnode = descriptor->u.vnode; 4016 4017 FUNCTION(("attr_dir_rewind(descriptor = %p)\n", descriptor)); 4018 4019 if (HAS_FS_CALL(vnode, rewind_attr_dir)) 4020 return FS_CALL(vnode, rewind_attr_dir, descriptor->cookie); 4021 4022 return FSSH_EOPNOTSUPP; 4023 } 4024 4025 4026 static int 4027 attr_create(int fd, const char *name, uint32_t type, int openMode, bool kernel) 4028 { 4029 struct vnode *vnode; 4030 void *cookie; 4031 int status; 4032 4033 if (name == NULL || *name == '\0') 4034 return FSSH_B_BAD_VALUE; 4035 4036 vnode = get_vnode_from_fd(fd, kernel); 4037 if (vnode == NULL) 4038 return FSSH_B_FILE_ERROR; 4039 4040 if (!HAS_FS_CALL(vnode, create_attr)) { 4041 status = FSSH_EROFS; 4042 goto err; 4043 } 4044 4045 status = FS_CALL(vnode, create_attr, name, type, openMode, &cookie); 4046 if (status < FSSH_B_OK) 4047 goto err; 4048 4049 if ((status = get_new_fd(FDTYPE_ATTR, NULL, vnode, cookie, openMode, kernel)) >= 0) 4050 return status; 4051 4052 FS_CALL(vnode, close_attr, cookie); 4053 FS_CALL(vnode, free_attr_cookie, cookie); 4054 4055 FS_CALL(vnode, remove_attr, name); 4056 4057 err: 4058 put_vnode(vnode); 4059 4060 return status; 4061 } 4062 4063 4064 static int 4065 attr_open(int fd, const char *name, int openMode, bool kernel) 4066 { 4067 struct vnode *vnode; 4068 void *cookie; 4069 int status; 4070 4071 if (name == NULL || *name == '\0') 4072 return FSSH_B_BAD_VALUE; 4073 4074 vnode = get_vnode_from_fd(fd, kernel); 4075 if (vnode == NULL) 4076 return FSSH_B_FILE_ERROR; 4077 4078 if (!HAS_FS_CALL(vnode, open_attr)) { 4079 status = FSSH_EOPNOTSUPP; 4080 goto err; 4081 } 4082 4083 status = FS_CALL(vnode, open_attr, name, openMode, &cookie); 4084 if (status < FSSH_B_OK) 4085 goto err; 4086 4087 // now we only need a file descriptor for this attribute and we're done 4088 if ((status = get_new_fd(FDTYPE_ATTR, NULL, vnode, cookie, openMode, kernel)) >= 0) 4089 return status; 4090 4091 FS_CALL(vnode, close_attr, cookie); 4092 FS_CALL(vnode, free_attr_cookie, cookie); 4093 4094 err: 4095 put_vnode(vnode); 4096 4097 return status; 4098 } 4099 4100 4101 static fssh_status_t 4102 attr_close(struct file_descriptor *descriptor) 4103 { 4104 struct vnode *vnode = descriptor->u.vnode; 4105 4106 FUNCTION(("attr_close(descriptor = %p)\n", descriptor)); 4107 4108 if (HAS_FS_CALL(vnode, close_attr)) 4109 return FS_CALL(vnode, close_attr, descriptor->cookie); 4110 4111 return FSSH_B_OK; 4112 } 4113 4114 4115 static void 4116 attr_free_fd(struct file_descriptor *descriptor) 4117 { 4118 struct vnode *vnode = descriptor->u.vnode; 4119 4120 if (vnode != NULL) { 4121 FS_CALL(vnode, free_attr_cookie, descriptor->cookie); 4122 put_vnode(vnode); 4123 } 4124 } 4125 4126 4127 static fssh_status_t 4128 attr_read(struct file_descriptor *descriptor, fssh_off_t pos, void *buffer, fssh_size_t *length) 4129 { 4130 struct vnode *vnode = descriptor->u.vnode; 4131 4132 FUNCTION(("attr_read: buf %p, pos %Ld, len %p = %ld\n", buffer, pos, length, *length)); 4133 if (!HAS_FS_CALL(vnode, read_attr)) 4134 return FSSH_EOPNOTSUPP; 4135 4136 return FS_CALL(vnode, read_attr, descriptor->cookie, pos, buffer, length); 4137 } 4138 4139 4140 static fssh_status_t 4141 attr_write(struct file_descriptor *descriptor, fssh_off_t pos, const void *buffer, fssh_size_t *length) 4142 { 4143 struct vnode *vnode = descriptor->u.vnode; 4144 4145 FUNCTION(("attr_write: buf %p, pos %Ld, len %p\n", buffer, pos, length)); 4146 if (!HAS_FS_CALL(vnode, write_attr)) 4147 return FSSH_EOPNOTSUPP; 4148 4149 return FS_CALL(vnode, write_attr, descriptor->cookie, pos, buffer, length); 4150 } 4151 4152 4153 static fssh_off_t 4154 attr_seek(struct file_descriptor *descriptor, fssh_off_t pos, int seekType) 4155 { 4156 fssh_off_t offset; 4157 4158 switch (seekType) { 4159 case FSSH_SEEK_SET: 4160 offset = 0; 4161 break; 4162 case FSSH_SEEK_CUR: 4163 offset = descriptor->pos; 4164 break; 4165 case FSSH_SEEK_END: 4166 { 4167 struct vnode *vnode = descriptor->u.vnode; 4168 struct fssh_stat stat; 4169 fssh_status_t status; 4170 4171 if (!HAS_FS_CALL(vnode, read_stat)) 4172 return FSSH_EOPNOTSUPP; 4173 4174 status = FS_CALL(vnode, read_attr_stat, descriptor->cookie, &stat); 4175 if (status < FSSH_B_OK) 4176 return status; 4177 4178 offset = stat.fssh_st_size; 4179 break; 4180 } 4181 default: 4182 return FSSH_B_BAD_VALUE; 4183 } 4184 4185 // assumes fssh_off_t is 64 bits wide 4186 if (offset > 0 && LLONG_MAX - offset < pos) 4187 return FSSH_EOVERFLOW; 4188 4189 pos += offset; 4190 if (pos < 0) 4191 return FSSH_B_BAD_VALUE; 4192 4193 return descriptor->pos = pos; 4194 } 4195 4196 4197 static fssh_status_t 4198 attr_read_stat(struct file_descriptor *descriptor, struct fssh_stat *stat) 4199 { 4200 struct vnode *vnode = descriptor->u.vnode; 4201 4202 FUNCTION(("attr_read_stat: stat 0x%p\n", stat)); 4203 4204 if (!HAS_FS_CALL(vnode, read_attr_stat)) 4205 return FSSH_EOPNOTSUPP; 4206 4207 return FS_CALL(vnode, read_attr_stat, descriptor->cookie, stat); 4208 } 4209 4210 4211 static fssh_status_t 4212 attr_write_stat(struct file_descriptor *descriptor, 4213 const struct fssh_stat *stat, int statMask) 4214 { 4215 struct vnode *vnode = descriptor->u.vnode; 4216 4217 FUNCTION(("attr_write_stat: stat = %p, statMask %d\n", stat, statMask)); 4218 4219 if (!HAS_FS_CALL(vnode, write_attr_stat)) 4220 return FSSH_EROFS; 4221 4222 return FS_CALL(vnode, write_attr_stat, descriptor->cookie, stat, statMask); 4223 } 4224 4225 4226 static fssh_status_t 4227 attr_remove(int fd, const char *name, bool kernel) 4228 { 4229 struct file_descriptor *descriptor; 4230 struct vnode *vnode; 4231 fssh_status_t status; 4232 4233 if (name == NULL || *name == '\0') 4234 return FSSH_B_BAD_VALUE; 4235 4236 FUNCTION(("attr_remove: fd = %d, name = \"%s\", kernel %d\n", fd, name, kernel)); 4237 4238 descriptor = get_fd_and_vnode(fd, &vnode, kernel); 4239 if (descriptor == NULL) 4240 return FSSH_B_FILE_ERROR; 4241 4242 if (HAS_FS_CALL(vnode, remove_attr)) 4243 status = FS_CALL(vnode, remove_attr, name); 4244 else 4245 status = FSSH_EROFS; 4246 4247 put_fd(descriptor); 4248 4249 return status; 4250 } 4251 4252 4253 static fssh_status_t 4254 attr_rename(int fromfd, const char *fromName, int tofd, const char *toName, bool kernel) 4255 { 4256 struct file_descriptor *fromDescriptor, *toDescriptor; 4257 struct vnode *fromVnode, *toVnode; 4258 fssh_status_t status; 4259 4260 if (fromName == NULL || *fromName == '\0' || toName == NULL || *toName == '\0') 4261 return FSSH_B_BAD_VALUE; 4262 4263 FUNCTION(("attr_rename: from fd = %d, from name = \"%s\", to fd = %d, to name = \"%s\", kernel %d\n", fromfd, fromName, tofd, toName, kernel)); 4264 4265 fromDescriptor = get_fd_and_vnode(fromfd, &fromVnode, kernel); 4266 if (fromDescriptor == NULL) 4267 return FSSH_B_FILE_ERROR; 4268 4269 toDescriptor = get_fd_and_vnode(tofd, &toVnode, kernel); 4270 if (toDescriptor == NULL) { 4271 status = FSSH_B_FILE_ERROR; 4272 goto err; 4273 } 4274 4275 // are the files on the same volume? 4276 if (fromVnode->device != toVnode->device) { 4277 status = FSSH_B_CROSS_DEVICE_LINK; 4278 goto err1; 4279 } 4280 4281 if (HAS_FS_CALL(fromVnode, rename_attr)) 4282 status = FS_CALL(fromVnode, rename_attr, fromName, toVnode, toName); 4283 else 4284 status = FSSH_EROFS; 4285 4286 err1: 4287 put_fd(toDescriptor); 4288 err: 4289 put_fd(fromDescriptor); 4290 4291 return status; 4292 } 4293 4294 4295 static fssh_status_t 4296 index_dir_open(fssh_mount_id mountID, bool kernel) 4297 { 4298 struct fs_mount *mount; 4299 void *cookie; 4300 4301 FUNCTION(("index_dir_open(mountID = %ld, kernel = %d)\n", mountID, kernel)); 4302 4303 fssh_status_t status = get_mount(mountID, &mount); 4304 if (status < FSSH_B_OK) 4305 return status; 4306 4307 if (!HAS_FS_MOUNT_CALL(mount, open_index_dir)) { 4308 status = FSSH_EOPNOTSUPP; 4309 goto out; 4310 } 4311 4312 status = FS_MOUNT_CALL(mount, open_index_dir, &cookie); 4313 if (status < FSSH_B_OK) 4314 goto out; 4315 4316 // get fd for the index directory 4317 status = get_new_fd(FDTYPE_INDEX_DIR, mount, NULL, cookie, 0, kernel); 4318 if (status >= 0) 4319 goto out; 4320 4321 // something went wrong 4322 FS_MOUNT_CALL(mount, close_index_dir, cookie); 4323 FS_MOUNT_CALL(mount, free_index_dir_cookie, cookie); 4324 4325 out: 4326 put_mount(mount); 4327 return status; 4328 } 4329 4330 4331 static fssh_status_t 4332 index_dir_close(struct file_descriptor *descriptor) 4333 { 4334 struct fs_mount *mount = descriptor->u.mount; 4335 4336 FUNCTION(("index_dir_close(descriptor = %p)\n", descriptor)); 4337 4338 if (HAS_FS_MOUNT_CALL(mount, close_index_dir)) 4339 return FS_MOUNT_CALL(mount, close_index_dir, descriptor->cookie); 4340 4341 return FSSH_B_OK; 4342 } 4343 4344 4345 static void 4346 index_dir_free_fd(struct file_descriptor *descriptor) 4347 { 4348 struct fs_mount *mount = descriptor->u.mount; 4349 4350 if (mount != NULL) { 4351 FS_MOUNT_CALL(mount, free_index_dir_cookie, descriptor->cookie); 4352 // ToDo: find a replacement ref_count object - perhaps the root dir? 4353 //put_vnode(vnode); 4354 } 4355 } 4356 4357 4358 static fssh_status_t 4359 index_dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer, 4360 fssh_size_t bufferSize, uint32_t *_count) 4361 { 4362 struct fs_mount *mount = descriptor->u.mount; 4363 4364 if (HAS_FS_MOUNT_CALL(mount, read_index_dir)) 4365 return FS_MOUNT_CALL(mount, read_index_dir, descriptor->cookie, buffer, bufferSize, _count); 4366 4367 return FSSH_EOPNOTSUPP; 4368 } 4369 4370 4371 static fssh_status_t 4372 index_dir_rewind(struct file_descriptor *descriptor) 4373 { 4374 struct fs_mount *mount = descriptor->u.mount; 4375 4376 if (HAS_FS_MOUNT_CALL(mount, rewind_index_dir)) 4377 return FS_MOUNT_CALL(mount, rewind_index_dir, descriptor->cookie); 4378 4379 return FSSH_EOPNOTSUPP; 4380 } 4381 4382 4383 static fssh_status_t 4384 index_create(fssh_mount_id mountID, const char *name, uint32_t type, uint32_t flags, bool kernel) 4385 { 4386 FUNCTION(("index_create(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel)); 4387 4388 struct fs_mount *mount; 4389 fssh_status_t status = get_mount(mountID, &mount); 4390 if (status < FSSH_B_OK) 4391 return status; 4392 4393 if (!HAS_FS_MOUNT_CALL(mount, create_index)) { 4394 status = FSSH_EROFS; 4395 goto out; 4396 } 4397 4398 status = FS_MOUNT_CALL(mount, create_index, name, type, flags); 4399 4400 out: 4401 put_mount(mount); 4402 return status; 4403 } 4404 4405 4406 static fssh_status_t 4407 index_name_read_stat(fssh_mount_id mountID, const char *name, 4408 struct fssh_stat *stat, bool kernel) 4409 { 4410 FUNCTION(("index_remove(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel)); 4411 4412 struct fs_mount *mount; 4413 fssh_status_t status = get_mount(mountID, &mount); 4414 if (status < FSSH_B_OK) 4415 return status; 4416 4417 if (!HAS_FS_MOUNT_CALL(mount, read_index_stat)) { 4418 status = FSSH_EOPNOTSUPP; 4419 goto out; 4420 } 4421 4422 status = FS_MOUNT_CALL(mount, read_index_stat, name, stat); 4423 4424 out: 4425 put_mount(mount); 4426 return status; 4427 } 4428 4429 4430 static fssh_status_t 4431 index_remove(fssh_mount_id mountID, const char *name, bool kernel) 4432 { 4433 FUNCTION(("index_remove(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel)); 4434 4435 struct fs_mount *mount; 4436 fssh_status_t status = get_mount(mountID, &mount); 4437 if (status < FSSH_B_OK) 4438 return status; 4439 4440 if (!HAS_FS_MOUNT_CALL(mount, remove_index)) { 4441 status = FSSH_EROFS; 4442 goto out; 4443 } 4444 4445 status = FS_MOUNT_CALL(mount, remove_index, name); 4446 4447 out: 4448 put_mount(mount); 4449 return status; 4450 } 4451 4452 4453 /*! ToDo: the query FS API is still the pretty much the same as in R5. 4454 It would be nice if the FS would find some more kernel support 4455 for them. 4456 For example, query parsing should be moved into the kernel. 4457 */ 4458 static int 4459 query_open(fssh_dev_t device, const char *query, uint32_t flags, 4460 fssh_port_id port, int32_t token, bool kernel) 4461 { 4462 struct fs_mount *mount; 4463 void *cookie; 4464 4465 FUNCTION(("query_open(device = %ld, query = \"%s\", kernel = %d)\n", device, query, kernel)); 4466 4467 fssh_status_t status = get_mount(device, &mount); 4468 if (status < FSSH_B_OK) 4469 return status; 4470 4471 if (!HAS_FS_MOUNT_CALL(mount, open_query)) { 4472 status = FSSH_EOPNOTSUPP; 4473 goto out; 4474 } 4475 4476 status = FS_MOUNT_CALL(mount, open_query, query, flags, port, token, &cookie); 4477 if (status < FSSH_B_OK) 4478 goto out; 4479 4480 // get fd for the index directory 4481 status = get_new_fd(FDTYPE_QUERY, mount, NULL, cookie, 0, kernel); 4482 if (status >= 0) 4483 goto out; 4484 4485 // something went wrong 4486 FS_MOUNT_CALL(mount, close_query, cookie); 4487 FS_MOUNT_CALL(mount, free_query_cookie, cookie); 4488 4489 out: 4490 put_mount(mount); 4491 return status; 4492 } 4493 4494 4495 static fssh_status_t 4496 query_close(struct file_descriptor *descriptor) 4497 { 4498 struct fs_mount *mount = descriptor->u.mount; 4499 4500 FUNCTION(("query_close(descriptor = %p)\n", descriptor)); 4501 4502 if (HAS_FS_MOUNT_CALL(mount, close_query)) 4503 return FS_MOUNT_CALL(mount, close_query, descriptor->cookie); 4504 4505 return FSSH_B_OK; 4506 } 4507 4508 4509 static void 4510 query_free_fd(struct file_descriptor *descriptor) 4511 { 4512 struct fs_mount *mount = descriptor->u.mount; 4513 4514 if (mount != NULL) { 4515 FS_MOUNT_CALL(mount, free_query_cookie, descriptor->cookie); 4516 // ToDo: find a replacement ref_count object - perhaps the root dir? 4517 //put_vnode(vnode); 4518 } 4519 } 4520 4521 4522 static fssh_status_t 4523 query_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer, 4524 fssh_size_t bufferSize, uint32_t *_count) 4525 { 4526 struct fs_mount *mount = descriptor->u.mount; 4527 4528 if (HAS_FS_MOUNT_CALL(mount, read_query)) 4529 return FS_MOUNT_CALL(mount, read_query, descriptor->cookie, buffer, bufferSize, _count); 4530 4531 return FSSH_EOPNOTSUPP; 4532 } 4533 4534 4535 static fssh_status_t 4536 query_rewind(struct file_descriptor *descriptor) 4537 { 4538 struct fs_mount *mount = descriptor->u.mount; 4539 4540 if (HAS_FS_MOUNT_CALL(mount, rewind_query)) 4541 return FS_MOUNT_CALL(mount, rewind_query, descriptor->cookie); 4542 4543 return FSSH_EOPNOTSUPP; 4544 } 4545 4546 4547 // #pragma mark - 4548 // General File System functions 4549 4550 4551 static fssh_dev_t 4552 fs_mount(char *path, const char *device, const char *fsName, uint32_t flags, 4553 const char *args, bool kernel) 4554 { 4555 struct fs_mount *mount; 4556 fssh_status_t status = 0; 4557 4558 FUNCTION(("fs_mount: entry. path = '%s', fs_name = '%s'\n", path, fsName)); 4559 4560 // The path is always safe, we just have to make sure that fsName is 4561 // almost valid - we can't make any assumptions about args, though. 4562 // A NULL fsName is OK, if a device was given and the FS is not virtual. 4563 // We'll get it from the DDM later. 4564 if (fsName == NULL) { 4565 if (!device || flags & FSSH_B_MOUNT_VIRTUAL_DEVICE) 4566 return FSSH_B_BAD_VALUE; 4567 } else if (fsName[0] == '\0') 4568 return FSSH_B_BAD_VALUE; 4569 4570 RecursiveLocker mountOpLocker(sMountOpLock); 4571 4572 // If the file system is not a "virtual" one, the device argument should 4573 // point to a real file/device (if given at all). 4574 // get the partition 4575 KPath normalizedDevice; 4576 4577 if (!(flags & FSSH_B_MOUNT_VIRTUAL_DEVICE) && device) { 4578 // normalize the device path 4579 // status = normalizedDevice.SetTo(device, true); 4580 // NOTE: normalizing works only in our namespace. 4581 status = normalizedDevice.SetTo(device, false); 4582 if (status != FSSH_B_OK) 4583 return status; 4584 4585 device = normalizedDevice.Path(); 4586 // correct path to file device 4587 } 4588 4589 mount = (struct fs_mount *)malloc(sizeof(struct fs_mount)); 4590 if (mount == NULL) 4591 return FSSH_B_NO_MEMORY; 4592 4593 mount->volume = (fssh_fs_volume*)malloc(sizeof(fssh_fs_volume)); 4594 if (mount->volume == NULL) { 4595 free(mount); 4596 return FSSH_B_NO_MEMORY; 4597 } 4598 4599 list_init_etc(&mount->vnodes, fssh_offsetof(struct vnode, mount_link)); 4600 4601 mount->fs_name = get_file_system_name(fsName); 4602 if (mount->fs_name == NULL) { 4603 status = FSSH_B_NO_MEMORY; 4604 goto err1; 4605 } 4606 4607 mount->device_name = fssh_strdup(device); 4608 // "device" can be NULL 4609 4610 mount->fs = get_file_system(fsName); 4611 if (mount->fs == NULL) { 4612 status = FSSH_ENODEV; 4613 goto err3; 4614 } 4615 4616 fssh_recursive_lock_init(&mount->rlock, "mount rlock"); 4617 4618 // initialize structure 4619 mount->id = sNextMountID++; 4620 mount->root_vnode = NULL; 4621 mount->covers_vnode = NULL; 4622 mount->unmounting = false; 4623 mount->owns_file_device = false; 4624 4625 mount->volume->id = mount->id; 4626 mount->volume->layer = 0; 4627 mount->volume->private_volume = NULL; 4628 mount->volume->ops = NULL; 4629 mount->volume->sub_volume = NULL; 4630 mount->volume->super_volume = NULL; 4631 4632 // insert mount struct into list before we call FS's mount() function 4633 // so that vnodes can be created for this mount 4634 fssh_mutex_lock(&sMountMutex); 4635 hash_insert(sMountsTable, mount); 4636 fssh_mutex_unlock(&sMountMutex); 4637 4638 fssh_vnode_id rootID; 4639 4640 if (!sRoot) { 4641 // we haven't mounted anything yet 4642 if (fssh_strcmp(path, "/") != 0) { 4643 status = FSSH_B_ERROR; 4644 goto err4; 4645 } 4646 4647 status = mount->fs->mount(mount->volume, device, flags, args, &rootID); 4648 if (status < 0) { 4649 // ToDo: why should we hide the error code from the file system here? 4650 //status = ERR_VFS_GENERAL; 4651 goto err4; 4652 } 4653 } else { 4654 struct vnode *coveredVnode; 4655 status = path_to_vnode(path, true, &coveredVnode, NULL, kernel); 4656 if (status < FSSH_B_OK) 4657 goto err4; 4658 4659 // make sure covered_vnode is a DIR 4660 struct fssh_stat coveredNodeStat; 4661 status = FS_CALL(coveredVnode, read_stat, &coveredNodeStat); 4662 if (status < FSSH_B_OK) 4663 goto err4; 4664 4665 if (!FSSH_S_ISDIR(coveredNodeStat.fssh_st_mode)) { 4666 status = FSSH_B_NOT_A_DIRECTORY; 4667 goto err4; 4668 } 4669 4670 if (coveredVnode->mount->root_vnode == coveredVnode) { 4671 // this is already a mount point 4672 status = FSSH_B_BUSY; 4673 goto err4; 4674 } 4675 4676 mount->covers_vnode = coveredVnode; 4677 4678 // mount it 4679 status = mount->fs->mount(mount->volume, device, flags, args, &rootID); 4680 if (status < FSSH_B_OK) 4681 goto err5; 4682 } 4683 4684 // the root node is supposed to be owned by the file system - it must 4685 // exist at this point 4686 mount->root_vnode = lookup_vnode(mount->id, rootID); 4687 if (mount->root_vnode == NULL || mount->root_vnode->ref_count != 1) { 4688 fssh_panic("fs_mount: file system does not own its root node!\n"); 4689 status = FSSH_B_ERROR; 4690 goto err6; 4691 } 4692 4693 // No race here, since fs_mount() is the only function changing 4694 // covers_vnode (and holds sMountOpLock at that time). 4695 fssh_mutex_lock(&sVnodeCoveredByMutex); 4696 if (mount->covers_vnode) 4697 mount->covers_vnode->covered_by = mount->root_vnode; 4698 fssh_mutex_unlock(&sVnodeCoveredByMutex); 4699 4700 if (!sRoot) 4701 sRoot = mount->root_vnode; 4702 4703 return mount->id; 4704 4705 err6: 4706 FS_MOUNT_CALL_NO_PARAMS(mount, unmount); 4707 err5: 4708 if (mount->covers_vnode) 4709 put_vnode(mount->covers_vnode); 4710 4711 err4: 4712 fssh_mutex_lock(&sMountMutex); 4713 hash_remove(sMountsTable, mount); 4714 fssh_mutex_unlock(&sMountMutex); 4715 4716 fssh_recursive_lock_destroy(&mount->rlock); 4717 4718 put_file_system(mount->fs); 4719 free(mount->device_name); 4720 err3: 4721 free(mount->fs_name); 4722 err1: 4723 free(mount->volume); 4724 free(mount); 4725 4726 return status; 4727 } 4728 4729 4730 static fssh_status_t 4731 fs_unmount(char *path, uint32_t flags, bool kernel) 4732 { 4733 struct fs_mount *mount; 4734 struct vnode *vnode; 4735 fssh_status_t err; 4736 4737 FUNCTION(("vfs_unmount: entry. path = '%s', kernel %d\n", path, kernel)); 4738 4739 err = path_to_vnode(path, true, &vnode, NULL, kernel); 4740 if (err < 0) 4741 return FSSH_B_ENTRY_NOT_FOUND; 4742 4743 RecursiveLocker mountOpLocker(sMountOpLock); 4744 4745 mount = find_mount(vnode->device); 4746 if (!mount) 4747 fssh_panic("vfs_unmount: find_mount() failed on root vnode @%p of mount\n", vnode); 4748 4749 if (mount->root_vnode != vnode) { 4750 // not mountpoint 4751 put_vnode(vnode); 4752 return FSSH_B_BAD_VALUE; 4753 } 4754 4755 // grab the vnode master mutex to keep someone from creating 4756 // a vnode while we're figuring out if we can continue 4757 fssh_mutex_lock(&sVnodeMutex); 4758 4759 bool disconnectedDescriptors = false; 4760 4761 while (true) { 4762 bool busy = false; 4763 4764 // cycle through the list of vnodes associated with this mount and 4765 // make sure all of them are not busy or have refs on them 4766 vnode = NULL; 4767 while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) { 4768 // The root vnode ref_count needs to be 2 here: one for the file 4769 // system, one from the path_to_vnode() call above 4770 if (vnode->busy 4771 || ((vnode->ref_count != 0 && mount->root_vnode != vnode) 4772 || (vnode->ref_count != 2 && mount->root_vnode == vnode))) { 4773 // there are still vnodes in use on this mount, so we cannot 4774 // unmount yet 4775 busy = true; 4776 break; 4777 } 4778 } 4779 4780 if (!busy) 4781 break; 4782 4783 if ((flags & FSSH_B_FORCE_UNMOUNT) == 0) { 4784 fssh_mutex_unlock(&sVnodeMutex); 4785 put_vnode(mount->root_vnode); 4786 4787 return FSSH_B_BUSY; 4788 } 4789 4790 if (disconnectedDescriptors) { 4791 // wait a bit until the last access is finished, and then try again 4792 fssh_mutex_unlock(&sVnodeMutex); 4793 fssh_snooze(100000); 4794 // TODO: if there is some kind of bug that prevents the ref counts 4795 // from getting back to zero, this will fall into an endless loop... 4796 fssh_mutex_lock(&sVnodeMutex); 4797 continue; 4798 } 4799 4800 // the file system is still busy - but we're forced to unmount it, 4801 // so let's disconnect all open file descriptors 4802 4803 mount->unmounting = true; 4804 // prevent new vnodes from being created 4805 4806 fssh_mutex_unlock(&sVnodeMutex); 4807 4808 disconnect_mount_or_vnode_fds(mount, NULL); 4809 disconnectedDescriptors = true; 4810 4811 fssh_mutex_lock(&sVnodeMutex); 4812 } 4813 4814 // we can safely continue, mark all of the vnodes busy and this mount 4815 // structure in unmounting state 4816 mount->unmounting = true; 4817 4818 while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) { 4819 vnode->busy = true; 4820 4821 if (vnode->ref_count == 0) { 4822 // this vnode has been unused before 4823 list_remove_item(&sUnusedVnodeList, vnode); 4824 sUnusedVnodes--; 4825 } 4826 } 4827 4828 // The ref_count of the root node is 2 at this point, see above why this is 4829 mount->root_vnode->ref_count -= 2; 4830 4831 fssh_mutex_unlock(&sVnodeMutex); 4832 4833 fssh_mutex_lock(&sVnodeCoveredByMutex); 4834 mount->covers_vnode->covered_by = NULL; 4835 fssh_mutex_unlock(&sVnodeCoveredByMutex); 4836 put_vnode(mount->covers_vnode); 4837 4838 // Free all vnodes associated with this mount. 4839 // They will be removed from the mount list by free_vnode(), so 4840 // we don't have to do this. 4841 while ((vnode = (struct vnode *)list_get_first_item(&mount->vnodes)) != NULL) { 4842 free_vnode(vnode, false); 4843 } 4844 4845 // remove the mount structure from the hash table 4846 fssh_mutex_lock(&sMountMutex); 4847 hash_remove(sMountsTable, mount); 4848 fssh_mutex_unlock(&sMountMutex); 4849 4850 mountOpLocker.Unlock(); 4851 4852 FS_MOUNT_CALL_NO_PARAMS(mount, unmount); 4853 4854 // release the file system 4855 put_file_system(mount->fs); 4856 4857 free(mount->device_name); 4858 free(mount->fs_name); 4859 free(mount); 4860 4861 return FSSH_B_OK; 4862 } 4863 4864 4865 static fssh_status_t 4866 fs_sync(fssh_dev_t device) 4867 { 4868 struct fs_mount *mount; 4869 fssh_status_t status = get_mount(device, &mount); 4870 if (status < FSSH_B_OK) 4871 return status; 4872 4873 fssh_mutex_lock(&sMountMutex); 4874 4875 if (HAS_FS_MOUNT_CALL(mount, sync)) 4876 status = FS_MOUNT_CALL_NO_PARAMS(mount, sync); 4877 4878 fssh_mutex_unlock(&sMountMutex); 4879 4880 struct vnode *previousVnode = NULL; 4881 while (true) { 4882 // synchronize access to vnode list 4883 fssh_recursive_lock_lock(&mount->rlock); 4884 4885 struct vnode *vnode = (struct vnode *)list_get_next_item(&mount->vnodes, 4886 previousVnode); 4887 4888 fssh_vnode_id id = -1; 4889 if (vnode != NULL) 4890 id = vnode->id; 4891 4892 fssh_recursive_lock_unlock(&mount->rlock); 4893 4894 if (vnode == NULL) 4895 break; 4896 4897 // acquire a reference to the vnode 4898 4899 if (get_vnode(mount->id, id, &vnode, true) == FSSH_B_OK) { 4900 if (previousVnode != NULL) 4901 put_vnode(previousVnode); 4902 4903 if (HAS_FS_CALL(vnode, fsync)) 4904 FS_CALL_NO_PARAMS(vnode, fsync); 4905 4906 // the next vnode might change until we lock the vnode list again, 4907 // but this vnode won't go away since we keep a reference to it. 4908 previousVnode = vnode; 4909 } else { 4910 fssh_dprintf("syncing of mount %d stopped due to vnode %" 4911 FSSH_B_PRIdINO ".\n", (int)mount->id, id); 4912 break; 4913 } 4914 } 4915 4916 if (previousVnode != NULL) 4917 put_vnode(previousVnode); 4918 4919 put_mount(mount); 4920 return status; 4921 } 4922 4923 4924 static fssh_status_t 4925 fs_read_info(fssh_dev_t device, struct fssh_fs_info *info) 4926 { 4927 struct fs_mount *mount; 4928 fssh_status_t status = get_mount(device, &mount); 4929 if (status < FSSH_B_OK) 4930 return status; 4931 4932 fssh_memset(info, 0, sizeof(struct fssh_fs_info)); 4933 4934 if (HAS_FS_MOUNT_CALL(mount, read_fs_info)) 4935 status = FS_MOUNT_CALL(mount, read_fs_info, info); 4936 4937 // fill in info the file system doesn't (have to) know about 4938 if (status == FSSH_B_OK) { 4939 info->dev = mount->id; 4940 info->root = mount->root_vnode->id; 4941 fssh_strlcpy(info->fsh_name, mount->fs_name, sizeof(info->fsh_name)); 4942 if (mount->device_name != NULL) { 4943 fssh_strlcpy(info->device_name, mount->device_name, 4944 sizeof(info->device_name)); 4945 } 4946 } 4947 4948 // if the call is not supported by the file system, there are still 4949 // the parts that we filled out ourselves 4950 4951 put_mount(mount); 4952 return status; 4953 } 4954 4955 4956 static fssh_status_t 4957 fs_write_info(fssh_dev_t device, const struct fssh_fs_info *info, int mask) 4958 { 4959 struct fs_mount *mount; 4960 fssh_status_t status = get_mount(device, &mount); 4961 if (status < FSSH_B_OK) 4962 return status; 4963 4964 if (HAS_FS_MOUNT_CALL(mount, write_fs_info)) 4965 status = FS_MOUNT_CALL(mount, write_fs_info, info, mask); 4966 else 4967 status = FSSH_EROFS; 4968 4969 put_mount(mount); 4970 return status; 4971 } 4972 4973 4974 static fssh_dev_t 4975 fs_next_device(int32_t *_cookie) 4976 { 4977 struct fs_mount *mount = NULL; 4978 fssh_dev_t device = *_cookie; 4979 4980 fssh_mutex_lock(&sMountMutex); 4981 4982 // Since device IDs are assigned sequentially, this algorithm 4983 // does work good enough. It makes sure that the device list 4984 // returned is sorted, and that no device is skipped when an 4985 // already visited device got unmounted. 4986 4987 while (device < sNextMountID) { 4988 mount = find_mount(device++); 4989 if (mount != NULL && mount->volume->private_volume != NULL) 4990 break; 4991 } 4992 4993 *_cookie = device; 4994 4995 if (mount != NULL) 4996 device = mount->id; 4997 else 4998 device = FSSH_B_BAD_VALUE; 4999 5000 fssh_mutex_unlock(&sMountMutex); 5001 5002 return device; 5003 } 5004 5005 5006 static fssh_status_t 5007 get_cwd(char *buffer, fssh_size_t size, bool kernel) 5008 { 5009 // Get current working directory from io context 5010 struct io_context *context = get_current_io_context(kernel); 5011 fssh_status_t status; 5012 5013 FUNCTION(("vfs_get_cwd: buf %p, size %ld\n", buffer, size)); 5014 5015 fssh_mutex_lock(&context->io_mutex); 5016 5017 if (context->cwd) 5018 status = dir_vnode_to_path(context->cwd, buffer, size); 5019 else 5020 status = FSSH_B_ERROR; 5021 5022 fssh_mutex_unlock(&context->io_mutex); 5023 return status; 5024 } 5025 5026 5027 static fssh_status_t 5028 set_cwd(int fd, char *path, bool kernel) 5029 { 5030 struct io_context *context; 5031 struct vnode *vnode = NULL; 5032 struct vnode *oldDirectory; 5033 struct fssh_stat stat; 5034 fssh_status_t status; 5035 5036 FUNCTION(("set_cwd: path = \'%s\'\n", path)); 5037 5038 // Get vnode for passed path, and bail if it failed 5039 status = fd_and_path_to_vnode(fd, path, true, &vnode, NULL, kernel); 5040 if (status < 0) 5041 return status; 5042 5043 status = FS_CALL(vnode, read_stat, &stat); 5044 if (status < 0) 5045 goto err; 5046 5047 if (!FSSH_S_ISDIR(stat.fssh_st_mode)) { 5048 // nope, can't cwd to here 5049 status = FSSH_B_NOT_A_DIRECTORY; 5050 goto err; 5051 } 5052 5053 // Get current io context and lock 5054 context = get_current_io_context(kernel); 5055 fssh_mutex_lock(&context->io_mutex); 5056 5057 // save the old current working directory first 5058 oldDirectory = context->cwd; 5059 context->cwd = vnode; 5060 5061 fssh_mutex_unlock(&context->io_mutex); 5062 5063 if (oldDirectory) 5064 put_vnode(oldDirectory); 5065 5066 return FSSH_B_NO_ERROR; 5067 5068 err: 5069 put_vnode(vnode); 5070 return status; 5071 } 5072 5073 5074 // #pragma mark - 5075 // Calls from within the kernel 5076 5077 5078 fssh_dev_t 5079 _kern_mount(const char *path, const char *device, const char *fsName, 5080 uint32_t flags, const char *args, fssh_size_t argsLength) 5081 { 5082 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5083 if (pathBuffer.InitCheck() != FSSH_B_OK) 5084 return FSSH_B_NO_MEMORY; 5085 5086 return fs_mount(pathBuffer.LockBuffer(), device, fsName, flags, args, true); 5087 } 5088 5089 5090 fssh_status_t 5091 _kern_unmount(const char *path, uint32_t flags) 5092 { 5093 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5094 if (pathBuffer.InitCheck() != FSSH_B_OK) 5095 return FSSH_B_NO_MEMORY; 5096 5097 return fs_unmount(pathBuffer.LockBuffer(), flags, true); 5098 } 5099 5100 5101 fssh_status_t 5102 _kern_read_fs_info(fssh_dev_t device, struct fssh_fs_info *info) 5103 { 5104 if (info == NULL) 5105 return FSSH_B_BAD_VALUE; 5106 5107 return fs_read_info(device, info); 5108 } 5109 5110 5111 fssh_status_t 5112 _kern_write_fs_info(fssh_dev_t device, const struct fssh_fs_info *info, int mask) 5113 { 5114 if (info == NULL) 5115 return FSSH_B_BAD_VALUE; 5116 5117 return fs_write_info(device, info, mask); 5118 } 5119 5120 5121 fssh_status_t 5122 _kern_sync(void) 5123 { 5124 // Note: _kern_sync() is also called from _user_sync() 5125 int32_t cookie = 0; 5126 fssh_dev_t device; 5127 while ((device = fs_next_device(&cookie)) >= 0) { 5128 fssh_status_t status = fs_sync(device); 5129 if (status != FSSH_B_OK && status != FSSH_B_BAD_VALUE) 5130 fssh_dprintf("sync: device %d couldn't sync: %s\n", (int)device, fssh_strerror(status)); 5131 } 5132 5133 return FSSH_B_OK; 5134 } 5135 5136 5137 fssh_dev_t 5138 _kern_next_device(int32_t *_cookie) 5139 { 5140 return fs_next_device(_cookie); 5141 } 5142 5143 5144 int 5145 _kern_open_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name, int openMode, int perms) 5146 { 5147 if (openMode & FSSH_O_CREAT) 5148 return file_create_entry_ref(device, inode, name, openMode, perms, true); 5149 5150 return file_open_entry_ref(device, inode, name, openMode, true); 5151 } 5152 5153 5154 /** \brief Opens a node specified by a FD + path pair. 5155 * 5156 * At least one of \a fd and \a path must be specified. 5157 * If only \a fd is given, the function opens the node identified by this 5158 * FD. If only a path is given, this path is opened. If both are given and 5159 * the path is absolute, \a fd is ignored; a relative path is reckoned off 5160 * of the directory (!) identified by \a fd. 5161 * 5162 * \param fd The FD. May be < 0. 5163 * \param path The absolute or relative path. May be \c NULL. 5164 * \param openMode The open mode. 5165 * \return A FD referring to the newly opened node, or an error code, 5166 * if an error occurs. 5167 */ 5168 5169 int 5170 _kern_open(int fd, const char *path, int openMode, int perms) 5171 { 5172 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5173 if (pathBuffer.InitCheck() != FSSH_B_OK) 5174 return FSSH_B_NO_MEMORY; 5175 5176 if (openMode & FSSH_O_CREAT) 5177 return file_create(fd, pathBuffer.LockBuffer(), openMode, perms, true); 5178 5179 return file_open(fd, pathBuffer.LockBuffer(), openMode, true); 5180 } 5181 5182 5183 /** \brief Opens a directory specified by entry_ref or node_ref. 5184 * 5185 * The supplied name may be \c NULL, in which case directory identified 5186 * by \a device and \a inode will be opened. Otherwise \a device and 5187 * \a inode identify the parent directory of the directory to be opened 5188 * and \a name its entry name. 5189 * 5190 * \param device If \a name is specified the ID of the device the parent 5191 * directory of the directory to be opened resides on, otherwise 5192 * the device of the directory itself. 5193 * \param inode If \a name is specified the node ID of the parent 5194 * directory of the directory to be opened, otherwise node ID of the 5195 * directory itself. 5196 * \param name The entry name of the directory to be opened. If \c NULL, 5197 * the \a device + \a inode pair identify the node to be opened. 5198 * \return The FD of the newly opened directory or an error code, if 5199 * something went wrong. 5200 */ 5201 5202 int 5203 _kern_open_dir_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name) 5204 { 5205 return dir_open_entry_ref(device, inode, name, true); 5206 } 5207 5208 5209 /** \brief Opens a directory specified by a FD + path pair. 5210 * 5211 * At least one of \a fd and \a path must be specified. 5212 * If only \a fd is given, the function opens the directory identified by this 5213 * FD. If only a path is given, this path is opened. If both are given and 5214 * the path is absolute, \a fd is ignored; a relative path is reckoned off 5215 * of the directory (!) identified by \a fd. 5216 * 5217 * \param fd The FD. May be < 0. 5218 * \param path The absolute or relative path. May be \c NULL. 5219 * \return A FD referring to the newly opened directory, or an error code, 5220 * if an error occurs. 5221 */ 5222 5223 int 5224 _kern_open_dir(int fd, const char *path) 5225 { 5226 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5227 if (pathBuffer.InitCheck() != FSSH_B_OK) 5228 return FSSH_B_NO_MEMORY; 5229 5230 return dir_open(fd, pathBuffer.LockBuffer(), true); 5231 } 5232 5233 5234 fssh_status_t 5235 _kern_fcntl(int fd, int op, uint32_t argument) 5236 { 5237 return common_fcntl(fd, op, argument, true); 5238 } 5239 5240 5241 fssh_status_t 5242 _kern_fsync(int fd) 5243 { 5244 return common_sync(fd, true); 5245 } 5246 5247 5248 fssh_status_t 5249 _kern_lock_node(int fd) 5250 { 5251 return common_lock_node(fd, true); 5252 } 5253 5254 5255 fssh_status_t 5256 _kern_unlock_node(int fd) 5257 { 5258 return common_unlock_node(fd, true); 5259 } 5260 5261 5262 fssh_status_t 5263 _kern_create_dir_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name, int perms) 5264 { 5265 return dir_create_entry_ref(device, inode, name, perms, true); 5266 } 5267 5268 5269 /** \brief Creates a directory specified by a FD + path pair. 5270 * 5271 * \a path must always be specified (it contains the name of the new directory 5272 * at least). If only a path is given, this path identifies the location at 5273 * which the directory shall be created. If both \a fd and \a path are given and 5274 * the path is absolute, \a fd is ignored; a relative path is reckoned off 5275 * of the directory (!) identified by \a fd. 5276 * 5277 * \param fd The FD. May be < 0. 5278 * \param path The absolute or relative path. Must not be \c NULL. 5279 * \param perms The access permissions the new directory shall have. 5280 * \return \c FSSH_B_OK, if the directory has been created successfully, another 5281 * error code otherwise. 5282 */ 5283 5284 fssh_status_t 5285 _kern_create_dir(int fd, const char *path, int perms) 5286 { 5287 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5288 if (pathBuffer.InitCheck() != FSSH_B_OK) 5289 return FSSH_B_NO_MEMORY; 5290 5291 return dir_create(fd, pathBuffer.LockBuffer(), perms, true); 5292 } 5293 5294 5295 fssh_status_t 5296 _kern_remove_dir(int fd, const char *path) 5297 { 5298 if (path) { 5299 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5300 if (pathBuffer.InitCheck() != FSSH_B_OK) 5301 return FSSH_B_NO_MEMORY; 5302 5303 return dir_remove(fd, pathBuffer.LockBuffer(), true); 5304 } 5305 5306 return dir_remove(fd, NULL, true); 5307 } 5308 5309 5310 /** \brief Reads the contents of a symlink referred to by a FD + path pair. 5311 * 5312 * At least one of \a fd and \a path must be specified. 5313 * If only \a fd is given, the function the symlink to be read is the node 5314 * identified by this FD. If only a path is given, this path identifies the 5315 * symlink to be read. If both are given and the path is absolute, \a fd is 5316 * ignored; a relative path is reckoned off of the directory (!) identified 5317 * by \a fd. 5318 * If this function fails with FSSH_B_BUFFER_OVERFLOW, the \a _bufferSize pointer 5319 * will still be updated to reflect the required buffer size. 5320 * 5321 * \param fd The FD. May be < 0. 5322 * \param path The absolute or relative path. May be \c NULL. 5323 * \param buffer The buffer into which the contents of the symlink shall be 5324 * written. 5325 * \param _bufferSize A pointer to the size of the supplied buffer. 5326 * \return The length of the link on success or an appropriate error code 5327 */ 5328 5329 fssh_status_t 5330 _kern_read_link(int fd, const char *path, char *buffer, fssh_size_t *_bufferSize) 5331 { 5332 if (path) { 5333 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5334 if (pathBuffer.InitCheck() != FSSH_B_OK) 5335 return FSSH_B_NO_MEMORY; 5336 5337 return common_read_link(fd, pathBuffer.LockBuffer(), 5338 buffer, _bufferSize, true); 5339 } 5340 5341 return common_read_link(fd, NULL, buffer, _bufferSize, true); 5342 } 5343 5344 5345 /** \brief Creates a symlink specified by a FD + path pair. 5346 * 5347 * \a path must always be specified (it contains the name of the new symlink 5348 * at least). If only a path is given, this path identifies the location at 5349 * which the symlink shall be created. If both \a fd and \a path are given and 5350 * the path is absolute, \a fd is ignored; a relative path is reckoned off 5351 * of the directory (!) identified by \a fd. 5352 * 5353 * \param fd The FD. May be < 0. 5354 * \param toPath The absolute or relative path. Must not be \c NULL. 5355 * \param mode The access permissions the new symlink shall have. 5356 * \return \c FSSH_B_OK, if the symlink has been created successfully, another 5357 * error code otherwise. 5358 */ 5359 5360 fssh_status_t 5361 _kern_create_symlink(int fd, const char *path, const char *toPath, int mode) 5362 { 5363 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5364 KPath toPathBuffer(toPath, false, FSSH_B_PATH_NAME_LENGTH + 1); 5365 if (pathBuffer.InitCheck() != FSSH_B_OK || toPathBuffer.InitCheck() != FSSH_B_OK) 5366 return FSSH_B_NO_MEMORY; 5367 5368 char *toBuffer = toPathBuffer.LockBuffer(); 5369 5370 fssh_status_t status = check_path(toBuffer); 5371 if (status < FSSH_B_OK) 5372 return status; 5373 5374 return common_create_symlink(fd, pathBuffer.LockBuffer(), 5375 toBuffer, mode, true); 5376 } 5377 5378 5379 fssh_status_t 5380 _kern_create_link(const char *path, const char *toPath) 5381 { 5382 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5383 KPath toPathBuffer(toPath, false, FSSH_B_PATH_NAME_LENGTH + 1); 5384 if (pathBuffer.InitCheck() != FSSH_B_OK || toPathBuffer.InitCheck() != FSSH_B_OK) 5385 return FSSH_B_NO_MEMORY; 5386 5387 return common_create_link(pathBuffer.LockBuffer(), 5388 toPathBuffer.LockBuffer(), true); 5389 } 5390 5391 5392 /** \brief Removes an entry specified by a FD + path pair from its directory. 5393 * 5394 * \a path must always be specified (it contains at least the name of the entry 5395 * to be deleted). If only a path is given, this path identifies the entry 5396 * directly. If both \a fd and \a path are given and the path is absolute, 5397 * \a fd is ignored; a relative path is reckoned off of the directory (!) 5398 * identified by \a fd. 5399 * 5400 * \param fd The FD. May be < 0. 5401 * \param path The absolute or relative path. Must not be \c NULL. 5402 * \return \c FSSH_B_OK, if the entry has been removed successfully, another 5403 * error code otherwise. 5404 */ 5405 5406 fssh_status_t 5407 _kern_unlink(int fd, const char *path) 5408 { 5409 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5410 if (pathBuffer.InitCheck() != FSSH_B_OK) 5411 return FSSH_B_NO_MEMORY; 5412 5413 return common_unlink(fd, pathBuffer.LockBuffer(), true); 5414 } 5415 5416 5417 /** \brief Moves an entry specified by a FD + path pair to a an entry specified 5418 * by another FD + path pair. 5419 * 5420 * \a oldPath and \a newPath must always be specified (they contain at least 5421 * the name of the entry). If only a path is given, this path identifies the 5422 * entry directly. If both a FD and a path are given and the path is absolute, 5423 * the FD is ignored; a relative path is reckoned off of the directory (!) 5424 * identified by the respective FD. 5425 * 5426 * \param oldFD The FD of the old location. May be < 0. 5427 * \param oldPath The absolute or relative path of the old location. Must not 5428 * be \c NULL. 5429 * \param newFD The FD of the new location. May be < 0. 5430 * \param newPath The absolute or relative path of the new location. Must not 5431 * be \c NULL. 5432 * \return \c FSSH_B_OK, if the entry has been moved successfully, another 5433 * error code otherwise. 5434 */ 5435 5436 fssh_status_t 5437 _kern_rename(int oldFD, const char *oldPath, int newFD, const char *newPath) 5438 { 5439 KPath oldPathBuffer(oldPath, false, FSSH_B_PATH_NAME_LENGTH + 1); 5440 KPath newPathBuffer(newPath, false, FSSH_B_PATH_NAME_LENGTH + 1); 5441 if (oldPathBuffer.InitCheck() != FSSH_B_OK || newPathBuffer.InitCheck() != FSSH_B_OK) 5442 return FSSH_B_NO_MEMORY; 5443 5444 return common_rename(oldFD, oldPathBuffer.LockBuffer(), 5445 newFD, newPathBuffer.LockBuffer(), true); 5446 } 5447 5448 5449 fssh_status_t 5450 _kern_access(const char *path, int mode) 5451 { 5452 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5453 if (pathBuffer.InitCheck() != FSSH_B_OK) 5454 return FSSH_B_NO_MEMORY; 5455 5456 return common_access(pathBuffer.LockBuffer(), mode, true); 5457 } 5458 5459 5460 /** \brief Reads stat data of an entity specified by a FD + path pair. 5461 * 5462 * If only \a fd is given, the stat operation associated with the type 5463 * of the FD (node, attr, attr dir etc.) is performed. If only \a path is 5464 * given, this path identifies the entry for whose node to retrieve the 5465 * stat data. If both \a fd and \a path are given and the path is absolute, 5466 * \a fd is ignored; a relative path is reckoned off of the directory (!) 5467 * identified by \a fd and specifies the entry whose stat data shall be 5468 * retrieved. 5469 * 5470 * \param fd The FD. May be < 0. 5471 * \param path The absolute or relative path. Must not be \c NULL. 5472 * \param traverseLeafLink If \a path is given, \c true specifies that the 5473 * function shall not stick to symlinks, but traverse them. 5474 * \param stat The buffer the stat data shall be written into. 5475 * \param statSize The size of the supplied stat buffer. 5476 * \return \c FSSH_B_OK, if the the stat data have been read successfully, another 5477 * error code otherwise. 5478 */ 5479 5480 fssh_status_t 5481 _kern_read_stat(int fd, const char *path, bool traverseLeafLink, 5482 fssh_struct_stat *stat, fssh_size_t statSize) 5483 { 5484 fssh_struct_stat completeStat; 5485 fssh_struct_stat *originalStat = NULL; 5486 fssh_status_t status; 5487 5488 if (statSize > sizeof(fssh_struct_stat)) 5489 return FSSH_B_BAD_VALUE; 5490 5491 // this supports different stat extensions 5492 if (statSize < sizeof(fssh_struct_stat)) { 5493 originalStat = stat; 5494 stat = &completeStat; 5495 } 5496 5497 if (path) { 5498 // path given: get the stat of the node referred to by (fd, path) 5499 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5500 if (pathBuffer.InitCheck() != FSSH_B_OK) 5501 return FSSH_B_NO_MEMORY; 5502 5503 status = common_path_read_stat(fd, pathBuffer.LockBuffer(), 5504 traverseLeafLink, stat, true); 5505 } else { 5506 // no path given: get the FD and use the FD operation 5507 struct file_descriptor *descriptor 5508 = get_fd(get_current_io_context(true), fd); 5509 if (descriptor == NULL) 5510 return FSSH_B_FILE_ERROR; 5511 5512 if (descriptor->ops->fd_read_stat) 5513 status = descriptor->ops->fd_read_stat(descriptor, stat); 5514 else 5515 status = FSSH_EOPNOTSUPP; 5516 5517 put_fd(descriptor); 5518 } 5519 5520 if (status == FSSH_B_OK && originalStat != NULL) 5521 fssh_memcpy(originalStat, stat, statSize); 5522 5523 return status; 5524 } 5525 5526 5527 /** \brief Writes stat data of an entity specified by a FD + path pair. 5528 * 5529 * If only \a fd is given, the stat operation associated with the type 5530 * of the FD (node, attr, attr dir etc.) is performed. If only \a path is 5531 * given, this path identifies the entry for whose node to write the 5532 * stat data. If both \a fd and \a path are given and the path is absolute, 5533 * \a fd is ignored; a relative path is reckoned off of the directory (!) 5534 * identified by \a fd and specifies the entry whose stat data shall be 5535 * written. 5536 * 5537 * \param fd The FD. May be < 0. 5538 * \param path The absolute or relative path. Must not be \c NULL. 5539 * \param traverseLeafLink If \a path is given, \c true specifies that the 5540 * function shall not stick to symlinks, but traverse them. 5541 * \param stat The buffer containing the stat data to be written. 5542 * \param statSize The size of the supplied stat buffer. 5543 * \param statMask A mask specifying which parts of the stat data shall be 5544 * written. 5545 * \return \c FSSH_B_OK, if the the stat data have been written successfully, 5546 * another error code otherwise. 5547 */ 5548 5549 fssh_status_t 5550 _kern_write_stat(int fd, const char *path, bool traverseLeafLink, 5551 const fssh_struct_stat *stat, fssh_size_t statSize, int statMask) 5552 { 5553 fssh_struct_stat completeStat; 5554 5555 if (statSize > sizeof(fssh_struct_stat)) 5556 return FSSH_B_BAD_VALUE; 5557 5558 // this supports different stat extensions 5559 if (statSize < sizeof(fssh_struct_stat)) { 5560 fssh_memset((uint8_t *)&completeStat + statSize, 0, sizeof(fssh_struct_stat) - statSize); 5561 fssh_memcpy(&completeStat, stat, statSize); 5562 stat = &completeStat; 5563 } 5564 5565 fssh_status_t status; 5566 5567 if (path) { 5568 // path given: write the stat of the node referred to by (fd, path) 5569 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5570 if (pathBuffer.InitCheck() != FSSH_B_OK) 5571 return FSSH_B_NO_MEMORY; 5572 5573 status = common_path_write_stat(fd, pathBuffer.LockBuffer(), 5574 traverseLeafLink, stat, statMask, true); 5575 } else { 5576 // no path given: get the FD and use the FD operation 5577 struct file_descriptor *descriptor 5578 = get_fd(get_current_io_context(true), fd); 5579 if (descriptor == NULL) 5580 return FSSH_B_FILE_ERROR; 5581 5582 if (descriptor->ops->fd_write_stat) 5583 status = descriptor->ops->fd_write_stat(descriptor, stat, statMask); 5584 else 5585 status = FSSH_EOPNOTSUPP; 5586 5587 put_fd(descriptor); 5588 } 5589 5590 return status; 5591 } 5592 5593 5594 int 5595 _kern_open_attr_dir(int fd, const char *path) 5596 { 5597 KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); 5598 if (pathBuffer.InitCheck() != FSSH_B_OK) 5599 return FSSH_B_NO_MEMORY; 5600 5601 if (path != NULL) 5602 pathBuffer.SetTo(path); 5603 5604 return attr_dir_open(fd, path ? pathBuffer.LockBuffer() : NULL, true); 5605 } 5606 5607 5608 int 5609 _kern_create_attr(int fd, const char *name, uint32_t type, int openMode) 5610 { 5611 return attr_create(fd, name, type, openMode, true); 5612 } 5613 5614 5615 int 5616 _kern_open_attr(int fd, const char *name, int openMode) 5617 { 5618 return attr_open(fd, name, openMode, true); 5619 } 5620 5621 5622 fssh_status_t 5623 _kern_remove_attr(int fd, const char *name) 5624 { 5625 return attr_remove(fd, name, true); 5626 } 5627 5628 5629 fssh_status_t 5630 _kern_rename_attr(int fromFile, const char *fromName, int toFile, const char *toName) 5631 { 5632 return attr_rename(fromFile, fromName, toFile, toName, true); 5633 } 5634 5635 5636 int 5637 _kern_open_index_dir(fssh_dev_t device) 5638 { 5639 return index_dir_open(device, true); 5640 } 5641 5642 5643 fssh_status_t 5644 _kern_create_index(fssh_dev_t device, const char *name, uint32_t type, uint32_t flags) 5645 { 5646 return index_create(device, name, type, flags, true); 5647 } 5648 5649 5650 fssh_status_t 5651 _kern_read_index_stat(fssh_dev_t device, const char *name, fssh_struct_stat *stat) 5652 { 5653 return index_name_read_stat(device, name, stat, true); 5654 } 5655 5656 5657 fssh_status_t 5658 _kern_remove_index(fssh_dev_t device, const char *name) 5659 { 5660 return index_remove(device, name, true); 5661 } 5662 5663 5664 fssh_status_t 5665 _kern_getcwd(char *buffer, fssh_size_t size) 5666 { 5667 TRACE(("_kern_getcwd: buf %p, %ld\n", buffer, size)); 5668 5669 // Call vfs to get current working directory 5670 return get_cwd(buffer, size, true); 5671 } 5672 5673 5674 fssh_status_t 5675 _kern_setcwd(int fd, const char *path) 5676 { 5677 KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); 5678 if (pathBuffer.InitCheck() != FSSH_B_OK) 5679 return FSSH_B_NO_MEMORY; 5680 5681 if (path != NULL) 5682 pathBuffer.SetTo(path); 5683 5684 return set_cwd(fd, path != NULL ? pathBuffer.LockBuffer() : NULL, true); 5685 } 5686 5687 5688 fssh_status_t 5689 _kern_initialize_volume(const char* fsName, const char *partition, 5690 const char *name, const char *parameters) 5691 { 5692 if (!fsName || ! partition) 5693 return FSSH_B_BAD_VALUE; 5694 5695 // The partition argument should point to a real file/device. 5696 5697 // open partition 5698 int fd = fssh_open(partition, FSSH_O_RDWR); 5699 if (fd < 0) 5700 return fssh_errno; 5701 5702 // get the file system module 5703 fssh_file_system_module_info* fsModule = get_file_system(fsName); 5704 if (fsModule == NULL) { 5705 fssh_close(fd); 5706 return FSSH_ENODEV; 5707 } 5708 5709 // initialize 5710 fssh_status_t status; 5711 if (fsModule->initialize) { 5712 status = (*fsModule->initialize)(fd, -1, name, parameters, 0, -1); 5713 // We've got no partition or job IDs -- the FS will hopefully 5714 // ignore that. 5715 // TODO: Get the actual size! 5716 } else 5717 status = FSSH_B_NOT_SUPPORTED; 5718 5719 // put the file system module, close partition 5720 put_file_system(fsModule); 5721 fssh_close(fd); 5722 5723 return status; 5724 } 5725 5726 5727 fssh_status_t 5728 _kern_entry_ref_to_path(fssh_dev_t device, fssh_ino_t inode, const char *leaf, 5729 char* path, fssh_size_t pathLength) 5730 { 5731 return vfs_entry_ref_to_path(device, inode, leaf, true, path, pathLength); 5732 } 5733 5734 5735 int 5736 _kern_open_query(fssh_dev_t device, const char *query, fssh_size_t queryLength, 5737 uint32_t flags, fssh_port_id port, int32_t token) 5738 { 5739 return query_open(device, query, flags, port, token, false); 5740 } 5741 5742 5743 } // namespace FSShell 5744 5745 5746 #include "vfs_request_io.cpp" 5747