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