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