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